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 }