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 }