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