-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathconditionParser.mts
118 lines (92 loc) · 2.66 KB
/
conditionParser.mts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
const thisFile = import.meta.url;
const waitOneTick = await Promise.resolve();
export function parse(
source: string
): {
test: string;
consequent: string | null;
alternate: string | null;
hash: string | null;
} {
const PROTOCOL = "condition:";
if (!source.startsWith(PROTOCOL, 0)) {
throw new Error(`Expected 'condition:' at index 0 (${source})`);
}
let pos = PROTOCOL.length;
skipWs();
const test = eatRegExp(/[\w-]+/y);
if (!test) {
throw new Error(`Expected an identifier at index ${pos} (${source})`);
}
skipWs();
expect("?");
skipWs();
let consequent = null;
if (source[pos] === "(") {
consequent = eatParenthesized().trim() || null;
skipWs();
} else if (source[pos] !== ":") {
consequent = eatUntil(":").trimRight() || null;
}
expect(":");
skipWs();
let alternate = null;
if (pos < source.length) {
if (source[pos] === "(") {
alternate = eatParenthesized().trim() || null;
skipWs();
} else if (source[pos] !== ":") {
alternate = eatUntil("#").trimRight() || null;
}
}
let hash = null;
if (pos < source.length && source[pos] === "#") {
pos++;
hash = eatRegExp(/\w+/y);
skipWs();
}
if (pos !== source.length) {
throw new Error(`Unexpected '${source[pos]}' at index ${pos} (${source})`);
}
return { test, consequent, alternate, hash };
function expect(ch: string) {
if (source[pos] !== ch) {
throw new Error(`Expected '${ch}' at index ${pos} (${source})`);
}
pos++;
}
function skipWs() {
eatRegExp(/\s*/y);
}
function eatRegExp(re: RegExp) {
re.lastIndex = pos;
const match = re.exec(source);
if (!match) return null;
pos += match[0].length;
return match[0];
}
function eatUntil(end: string) {
const start = pos;
pos = source.indexOf(end, start);
if (pos === -1) pos = source.length;
return source.slice(start, pos);
}
function eatParenthesized() {
expect("(");
let depth = 1;
let contents = "";
while (depth) {
if (pos === source.length) {
throw new Error(`Expected ')' at index ${pos} (${source})`);
}
const ch = source[pos];
if (ch === "(") depth++;
if (ch === ")") depth--;
if (ch !== ")" || depth > 0) {
contents += ch;
}
pos++;
}
return contents;
}
}