mevento 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -0
- package/esm2020/lib/mevento.mjs +1560 -0
- package/esm2020/mevento.mjs +5 -0
- package/esm2020/public-api.mjs +5 -0
- package/fesm2015/mevento.mjs +1619 -0
- package/fesm2015/mevento.mjs.map +1 -0
- package/fesm2020/mevento.mjs +1570 -0
- package/fesm2020/mevento.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/lib/mevento.d.ts +169 -0
- package/package.json +27 -0
- package/public-api.d.ts +1 -0
|
@@ -0,0 +1,1570 @@
|
|
|
1
|
+
function error(token) {
|
|
2
|
+
const text = `Invalid token ${token.type}[${token.value}] at ${token.line}, ${token.col}\n`;
|
|
3
|
+
throw (text);
|
|
4
|
+
}
|
|
5
|
+
function isNum(value) {
|
|
6
|
+
return typeof value === 'number';
|
|
7
|
+
}
|
|
8
|
+
function isBoolean(value) {
|
|
9
|
+
return typeof value === 'boolean';
|
|
10
|
+
}
|
|
11
|
+
function isString(value) {
|
|
12
|
+
return typeof value === 'string';
|
|
13
|
+
}
|
|
14
|
+
function _boolValue(test) {
|
|
15
|
+
return test !== null &&
|
|
16
|
+
((isNum(test) && test !== 0) ||
|
|
17
|
+
(isString(test) && test.length > 0) ||
|
|
18
|
+
(isBoolean(test) && test));
|
|
19
|
+
}
|
|
20
|
+
;
|
|
21
|
+
function hashCode(str) {
|
|
22
|
+
let hash = 0;
|
|
23
|
+
let i = 0;
|
|
24
|
+
let chr;
|
|
25
|
+
if (str.length === 0)
|
|
26
|
+
return hash;
|
|
27
|
+
for (i = 0; i < str.length; i++) {
|
|
28
|
+
chr = str.charCodeAt(i);
|
|
29
|
+
// tslint:disable-next-line:no-bitwise
|
|
30
|
+
hash = ((hash << 5) - hash) + chr;
|
|
31
|
+
// tslint:disable-next-line:no-bitwise
|
|
32
|
+
hash |= 0; // Convert to 32bit integer
|
|
33
|
+
}
|
|
34
|
+
return hash;
|
|
35
|
+
}
|
|
36
|
+
;
|
|
37
|
+
class TokenType {
|
|
38
|
+
}
|
|
39
|
+
TokenType.id = 0;
|
|
40
|
+
TokenType.comma = 1;
|
|
41
|
+
TokenType.semi = 2;
|
|
42
|
+
TokenType.numberConst = 3;
|
|
43
|
+
TokenType.stringConst = 4;
|
|
44
|
+
TokenType.equal = 5;
|
|
45
|
+
TokenType.lparen = 6;
|
|
46
|
+
TokenType.rparen = 7;
|
|
47
|
+
TokenType.eol = 8;
|
|
48
|
+
TokenType.eof = 9;
|
|
49
|
+
TokenType.lbrace = 10;
|
|
50
|
+
TokenType.rbrace = 11;
|
|
51
|
+
TokenType.lbracket = 12;
|
|
52
|
+
TokenType.rbracket = 13;
|
|
53
|
+
TokenType.great = 14;
|
|
54
|
+
TokenType.greatEq = 15;
|
|
55
|
+
TokenType.less = 16;
|
|
56
|
+
TokenType.lessEq = 17;
|
|
57
|
+
TokenType.eqeq = 18;
|
|
58
|
+
TokenType.IF = 19;
|
|
59
|
+
TokenType.ELSE = 20;
|
|
60
|
+
TokenType.TRUE = 21;
|
|
61
|
+
TokenType.FALSE = 22;
|
|
62
|
+
TokenType.NULL = 23;
|
|
63
|
+
TokenType.not = 24;
|
|
64
|
+
TokenType.notEq = 25;
|
|
65
|
+
TokenType.and = 26;
|
|
66
|
+
TokenType.or = 27;
|
|
67
|
+
TokenType.plus = 28;
|
|
68
|
+
TokenType.minus = 29;
|
|
69
|
+
TokenType.div = 30;
|
|
70
|
+
TokenType.mult = 31;
|
|
71
|
+
TokenType.mod = 32;
|
|
72
|
+
TokenType.invalid = 33;
|
|
73
|
+
TokenType.colon = 34;
|
|
74
|
+
class Token {
|
|
75
|
+
constructor(type, value, line = 1, col = 1) {
|
|
76
|
+
this.type = type;
|
|
77
|
+
this.value = value;
|
|
78
|
+
this.line = line;
|
|
79
|
+
this.col = col;
|
|
80
|
+
}
|
|
81
|
+
static from(type, value) {
|
|
82
|
+
return new Token(type, value);
|
|
83
|
+
}
|
|
84
|
+
toString() {
|
|
85
|
+
return `[${this.type.toString()}, ${this.value}]`;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
class Chars {
|
|
89
|
+
}
|
|
90
|
+
Chars.equal = '='.charCodeAt(0);
|
|
91
|
+
Chars.comma = ','.charCodeAt(0);
|
|
92
|
+
Chars.semiColon = ';'.charCodeAt(0);
|
|
93
|
+
Chars.lparen = '('.charCodeAt(0);
|
|
94
|
+
Chars.rparen = ')'.charCodeAt(0);
|
|
95
|
+
Chars.backslash = '\\'.charCodeAt(0);
|
|
96
|
+
Chars.quote = '"'.charCodeAt(0);
|
|
97
|
+
Chars.squote = "'".charCodeAt(0);
|
|
98
|
+
Chars.plus = "+".charCodeAt(0);
|
|
99
|
+
Chars.minus = "-".charCodeAt(0);
|
|
100
|
+
Chars.star = "*".charCodeAt(0);
|
|
101
|
+
Chars.slash = "/".charCodeAt(0);
|
|
102
|
+
Chars.percent = "%".charCodeAt(0);
|
|
103
|
+
Chars.lbrace = "{".charCodeAt(0);
|
|
104
|
+
Chars.rbrace = "}".charCodeAt(0);
|
|
105
|
+
Chars.lbracket = "[".charCodeAt(0);
|
|
106
|
+
Chars.rbracket = "]".charCodeAt(0);
|
|
107
|
+
Chars.not = "!".charCodeAt(0);
|
|
108
|
+
Chars.great = ">".charCodeAt(0);
|
|
109
|
+
Chars.less = "<".charCodeAt(0);
|
|
110
|
+
Chars.and = "&".charCodeAt(0);
|
|
111
|
+
Chars.pipe = "|".charCodeAt(0);
|
|
112
|
+
Chars.colon = ":".charCodeAt(0);
|
|
113
|
+
class LexerDictionary {
|
|
114
|
+
constructor(lang, keywords) {
|
|
115
|
+
this.keywords = {};
|
|
116
|
+
this.keywords = { ...keywords };
|
|
117
|
+
this.lang = lang;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Lexing => Parsing ===> AST ===> Walking
|
|
122
|
+
*/
|
|
123
|
+
/**
|
|
124
|
+
* Lexer
|
|
125
|
+
*/
|
|
126
|
+
class Lexer {
|
|
127
|
+
constructor(source) {
|
|
128
|
+
this._position = 0;
|
|
129
|
+
this._line = 1;
|
|
130
|
+
this._col = 1;
|
|
131
|
+
this._currentChar = -1;
|
|
132
|
+
this._source = source;
|
|
133
|
+
this._currentChar = this._source[this._position].charCodeAt(0);
|
|
134
|
+
this._resolveLanguage();
|
|
135
|
+
}
|
|
136
|
+
get source() {
|
|
137
|
+
return this._source;
|
|
138
|
+
}
|
|
139
|
+
_resolveLanguage() {
|
|
140
|
+
let token = this.nextToken();
|
|
141
|
+
if (token.type === TokenType.less) {
|
|
142
|
+
// language setting
|
|
143
|
+
const idToken = this.nextToken();
|
|
144
|
+
if (idToken.type !== TokenType.id) {
|
|
145
|
+
error(token);
|
|
146
|
+
}
|
|
147
|
+
const lang = idToken.value.toString();
|
|
148
|
+
this._language = Lexer.languages.find((element) => element.lang === lang) || Lexer._defaultLanguage;
|
|
149
|
+
token = this.nextToken();
|
|
150
|
+
if (token.type !== TokenType.great) {
|
|
151
|
+
error(token);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
this._language = Lexer._defaultLanguage;
|
|
156
|
+
// reset reading
|
|
157
|
+
this._position = 0;
|
|
158
|
+
this._currentChar = this._source[this._position].charCodeAt(0);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
_advance() {
|
|
162
|
+
this._position++;
|
|
163
|
+
if (this._position >= this._source.length) {
|
|
164
|
+
this._currentChar = -1;
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
this._currentChar = this._source[this._position].charCodeAt(0);
|
|
168
|
+
this._col++;
|
|
169
|
+
}
|
|
170
|
+
_pick() {
|
|
171
|
+
if (this._position >= this._source.length) {
|
|
172
|
+
return -1;
|
|
173
|
+
}
|
|
174
|
+
return this._source[this._position].charCodeAt(0);
|
|
175
|
+
}
|
|
176
|
+
_isId(code) {
|
|
177
|
+
if (code < 48) {
|
|
178
|
+
return code === 36;
|
|
179
|
+
}
|
|
180
|
+
if (code < 58) {
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
if (code < 65) {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
if (code < 91) {
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
if (code < 97) {
|
|
190
|
+
return code === 95;
|
|
191
|
+
}
|
|
192
|
+
if (code < 123) {
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
_isIdStart(code) {
|
|
198
|
+
if (code < 65) {
|
|
199
|
+
return code === 36;
|
|
200
|
+
}
|
|
201
|
+
if (code < 91) {
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
if (code < 97) {
|
|
205
|
+
return code === 95;
|
|
206
|
+
}
|
|
207
|
+
if (code < 123) {
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
_id() {
|
|
213
|
+
let ret = "";
|
|
214
|
+
const lastC = this._col;
|
|
215
|
+
const lastP = this._position;
|
|
216
|
+
const line = this._line;
|
|
217
|
+
while (this._isId(this._currentChar)) {
|
|
218
|
+
ret += String.fromCharCode(this._currentChar);
|
|
219
|
+
this._advance();
|
|
220
|
+
}
|
|
221
|
+
const translatedId = this._language
|
|
222
|
+
? (this._language.keywords[ret]) || ret
|
|
223
|
+
: ret;
|
|
224
|
+
return Lexer.RESERVED[translatedId] || new Token(TokenType.id, ret, line, lastC);
|
|
225
|
+
}
|
|
226
|
+
_isLineEnd(c) {
|
|
227
|
+
return c === 10 ||
|
|
228
|
+
c === 13 ||
|
|
229
|
+
['\n', '\r', '\u2028', '\u2029'].includes(String.fromCharCode(c));
|
|
230
|
+
}
|
|
231
|
+
_isWhiteSpace(c) {
|
|
232
|
+
return String.fromCharCode(c).trim() === "" &&
|
|
233
|
+
String.fromCharCode(c).length > 0;
|
|
234
|
+
}
|
|
235
|
+
_isDigit(c) {
|
|
236
|
+
// tslint:disable-next-line:no-bitwise
|
|
237
|
+
return c > 0 && (c ^ 0x30) <= 9;
|
|
238
|
+
}
|
|
239
|
+
_skipWhiteSpace() {
|
|
240
|
+
while (this._isWhiteSpace(this._currentChar) === true) {
|
|
241
|
+
this._advance();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
_number() {
|
|
245
|
+
let ret = "";
|
|
246
|
+
const lastC = this._col;
|
|
247
|
+
const lastP = this._position;
|
|
248
|
+
const line = this._line;
|
|
249
|
+
const first = String.fromCharCode(this._currentChar);
|
|
250
|
+
this._advance();
|
|
251
|
+
const bc = String.fromCharCode(this._currentChar);
|
|
252
|
+
let base = 10;
|
|
253
|
+
if (first === '0' && ['b', 'B', 'x', 'X', 'o', 'O'].includes(bc)) {
|
|
254
|
+
// look ahead to see that the number is tagged
|
|
255
|
+
this._advance();
|
|
256
|
+
switch (bc.toLowerCase()) {
|
|
257
|
+
case 'b':
|
|
258
|
+
base = 2;
|
|
259
|
+
break;
|
|
260
|
+
case "o":
|
|
261
|
+
base = 8;
|
|
262
|
+
break;
|
|
263
|
+
case "x":
|
|
264
|
+
base = 16;
|
|
265
|
+
break;
|
|
266
|
+
default:
|
|
267
|
+
base = 10;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
ret += first;
|
|
272
|
+
base = 10;
|
|
273
|
+
}
|
|
274
|
+
while (this._isDigit(this._currentChar) ||
|
|
275
|
+
(base === 16 &&
|
|
276
|
+
['A', 'a', 'B', 'b', 'C', 'c', 'D', 'd', 'E', 'e', 'F', 'f']
|
|
277
|
+
.includes(String.fromCharCode(this._currentChar)))) {
|
|
278
|
+
ret += String.fromCharCode(this._currentChar);
|
|
279
|
+
this._advance();
|
|
280
|
+
}
|
|
281
|
+
if (String.fromCharCode(this._currentChar) === '.' && this._isDigit(this._pick()) === true) {
|
|
282
|
+
if (base !== 10) {
|
|
283
|
+
error(new Token(TokenType.id, bc, line, lastP));
|
|
284
|
+
}
|
|
285
|
+
// floating number
|
|
286
|
+
ret += String.fromCharCode(this._currentChar);
|
|
287
|
+
this._advance();
|
|
288
|
+
while (this._isDigit(this._currentChar)) {
|
|
289
|
+
ret += String.fromCharCode(this._currentChar);
|
|
290
|
+
this._advance();
|
|
291
|
+
}
|
|
292
|
+
return new Token(TokenType.numberConst, parseFloat(ret), line, lastC);
|
|
293
|
+
}
|
|
294
|
+
return new Token(TokenType.numberConst, parseInt(ret, base), line, lastC);
|
|
295
|
+
}
|
|
296
|
+
_literalString(startChar) {
|
|
297
|
+
let ret = "";
|
|
298
|
+
let lastChar = -1;
|
|
299
|
+
const lastP = this._position;
|
|
300
|
+
const lastC = this._col;
|
|
301
|
+
const lastL = this._line;
|
|
302
|
+
while (this._currentChar !== -1) {
|
|
303
|
+
const next = String.fromCharCode(this._pick());
|
|
304
|
+
if (this._currentChar === Chars.backslash) {
|
|
305
|
+
// scape char
|
|
306
|
+
if ([
|
|
307
|
+
startChar,
|
|
308
|
+
'\\',
|
|
309
|
+
'0',
|
|
310
|
+
'a',
|
|
311
|
+
'b',
|
|
312
|
+
'e',
|
|
313
|
+
'f',
|
|
314
|
+
'n',
|
|
315
|
+
'r',
|
|
316
|
+
't',
|
|
317
|
+
'u',
|
|
318
|
+
'U',
|
|
319
|
+
'v',
|
|
320
|
+
'x'
|
|
321
|
+
].includes(next)) {
|
|
322
|
+
lastChar = this._currentChar;
|
|
323
|
+
this._advance();
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (this._currentChar === startChar && lastChar !== Chars.backslash) {
|
|
328
|
+
// end
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
ret += String.fromCharCode(this._currentChar);
|
|
332
|
+
lastChar = this._currentChar;
|
|
333
|
+
this._advance();
|
|
334
|
+
}
|
|
335
|
+
return new Token(TokenType.stringConst, ret, lastL, lastC);
|
|
336
|
+
}
|
|
337
|
+
// f=SUBMIT_FORM(xxx);
|
|
338
|
+
// r = CALL_API(API_ID, f);
|
|
339
|
+
// DISPLAY("MSG #r")
|
|
340
|
+
nextToken() {
|
|
341
|
+
const lastL = this._line;
|
|
342
|
+
const lastC = this._col;
|
|
343
|
+
const lastP = this._position;
|
|
344
|
+
while (this._currentChar !== -1) {
|
|
345
|
+
if (this._isLineEnd(this._currentChar)) {
|
|
346
|
+
this._line++;
|
|
347
|
+
this._col = 1;
|
|
348
|
+
this._advance();
|
|
349
|
+
return new Token(TokenType.eol, "\n", lastL, lastC);
|
|
350
|
+
}
|
|
351
|
+
if (this._isWhiteSpace(this._currentChar)) {
|
|
352
|
+
this._skipWhiteSpace();
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
if (this._isDigit(this._currentChar)) {
|
|
356
|
+
return this._number();
|
|
357
|
+
}
|
|
358
|
+
if (this._isIdStart(this._currentChar)) {
|
|
359
|
+
return this._id();
|
|
360
|
+
}
|
|
361
|
+
if (this._currentChar === Chars.equal) {
|
|
362
|
+
this._advance();
|
|
363
|
+
if (this._currentChar === Chars.equal) {
|
|
364
|
+
this._advance();
|
|
365
|
+
return new Token(TokenType.eqeq, "==", lastL, lastC);
|
|
366
|
+
}
|
|
367
|
+
return new Token(TokenType.equal, "=", lastL, lastC);
|
|
368
|
+
}
|
|
369
|
+
if (this._currentChar === Chars.great) {
|
|
370
|
+
this._advance();
|
|
371
|
+
if (this._currentChar === Chars.equal) {
|
|
372
|
+
this._advance();
|
|
373
|
+
return new Token(TokenType.greatEq, ">=", lastL, lastC);
|
|
374
|
+
}
|
|
375
|
+
return new Token(TokenType.great, ">", lastL, lastC);
|
|
376
|
+
}
|
|
377
|
+
if (this._currentChar === Chars.less) {
|
|
378
|
+
this._advance();
|
|
379
|
+
if (this._currentChar === Chars.equal) {
|
|
380
|
+
this._advance();
|
|
381
|
+
return new Token(TokenType.lessEq, "<=", lastL, lastC);
|
|
382
|
+
}
|
|
383
|
+
return new Token(TokenType.less, "<", lastL, lastC);
|
|
384
|
+
}
|
|
385
|
+
if (this._currentChar === Chars.semiColon) {
|
|
386
|
+
this._advance();
|
|
387
|
+
return new Token(TokenType.semi, ";", lastL, lastC);
|
|
388
|
+
}
|
|
389
|
+
if (this._currentChar === Chars.lparen) {
|
|
390
|
+
this._advance();
|
|
391
|
+
return new Token(TokenType.lparen, "(", lastL, lastC);
|
|
392
|
+
}
|
|
393
|
+
if (this._currentChar === Chars.rparen) {
|
|
394
|
+
this._advance();
|
|
395
|
+
return new Token(TokenType.rparen, ")", lastL, lastC);
|
|
396
|
+
}
|
|
397
|
+
if (this._currentChar === Chars.comma) {
|
|
398
|
+
this._advance();
|
|
399
|
+
return new Token(TokenType.comma, ",", lastL, lastC);
|
|
400
|
+
}
|
|
401
|
+
if (this._currentChar === Chars.lbrace) {
|
|
402
|
+
this._advance();
|
|
403
|
+
return new Token(TokenType.lbrace, "{", lastL, lastC);
|
|
404
|
+
}
|
|
405
|
+
if (this._currentChar === Chars.rbrace) {
|
|
406
|
+
this._advance();
|
|
407
|
+
return new Token(TokenType.rbrace, "}", lastL, lastC);
|
|
408
|
+
}
|
|
409
|
+
if (this._currentChar === Chars.lbracket) {
|
|
410
|
+
this._advance();
|
|
411
|
+
return new Token(TokenType.lbracket, "[", lastL, lastC);
|
|
412
|
+
}
|
|
413
|
+
if (this._currentChar === Chars.rbracket) {
|
|
414
|
+
this._advance();
|
|
415
|
+
return new Token(TokenType.rbracket, "]", lastL, lastC);
|
|
416
|
+
}
|
|
417
|
+
if (this._currentChar === Chars.plus) {
|
|
418
|
+
this._advance();
|
|
419
|
+
return new Token(TokenType.plus, "+", lastL, lastC);
|
|
420
|
+
}
|
|
421
|
+
if (this._currentChar === Chars.minus) {
|
|
422
|
+
this._advance();
|
|
423
|
+
return new Token(TokenType.minus, "-", lastL, lastC);
|
|
424
|
+
}
|
|
425
|
+
if (this._currentChar === Chars.slash) {
|
|
426
|
+
this._advance();
|
|
427
|
+
return new Token(TokenType.div, "/", lastL, lastC);
|
|
428
|
+
}
|
|
429
|
+
if (this._currentChar === Chars.star) {
|
|
430
|
+
this._advance();
|
|
431
|
+
return new Token(TokenType.mult, "*", lastL, lastC);
|
|
432
|
+
}
|
|
433
|
+
if (this._currentChar === Chars.percent) {
|
|
434
|
+
this._advance();
|
|
435
|
+
return new Token(TokenType.mod, "%", lastL, lastC);
|
|
436
|
+
}
|
|
437
|
+
if (this._currentChar === Chars.colon) {
|
|
438
|
+
this._advance();
|
|
439
|
+
return new Token(TokenType.colon, ":", lastL, lastC);
|
|
440
|
+
}
|
|
441
|
+
if (this._currentChar === Chars.not) {
|
|
442
|
+
this._advance();
|
|
443
|
+
if (this._currentChar === Chars.equal) {
|
|
444
|
+
this._advance();
|
|
445
|
+
return new Token(TokenType.notEq, "!=", lastL, lastC);
|
|
446
|
+
}
|
|
447
|
+
return new Token(TokenType.not, "!", lastL, lastC);
|
|
448
|
+
}
|
|
449
|
+
if (this._currentChar === Chars.and && this._pick() === Chars.and) {
|
|
450
|
+
this._advance();
|
|
451
|
+
this._advance();
|
|
452
|
+
return new Token(TokenType.and, "&&", lastL, lastC);
|
|
453
|
+
}
|
|
454
|
+
if (this._currentChar === Chars.pipe && this._pick() === Chars.pipe) {
|
|
455
|
+
this._advance();
|
|
456
|
+
this._advance();
|
|
457
|
+
return new Token(TokenType.and, '||', lastL, lastC);
|
|
458
|
+
}
|
|
459
|
+
if (this._currentChar === Chars.quote || this._currentChar === Chars.squote) {
|
|
460
|
+
const startChar = this._currentChar;
|
|
461
|
+
this._advance();
|
|
462
|
+
const t = this._literalString(startChar);
|
|
463
|
+
this._advance();
|
|
464
|
+
return t;
|
|
465
|
+
}
|
|
466
|
+
return new Token(TokenType.invalid, String.fromCharCode(this._currentChar), lastL, lastC);
|
|
467
|
+
}
|
|
468
|
+
return new Token(TokenType.eof, "", lastL, lastC);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
Lexer._defaultLanguage = new LexerDictionary("en", {
|
|
472
|
+
"if": "if",
|
|
473
|
+
"else": "else",
|
|
474
|
+
"true": "true",
|
|
475
|
+
"false": "false",
|
|
476
|
+
"null": "null",
|
|
477
|
+
});
|
|
478
|
+
Lexer.languages = [
|
|
479
|
+
Lexer._defaultLanguage,
|
|
480
|
+
new LexerDictionary("fr", {
|
|
481
|
+
"si": "if",
|
|
482
|
+
"sinon": "else",
|
|
483
|
+
"vrai": "else",
|
|
484
|
+
"faux": "false",
|
|
485
|
+
"nul": "null",
|
|
486
|
+
}),
|
|
487
|
+
new LexerDictionary("bm", {
|
|
488
|
+
"nii": "if",
|
|
489
|
+
"note": "else",
|
|
490
|
+
"tien": "true",
|
|
491
|
+
"galon": "false",
|
|
492
|
+
"gansan": "null",
|
|
493
|
+
}),
|
|
494
|
+
];
|
|
495
|
+
Lexer.RESERVED = {
|
|
496
|
+
"if": Token.from(TokenType.IF, "if"),
|
|
497
|
+
"else": Token.from(TokenType.ELSE, "else"),
|
|
498
|
+
"true": Token.from(TokenType.TRUE, true),
|
|
499
|
+
"false": Token.from(TokenType.FALSE, false),
|
|
500
|
+
"null": Token.from(TokenType.NULL, null),
|
|
501
|
+
};
|
|
502
|
+
/**
|
|
503
|
+
* Parser
|
|
504
|
+
*/
|
|
505
|
+
class AST {
|
|
506
|
+
constructor(line, col) {
|
|
507
|
+
this.line = line;
|
|
508
|
+
this.col = col;
|
|
509
|
+
}
|
|
510
|
+
dump() {
|
|
511
|
+
return this.toString();
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
class RootAST extends AST {
|
|
515
|
+
constructor(body, name, source) {
|
|
516
|
+
super(1, 1);
|
|
517
|
+
this.body = body;
|
|
518
|
+
this.name = name;
|
|
519
|
+
this.source = source;
|
|
520
|
+
}
|
|
521
|
+
dump() {
|
|
522
|
+
let builder = `Module ${this.name} Start {`;
|
|
523
|
+
for (const it of this.body) {
|
|
524
|
+
builder += `${it.dump()}\n`;
|
|
525
|
+
}
|
|
526
|
+
builder += "}";
|
|
527
|
+
return builder;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
class BlockStatementAST extends AST {
|
|
531
|
+
constructor(body, token) {
|
|
532
|
+
super(token.line, token.col);
|
|
533
|
+
this.body = body;
|
|
534
|
+
}
|
|
535
|
+
toString() {
|
|
536
|
+
return `{\n${this.body.map((e) => e.toString()).join("\n")}}`;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
class IdentifierAST extends AST {
|
|
540
|
+
constructor(token) {
|
|
541
|
+
super(token.line, token.col);
|
|
542
|
+
this.value = token.value.toString();
|
|
543
|
+
}
|
|
544
|
+
toString() {
|
|
545
|
+
return this.value;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
class LiteralAST extends AST {
|
|
549
|
+
constructor(token, raw) {
|
|
550
|
+
super(token.line, token.col);
|
|
551
|
+
this.value = token.value;
|
|
552
|
+
this.raw = raw;
|
|
553
|
+
}
|
|
554
|
+
toString() {
|
|
555
|
+
return this.value.toString();
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
class AssignmentExpressionAST extends AST {
|
|
559
|
+
constructor(identifier, init) {
|
|
560
|
+
super(identifier.line, identifier.col);
|
|
561
|
+
this.identifier = identifier;
|
|
562
|
+
this.init = init;
|
|
563
|
+
}
|
|
564
|
+
toString() {
|
|
565
|
+
return `${this.identifier} = ${this.init}`;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
class ExpressionStatementAST extends AST {
|
|
569
|
+
constructor(expression) {
|
|
570
|
+
super(expression.line, expression.col);
|
|
571
|
+
this.expression = expression;
|
|
572
|
+
}
|
|
573
|
+
toString() {
|
|
574
|
+
return this.expression.toString();
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
class CallExpressionAST extends AST {
|
|
578
|
+
constructor(callee, ags) {
|
|
579
|
+
super(callee.line, callee.col);
|
|
580
|
+
this.callee = callee;
|
|
581
|
+
this.arguments = ags;
|
|
582
|
+
}
|
|
583
|
+
toString() {
|
|
584
|
+
return `${this.callee.toString()}(...${this.arguments.length})`;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
class BinaryExpressionAST extends AST {
|
|
588
|
+
constructor(left, operation, right) {
|
|
589
|
+
super(left.line, left.col);
|
|
590
|
+
this.left = left;
|
|
591
|
+
this.operation = operation;
|
|
592
|
+
this.right = right;
|
|
593
|
+
}
|
|
594
|
+
toString() {
|
|
595
|
+
return `${this.left} ${this.operation} ${this.right}`;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
class UnaryExpressionAST extends AST {
|
|
599
|
+
constructor(operation, argument) {
|
|
600
|
+
super(operation.line, operation.col);
|
|
601
|
+
this.operation = operation, this.argument = argument;
|
|
602
|
+
}
|
|
603
|
+
toString() {
|
|
604
|
+
return `${this.operation} ${this.argument}`;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
class IfStatementAST extends AST {
|
|
608
|
+
constructor(test, consequent, alternate) {
|
|
609
|
+
super(test.line, test.col);
|
|
610
|
+
this.test = test;
|
|
611
|
+
this.consequent = consequent;
|
|
612
|
+
this.alternate = alternate;
|
|
613
|
+
}
|
|
614
|
+
toString() {
|
|
615
|
+
return `if ${this.test} ${this.consequent} ${this.alternate !== null ? `else ${this.alternate} ` : ''}`;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
class LogicalExpressionAST extends AST {
|
|
619
|
+
constructor(left, operator, right) {
|
|
620
|
+
super(left.line, left.col);
|
|
621
|
+
this.left = left;
|
|
622
|
+
this.operator = operator;
|
|
623
|
+
this.right = right;
|
|
624
|
+
}
|
|
625
|
+
toString() {
|
|
626
|
+
return `${this.left} ${this.operator.value} ${this.right}`;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
class IndexAccessorAST extends AST {
|
|
630
|
+
constructor(owner, key, computed = false) {
|
|
631
|
+
super(owner.line, owner.col);
|
|
632
|
+
this.computed = false;
|
|
633
|
+
this.owner = owner;
|
|
634
|
+
this.key = key;
|
|
635
|
+
this.computed = computed;
|
|
636
|
+
}
|
|
637
|
+
toString() {
|
|
638
|
+
return `${this.owner}[${this.key}]`;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
class ObjectExpression extends AST {
|
|
642
|
+
constructor(props, startToken, endToken) {
|
|
643
|
+
super(startToken?.line, startToken?.col);
|
|
644
|
+
this.properties = props;
|
|
645
|
+
}
|
|
646
|
+
toString() {
|
|
647
|
+
return "{...}";
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
class ArrayExpression extends AST {
|
|
651
|
+
constructor(props, start, end) {
|
|
652
|
+
super(start?.line, start?.col);
|
|
653
|
+
this.elements = props;
|
|
654
|
+
}
|
|
655
|
+
toString() {
|
|
656
|
+
return "[...]";
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
class ObjectProperty extends AST {
|
|
660
|
+
constructor(key, value) {
|
|
661
|
+
super(key.line, key.col);
|
|
662
|
+
this.value = value;
|
|
663
|
+
this.key = key;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
class Parser {
|
|
667
|
+
constructor(lexer) {
|
|
668
|
+
this.currentToken = lexer.nextToken();
|
|
669
|
+
this.lexer = lexer;
|
|
670
|
+
}
|
|
671
|
+
_eat(type) {
|
|
672
|
+
if (this.currentToken?.type === type) {
|
|
673
|
+
this.currentToken = this.lexer.nextToken();
|
|
674
|
+
}
|
|
675
|
+
else {
|
|
676
|
+
error(this.currentToken);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
_eatEOL() {
|
|
680
|
+
while (this.currentToken?.type === TokenType.eol) {
|
|
681
|
+
this._eat(TokenType.eol);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
_eatSemiOrEOL() {
|
|
685
|
+
while (this.currentToken?.type === TokenType.eol ||
|
|
686
|
+
this.currentToken?.type === TokenType.semi) {
|
|
687
|
+
this._eat(this.currentToken.type);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
_eatSemi() {
|
|
691
|
+
while (this.currentToken?.type === TokenType.semi) {
|
|
692
|
+
this._eat(TokenType.semi);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
_variable() {
|
|
696
|
+
const node = new IdentifierAST(this.currentToken);
|
|
697
|
+
this._eat(TokenType.id);
|
|
698
|
+
return node;
|
|
699
|
+
}
|
|
700
|
+
_factor() {
|
|
701
|
+
const token = this.currentToken;
|
|
702
|
+
switch (token.type) {
|
|
703
|
+
case TokenType.plus:
|
|
704
|
+
case TokenType.minus:
|
|
705
|
+
case TokenType.not:
|
|
706
|
+
this._eat(this.currentToken.type);
|
|
707
|
+
return new UnaryExpressionAST(token, this._factor());
|
|
708
|
+
case TokenType.numberConst:
|
|
709
|
+
this._eat(TokenType.numberConst);
|
|
710
|
+
return new LiteralAST(token, token.value.toString());
|
|
711
|
+
case TokenType.stringConst:
|
|
712
|
+
this._eat(TokenType.stringConst);
|
|
713
|
+
return new LiteralAST(token, token.value.toString());
|
|
714
|
+
case TokenType.lparen:
|
|
715
|
+
this._eat(TokenType.lparen);
|
|
716
|
+
const node = this._expression();
|
|
717
|
+
this._eat(TokenType.rparen);
|
|
718
|
+
return node;
|
|
719
|
+
case TokenType.TRUE:
|
|
720
|
+
case TokenType.FALSE:
|
|
721
|
+
this._eat(this.currentToken.type);
|
|
722
|
+
return new LiteralAST(token, token.value.toString());
|
|
723
|
+
case TokenType.NULL:
|
|
724
|
+
this._eat(TokenType.NULL);
|
|
725
|
+
return new LiteralAST(token, "null");
|
|
726
|
+
case TokenType.lbracket:
|
|
727
|
+
// literal array expression
|
|
728
|
+
return this._arrayExpression();
|
|
729
|
+
case TokenType.lbrace:
|
|
730
|
+
return this._objectExpression();
|
|
731
|
+
default:
|
|
732
|
+
return this._variable();
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
_term() {
|
|
736
|
+
let node = this._factor();
|
|
737
|
+
node = this._tryParsingMemberExpression(node);
|
|
738
|
+
node = this._tryParsingFunctionCall(node);
|
|
739
|
+
return node;
|
|
740
|
+
}
|
|
741
|
+
_expression() {
|
|
742
|
+
let node = this._term();
|
|
743
|
+
node = this._tryBinaryExpression(0, node);
|
|
744
|
+
while ([TokenType.and, TokenType.or].includes(this.currentToken.type)) {
|
|
745
|
+
const token = this.currentToken;
|
|
746
|
+
this._eat(token.type);
|
|
747
|
+
node = new LogicalExpressionAST(node, token, this._expression());
|
|
748
|
+
}
|
|
749
|
+
if (this._expect(TokenType.equal)) {
|
|
750
|
+
if (node instanceof IdentifierAST || node instanceof IndexAccessorAST) {
|
|
751
|
+
const token = this.currentToken;
|
|
752
|
+
this._eat(TokenType.equal);
|
|
753
|
+
node = new AssignmentExpressionAST(node, this._expression());
|
|
754
|
+
}
|
|
755
|
+
else {
|
|
756
|
+
throw new Error("Unexpected token");
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
return node;
|
|
760
|
+
}
|
|
761
|
+
_objectProperty() {
|
|
762
|
+
let key;
|
|
763
|
+
switch (this.currentToken?.type) {
|
|
764
|
+
case TokenType.stringConst:
|
|
765
|
+
{
|
|
766
|
+
key = new LiteralAST(this.currentToken, this.currentToken.value);
|
|
767
|
+
this._eat(TokenType.stringConst);
|
|
768
|
+
break;
|
|
769
|
+
}
|
|
770
|
+
case TokenType.lbracket:
|
|
771
|
+
{
|
|
772
|
+
this._eat(TokenType.lbracket);
|
|
773
|
+
var expr = this._expression();
|
|
774
|
+
this._eat(TokenType.rbracket);
|
|
775
|
+
key = expr;
|
|
776
|
+
break;
|
|
777
|
+
}
|
|
778
|
+
case TokenType.id:
|
|
779
|
+
{
|
|
780
|
+
const id = this._variable();
|
|
781
|
+
key = new LiteralAST(new Token(TokenType.id, id.value, id.line, id.col), id.value);
|
|
782
|
+
break;
|
|
783
|
+
}
|
|
784
|
+
default:
|
|
785
|
+
throw `Unexpected token ${this.currentToken}`;
|
|
786
|
+
}
|
|
787
|
+
this._eat(TokenType.colon);
|
|
788
|
+
var value = this._expression();
|
|
789
|
+
return new ObjectProperty(key, value);
|
|
790
|
+
}
|
|
791
|
+
_property() {
|
|
792
|
+
return this._objectProperty();
|
|
793
|
+
}
|
|
794
|
+
_objectProperties() {
|
|
795
|
+
const properties = [];
|
|
796
|
+
if (this.currentToken?.type != TokenType.rbrace) {
|
|
797
|
+
properties.push(this._property());
|
|
798
|
+
}
|
|
799
|
+
while (this.currentToken?.type == TokenType.comma) {
|
|
800
|
+
this._eat(TokenType.comma);
|
|
801
|
+
properties.push(this._property());
|
|
802
|
+
}
|
|
803
|
+
return properties;
|
|
804
|
+
}
|
|
805
|
+
_objectExpression(braceToken) {
|
|
806
|
+
var token = braceToken ?? this.currentToken;
|
|
807
|
+
if (!braceToken)
|
|
808
|
+
this._eat(TokenType.lbrace);
|
|
809
|
+
var properties = this._objectProperties();
|
|
810
|
+
this._eat(TokenType.rbrace);
|
|
811
|
+
return new ObjectExpression(properties, token, this.currentToken);
|
|
812
|
+
}
|
|
813
|
+
_arrayExpression() {
|
|
814
|
+
this._eat(TokenType.lbracket);
|
|
815
|
+
const elements = this._expect(TokenType.rbracket) ? [] : this._expressionsList();
|
|
816
|
+
this._eat(TokenType.rbracket);
|
|
817
|
+
var startNode = elements.length !== 0 ? elements[0] : undefined;
|
|
818
|
+
var lastNode = elements.length !== 0 ? elements[elements.length - 1] : undefined;
|
|
819
|
+
return new ArrayExpression(elements, startNode, lastNode);
|
|
820
|
+
}
|
|
821
|
+
_tryParsingMemberExpression(n) {
|
|
822
|
+
let node = n;
|
|
823
|
+
while (this.currentToken.type === TokenType.lbracket) {
|
|
824
|
+
this._eat(TokenType.lbracket);
|
|
825
|
+
const key = this._expression();
|
|
826
|
+
node = new IndexAccessorAST(node, key);
|
|
827
|
+
this._eat(TokenType.rbracket);
|
|
828
|
+
}
|
|
829
|
+
return node;
|
|
830
|
+
}
|
|
831
|
+
_tryBinaryExpression(prec, left) {
|
|
832
|
+
let node = left;
|
|
833
|
+
while (true) {
|
|
834
|
+
const currentPrec = Parser._binopPrecdences[this.currentToken.type] || -1;
|
|
835
|
+
if (currentPrec < prec)
|
|
836
|
+
return node;
|
|
837
|
+
const operator = this.currentToken;
|
|
838
|
+
this._eat(operator.type);
|
|
839
|
+
let right = this._term();
|
|
840
|
+
const nextPrec = Parser._binopPrecdences[this.currentToken.type] || -1;
|
|
841
|
+
if (currentPrec < nextPrec) {
|
|
842
|
+
const tmp = this._tryBinaryExpression(currentPrec + 1, right);
|
|
843
|
+
if (tmp === node)
|
|
844
|
+
return tmp;
|
|
845
|
+
right = tmp;
|
|
846
|
+
}
|
|
847
|
+
node = new BinaryExpressionAST(node, operator, right);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
_expressionsList() {
|
|
851
|
+
this._eatEOL();
|
|
852
|
+
// if (expect(Lexer.TokenType.RPAREN)) return emptyList()
|
|
853
|
+
let expr = this._expression();
|
|
854
|
+
const result = [expr];
|
|
855
|
+
while (this.currentToken?.type === TokenType.comma) {
|
|
856
|
+
this._eat(TokenType.comma);
|
|
857
|
+
expr = this._expression();
|
|
858
|
+
result.push(expr);
|
|
859
|
+
}
|
|
860
|
+
return result;
|
|
861
|
+
}
|
|
862
|
+
_callExpression(node) {
|
|
863
|
+
this._eat(TokenType.lparen);
|
|
864
|
+
let args = [];
|
|
865
|
+
if (!this._expect(TokenType.rparen)) {
|
|
866
|
+
args = this._expressionsList();
|
|
867
|
+
}
|
|
868
|
+
this._eat(TokenType.rparen);
|
|
869
|
+
if (!(node instanceof IdentifierAST)) {
|
|
870
|
+
error(this.currentToken);
|
|
871
|
+
}
|
|
872
|
+
return new CallExpressionAST(node, args);
|
|
873
|
+
}
|
|
874
|
+
_tryParsingFunctionCall(n) {
|
|
875
|
+
let node = n;
|
|
876
|
+
while (this.currentToken.type === TokenType.lparen) {
|
|
877
|
+
// probable function call
|
|
878
|
+
node = this._callExpression(node);
|
|
879
|
+
}
|
|
880
|
+
return node;
|
|
881
|
+
}
|
|
882
|
+
_statementExpression() {
|
|
883
|
+
const node = this._expression();
|
|
884
|
+
if (![TokenType.semi, TokenType.eol, TokenType.eof]
|
|
885
|
+
.includes(this.currentToken.type)) {
|
|
886
|
+
error(this.currentToken);
|
|
887
|
+
}
|
|
888
|
+
return node;
|
|
889
|
+
}
|
|
890
|
+
_blockStatement(ignoreFirstBrace = false) {
|
|
891
|
+
if (!ignoreFirstBrace)
|
|
892
|
+
this._eat(TokenType.lbrace);
|
|
893
|
+
this._eatEOL();
|
|
894
|
+
if (this._expect(TokenType.rbrace)) {
|
|
895
|
+
this._eat(TokenType.rbrace);
|
|
896
|
+
return new BlockStatementAST([], this.currentToken);
|
|
897
|
+
}
|
|
898
|
+
// const body = _statements();
|
|
899
|
+
const body = [this._statement()];
|
|
900
|
+
while (this.currentToken.type === TokenType.eol ||
|
|
901
|
+
this.currentToken.type === TokenType.semi) {
|
|
902
|
+
this._eatSemiOrEOL();
|
|
903
|
+
if (this.currentToken.type === TokenType.rbrace || this.currentToken.type === TokenType.eof) {
|
|
904
|
+
// _eat(this.currentToken!.type);
|
|
905
|
+
break;
|
|
906
|
+
}
|
|
907
|
+
body.push(this._statement());
|
|
908
|
+
if (this.currentToken.type !== TokenType.eol &&
|
|
909
|
+
this.currentToken.type !== TokenType.semi &&
|
|
910
|
+
this.currentToken.type !== TokenType.eof) {
|
|
911
|
+
error(this.currentToken);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
this._eat(TokenType.rbrace);
|
|
915
|
+
if (this.currentToken?.type === TokenType.rbrace) {
|
|
916
|
+
// endBlock
|
|
917
|
+
this._eat(TokenType.rbrace);
|
|
918
|
+
}
|
|
919
|
+
return new BlockStatementAST(body, this.currentToken);
|
|
920
|
+
}
|
|
921
|
+
_ifStatement() {
|
|
922
|
+
this._eat(TokenType.IF);
|
|
923
|
+
const withPar = this.currentToken.type === TokenType.lparen;
|
|
924
|
+
if (withPar) {
|
|
925
|
+
this._eat(TokenType.lparen);
|
|
926
|
+
}
|
|
927
|
+
const test = this._expression();
|
|
928
|
+
if (withPar) {
|
|
929
|
+
this._eat(TokenType.rparen);
|
|
930
|
+
}
|
|
931
|
+
let consequent;
|
|
932
|
+
if (this.currentToken.type === TokenType.lbrace) {
|
|
933
|
+
consequent = this._blockStatement();
|
|
934
|
+
}
|
|
935
|
+
else {
|
|
936
|
+
consequent = this._expression();
|
|
937
|
+
}
|
|
938
|
+
this._eatSemiOrEOL();
|
|
939
|
+
let alternate;
|
|
940
|
+
if (this.currentToken.type === TokenType.ELSE) {
|
|
941
|
+
this._eat(TokenType.ELSE);
|
|
942
|
+
switch (this.currentToken.type) {
|
|
943
|
+
case TokenType.IF:
|
|
944
|
+
alternate = this._ifStatement();
|
|
945
|
+
break;
|
|
946
|
+
case TokenType.lbrace:
|
|
947
|
+
alternate = this._blockStatement();
|
|
948
|
+
break;
|
|
949
|
+
default:
|
|
950
|
+
alternate = this._expression();
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
return new IfStatementAST(test, consequent, alternate);
|
|
954
|
+
}
|
|
955
|
+
_statement() {
|
|
956
|
+
switch (this.currentToken.type) {
|
|
957
|
+
case TokenType.semi:
|
|
958
|
+
this._eatSemi();
|
|
959
|
+
return this._statement();
|
|
960
|
+
case TokenType.eol:
|
|
961
|
+
this._eatEOL();
|
|
962
|
+
return this._statement();
|
|
963
|
+
case TokenType.IF:
|
|
964
|
+
return this._ifStatement();
|
|
965
|
+
default:
|
|
966
|
+
return this._statementExpression();
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
_expect(type) {
|
|
970
|
+
return this.currentToken?.type === type;
|
|
971
|
+
}
|
|
972
|
+
_statements() {
|
|
973
|
+
this._eatEOL();
|
|
974
|
+
if (this._expect(TokenType.eof)) {
|
|
975
|
+
return [];
|
|
976
|
+
}
|
|
977
|
+
const results = [this._statement()];
|
|
978
|
+
while ((this.currentToken.type === TokenType.eol ||
|
|
979
|
+
this.currentToken.type === TokenType.semi)) {
|
|
980
|
+
this._eatSemiOrEOL();
|
|
981
|
+
if (this.currentToken.type === TokenType.eof) {
|
|
982
|
+
this._eat(TokenType.eof);
|
|
983
|
+
break;
|
|
984
|
+
}
|
|
985
|
+
results.push(this._statement());
|
|
986
|
+
if (this.currentToken.type !== TokenType.eol &&
|
|
987
|
+
this.currentToken.type !== TokenType.semi &&
|
|
988
|
+
this.currentToken.type !== TokenType.eof) {
|
|
989
|
+
error(this.currentToken);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
return results;
|
|
993
|
+
}
|
|
994
|
+
_root() {
|
|
995
|
+
const source = this.lexer.source;
|
|
996
|
+
const name = "<module>";
|
|
997
|
+
const list = this._statements();
|
|
998
|
+
return new RootAST(list, name, source);
|
|
999
|
+
}
|
|
1000
|
+
parse() {
|
|
1001
|
+
return this._root();
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
Parser._binopPrecdences = {
|
|
1005
|
+
[TokenType.eqeq]: 10,
|
|
1006
|
+
[TokenType.notEq]: 10,
|
|
1007
|
+
[TokenType.great]: 10,
|
|
1008
|
+
[TokenType.greatEq]: 10,
|
|
1009
|
+
[TokenType.less]: 10,
|
|
1010
|
+
[TokenType.lessEq]: 10,
|
|
1011
|
+
[TokenType.plus]: 20,
|
|
1012
|
+
[TokenType.minus]: 20,
|
|
1013
|
+
[TokenType.mult]: 40,
|
|
1014
|
+
[TokenType.div]: 40,
|
|
1015
|
+
[TokenType.mod]: 40,
|
|
1016
|
+
};
|
|
1017
|
+
// AST Wallker
|
|
1018
|
+
class ANodeVisitor {
|
|
1019
|
+
constructor() {
|
|
1020
|
+
this._nodesVisitors = {};
|
|
1021
|
+
}
|
|
1022
|
+
registerVisitor(type, visitor) {
|
|
1023
|
+
const methodName = `visit${type.name}`;
|
|
1024
|
+
// console.log('Reg::::::', methodName, type);
|
|
1025
|
+
this._nodesVisitors[methodName] = visitor;
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
class AsyncNodeVisitor extends ANodeVisitor {
|
|
1029
|
+
async visit(node) {
|
|
1030
|
+
const methodName = `visit${node.constructor.name}`;
|
|
1031
|
+
// const fn = Object.getPrototypeOf(this)[methodName];
|
|
1032
|
+
const fn = this._nodesVisitors[methodName];
|
|
1033
|
+
return await fn?.call(this, node);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
class NodeVisitor extends ANodeVisitor {
|
|
1037
|
+
visit(node) {
|
|
1038
|
+
const methodName = `visit${node.constructor.name}`;
|
|
1039
|
+
// const fn = Object.getPrototypeOf(this)[methodName];
|
|
1040
|
+
const fn = this._nodesVisitors[methodName];
|
|
1041
|
+
return fn?.call(this, node);
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
class MEvento extends NodeVisitor {
|
|
1045
|
+
constructor() {
|
|
1046
|
+
super();
|
|
1047
|
+
this._memory = {};
|
|
1048
|
+
this._functionsRegistry = {};
|
|
1049
|
+
this.registerVisitor(RootAST, this.visitRootAST);
|
|
1050
|
+
this.registerVisitor(BlockStatementAST, this.visitBlockStatementAST);
|
|
1051
|
+
this.registerVisitor(IdentifierAST, this.visitIdentifierAST);
|
|
1052
|
+
this.registerVisitor(LiteralAST, this.visitLiteralAST);
|
|
1053
|
+
this.registerVisitor(AssignmentExpressionAST, this.visitAssignmentExpressionAST);
|
|
1054
|
+
this.registerVisitor(ExpressionStatementAST, this.visitExpressionStatementAST);
|
|
1055
|
+
this.registerVisitor(CallExpressionAST, this.visitCallExpressionAST);
|
|
1056
|
+
this.registerVisitor(BinaryExpressionAST, this.visitBinaryExpressionAST);
|
|
1057
|
+
this.registerVisitor(UnaryExpressionAST, this.visitUnaryExpressionAST);
|
|
1058
|
+
this.registerVisitor(IfStatementAST, this.visitIfStatementAST);
|
|
1059
|
+
this.registerVisitor(LogicalExpressionAST, this.visitLogicalExpressionAST);
|
|
1060
|
+
this.registerVisitor(IndexAccessorAST, this.visitIndexAccessorAST);
|
|
1061
|
+
this.registerVisitor(ObjectProperty, this.visitObjectProperty);
|
|
1062
|
+
this.registerVisitor(ObjectExpression, this.visitObjectExpression);
|
|
1063
|
+
this.registerVisitor(ArrayExpression, this.visitArrayExpression);
|
|
1064
|
+
this._functionsRegistry = { ...MEvento._globalFunctionsRegistry };
|
|
1065
|
+
}
|
|
1066
|
+
visitRootAST(node) {
|
|
1067
|
+
const list = node.body;
|
|
1068
|
+
let last;
|
|
1069
|
+
for (const n of list) {
|
|
1070
|
+
last = this.visit(n);
|
|
1071
|
+
}
|
|
1072
|
+
return last;
|
|
1073
|
+
}
|
|
1074
|
+
visitBlockStatementAST(node) {
|
|
1075
|
+
const list = node.body;
|
|
1076
|
+
let last;
|
|
1077
|
+
for (const n of list) {
|
|
1078
|
+
last = this.visit(n);
|
|
1079
|
+
}
|
|
1080
|
+
return last;
|
|
1081
|
+
}
|
|
1082
|
+
visitIdentifierAST(node) {
|
|
1083
|
+
const id = node.value;
|
|
1084
|
+
return this._memory[id];
|
|
1085
|
+
}
|
|
1086
|
+
visitLiteralAST(node) {
|
|
1087
|
+
const value = node.value;
|
|
1088
|
+
return value;
|
|
1089
|
+
}
|
|
1090
|
+
visitAssignmentExpressionAST(node) {
|
|
1091
|
+
const id = node.identifier;
|
|
1092
|
+
const init = node.init;
|
|
1093
|
+
let value = null;
|
|
1094
|
+
if (id instanceof IndexAccessorAST) {
|
|
1095
|
+
var target = this.visit(id.owner);
|
|
1096
|
+
value = this.visit(node.init);
|
|
1097
|
+
var property = this.visit(id.key);
|
|
1098
|
+
this.assignProperty(target, property, value);
|
|
1099
|
+
}
|
|
1100
|
+
else if (id instanceof IdentifierAST) {
|
|
1101
|
+
value = this.visit(init);
|
|
1102
|
+
this._memory[id.value] = value;
|
|
1103
|
+
}
|
|
1104
|
+
return value;
|
|
1105
|
+
}
|
|
1106
|
+
assignProperty(owner, property, value) {
|
|
1107
|
+
if (Array.isArray(owner)) {
|
|
1108
|
+
owner[property] = value;
|
|
1109
|
+
}
|
|
1110
|
+
else if (typeof owner === 'object') {
|
|
1111
|
+
owner[property] = value;
|
|
1112
|
+
}
|
|
1113
|
+
else { }
|
|
1114
|
+
}
|
|
1115
|
+
visitExpressionStatementAST(node) {
|
|
1116
|
+
const expr = node.expression;
|
|
1117
|
+
return this.visit(expr);
|
|
1118
|
+
}
|
|
1119
|
+
visitCallExpressionAST(node) {
|
|
1120
|
+
const callee = node.callee;
|
|
1121
|
+
const args = node.arguments;
|
|
1122
|
+
const calleeName = callee.value;
|
|
1123
|
+
const fn = this._functionsRegistry[calleeName];
|
|
1124
|
+
const argValues = args.map((e) => this.visit(e));
|
|
1125
|
+
// print("argValues::", argValues)
|
|
1126
|
+
return fn?.(argValues, this);
|
|
1127
|
+
}
|
|
1128
|
+
visitBinaryExpressionAST(node) {
|
|
1129
|
+
const left = node.left;
|
|
1130
|
+
const right = node.right;
|
|
1131
|
+
const op = node.operation;
|
|
1132
|
+
const lValue = this.visit(left);
|
|
1133
|
+
const rValue = this.visit(right);
|
|
1134
|
+
switch (op.type) {
|
|
1135
|
+
case TokenType.plus:
|
|
1136
|
+
if (isNum(lValue) && isNum(rValue)) {
|
|
1137
|
+
return lValue + rValue;
|
|
1138
|
+
}
|
|
1139
|
+
return `${lValue}${rValue}`;
|
|
1140
|
+
case TokenType.minus:
|
|
1141
|
+
if (isNum(lValue) && isNum(rValue)) {
|
|
1142
|
+
return lValue - rValue;
|
|
1143
|
+
}
|
|
1144
|
+
if (isString(lValue) && isNum(rValue)) {
|
|
1145
|
+
return lValue * rValue;
|
|
1146
|
+
}
|
|
1147
|
+
if (isNum(lValue) && isString(rValue)) {
|
|
1148
|
+
return rValue * lValue;
|
|
1149
|
+
}
|
|
1150
|
+
throw new Error(`Operation ${op.value} not allowed no num value`);
|
|
1151
|
+
case TokenType.mult:
|
|
1152
|
+
if (isNum(lValue) && isNum(rValue)) {
|
|
1153
|
+
return lValue * rValue;
|
|
1154
|
+
}
|
|
1155
|
+
throw new Error(`Operation ${op.value} not allowed no num value`);
|
|
1156
|
+
case TokenType.div:
|
|
1157
|
+
if (isNum(lValue) && isNum(rValue)) {
|
|
1158
|
+
if (rValue === 0) {
|
|
1159
|
+
throw new Error("Invalid division by 0");
|
|
1160
|
+
}
|
|
1161
|
+
return lValue / rValue;
|
|
1162
|
+
}
|
|
1163
|
+
throw new Error(`Operation ${op.value} not allowed no num value`);
|
|
1164
|
+
case TokenType.mod:
|
|
1165
|
+
if (isNum(lValue) && isNum(rValue)) {
|
|
1166
|
+
return lValue % rValue;
|
|
1167
|
+
}
|
|
1168
|
+
throw new Error(`Operation ${op.value} not allowed no num value`);
|
|
1169
|
+
case TokenType.great:
|
|
1170
|
+
if (isNum(lValue) && isNum(rValue)) {
|
|
1171
|
+
return lValue > rValue;
|
|
1172
|
+
}
|
|
1173
|
+
throw new Error(`Operation ${op.value} not allowed no num value`);
|
|
1174
|
+
case TokenType.greatEq:
|
|
1175
|
+
if (isNum(lValue) && isNum(rValue)) {
|
|
1176
|
+
return lValue >= rValue;
|
|
1177
|
+
}
|
|
1178
|
+
throw new Error(`Operation ${op.value} not allowed no num value`);
|
|
1179
|
+
case TokenType.less:
|
|
1180
|
+
if (isNum(lValue) && isNum(rValue)) {
|
|
1181
|
+
return lValue < rValue;
|
|
1182
|
+
}
|
|
1183
|
+
throw new Error(`Operation ${op.value} not allowed no num value`);
|
|
1184
|
+
case TokenType.lessEq:
|
|
1185
|
+
if (isNum(lValue) && isNum(rValue)) {
|
|
1186
|
+
return lValue <= rValue;
|
|
1187
|
+
}
|
|
1188
|
+
throw new Error(`Operation ${op.value} not allowed no num value`);
|
|
1189
|
+
case TokenType.eqeq:
|
|
1190
|
+
return lValue === rValue;
|
|
1191
|
+
case TokenType.notEq:
|
|
1192
|
+
return lValue !== rValue;
|
|
1193
|
+
default:
|
|
1194
|
+
throw new Error(`Operation ${op.value} not allowed no num value`);
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
visitUnaryExpressionAST(node) {
|
|
1198
|
+
const arg = node.argument;
|
|
1199
|
+
const op = node.operation;
|
|
1200
|
+
const argValue = this.visit(arg);
|
|
1201
|
+
if (op.type === TokenType.not) {
|
|
1202
|
+
return _boolValue(argValue) === false;
|
|
1203
|
+
}
|
|
1204
|
+
if (isNum(argValue)) {
|
|
1205
|
+
throw new Error(`Operation ${op.value} not allowed no num value`);
|
|
1206
|
+
}
|
|
1207
|
+
if (op.type === TokenType.plus) {
|
|
1208
|
+
return argValue;
|
|
1209
|
+
}
|
|
1210
|
+
else if (op.type === TokenType.minus) {
|
|
1211
|
+
return -argValue;
|
|
1212
|
+
}
|
|
1213
|
+
throw new Error(`Operation ${op.value} not allowed no num value`);
|
|
1214
|
+
}
|
|
1215
|
+
visitIfStatementAST(node) {
|
|
1216
|
+
const testNode = node.test;
|
|
1217
|
+
const test = this.visit(testNode);
|
|
1218
|
+
const testBoolValue = _boolValue(test);
|
|
1219
|
+
if (testBoolValue) {
|
|
1220
|
+
return this.visit(node.consequent);
|
|
1221
|
+
}
|
|
1222
|
+
if (node.alternate !== null) {
|
|
1223
|
+
return this.visit(node.alternate);
|
|
1224
|
+
}
|
|
1225
|
+
return null;
|
|
1226
|
+
}
|
|
1227
|
+
visitLogicalExpressionAST(node) {
|
|
1228
|
+
const leftNode = node.left;
|
|
1229
|
+
const test = this.visit(leftNode);
|
|
1230
|
+
const testBoolValue = _boolValue(test);
|
|
1231
|
+
if (node.operator.type === TokenType.and) {
|
|
1232
|
+
return testBoolValue && _boolValue(this.visit(node.right));
|
|
1233
|
+
}
|
|
1234
|
+
if (node.operator.type === TokenType.or) {
|
|
1235
|
+
return testBoolValue || _boolValue(this.visit(node.right));
|
|
1236
|
+
}
|
|
1237
|
+
return false;
|
|
1238
|
+
}
|
|
1239
|
+
visitIndexAccessorAST(node) {
|
|
1240
|
+
const ownerNode = node.owner;
|
|
1241
|
+
const owner = this.visit(ownerNode);
|
|
1242
|
+
if (typeof owner === 'object') {
|
|
1243
|
+
const key = this.visit(node.key);
|
|
1244
|
+
return owner[key];
|
|
1245
|
+
}
|
|
1246
|
+
else if (Array.isArray(owner)) {
|
|
1247
|
+
const key = this.visit(node.key);
|
|
1248
|
+
return isNum(key) && owner.length < key && key >= 0 ? owner[key] : null;
|
|
1249
|
+
}
|
|
1250
|
+
return null;
|
|
1251
|
+
}
|
|
1252
|
+
visitObjectProperty(node) { }
|
|
1253
|
+
visitObjectExpression(ast) {
|
|
1254
|
+
const instance = {};
|
|
1255
|
+
const node = ast;
|
|
1256
|
+
for (const it of node.properties) {
|
|
1257
|
+
if (it instanceof ObjectProperty) {
|
|
1258
|
+
let key = this.visit(it.key);
|
|
1259
|
+
key = isString(key) ? key : key.toString();
|
|
1260
|
+
instance[key] = this.visit(it.value);
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
return instance;
|
|
1264
|
+
}
|
|
1265
|
+
visitArrayExpression(ast) {
|
|
1266
|
+
const node = ast;
|
|
1267
|
+
const args = this.resolveArguments(node.elements);
|
|
1268
|
+
return args;
|
|
1269
|
+
}
|
|
1270
|
+
resolveArguments(args) {
|
|
1271
|
+
return args.map((it) => {
|
|
1272
|
+
return this.visit(it);
|
|
1273
|
+
});
|
|
1274
|
+
}
|
|
1275
|
+
registerFunction(id, fn) {
|
|
1276
|
+
this._functionsRegistry[id] = fn;
|
|
1277
|
+
}
|
|
1278
|
+
unregisterFunction(id) {
|
|
1279
|
+
delete this._functionsRegistry[id];
|
|
1280
|
+
}
|
|
1281
|
+
execute(source, cache = true) {
|
|
1282
|
+
const module = MEvento.compile(source, cache);
|
|
1283
|
+
return this.visit(module);
|
|
1284
|
+
}
|
|
1285
|
+
static compile(source, cache = false) {
|
|
1286
|
+
const hash = hashCode(source);
|
|
1287
|
+
if (cache && this._cache.has(hash)) {
|
|
1288
|
+
return this._cache.get(hash);
|
|
1289
|
+
}
|
|
1290
|
+
const lexer = new Lexer(source);
|
|
1291
|
+
const parser = new Parser(lexer);
|
|
1292
|
+
const module = parser.parse();
|
|
1293
|
+
if (cache)
|
|
1294
|
+
this._cache.set(hash, module);
|
|
1295
|
+
return module;
|
|
1296
|
+
}
|
|
1297
|
+
static register(id, fn) {
|
|
1298
|
+
MEvento._globalFunctionsRegistry[id] = fn;
|
|
1299
|
+
}
|
|
1300
|
+
static unregister(id) {
|
|
1301
|
+
delete MEvento._globalFunctionsRegistry[id];
|
|
1302
|
+
}
|
|
1303
|
+
static run(source, cache = false) {
|
|
1304
|
+
const mevento = new MEvento();
|
|
1305
|
+
const module = MEvento.compile(source, cache);
|
|
1306
|
+
return mevento.visit(module);
|
|
1307
|
+
}
|
|
1308
|
+
static newInstance() {
|
|
1309
|
+
// const lexer = Lexer(source);
|
|
1310
|
+
// const parser = Parser(lexer);
|
|
1311
|
+
// const module = parser.parse();
|
|
1312
|
+
return new MEvento();
|
|
1313
|
+
}
|
|
1314
|
+
clone() {
|
|
1315
|
+
var ret = new MEvento();
|
|
1316
|
+
ret._functionsRegistry = { ...this._functionsRegistry };
|
|
1317
|
+
ret._memory = { ...this._memory };
|
|
1318
|
+
return ret;
|
|
1319
|
+
}
|
|
1320
|
+
newAsyncInstance() {
|
|
1321
|
+
const asyncIns = MEventoAsync.newInstance();
|
|
1322
|
+
asyncIns._memory = this._memory;
|
|
1323
|
+
/// ????
|
|
1324
|
+
asyncIns._functionsRegistry = this._functionsRegistry;
|
|
1325
|
+
return asyncIns;
|
|
1326
|
+
}
|
|
1327
|
+
;
|
|
1328
|
+
}
|
|
1329
|
+
MEvento._globalFunctionsRegistry = {};
|
|
1330
|
+
MEvento._cache = new Map();
|
|
1331
|
+
class MEventoAsync extends MEvento {
|
|
1332
|
+
constructor() {
|
|
1333
|
+
super();
|
|
1334
|
+
}
|
|
1335
|
+
async visitRootAST(node) {
|
|
1336
|
+
const list = node.body;
|
|
1337
|
+
let last;
|
|
1338
|
+
for (const n of list) {
|
|
1339
|
+
last = await this.visit(n);
|
|
1340
|
+
}
|
|
1341
|
+
return last;
|
|
1342
|
+
}
|
|
1343
|
+
async visitBlockStatementAST(node) {
|
|
1344
|
+
const list = node.body;
|
|
1345
|
+
let last;
|
|
1346
|
+
for (const n of list) {
|
|
1347
|
+
last = await this.visit(n);
|
|
1348
|
+
}
|
|
1349
|
+
return last;
|
|
1350
|
+
}
|
|
1351
|
+
async visitIdentifierAST(node) {
|
|
1352
|
+
const id = node.value;
|
|
1353
|
+
return this._memory[id];
|
|
1354
|
+
}
|
|
1355
|
+
async visitLiteralAST(node) {
|
|
1356
|
+
const value = node.value;
|
|
1357
|
+
return value;
|
|
1358
|
+
}
|
|
1359
|
+
async visitAssignmentExpressionAST(node) {
|
|
1360
|
+
const id = node.identifier;
|
|
1361
|
+
const init = node.init;
|
|
1362
|
+
let value = null;
|
|
1363
|
+
if (id instanceof IndexAccessorAST) {
|
|
1364
|
+
var target = await this.visit(id.owner);
|
|
1365
|
+
value = await this.visit(node.init);
|
|
1366
|
+
var property = await this.visit(id.key);
|
|
1367
|
+
this.assignProperty(target, property, value);
|
|
1368
|
+
}
|
|
1369
|
+
else if (id instanceof IdentifierAST) {
|
|
1370
|
+
value = await this.visit(init);
|
|
1371
|
+
this._memory[id.value] = value;
|
|
1372
|
+
}
|
|
1373
|
+
return value;
|
|
1374
|
+
}
|
|
1375
|
+
async visitExpressionStatementAST(node) {
|
|
1376
|
+
const expr = node.expression;
|
|
1377
|
+
return await this.visit(expr);
|
|
1378
|
+
}
|
|
1379
|
+
async visitCallExpressionAST(node) {
|
|
1380
|
+
const callee = node.callee;
|
|
1381
|
+
const args = node.arguments;
|
|
1382
|
+
const calleeName = callee.value;
|
|
1383
|
+
const fn = this._functionsRegistry[calleeName];
|
|
1384
|
+
const argValues = await Promise.all(args.map((e) => this.visit(e)));
|
|
1385
|
+
// print("argValues::", argValues)
|
|
1386
|
+
return await fn?.(argValues, this);
|
|
1387
|
+
}
|
|
1388
|
+
async visitBinaryExpressionAST(node) {
|
|
1389
|
+
const left = node.left;
|
|
1390
|
+
const right = node.right;
|
|
1391
|
+
const op = node.operation;
|
|
1392
|
+
const lValue = await this.visit(left);
|
|
1393
|
+
const rValue = await this.visit(right);
|
|
1394
|
+
switch (op.type) {
|
|
1395
|
+
case TokenType.plus:
|
|
1396
|
+
if (isNum(lValue) && isNum(rValue)) {
|
|
1397
|
+
return lValue + rValue;
|
|
1398
|
+
}
|
|
1399
|
+
return `${lValue}${rValue}`;
|
|
1400
|
+
case TokenType.minus:
|
|
1401
|
+
if (isNum(lValue) && isNum(rValue)) {
|
|
1402
|
+
return lValue - rValue;
|
|
1403
|
+
}
|
|
1404
|
+
if (isString(lValue) && isNum(rValue)) {
|
|
1405
|
+
return lValue * rValue;
|
|
1406
|
+
}
|
|
1407
|
+
if (isNum(lValue) && isString(rValue)) {
|
|
1408
|
+
return rValue * lValue;
|
|
1409
|
+
}
|
|
1410
|
+
throw new Error(`Operation ${op.value} not allowed no num value`);
|
|
1411
|
+
case TokenType.mult:
|
|
1412
|
+
if (isNum(lValue) && isNum(rValue)) {
|
|
1413
|
+
return lValue * rValue;
|
|
1414
|
+
}
|
|
1415
|
+
throw new Error(`Operation ${op.value} not allowed no num value`);
|
|
1416
|
+
case TokenType.div:
|
|
1417
|
+
if (isNum(lValue) && isNum(rValue)) {
|
|
1418
|
+
if (rValue === 0) {
|
|
1419
|
+
throw new Error("Invalid division by 0");
|
|
1420
|
+
}
|
|
1421
|
+
return lValue / rValue;
|
|
1422
|
+
}
|
|
1423
|
+
throw new Error(`Operation ${op.value} not allowed no num value`);
|
|
1424
|
+
case TokenType.mod:
|
|
1425
|
+
if (isNum(lValue) && isNum(rValue)) {
|
|
1426
|
+
return lValue % rValue;
|
|
1427
|
+
}
|
|
1428
|
+
throw new Error(`Operation ${op.value} not allowed no num value`);
|
|
1429
|
+
case TokenType.great:
|
|
1430
|
+
if (isNum(lValue) && isNum(rValue)) {
|
|
1431
|
+
return lValue > rValue;
|
|
1432
|
+
}
|
|
1433
|
+
throw new Error(`Operation ${op.value} not allowed no num value`);
|
|
1434
|
+
case TokenType.greatEq:
|
|
1435
|
+
if (isNum(lValue) && isNum(rValue)) {
|
|
1436
|
+
return lValue >= rValue;
|
|
1437
|
+
}
|
|
1438
|
+
throw new Error(`Operation ${op.value} not allowed no num value`);
|
|
1439
|
+
case TokenType.less:
|
|
1440
|
+
if (isNum(lValue) && isNum(rValue)) {
|
|
1441
|
+
return lValue < rValue;
|
|
1442
|
+
}
|
|
1443
|
+
throw new Error(`Operation ${op.value} not allowed no num value`);
|
|
1444
|
+
case TokenType.lessEq:
|
|
1445
|
+
if (isNum(lValue) && isNum(rValue)) {
|
|
1446
|
+
return lValue <= rValue;
|
|
1447
|
+
}
|
|
1448
|
+
throw new Error(`Operation ${op.value} not allowed no num value`);
|
|
1449
|
+
case TokenType.eqeq:
|
|
1450
|
+
return lValue === rValue;
|
|
1451
|
+
case TokenType.notEq:
|
|
1452
|
+
return lValue !== rValue;
|
|
1453
|
+
default:
|
|
1454
|
+
throw new Error(`Operation ${op.value} not allowed no num value`);
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
async visitUnaryExpressionAST(node) {
|
|
1458
|
+
const arg = node.argument;
|
|
1459
|
+
const op = node.operation;
|
|
1460
|
+
const argValue = await this.visit(arg);
|
|
1461
|
+
if (isNum(argValue)) {
|
|
1462
|
+
throw new Error(`Operation ${op.value} not allowed no num value`);
|
|
1463
|
+
}
|
|
1464
|
+
if (op.type === TokenType.plus) {
|
|
1465
|
+
return argValue;
|
|
1466
|
+
}
|
|
1467
|
+
else if (op.type === TokenType.minus) {
|
|
1468
|
+
return -argValue;
|
|
1469
|
+
}
|
|
1470
|
+
throw new Error(`Operation ${op.value} not allowed no num value`);
|
|
1471
|
+
}
|
|
1472
|
+
async visitIfStatementAST(node) {
|
|
1473
|
+
const testNode = node.test;
|
|
1474
|
+
const test = await this.visit(testNode);
|
|
1475
|
+
const testBoolValue = _boolValue(test);
|
|
1476
|
+
if (testBoolValue) {
|
|
1477
|
+
return await this.visit(node.consequent);
|
|
1478
|
+
}
|
|
1479
|
+
if (node.alternate !== null) {
|
|
1480
|
+
return await this.visit(node.alternate);
|
|
1481
|
+
}
|
|
1482
|
+
return null;
|
|
1483
|
+
}
|
|
1484
|
+
async visitLogicalExpressionAST(node) {
|
|
1485
|
+
const leftNode = node.left;
|
|
1486
|
+
const test = await this.visit(leftNode);
|
|
1487
|
+
const testBoolValue = _boolValue(test);
|
|
1488
|
+
if (node.operator.type === TokenType.and) {
|
|
1489
|
+
return testBoolValue && _boolValue(await this.visit(node.right));
|
|
1490
|
+
}
|
|
1491
|
+
if (node.operator.type === TokenType.or) {
|
|
1492
|
+
return testBoolValue || _boolValue(await this.visit(node.right));
|
|
1493
|
+
}
|
|
1494
|
+
return false;
|
|
1495
|
+
}
|
|
1496
|
+
async visitIndexAccessorAST(node) {
|
|
1497
|
+
const ownerNode = node.owner;
|
|
1498
|
+
const owner = await this.visit(ownerNode);
|
|
1499
|
+
if (typeof owner === 'object') {
|
|
1500
|
+
const key = await this.visit(node.key);
|
|
1501
|
+
return owner[key];
|
|
1502
|
+
}
|
|
1503
|
+
else if (Array.isArray(owner)) {
|
|
1504
|
+
const key = await this.visit(node.key);
|
|
1505
|
+
return isNum(key) && owner.length < key && key >= 0 ? owner[key] : null;
|
|
1506
|
+
}
|
|
1507
|
+
return null;
|
|
1508
|
+
}
|
|
1509
|
+
async visitObjectProperty(node) { }
|
|
1510
|
+
async visitObjectExpression(ast) {
|
|
1511
|
+
const instance = {};
|
|
1512
|
+
const node = ast;
|
|
1513
|
+
for (const it of node.properties) {
|
|
1514
|
+
if (it instanceof ObjectProperty) {
|
|
1515
|
+
let key = await this.visit(it.key);
|
|
1516
|
+
key = isString(key) ? key : key.toString();
|
|
1517
|
+
instance[key] = await this.visit(it.value);
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
return instance;
|
|
1521
|
+
}
|
|
1522
|
+
async visitArrayExpression(ast) {
|
|
1523
|
+
const node = ast;
|
|
1524
|
+
const args = await this.resolveArgumentsAsync(node.elements);
|
|
1525
|
+
return args;
|
|
1526
|
+
}
|
|
1527
|
+
async resolveArgumentsAsync(args) {
|
|
1528
|
+
return await Promise.all(args.map(async (it) => {
|
|
1529
|
+
return await this.visit(it);
|
|
1530
|
+
}));
|
|
1531
|
+
}
|
|
1532
|
+
registerFunction(id, fn) {
|
|
1533
|
+
this._functionsRegistry[id] = fn;
|
|
1534
|
+
}
|
|
1535
|
+
unregisterFunction(id) {
|
|
1536
|
+
delete this._functionsRegistry[id];
|
|
1537
|
+
}
|
|
1538
|
+
async execute(source, cache = true) {
|
|
1539
|
+
const module = MEvento.compile(source, cache);
|
|
1540
|
+
return await this.visit(module);
|
|
1541
|
+
}
|
|
1542
|
+
static async run(source, cache = false) {
|
|
1543
|
+
const mevento = new MEventoAsync();
|
|
1544
|
+
const module = MEvento.compile(source, cache);
|
|
1545
|
+
return await mevento.visit(module);
|
|
1546
|
+
}
|
|
1547
|
+
static newInstance() {
|
|
1548
|
+
// const lexer = Lexer(source);
|
|
1549
|
+
// const parser = Parser(lexer);
|
|
1550
|
+
// const module = parser.parse();
|
|
1551
|
+
return new MEventoAsync();
|
|
1552
|
+
}
|
|
1553
|
+
clone() {
|
|
1554
|
+
var ret = new MEventoAsync();
|
|
1555
|
+
ret._functionsRegistry = { ...this._functionsRegistry };
|
|
1556
|
+
ret._memory = { ...this._memory };
|
|
1557
|
+
return ret;
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
/*
|
|
1562
|
+
* Public API Surface of mevento
|
|
1563
|
+
*/
|
|
1564
|
+
|
|
1565
|
+
/**
|
|
1566
|
+
* Generated bundle index. Do not edit.
|
|
1567
|
+
*/
|
|
1568
|
+
|
|
1569
|
+
export { MEvento, MEventoAsync };
|
|
1570
|
+
//# sourceMappingURL=mevento.mjs.map
|