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 }