1 module context;
2 
3 
4 import std.string;
5 
6 import tags;
7 import utils;
8 
9 
10 class ParserException : Throwable {
11 	this(string msg, Context context) {
12 		super(msg);
13 		this.context = context;
14 	}
15 
16 	Context context;
17 }
18 
19 
20 class Context {
21 	this(string sourceName, string source, size_t line = 1, size_t cursor = 0) {
22 		this.sourceName = sourceName;
23 		this.source = source;
24 		this.line = line;
25 		this.cursor = cursor;
26 	}
27 
28 	auto advance(size_t offset) {
29 		line += source[cursor..cursor + offset].countLines;
30 		cursor += offset;
31 		return this;
32 	}
33 
34 	auto remaining() {
35 		return source[cursor..$];
36 	}
37 
38 	auto defining() {
39 		return defineCount > 0;
40 	}
41 
42 	auto tagOpen(string tag, string content) {
43 		tagOpens ~= TagOpen(line, cursor, tag, content);
44 		if (tag == Tag.Define)
45 			++defineCount;
46 	}
47 
48 	auto tagClose() {
49 		if (tagOpens.length == 0)
50 			throw new ParserException("unexpected '/' tag; no tag is open", this);
51 		auto tag = tagOpens[$-1];
52 		if (tag.tag == Tag.Define)
53 			--defineCount;
54 		tagOpens = tagOpens[0..$-1];
55 		return tag;
56 	}
57 
58 	auto expectTagClosed() {
59 		if (tagOpens.length) {
60 			auto lastOpen = tagOpens[$-1];
61 			throw new ParserException("unexpected EOF; missing '/' tag for '" ~ lastOpen.tag ~ "'", new Context(sourceName, source, lastOpen.line, lastOpen.cursor));
62 		}
63 	}
64 
65 	auto expectTagOpen(string tag, string related) {
66 		if (!tagOpens.length || tagOpens[$-1].tag != tag)
67 			throw new ParserException("unexpected '" ~ related ~ "' tag; no '" ~ tag ~ "' tag in open", this);
68 	}
69 
70 	string sourceName;
71 	string source;
72 	size_t line;
73 	size_t cursor;
74 	size_t defineCount;
75 
76 	struct TagOpen {
77 		size_t line;
78 		size_t cursor;
79 		string tag;
80 		string content;
81 	}
82 
83 	private TagOpen[] tagOpens;
84 }