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 isOpen() const { 48 return opens_.length > 0; 49 } 50 51 auto open() const { 52 if (opens_.length == 0) 53 throw new ContextException(loc, "no tag is open"); 54 return opens_[$ - 1]; 55 } 56 57 auto close() { 58 if (opens_.length == 0) 59 throw new ContextException(loc, "unexpected '/' tag; no tag is open"); 60 auto tag = opens_[$ - 1]; 61 opens_ = opens_[0..$-1]; 62 return tag; 63 } 64 65 auto expectClosed() { 66 if (opens_.length) { 67 auto lastOpen = opens_[$-1]; 68 throw new ContextException(lastOpen.loc, format("unexpected end of file; missing '/' tag for '%s'", lastOpen.tag)); 69 } 70 } 71 72 auto expectOpen(string tag, string related) { 73 if (!opens_.length || opens_[$-1].tag != tag) 74 throw new ContextException(loc, format("unexpected '%s' tag; no '%s' tag in open", related, tag)); 75 } 76 77 Source source; 78 SourceLoc loc; 79 size_t cursor; 80 81 static struct Open { 82 SourceLoc loc; 83 size_t cursor; 84 string tag; 85 string content; 86 } 87 88 private Open[] opens_; 89 }