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   COMMA = 'COMMA',
7   STRING = 'STRING',
8   FUNCTION = 'FUNCTION',
9   IDENTIFIER = 'IDENTIFIER',
10   OPEN_PAREN = 'OPEN_PAREN',
11   CLOSE_PAREN = 'CLOSE_PAREN',
12   EXPRESSION = 'EXPRESSION',
13 }
14
15 type Token = {
16   type: WhenTokenType;
17   value: string;
18 };
19
20 const isAlpha = (char: string) => /[a-z]/i.test(char);
21
22 const isAlphaNumeric = (char: string) => /[A-Za-z0-9_\-/:\.]/i.test(char);
23
24 const lex = (input: string) : Token[] => {
25   let tokens = [] as any[];
26   let current = 0;
27
28   while (current < input.length) {
29     let char = input[current];
30
31     if (char === ' ') {
32       current++;
33       continue;
34     }
35
36     if (char === '(') {
37       tokens.push({ type: WhenTokenType.OPEN_PAREN, value: char });
38       current++;
39       continue;
40     }
41
42     if (char === ')') {
43       tokens.push({ type: WhenTokenType.CLOSE_PAREN, value: char });
44       current++;
45       continue;
46     }
47
48     if (char === '=') {
49       tokens.push({ type: WhenTokenType.EQUALS, value: char });
50       current++;
51       continue;
52     }
53
54     if (char === ',') {
55       tokens.push({ type: WhenTokenType.COMMA, value: char });
56       current++;
57       continue;
58     }
59
60     if (char === '\"' || char === '\'') {
61       let value = '';
62       let start = current;
63       current++;
64
65       while (current < input.length) {
66         let innerChar = input[current];
67         if (innerChar === '\\') {
68           value += input[current] + input[current + 1];
69           current += 2;
70         } else if (innerChar === input[start]) {
71           current++;
72           break;
73         } else {
74           value += innerChar;
75           current++;
76         }
77       }
78
79       tokens.push({ type: WhenTokenType.STRING, value });
80       continue;
81     }
82
83     if (isAlpha(char)) {
84       let value = '';
85       while (isAlpha(char)) {
86         value += char;
87         char = input[++current];
88       }
89
90       switch (value) {
91         case 'and':
92           tokens.push({ type: WhenTokenType.AND });
93           break;
94         case 'or':
95           tokens.push({ type: WhenTokenType.OR });
96           break;
97         case 'not':
98           tokens.push({ type: WhenTokenType.NOT });
99           break;
100         case 'eq':
101           tokens.push({ type: WhenTokenType.EQUALS });
102           break;
103         default:
104           while (isAlphaNumeric(char)) {
105             value += char;
106             char = input[++current];
107           }
108           tokens.push({ type: WhenTokenType.IDENTIFIER, value });
109       }
110
111       continue;
112     }
113     if (isAlphaNumeric(char)) {
114       let value = '';
115       while (isAlphaNumeric(char)) {
116         value += char;
117         char = input[++current];
118       }
119
120       tokens.push({ type: WhenTokenType.IDENTIFIER, value });
121       continue;
122     }
123     throw new TypeError(`I don't know what this character is: ${char}`);
124   }
125   return tokens;
126 };
127
128 type WhenAST = {
129   type: WhenTokenType;
130   left?: WhenAST;
131   right?: WhenAST;
132   value?: string | WhenAST;
133   name?: string;
134   args?: WhenAST[];
135 };
136
137 const precedence : { [index: string] : number } = {
138   'EQUALS': 4,
139   'NOT': 3,
140   'AND': 2,
141   'OR': 1,
142 };
143
144 const parseWhen = (whenExpression: string) => {
145   const tokens = lex(whenExpression);
146   let current = 0;
147
148   const walk = (precedenceLevel = 0) : WhenAST => {
149     let token = tokens[current];
150     let node: WhenAST | null = null;
151
152     if (token.type === WhenTokenType.OPEN_PAREN) {
153       token = tokens[++current];
154       let innerNode: WhenAST = { type: WhenTokenType.EXPRESSION, value: walk() };
155       token = tokens[current];
156
157       while (token.type !== WhenTokenType.CLOSE_PAREN) {
158         innerNode = {
159           type: token.type,
160           value: token.value,
161           left: innerNode,
162           right: walk(),
163         };
164         token = tokens[current];
165       }
166       current++;
167       return innerNode;
168     }
169
170     if (token.type === WhenTokenType.STRING ) {
171       current++;
172       node = { type: token.type, value: token.value };
173     }
174
175     if (token.type === WhenTokenType.NOT) {
176       token = tokens[++current];
177       node = { type: WhenTokenType.NOT, value: token.value, right: walk() };
178     }
179
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];
185
186         let args = [];
187         token = tokens[++current];
188
189         while (token.type !== WhenTokenType.CLOSE_PAREN) {
190           if (token.type === WhenTokenType.COMMA) {
191             current++;
192           } else {
193             args.push(walk());
194           }
195           token = tokens[current];
196         }
197
198         current++;
199         node = { type: WhenTokenType.FUNCTION, name, args };
200       } else {
201         current++;
202         node = { type: WhenTokenType.IDENTIFIER, value: token.value };
203       }
204     }   
205
206     if (!node) throw new TypeError('Unexpected token: ' + token.type);
207
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) {
213         current++;
214         node = {
215           type: token.type,
216           left: node,
217           right: walk(precedence[token.type]),
218         };
219       } else {
220         break;
221       }
222     }
223
224     return node;
225    
226   };
227
228   return walk();
229 };
230
231 export {
232   parseWhen,
233   WhenAST,
234   WhenTokenType,
235 };