Merge "fix oauth code"
[ccsdk/features.git] / sdnr / wt-odlux / odlux / apps / configurationApp / src / yang / whenParser.ts
1 enum WhenTokenType {
2   AND = 'AND',
3   OR = 'OR',
4   NOT = 'NOT',
5   EQUALS = 'EQUALS',
6   NOT_EQUALS = 'NOT_EQUALS',
7   COMMA = 'COMMA',
8   STRING = 'STRING',
9   FUNCTION = 'FUNCTION',
10   IDENTIFIER = 'IDENTIFIER',
11   OPEN_PAREN = 'OPEN_PAREN',
12   CLOSE_PAREN = 'CLOSE_PAREN',
13   EXPRESSION = 'EXPRESSION',
14 }
15
16 type Token = {
17   type: WhenTokenType;
18   value: string;
19 };
20
21 const isAlpha = (char: string) => {
22   if (!char) return false;
23   const code = char.charCodeAt(0);
24   return (code >= 65 && code <= 90) || (code >= 97 && code <= 122);
25 };
26
27 const isAlphaNumeric = (char: string) => {
28   if (!char) return false;
29   const code = char.charCodeAt(0);
30   return (
31     isAlpha(char) ||
32     (code >= 48 && code <= 57) ||
33     code === 95 || // underscore
34     code === 45 || // hyphen
35     code === 47 || // slash
36     code === 58 || // colon
37     code === 46 // dot
38   );
39 };
40
41 const isOperator = (char: string) => {
42   if (!char) return false;
43   const code = char.charCodeAt(0);
44   return code === 33 || code === 38 || code === 124 || code === 61;
45 };
46
47 const lex = (input: string) : Token[] => {
48   let tokens = [] as any[];
49   let current = 0;
50
51   while (current < input.length) {
52     let char = input[current];
53
54     if (char === ' ') {
55       current++;
56       continue;
57     }
58
59     if (char === '(') {
60       tokens.push({ type: WhenTokenType.OPEN_PAREN, value: char });
61       current++;
62       continue;
63     }
64
65     if (char === ')') {
66       tokens.push({ type: WhenTokenType.CLOSE_PAREN, value: char });
67       current++;
68       continue;
69     }
70
71     if (char === '=') {
72       tokens.push({ type: WhenTokenType.EQUALS, value: char });
73       current++;
74       continue;
75     }
76
77     if (char === ',') {
78       tokens.push({ type: WhenTokenType.COMMA, value: char });
79       current++;
80       continue;
81     }
82
83     if (char === '\"' || char === '\'') {
84       let value = '';
85       let start = current;
86       current++;
87
88       while (current < input.length) {
89         let innerChar = input[current];
90         if (innerChar === '\\') {
91           value += input[current] + input[current + 1];
92           current += 2;
93         } else if (innerChar === input[start]) {
94           current++;
95           break;
96         } else {
97           value += innerChar;
98           current++;
99         }
100       }
101
102       tokens.push({ type: WhenTokenType.STRING, value });
103       continue;
104     }
105
106     if (isAlpha(char)) {
107       let value = '';
108       while (isAlpha(char)) {
109         value += char;
110         char = input[++current];
111       }
112
113       switch (value) {
114         case 'and':
115           tokens.push({ type: WhenTokenType.AND });
116           break;
117         case 'or':
118           tokens.push({ type: WhenTokenType.OR });
119           break;
120         case 'not':
121           tokens.push({ type: WhenTokenType.NOT });
122           break;
123         case 'eq':
124           tokens.push({ type: WhenTokenType.EQUALS });
125           break;
126         default:
127           while (isAlphaNumeric(char)) {
128             value += char;
129             char = input[++current];
130           }
131           tokens.push({ type: WhenTokenType.IDENTIFIER, value });
132       }
133
134       continue;
135     }
136     
137     if (isAlphaNumeric(char)) {
138       let value = '';
139       while (isAlphaNumeric(char)) {
140         value += char;
141         char = input[++current];
142       }
143
144       tokens.push({ type: WhenTokenType.IDENTIFIER, value });
145       continue;
146     }
147
148     if (isOperator(char)) {
149       let value = '';
150       while (isOperator(char)) {
151         value += char;
152         char = input[++current];
153       }
154
155       switch (value) {
156         case '&&':
157           tokens.push({ type: WhenTokenType.AND });
158           break;
159         case '||':
160           tokens.push({ type: WhenTokenType.OR });
161           break;
162         case '!':
163           tokens.push({ type: WhenTokenType.NOT });
164           break;
165         case '==':
166           tokens.push({ type: WhenTokenType.EQUALS });
167           break;
168         case '!=':
169           tokens.push({ type: WhenTokenType.NOT_EQUALS });
170           break;  
171         default:
172           throw new TypeError(`I don't know what this operator is: ${value}`);
173       }
174       continue;
175     }
176     
177     throw new TypeError(`I don't know what this character is: ${char}`);
178   }
179   return tokens;
180 };
181
182 type WhenAST = {
183   type: WhenTokenType;
184   left?: WhenAST;
185   right?: WhenAST;
186   value?: string | WhenAST;
187   name?: string;
188   args?: WhenAST[];
189 };
190
191 const precedence : { [index: string] : number } = {
192   'EQUALS': 4,
193   'NOT': 3,
194   'AND': 2,
195   'OR': 1,
196 };
197
198 const parseWhen = (whenExpression: string) => {
199   const tokens = lex(whenExpression);
200   let current = 0;
201
202   const walk = (precedenceLevel = 0) : WhenAST => {
203     let token = tokens[current];
204     let node: WhenAST | null = null;
205
206     if (token.type === WhenTokenType.OPEN_PAREN) {
207       token = tokens[++current];
208       let innerNode: WhenAST = { type: WhenTokenType.EXPRESSION, value: walk() };
209       token = tokens[current];
210
211       while (token.type !== WhenTokenType.CLOSE_PAREN) {
212         innerNode = {
213           type: token.type,
214           value: token.value,
215           left: innerNode,
216           right: walk(),
217         };
218         token = tokens[current];
219       }
220       current++;
221       return innerNode;
222     }
223
224     if (token.type === WhenTokenType.STRING ) {
225       current++;
226       node = { type: token.type, value: token.value };
227     }
228
229     if (token.type === WhenTokenType.NOT) {
230       token = tokens[++current];
231       node = { type: WhenTokenType.NOT, value: token.value, right: walk() };
232     }
233
234     if (token.type === WhenTokenType.IDENTIFIER) {
235       const nextToken = tokens[current + 1];
236       if (nextToken.type === WhenTokenType.OPEN_PAREN) {
237         let name = token.value;
238         token = tokens[++current];
239
240         let args = [];
241         token = tokens[++current];
242
243         while (token.type !== WhenTokenType.CLOSE_PAREN) {
244           if (token.type === WhenTokenType.COMMA) {
245             current++;
246           } else {
247             args.push(walk());
248           }
249           token = tokens[current];
250         }
251
252         current++;
253         node = { type: WhenTokenType.FUNCTION, name, args };
254       } else {
255         current++;
256         node = { type: WhenTokenType.IDENTIFIER, value: token.value };
257       }
258     }   
259
260     if (!node) throw new TypeError('Unexpected token: ' + token.type);
261
262     token = tokens[current];
263     while (current < tokens.length && precedence[token.type] >= precedenceLevel) {
264       console.log(current, tokens[current], tokens[current].type, precedenceLevel, precedence[token.type]);
265       token = tokens[current];
266       if (token.type === WhenTokenType.EQUALS || token.type === WhenTokenType.AND || token.type === WhenTokenType.OR) {
267         current++;
268         node = {
269           type: token.type,
270           left: node,
271           right: walk(precedence[token.type]),
272         };
273       } else {
274         break;
275       }
276     }
277
278     return node;
279    
280   };
281
282   return walk();
283 };
284
285 export {
286   parseWhen,
287   WhenAST,
288   WhenTokenType,
289 };