1 module vayne.source.context; 2 3 4 import std.format; 5 6 import vayne.source.source; 7 import vayne.source.token; 8 9 10 class ContextException : Exception { 11 this(SourceLoc loc, string msg) { 12 super(msg); 13 14 this.loc = loc; 15 } 16 17 SourceLoc loc; 18 } 19 20 21 class Context { 22 this(Source source) { 23 this.source = source; 24 25 loc = SourceLoc(source.id, 1, 0); 26 } 27 28 this(Source source, SourceLoc loc) { 29 this.source = source; 30 this.loc = loc; 31 } 32 33 auto advance(size_t offset) { 34 loc.line += source.buffer[cursor..cursor + offset].countLines; 35 cursor += offset; 36 return this; 37 } 38 39 auto remaining() { 40 return source.buffer[cursor..$]; 41 } 42 43 void open(string tag, string content) { 44 opens_ ~= Open(loc, cursor, tag, content); 45 } 46 47 auto open() const { 48 if (opens_.length == 0) 49 throw new ContextException(loc, "unexpected '/' tag; no tag is open"); 50 return opens_[$ - 1]; 51 } 52 53 auto close() { 54 if (opens_.length == 0) 55 throw new ContextException(loc, "unexpected '/' tag; no tag is open"); 56 auto tag = opens_[$ - 1]; 57 opens_ = opens_[0..$-1]; 58 return tag; 59 } 60 61 auto expectClosed() { 62 if (opens_.length) { 63 auto lastOpen = opens_[$-1]; 64 throw new ContextException(lastOpen.loc, format("unexpected end of file; missing '/' tag for '%s'", lastOpen.tag)); 65 } 66 } 67 68 auto expectOpen(string tag, string related) { 69 if (!opens_.length || opens_[$-1].tag != tag) 70 throw new ContextException(loc, format("unexpected '%s' tag; no '%s' tag in open", related, tag)); 71 } 72 73 Source source; 74 SourceLoc loc; 75 size_t cursor; 76 77 static struct Open { 78 SourceLoc loc; 79 size_t cursor; 80 string tag; 81 string content; 82 } 83 84 private Open[] opens_; 85 }