amis-formula 1.3.13 → 2.0.0-beta.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 +1 -1
- package/__mocks__/styleMock.js +6 -0
- package/__mocks__/svgMock.js +5 -0
- package/__tests__/__snapshots__/lexer.test.ts.snap +31 -0
- package/__tests__/__snapshots__/parser.test.ts.snap +2306 -0
- package/__tests__/evalute.test.ts +437 -0
- package/__tests__/fomula.test.ts +194 -0
- package/__tests__/jest.setup.js +22 -0
- package/__tests__/lexer.test.ts +55 -0
- package/__tests__/parser.test.ts +201 -0
- package/dist/evalutor.d.ts +5 -0
- package/dist/filter.d.ts +6 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +3032 -3841
- package/dist/lexer.d.ts +1 -29
- package/dist/parser.d.ts +1 -26
- package/dist/types.d.ts +83 -0
- package/package.json +1 -1
- package/rollup.config.js +3 -1
- package/scripts/genDoc.ts +162 -0
- package/scripts/lib.ts +55 -0
- package/src/evalutor.ts +1938 -0
- package/src/filter.ts +48 -0
- package/src/index.ts +35 -0
- package/src/lexer.ts +775 -0
- package/src/parser.ts +875 -0
- package/src/types.ts +119 -0
- package/tsconfig.json +36 -0
- package/.prettierrc +0 -20
- package/dist/util.d.ts +0 -36
package/src/parser.ts
ADDED
|
@@ -0,0 +1,875 @@
|
|
|
1
|
+
import {lexer as createLexer, TokenEnum, TokenName} from './lexer';
|
|
2
|
+
import {ParserOptions, ASTNode, Token, ASTNodeOrNull, Position} from './types';
|
|
3
|
+
|
|
4
|
+
const argListStates = {
|
|
5
|
+
START: 0,
|
|
6
|
+
COMMA: 1,
|
|
7
|
+
SET: 2
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const tempalteStates = {
|
|
11
|
+
START: 0,
|
|
12
|
+
SCRIPTING: 1
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const objectStates = {
|
|
16
|
+
START: 0,
|
|
17
|
+
KEY: 1,
|
|
18
|
+
COLON: 2,
|
|
19
|
+
VALUE: 3,
|
|
20
|
+
COMMA: 4
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export function parse(input: string, options?: ParserOptions): ASTNode {
|
|
24
|
+
let token: Token;
|
|
25
|
+
const lexer = createLexer(input, options);
|
|
26
|
+
const tokens: Array<Token> = [];
|
|
27
|
+
const tokenChunk: Array<Token> = [];
|
|
28
|
+
|
|
29
|
+
// 允许的变量名字空间
|
|
30
|
+
let variableNamespaces: Array<string> = options?.variableNamespaces ?? [
|
|
31
|
+
'window',
|
|
32
|
+
'cookie',
|
|
33
|
+
'ls',
|
|
34
|
+
'ss'
|
|
35
|
+
];
|
|
36
|
+
if (!Array.isArray(variableNamespaces)) {
|
|
37
|
+
variableNamespaces = [];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function next() {
|
|
41
|
+
token = tokenChunk.length ? tokenChunk.shift()! : lexer.next();
|
|
42
|
+
|
|
43
|
+
if (!token) {
|
|
44
|
+
throw new TypeError('next token is undefined');
|
|
45
|
+
}
|
|
46
|
+
tokens.push(token);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function back() {
|
|
50
|
+
tokenChunk.unshift(tokens.pop()!);
|
|
51
|
+
token = tokens[tokens.length - 1];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function matchPunctuator(operator: string | Array<string>) {
|
|
55
|
+
return (
|
|
56
|
+
token.type === TokenName[TokenEnum.Punctuator] &&
|
|
57
|
+
(Array.isArray(operator)
|
|
58
|
+
? ~operator.indexOf(token.value!)
|
|
59
|
+
: token.value === operator)
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function fatal() {
|
|
64
|
+
throw TypeError(
|
|
65
|
+
`Unexpected token ${token!.value} in ${token!.start.line}:${
|
|
66
|
+
token!.start.column
|
|
67
|
+
}`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function assert(result: any) {
|
|
72
|
+
if (!result) {
|
|
73
|
+
fatal();
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function expression(): ASTNodeOrNull {
|
|
79
|
+
return assignmentExpression();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function skipWhiteSpaceChar() {
|
|
83
|
+
while (
|
|
84
|
+
token.type === TokenName[TokenEnum.Char] &&
|
|
85
|
+
/^\s+$/m.test(token.value)
|
|
86
|
+
) {
|
|
87
|
+
next();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function collectFilterArg() {
|
|
92
|
+
const arg: Array<any> = [];
|
|
93
|
+
while (
|
|
94
|
+
!matchPunctuator(':') &&
|
|
95
|
+
token.type !== TokenName[TokenEnum.OpenFilter] &&
|
|
96
|
+
token.type !== TokenName[TokenEnum.CloseScript]
|
|
97
|
+
) {
|
|
98
|
+
const item =
|
|
99
|
+
literal() ||
|
|
100
|
+
numberLiteral() ||
|
|
101
|
+
stringLiteral() ||
|
|
102
|
+
template() ||
|
|
103
|
+
arrayLiteral() ||
|
|
104
|
+
rawScript() ||
|
|
105
|
+
objectLiteral();
|
|
106
|
+
|
|
107
|
+
if (item) {
|
|
108
|
+
arg.push(item);
|
|
109
|
+
} else {
|
|
110
|
+
assert(
|
|
111
|
+
~[
|
|
112
|
+
TokenName[TokenEnum.Identifier],
|
|
113
|
+
TokenName[TokenEnum.Punctuator],
|
|
114
|
+
TokenName[TokenEnum.Char]
|
|
115
|
+
].indexOf(token.type)
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// 其他的都当字符处理
|
|
119
|
+
if (arg.length && typeof arg[arg.length - 1] === 'string') {
|
|
120
|
+
arg[arg.length - 1] += token.raw || token.value;
|
|
121
|
+
} else {
|
|
122
|
+
arg.push(token.raw || token.value);
|
|
123
|
+
}
|
|
124
|
+
next();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (arg.length && typeof arg[arg.length - 1] === 'string') {
|
|
128
|
+
arg[arg.length - 1] = arg[arg.length - 1].replace(/\s+$/, '');
|
|
129
|
+
if (!arg[arg.length - 1]) {
|
|
130
|
+
arg.pop();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return arg;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function complexExpression(): ASTNodeOrNull {
|
|
137
|
+
let ast = expression();
|
|
138
|
+
|
|
139
|
+
const filters: Array<any> = [];
|
|
140
|
+
while (token.type === TokenName[TokenEnum.OpenFilter]) {
|
|
141
|
+
next();
|
|
142
|
+
|
|
143
|
+
skipWhiteSpaceChar();
|
|
144
|
+
const name = assert(identifier());
|
|
145
|
+
const fnName = name.name;
|
|
146
|
+
const args = [];
|
|
147
|
+
|
|
148
|
+
skipWhiteSpaceChar();
|
|
149
|
+
while (matchPunctuator(':')) {
|
|
150
|
+
next();
|
|
151
|
+
skipWhiteSpaceChar();
|
|
152
|
+
|
|
153
|
+
let argContents: any = collectFilterArg();
|
|
154
|
+
if (argContents.length === 1) {
|
|
155
|
+
argContents = argContents[0];
|
|
156
|
+
} else if (!argContents.length) {
|
|
157
|
+
argContents = '';
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
args.push(
|
|
161
|
+
Array.isArray(argContents)
|
|
162
|
+
? {
|
|
163
|
+
type: 'mixed',
|
|
164
|
+
body: argContents
|
|
165
|
+
}
|
|
166
|
+
: argContents
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
filters.push({
|
|
170
|
+
name: fnName,
|
|
171
|
+
args
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (filters.length) {
|
|
176
|
+
ast = {
|
|
177
|
+
type: 'filter',
|
|
178
|
+
input: ast,
|
|
179
|
+
filters,
|
|
180
|
+
start: ast!.start,
|
|
181
|
+
end: filters[filters.length - 1].end
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return ast;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function arrowFunction(): ASTNodeOrNull {
|
|
189
|
+
let ast: any = argList() || variable();
|
|
190
|
+
let args: Array<any> = [];
|
|
191
|
+
let start: Position;
|
|
192
|
+
|
|
193
|
+
if (ast?.type === 'variable') {
|
|
194
|
+
args = [ast];
|
|
195
|
+
start = ast.start;
|
|
196
|
+
} else if (ast?.type === 'arg-list') {
|
|
197
|
+
start = ast.start;
|
|
198
|
+
args = ast.body;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (Array.isArray(args) && matchPunctuator('=')) {
|
|
202
|
+
next();
|
|
203
|
+
if (matchPunctuator('>')) {
|
|
204
|
+
next();
|
|
205
|
+
const body = assert(expression());
|
|
206
|
+
return {
|
|
207
|
+
type: 'anonymous_function',
|
|
208
|
+
args: args,
|
|
209
|
+
return: body,
|
|
210
|
+
start: start!,
|
|
211
|
+
end: body.end
|
|
212
|
+
};
|
|
213
|
+
} else {
|
|
214
|
+
back();
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return ast;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function conditionalExpression(): ASTNodeOrNull {
|
|
222
|
+
const ast = logicalOrExpression();
|
|
223
|
+
|
|
224
|
+
if (!ast) {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (matchPunctuator('?')) {
|
|
229
|
+
next();
|
|
230
|
+
let consequent = assignmentExpression();
|
|
231
|
+
assert(consequent);
|
|
232
|
+
assert(matchPunctuator(':'));
|
|
233
|
+
|
|
234
|
+
next();
|
|
235
|
+
let alternate = assignmentExpression();
|
|
236
|
+
assert(alternate);
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
type: 'conditional',
|
|
240
|
+
test: ast,
|
|
241
|
+
consequent: consequent,
|
|
242
|
+
alternate: alternate,
|
|
243
|
+
start: ast.start,
|
|
244
|
+
end: alternate!.end
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return ast;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function binaryExpressionParser(
|
|
252
|
+
type: string,
|
|
253
|
+
operator: string,
|
|
254
|
+
parseFunction: () => any,
|
|
255
|
+
rightParseFunction = parseFunction,
|
|
256
|
+
leftKey = 'left',
|
|
257
|
+
rightKey = 'right'
|
|
258
|
+
) {
|
|
259
|
+
let ast = parseFunction();
|
|
260
|
+
if (!ast) {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (matchPunctuator(operator)) {
|
|
265
|
+
while (matchPunctuator(operator)) {
|
|
266
|
+
next();
|
|
267
|
+
const right = assert(rightParseFunction());
|
|
268
|
+
|
|
269
|
+
ast = {
|
|
270
|
+
type: type,
|
|
271
|
+
op: operator,
|
|
272
|
+
[leftKey]: ast,
|
|
273
|
+
[rightKey]: right,
|
|
274
|
+
start: ast.start,
|
|
275
|
+
end: right.end
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return ast;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function logicalOrExpression(): ASTNodeOrNull {
|
|
284
|
+
return binaryExpressionParser('or', '||', logicalAndExpression);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function logicalAndExpression(): ASTNodeOrNull {
|
|
288
|
+
return binaryExpressionParser('and', '&&', bitwiseOrExpression);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function bitwiseOrExpression(): ASTNodeOrNull {
|
|
292
|
+
return binaryExpressionParser('binary', '|', bitwiseXOrExpression);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function bitwiseXOrExpression(): ASTNodeOrNull {
|
|
296
|
+
return binaryExpressionParser('binary', '^', bitwiseAndExpression);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function bitwiseAndExpression(): ASTNodeOrNull {
|
|
300
|
+
return binaryExpressionParser('binary', '&', equalityExpression);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function equalityExpression(): ASTNodeOrNull {
|
|
304
|
+
return binaryExpressionParser('eq', '==', () =>
|
|
305
|
+
binaryExpressionParser('ne', '!=', () =>
|
|
306
|
+
binaryExpressionParser('streq', '===', () =>
|
|
307
|
+
binaryExpressionParser('strneq', '!==', relationalExpression)
|
|
308
|
+
)
|
|
309
|
+
)
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function relationalExpression(): ASTNodeOrNull {
|
|
314
|
+
return binaryExpressionParser('lt', '<', () =>
|
|
315
|
+
binaryExpressionParser('gt', '>', () =>
|
|
316
|
+
binaryExpressionParser('le', '<=', () =>
|
|
317
|
+
binaryExpressionParser('ge', '>=', shiftExpression)
|
|
318
|
+
)
|
|
319
|
+
)
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function shiftExpression(): ASTNodeOrNull {
|
|
324
|
+
return binaryExpressionParser('shift', '<<', () =>
|
|
325
|
+
binaryExpressionParser('shift', '>>', () =>
|
|
326
|
+
binaryExpressionParser('shift', '>>>', additiveExpression)
|
|
327
|
+
)
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function additiveExpression(): ASTNodeOrNull {
|
|
332
|
+
return binaryExpressionParser('add', '+', () =>
|
|
333
|
+
binaryExpressionParser('minus', '-', multiplicativeExpression)
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function multiplicativeExpression(): ASTNodeOrNull {
|
|
338
|
+
return binaryExpressionParser('multiply', '*', () =>
|
|
339
|
+
binaryExpressionParser('divide', '/', () =>
|
|
340
|
+
binaryExpressionParser('remainder', '%', powerExpression)
|
|
341
|
+
)
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function powerExpression(): ASTNodeOrNull {
|
|
346
|
+
return binaryExpressionParser('power', '**', unaryExpression);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function unaryExpression(): ASTNodeOrNull {
|
|
350
|
+
const unaryOperators = ['+', '-', '~', '!'];
|
|
351
|
+
const stack: Array<any> = [];
|
|
352
|
+
while (matchPunctuator(unaryOperators)) {
|
|
353
|
+
stack.push(token);
|
|
354
|
+
next();
|
|
355
|
+
}
|
|
356
|
+
let ast: any = postfixExpression();
|
|
357
|
+
assert(!stack.length || ast);
|
|
358
|
+
while (stack.length) {
|
|
359
|
+
const op = stack.pop();
|
|
360
|
+
|
|
361
|
+
ast = {
|
|
362
|
+
type: 'unary',
|
|
363
|
+
op: op.value,
|
|
364
|
+
value: ast,
|
|
365
|
+
start: op.start,
|
|
366
|
+
end: op.end
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
return ast;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function postfixExpression(
|
|
373
|
+
parseFunction: () => any = leftHandSideExpression
|
|
374
|
+
): ASTNodeOrNull {
|
|
375
|
+
let ast = parseFunction();
|
|
376
|
+
if (!ast) {
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
while (matchPunctuator('[') || matchPunctuator('.')) {
|
|
381
|
+
const isDot = matchPunctuator('.');
|
|
382
|
+
next();
|
|
383
|
+
const right = assert(
|
|
384
|
+
isDot ? identifier() || numberLiteral() || rawScript() : expression()
|
|
385
|
+
);
|
|
386
|
+
|
|
387
|
+
if (!isDot) {
|
|
388
|
+
assert(matchPunctuator(']'));
|
|
389
|
+
next();
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
ast = {
|
|
393
|
+
type: 'getter',
|
|
394
|
+
host: ast,
|
|
395
|
+
key: right,
|
|
396
|
+
start: ast.start,
|
|
397
|
+
end: right.end
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
return ast;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function leftHandSideExpression(): ASTNodeOrNull {
|
|
405
|
+
return functionCall() || arrowFunction() || primaryExpression();
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function varibleKey(allowVariable = false, inObject = false): ASTNodeOrNull {
|
|
409
|
+
return (
|
|
410
|
+
(allowVariable ? variable() : identifier()) ||
|
|
411
|
+
stringLiteral() ||
|
|
412
|
+
numberLiteral() ||
|
|
413
|
+
(inObject ? objectTemplateKey() : template())
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function objectTemplateKey(): ASTNodeOrNull {
|
|
418
|
+
if (matchPunctuator('[')) {
|
|
419
|
+
next();
|
|
420
|
+
const key = assert(template());
|
|
421
|
+
assert(matchPunctuator(']'));
|
|
422
|
+
next();
|
|
423
|
+
return key;
|
|
424
|
+
}
|
|
425
|
+
return null;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function stringLiteral(): ASTNodeOrNull {
|
|
429
|
+
if (token.type === TokenName[TokenEnum.StringLiteral]) {
|
|
430
|
+
const cToken = token;
|
|
431
|
+
next();
|
|
432
|
+
return {
|
|
433
|
+
type: 'string',
|
|
434
|
+
value: cToken.value,
|
|
435
|
+
start: cToken.start,
|
|
436
|
+
end: cToken.end
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function numberLiteral(): ASTNodeOrNull {
|
|
443
|
+
if (token.type === TokenName[TokenEnum.NumericLiteral]) {
|
|
444
|
+
const value = token.value;
|
|
445
|
+
const cToken = token;
|
|
446
|
+
next();
|
|
447
|
+
return {
|
|
448
|
+
type: 'literal',
|
|
449
|
+
value: value,
|
|
450
|
+
start: cToken.start,
|
|
451
|
+
end: cToken.end
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
return null;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function template(): ASTNodeOrNull {
|
|
459
|
+
if (matchPunctuator('`')) {
|
|
460
|
+
const start = token;
|
|
461
|
+
let end = start;
|
|
462
|
+
next();
|
|
463
|
+
let state = tempalteStates.START;
|
|
464
|
+
const ast: ASTNode = {
|
|
465
|
+
type: 'template',
|
|
466
|
+
body: [],
|
|
467
|
+
start: start.start,
|
|
468
|
+
end: start.end
|
|
469
|
+
};
|
|
470
|
+
while (true) {
|
|
471
|
+
if (state === tempalteStates.SCRIPTING) {
|
|
472
|
+
const exp = assert(expression());
|
|
473
|
+
ast.body.push(exp);
|
|
474
|
+
assert(token.type === TokenName[TokenEnum.TemplateRightBrace]);
|
|
475
|
+
next();
|
|
476
|
+
state = tempalteStates.START;
|
|
477
|
+
} else {
|
|
478
|
+
if (matchPunctuator('`')) {
|
|
479
|
+
end = token;
|
|
480
|
+
next();
|
|
481
|
+
break;
|
|
482
|
+
} else if (token.type === TokenName[TokenEnum.TemplateLeftBrace]) {
|
|
483
|
+
next();
|
|
484
|
+
state = tempalteStates.SCRIPTING;
|
|
485
|
+
} else if (token.type === TokenName[TokenEnum.TemplateRaw]) {
|
|
486
|
+
ast.body.push({
|
|
487
|
+
type: 'template_raw',
|
|
488
|
+
value: token.value,
|
|
489
|
+
start: token.start,
|
|
490
|
+
end: token.end
|
|
491
|
+
});
|
|
492
|
+
next();
|
|
493
|
+
} else {
|
|
494
|
+
fatal();
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
ast.end = end.end;
|
|
500
|
+
return ast;
|
|
501
|
+
}
|
|
502
|
+
return null;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
function identifier(): ASTNodeOrNull {
|
|
506
|
+
if (token.type === TokenName[TokenEnum.Identifier]) {
|
|
507
|
+
const cToken = token;
|
|
508
|
+
next();
|
|
509
|
+
return {
|
|
510
|
+
type: 'identifier',
|
|
511
|
+
name: cToken.value,
|
|
512
|
+
start: cToken.start,
|
|
513
|
+
end: cToken.end
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
return null;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
function primaryExpression(): ASTNodeOrNull {
|
|
520
|
+
return (
|
|
521
|
+
variable() ||
|
|
522
|
+
literal() ||
|
|
523
|
+
numberLiteral() ||
|
|
524
|
+
stringLiteral() ||
|
|
525
|
+
template() ||
|
|
526
|
+
arrayLiteral() ||
|
|
527
|
+
objectLiteral() ||
|
|
528
|
+
(() => {
|
|
529
|
+
const ast = expressionList();
|
|
530
|
+
|
|
531
|
+
if (ast?.body.length === 1) {
|
|
532
|
+
return ast.body[0];
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
return ast;
|
|
536
|
+
})() ||
|
|
537
|
+
rawScript()
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
function literal(): ASTNodeOrNull {
|
|
542
|
+
if (
|
|
543
|
+
token.type === TokenName[TokenEnum.Literal] ||
|
|
544
|
+
token.type === TokenName[TokenEnum.BooleanLiteral]
|
|
545
|
+
) {
|
|
546
|
+
const value = token.value;
|
|
547
|
+
const cToken = token;
|
|
548
|
+
next();
|
|
549
|
+
return {
|
|
550
|
+
type: 'literal',
|
|
551
|
+
value: value,
|
|
552
|
+
start: cToken.start,
|
|
553
|
+
end: cToken.end
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
function functionCall(): ASTNodeOrNull {
|
|
561
|
+
if (token.type === TokenName[TokenEnum.Identifier]) {
|
|
562
|
+
const id = token;
|
|
563
|
+
next();
|
|
564
|
+
if (matchPunctuator('(')) {
|
|
565
|
+
const argList = expressionList();
|
|
566
|
+
assert(argList);
|
|
567
|
+
return {
|
|
568
|
+
type: 'func_call',
|
|
569
|
+
identifier: id.value,
|
|
570
|
+
args: argList?.body,
|
|
571
|
+
start: id.start,
|
|
572
|
+
end: argList!.end
|
|
573
|
+
};
|
|
574
|
+
} else {
|
|
575
|
+
back();
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
return null;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
function arrayLiteral(): ASTNodeOrNull {
|
|
582
|
+
if (matchPunctuator('[')) {
|
|
583
|
+
const argList = expressionList('[', ']');
|
|
584
|
+
assert(argList);
|
|
585
|
+
return {
|
|
586
|
+
type: 'array',
|
|
587
|
+
members: argList?.body,
|
|
588
|
+
start: argList!.start,
|
|
589
|
+
end: argList!.end
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
return null;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
function expressionList(startOP = '(', endOp = ')'): ASTNodeOrNull {
|
|
596
|
+
if (matchPunctuator(startOP)) {
|
|
597
|
+
const start = token;
|
|
598
|
+
let end: Token;
|
|
599
|
+
next();
|
|
600
|
+
const args: Array<any> = [];
|
|
601
|
+
let state = argListStates.START;
|
|
602
|
+
|
|
603
|
+
while (true) {
|
|
604
|
+
if (state === argListStates.COMMA || !matchPunctuator(endOp)) {
|
|
605
|
+
const arg = assert(expression());
|
|
606
|
+
args.push(arg);
|
|
607
|
+
state = argListStates.START;
|
|
608
|
+
|
|
609
|
+
if (matchPunctuator(',')) {
|
|
610
|
+
next();
|
|
611
|
+
state = argListStates.COMMA;
|
|
612
|
+
}
|
|
613
|
+
} else if (matchPunctuator(endOp)) {
|
|
614
|
+
end = token;
|
|
615
|
+
next();
|
|
616
|
+
break;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
return {
|
|
620
|
+
type: 'expression-list',
|
|
621
|
+
body: args,
|
|
622
|
+
start: start.start,
|
|
623
|
+
end: end!.end
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
return null;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
function argList(startOP = '(', endOp = ')'): ASTNodeOrNull {
|
|
630
|
+
let count = 0;
|
|
631
|
+
let rollback = () => {
|
|
632
|
+
while (count-- > 0) {
|
|
633
|
+
back();
|
|
634
|
+
}
|
|
635
|
+
return null;
|
|
636
|
+
};
|
|
637
|
+
if (matchPunctuator(startOP)) {
|
|
638
|
+
const start = token;
|
|
639
|
+
let end: Token = start;
|
|
640
|
+
next();
|
|
641
|
+
count++;
|
|
642
|
+
const args: Array<any> = [];
|
|
643
|
+
let state = argListStates.START;
|
|
644
|
+
|
|
645
|
+
while (!matchPunctuator(endOp)) {
|
|
646
|
+
if (state === argListStates.COMMA || state === argListStates.START) {
|
|
647
|
+
const arg = variable(false);
|
|
648
|
+
|
|
649
|
+
if (!arg) {
|
|
650
|
+
return rollback();
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
count++;
|
|
654
|
+
args.push(arg);
|
|
655
|
+
state = argListStates.SET;
|
|
656
|
+
} else if (state === argListStates.SET && matchPunctuator(',')) {
|
|
657
|
+
next();
|
|
658
|
+
count++;
|
|
659
|
+
state = argListStates.COMMA;
|
|
660
|
+
} else {
|
|
661
|
+
return rollback();
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
if (matchPunctuator(endOp)) {
|
|
666
|
+
end = token;
|
|
667
|
+
next();
|
|
668
|
+
return {
|
|
669
|
+
type: 'arg-list',
|
|
670
|
+
body: args,
|
|
671
|
+
start: start.start,
|
|
672
|
+
end: end.end
|
|
673
|
+
};
|
|
674
|
+
} else {
|
|
675
|
+
return rollback();
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
return null;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
function objectLiteral(): ASTNodeOrNull {
|
|
682
|
+
if (matchPunctuator('{')) {
|
|
683
|
+
const start = token;
|
|
684
|
+
let end = start;
|
|
685
|
+
next();
|
|
686
|
+
let ast: ASTNode = {
|
|
687
|
+
type: 'object',
|
|
688
|
+
members: [],
|
|
689
|
+
start: start.start,
|
|
690
|
+
end: start.end
|
|
691
|
+
};
|
|
692
|
+
let state = objectStates.START;
|
|
693
|
+
let key: any, value: any;
|
|
694
|
+
while (true) {
|
|
695
|
+
if (state === objectStates.KEY) {
|
|
696
|
+
assert(matchPunctuator(':'));
|
|
697
|
+
next();
|
|
698
|
+
state = objectStates.COLON;
|
|
699
|
+
} else if (state === objectStates.COLON) {
|
|
700
|
+
value = assert(expression());
|
|
701
|
+
ast.members.push({
|
|
702
|
+
key,
|
|
703
|
+
value
|
|
704
|
+
});
|
|
705
|
+
state = objectStates.VALUE;
|
|
706
|
+
} else if (state === objectStates.VALUE) {
|
|
707
|
+
if (matchPunctuator(',')) {
|
|
708
|
+
next();
|
|
709
|
+
state = objectStates.COMMA;
|
|
710
|
+
} else if (matchPunctuator('}')) {
|
|
711
|
+
end = token;
|
|
712
|
+
next();
|
|
713
|
+
break;
|
|
714
|
+
} else {
|
|
715
|
+
fatal();
|
|
716
|
+
}
|
|
717
|
+
} else {
|
|
718
|
+
if (state != objectStates.COMMA && matchPunctuator('}')) {
|
|
719
|
+
end = token;
|
|
720
|
+
next();
|
|
721
|
+
break;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
key = assert(varibleKey(false, true));
|
|
725
|
+
state = objectStates.KEY;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
ast.end = end.end;
|
|
730
|
+
return ast;
|
|
731
|
+
}
|
|
732
|
+
return null;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
function assignmentExpression(): ASTNodeOrNull {
|
|
736
|
+
return conditionalExpression();
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
function contents(): ASTNodeOrNull {
|
|
740
|
+
const node: ASTNode = {
|
|
741
|
+
type: 'document',
|
|
742
|
+
body: [],
|
|
743
|
+
start: token.start,
|
|
744
|
+
end: token.end
|
|
745
|
+
};
|
|
746
|
+
while (token.type !== TokenName[TokenEnum.EOF]) {
|
|
747
|
+
const ast = raw() || rawScript() || oldVariable();
|
|
748
|
+
|
|
749
|
+
if (!ast) {
|
|
750
|
+
break;
|
|
751
|
+
}
|
|
752
|
+
node.body.push(ast);
|
|
753
|
+
}
|
|
754
|
+
if (node.body.length) {
|
|
755
|
+
node.end = node.body[node.body.length - 1].end;
|
|
756
|
+
}
|
|
757
|
+
return node;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
function raw(): ASTNodeOrNull {
|
|
761
|
+
if (token.type !== TokenName[TokenEnum.RAW]) {
|
|
762
|
+
return null;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
const cToken = token;
|
|
766
|
+
next();
|
|
767
|
+
return {
|
|
768
|
+
type: 'raw',
|
|
769
|
+
value: cToken.value,
|
|
770
|
+
start: cToken.start,
|
|
771
|
+
end: cToken.end
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
function rawScript(): ASTNodeOrNull {
|
|
776
|
+
if (token.type !== TokenName[TokenEnum.OpenScript]) {
|
|
777
|
+
return null;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
const start = token;
|
|
781
|
+
let end = start;
|
|
782
|
+
next();
|
|
783
|
+
const exp = assert(complexExpression());
|
|
784
|
+
assert(token.type === TokenName[TokenEnum.CloseScript]);
|
|
785
|
+
end = token;
|
|
786
|
+
next();
|
|
787
|
+
|
|
788
|
+
return {
|
|
789
|
+
type: 'script',
|
|
790
|
+
body: exp,
|
|
791
|
+
start: start.start,
|
|
792
|
+
end: end.end
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
function variable(allowNameSpace = true): ASTNodeOrNull {
|
|
797
|
+
if (token.type === TokenName[TokenEnum.Identifier]) {
|
|
798
|
+
const cToken = token;
|
|
799
|
+
next();
|
|
800
|
+
|
|
801
|
+
if (
|
|
802
|
+
allowNameSpace &&
|
|
803
|
+
matchPunctuator(':') &&
|
|
804
|
+
~variableNamespaces.indexOf(cToken.value)
|
|
805
|
+
) {
|
|
806
|
+
next();
|
|
807
|
+
const body = assert(postfixExpression());
|
|
808
|
+
return {
|
|
809
|
+
type: 'ns-variable',
|
|
810
|
+
namespace: cToken.value,
|
|
811
|
+
body,
|
|
812
|
+
start: cToken.start,
|
|
813
|
+
end: body.end
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
return {
|
|
818
|
+
type: 'variable',
|
|
819
|
+
name: cToken.value,
|
|
820
|
+
start: cToken.start,
|
|
821
|
+
end: cToken.end
|
|
822
|
+
};
|
|
823
|
+
} else if (matchPunctuator('&')) {
|
|
824
|
+
const v = token;
|
|
825
|
+
next();
|
|
826
|
+
return {
|
|
827
|
+
type: 'variable',
|
|
828
|
+
name: '&',
|
|
829
|
+
start: v.start,
|
|
830
|
+
end: v.end
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
return null;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
function oldVariable(): ASTNodeOrNull {
|
|
837
|
+
if (token.type !== TokenName[TokenEnum.Variable]) {
|
|
838
|
+
return null;
|
|
839
|
+
}
|
|
840
|
+
const prevToken = token;
|
|
841
|
+
next();
|
|
842
|
+
return {
|
|
843
|
+
type: 'script',
|
|
844
|
+
body: prevToken.value.split('.').reduce((prev: any, key: string) => {
|
|
845
|
+
return prev
|
|
846
|
+
? {
|
|
847
|
+
type: 'getter',
|
|
848
|
+
host: prev,
|
|
849
|
+
key,
|
|
850
|
+
start: prevToken.start,
|
|
851
|
+
end: prevToken.end
|
|
852
|
+
}
|
|
853
|
+
: {
|
|
854
|
+
type: 'variable',
|
|
855
|
+
name: key,
|
|
856
|
+
start: prevToken.start,
|
|
857
|
+
end: prevToken.end
|
|
858
|
+
};
|
|
859
|
+
}, null),
|
|
860
|
+
start: prevToken.start,
|
|
861
|
+
end: prevToken.end
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
next();
|
|
866
|
+
const ast = options?.variableMode
|
|
867
|
+
? postfixExpression(variable)
|
|
868
|
+
: options?.evalMode
|
|
869
|
+
? expression()
|
|
870
|
+
: contents();
|
|
871
|
+
|
|
872
|
+
assert(token!?.type === TokenName[TokenEnum.EOF]);
|
|
873
|
+
|
|
874
|
+
return ast!;
|
|
875
|
+
}
|