9 IDENTIFIER = 'IDENTIFIER',
10 OPEN_PAREN = 'OPEN_PAREN',
11 CLOSE_PAREN = 'CLOSE_PAREN',
12 EXPRESSION = 'EXPRESSION',
20 const isAlpha = (char: string) => /[a-z]/i.test(char);
22 const isAlphaNumeric = (char: string) => /[A-Za-z0-9_\-/:\.]/i.test(char);
24 const lex = (input: string) : Token[] => {
25 let tokens = [] as any[];
28 while (current < input.length) {
29 let char = input[current];
37 tokens.push({ type: WhenTokenType.OPEN_PAREN, value: char });
43 tokens.push({ type: WhenTokenType.CLOSE_PAREN, value: char });
49 tokens.push({ type: WhenTokenType.EQUALS, value: char });
55 tokens.push({ type: WhenTokenType.COMMA, value: char });
60 if (char === '\"' || char === '\'') {
65 while (current < input.length) {
66 let innerChar = input[current];
67 if (innerChar === '\\') {
68 value += input[current] + input[current + 1];
70 } else if (innerChar === input[start]) {
79 tokens.push({ type: WhenTokenType.STRING, value });
85 while (isAlpha(char)) {
87 char = input[++current];
92 tokens.push({ type: WhenTokenType.AND });
95 tokens.push({ type: WhenTokenType.OR });
98 tokens.push({ type: WhenTokenType.NOT });
101 tokens.push({ type: WhenTokenType.EQUALS });
104 while (isAlphaNumeric(char)) {
106 char = input[++current];
108 tokens.push({ type: WhenTokenType.IDENTIFIER, value });
113 if (isAlphaNumeric(char)) {
115 while (isAlphaNumeric(char)) {
117 char = input[++current];
120 tokens.push({ type: WhenTokenType.IDENTIFIER, value });
123 throw new TypeError(`I don't know what this character is: ${char}`);
132 value?: string | WhenAST;
137 const precedence : { [index: string] : number } = {
144 const parseWhen = (whenExpression: string) => {
145 const tokens = lex(whenExpression);
148 const walk = (precedenceLevel = 0) : WhenAST => {
149 let token = tokens[current];
150 let node: WhenAST | null = null;
152 if (token.type === WhenTokenType.OPEN_PAREN) {
153 token = tokens[++current];
154 let innerNode: WhenAST = { type: WhenTokenType.EXPRESSION, value: walk() };
155 token = tokens[current];
157 while (token.type !== WhenTokenType.CLOSE_PAREN) {
164 token = tokens[current];
170 if (token.type === WhenTokenType.STRING ) {
172 node = { type: token.type, value: token.value };
175 if (token.type === WhenTokenType.NOT) {
176 token = tokens[++current];
177 node = { type: WhenTokenType.NOT, value: token.value, right: walk() };
180 if (token.type === WhenTokenType.IDENTIFIER) {
181 const nextToken = tokens[current + 1];
182 if (nextToken.type === WhenTokenType.OPEN_PAREN) {
183 let name = token.value;
184 token = tokens[++current];
187 token = tokens[++current];
189 while (token.type !== WhenTokenType.CLOSE_PAREN) {
190 if (token.type === WhenTokenType.COMMA) {
195 token = tokens[current];
199 node = { type: WhenTokenType.FUNCTION, name, args };
202 node = { type: WhenTokenType.IDENTIFIER, value: token.value };
206 if (!node) throw new TypeError('Unexpected token: ' + token.type);
208 token = tokens[current];
209 while (current < tokens.length && precedence[token.type] >= precedenceLevel) {
210 console.log(current, tokens[current], tokens[current].type, precedenceLevel, precedence[token.type]);
211 token = tokens[current];
212 if (token.type === WhenTokenType.EQUALS || token.type === WhenTokenType.AND || token.type === WhenTokenType.OR) {
217 right: walk(precedence[token.type]),