affinirum 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/LICENSE +661 -0
- package/README.md +301 -0
- package/dst/Affinirum.d.ts +73 -0
- package/dst/Affinirum.js +536 -0
- package/dst/Constant.d.ts +15 -0
- package/dst/Constant.js +26 -0
- package/dst/Constants.d.ts +2 -0
- package/dst/Constants.js +22 -0
- package/dst/Functions.d.ts +2 -0
- package/dst/Functions.js +85 -0
- package/dst/Keywords.d.ts +1 -0
- package/dst/Keywords.js +17 -0
- package/dst/Node.d.ts +13 -0
- package/dst/Node.js +22 -0
- package/dst/ParserFrame.d.ts +13 -0
- package/dst/ParserFrame.js +80 -0
- package/dst/ParserState.d.ts +54 -0
- package/dst/ParserState.js +562 -0
- package/dst/StaticScope.d.ts +13 -0
- package/dst/StaticScope.js +44 -0
- package/dst/Type.d.ts +72 -0
- package/dst/Type.js +196 -0
- package/dst/Value.d.ts +3 -0
- package/dst/Value.js +1 -0
- package/dst/Variable.d.ts +13 -0
- package/dst/Variable.js +25 -0
- package/dst/atom/ArrayAtom.d.ts +11 -0
- package/dst/atom/ArrayAtom.js +38 -0
- package/dst/atom/FunctionAtom.d.ts +17 -0
- package/dst/atom/FunctionAtom.js +54 -0
- package/dst/atom/ObjectAtom.d.ts +11 -0
- package/dst/atom/ObjectAtom.js +44 -0
- package/dst/atom/PrimitiveAtom.d.ts +8 -0
- package/dst/atom/PrimitiveAtom.js +26 -0
- package/dst/cjs/Affinirum.js +540 -0
- package/dst/cjs/Constant.js +30 -0
- package/dst/cjs/Constants.js +25 -0
- package/dst/cjs/Functions.js +88 -0
- package/dst/cjs/Keywords.js +20 -0
- package/dst/cjs/Node.js +26 -0
- package/dst/cjs/ParserFrame.js +84 -0
- package/dst/cjs/ParserState.js +566 -0
- package/dst/cjs/StaticScope.js +48 -0
- package/dst/cjs/Type.js +200 -0
- package/dst/cjs/Value.js +2 -0
- package/dst/cjs/Variable.js +29 -0
- package/dst/cjs/atom/ArrayAtom.js +42 -0
- package/dst/cjs/atom/FunctionAtom.js +58 -0
- package/dst/cjs/atom/ObjectAtom.js +48 -0
- package/dst/cjs/atom/PrimitiveAtom.js +30 -0
- package/dst/cjs/constant/Array.js +93 -0
- package/dst/cjs/constant/Boolean.js +25 -0
- package/dst/cjs/constant/Buffer.js +68 -0
- package/dst/cjs/constant/Enumerable.js +34 -0
- package/dst/cjs/constant/Float.js +85 -0
- package/dst/cjs/constant/Integer.js +104 -0
- package/dst/cjs/constant/Iterable.js +24 -0
- package/dst/cjs/constant/Number.js +59 -0
- package/dst/cjs/constant/Object.js +13 -0
- package/dst/cjs/constant/String.js +197 -0
- package/dst/cjs/constant/Timestamp.js +56 -0
- package/dst/cjs/constant/Unknown.js +101 -0
- package/dst/cjs/constant/notation/AN.js +46 -0
- package/dst/cjs/constant/notation/JSON.js +14 -0
- package/dst/cjs/index.js +19 -0
- package/dst/cjs/node/ArrayNode.js +34 -0
- package/dst/cjs/node/BlockNode.js +33 -0
- package/dst/cjs/node/CallNode.js +54 -0
- package/dst/cjs/node/ConstantNode.js +33 -0
- package/dst/cjs/node/LoopNode.js +34 -0
- package/dst/cjs/node/ObjectNode.js +42 -0
- package/dst/cjs/node/SwitchNode.js +45 -0
- package/dst/cjs/node/VariableNode.js +30 -0
- package/dst/cjs/package.json +3 -0
- package/dst/constant/Array.d.ts +22 -0
- package/dst/constant/Array.js +90 -0
- package/dst/constant/Boolean.d.ts +11 -0
- package/dst/constant/Boolean.js +22 -0
- package/dst/constant/Buffer.d.ts +10 -0
- package/dst/constant/Buffer.js +61 -0
- package/dst/constant/Enumerable.d.ts +5 -0
- package/dst/constant/Enumerable.js +31 -0
- package/dst/constant/Float.d.ts +22 -0
- package/dst/constant/Float.js +80 -0
- package/dst/constant/Integer.d.ts +10 -0
- package/dst/constant/Integer.js +100 -0
- package/dst/constant/Iterable.d.ts +3 -0
- package/dst/constant/Iterable.js +21 -0
- package/dst/constant/Number.d.ts +14 -0
- package/dst/constant/Number.js +56 -0
- package/dst/constant/Object.d.ts +7 -0
- package/dst/constant/Object.js +10 -0
- package/dst/constant/String.d.ts +34 -0
- package/dst/constant/String.js +179 -0
- package/dst/constant/Timestamp.d.ts +20 -0
- package/dst/constant/Timestamp.js +50 -0
- package/dst/constant/Unknown.d.ts +10 -0
- package/dst/constant/Unknown.js +95 -0
- package/dst/constant/notation/AN.d.ts +6 -0
- package/dst/constant/notation/AN.js +42 -0
- package/dst/constant/notation/JSON.d.ts +7 -0
- package/dst/constant/notation/JSON.js +10 -0
- package/dst/index.d.ts +3 -0
- package/dst/index.js +3 -0
- package/dst/node/ArrayNode.d.ts +12 -0
- package/dst/node/ArrayNode.js +30 -0
- package/dst/node/BlockNode.d.ts +12 -0
- package/dst/node/BlockNode.js +29 -0
- package/dst/node/CallNode.d.ts +14 -0
- package/dst/node/CallNode.js +50 -0
- package/dst/node/ConstantNode.d.ts +15 -0
- package/dst/node/ConstantNode.js +29 -0
- package/dst/node/LoopNode.d.ts +13 -0
- package/dst/node/LoopNode.js +30 -0
- package/dst/node/ObjectNode.d.ts +13 -0
- package/dst/node/ObjectNode.js +38 -0
- package/dst/node/SwitchNode.d.ts +14 -0
- package/dst/node/SwitchNode.js +41 -0
- package/dst/node/VariableNode.d.ts +14 -0
- package/dst/node/VariableNode.js +26 -0
- package/package.json +67 -0
package/dst/Affinirum.js
ADDED
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
import { funcOr, funcAnd, funcNot } from './constant/Boolean.js';
|
|
2
|
+
import { funcAdd } from './constant/Enumerable.js';
|
|
3
|
+
import { funcAt } from './constant/Iterable.js';
|
|
4
|
+
import { funcGreaterThan, funcLessThan, funcGreaterOrEqual, funcLessOrEqual, funcSubtract, funcMultiply, funcDivide, funcRemainder, funcPower, funcNegate } from './constant/Number.js';
|
|
5
|
+
import { funcCoalesce, funcEqual, funcNotEqual } from './constant/Unknown.js';
|
|
6
|
+
import { Constant } from './Constant.js';
|
|
7
|
+
import { Variable } from './Variable.js';
|
|
8
|
+
import { Type } from './Type.js';
|
|
9
|
+
import { Keywords } from './Keywords.js';
|
|
10
|
+
import { Constants } from './Constants.js';
|
|
11
|
+
import { Functions } from './Functions.js';
|
|
12
|
+
import { ArrayNode } from './node/ArrayNode.js';
|
|
13
|
+
import { BlockNode } from './node/BlockNode.js';
|
|
14
|
+
import { ConstantNode } from './node/ConstantNode.js';
|
|
15
|
+
import { CallNode } from './node/CallNode.js';
|
|
16
|
+
import { LoopNode } from './node/LoopNode.js';
|
|
17
|
+
import { ObjectNode } from './node/ObjectNode.js';
|
|
18
|
+
import { SwitchNode } from './node/SwitchNode.js';
|
|
19
|
+
import { VariableNode } from './node/VariableNode.js';
|
|
20
|
+
import { ParserState } from './ParserState.js';
|
|
21
|
+
import { StaticScope } from './StaticScope.js';
|
|
22
|
+
export class Affinirum {
|
|
23
|
+
static keywords = [...Keywords, ...Constants.map((c) => c[0])];
|
|
24
|
+
_script;
|
|
25
|
+
_strict;
|
|
26
|
+
_root;
|
|
27
|
+
_vframes = new Map();
|
|
28
|
+
_variables = new Map();
|
|
29
|
+
_constants = new Map(Constants);
|
|
30
|
+
_functions = new Map(Functions);
|
|
31
|
+
_scope = new StaticScope();
|
|
32
|
+
/**
|
|
33
|
+
Creates compiled expression. Any parsed token not recognized as a constant or a function will be compiled as a variable.
|
|
34
|
+
@param script Math expression to compile.
|
|
35
|
+
@param config Optional expected type, strict mode, variable types, constant values and functions to add for the compilation.
|
|
36
|
+
If expected type is provided then expression return type is matched against it.
|
|
37
|
+
If strict mode is set then undeclared variables will not be allowed in expression.
|
|
38
|
+
*/
|
|
39
|
+
constructor(script, config) {
|
|
40
|
+
this._script = script;
|
|
41
|
+
this._strict = config?.strict ?? false;
|
|
42
|
+
if (config?.variables) {
|
|
43
|
+
for (const v in config.variables) {
|
|
44
|
+
this._variables.set(v, new Variable(config.variables[v]));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const state = new ParserState(this._script);
|
|
48
|
+
this._root = this._block(state.next(), this._scope);
|
|
49
|
+
if (!state.isVoid) {
|
|
50
|
+
state.throwError('unexpected expression token or expression end');
|
|
51
|
+
}
|
|
52
|
+
this._root = this._root.compile(config?.type ?? Type.Unknown);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
Returns original script text.
|
|
56
|
+
*/
|
|
57
|
+
get script() {
|
|
58
|
+
return this._script;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
Returns compiled expression return value type.
|
|
62
|
+
*/
|
|
63
|
+
get type() {
|
|
64
|
+
return this._root.type;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
Returns string representing parsed node tree structure.
|
|
68
|
+
@returns Parsed expression string.
|
|
69
|
+
*/
|
|
70
|
+
toString() {
|
|
71
|
+
return this._root.toString();
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
Returns record with compiled variable names and expected types.
|
|
75
|
+
@returns Record with variable names and types.
|
|
76
|
+
*/
|
|
77
|
+
variables() {
|
|
78
|
+
const types = {};
|
|
79
|
+
const variables = this._scope.variables();
|
|
80
|
+
for (const name in variables) {
|
|
81
|
+
types[name] = variables[name].type;
|
|
82
|
+
}
|
|
83
|
+
return types;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
Evaluates compiled expression using provided variable values.
|
|
87
|
+
@param values Record with variable names and values.
|
|
88
|
+
@returns Calculated value.
|
|
89
|
+
*/
|
|
90
|
+
evaluate(values) {
|
|
91
|
+
const variables = this._scope.variables();
|
|
92
|
+
for (const name in variables) {
|
|
93
|
+
if (!Object.prototype.hasOwnProperty.call(values, name)) {
|
|
94
|
+
this._vframes.get(name)?.throwError(`undefined variable ${name}:\n`);
|
|
95
|
+
}
|
|
96
|
+
const variable = variables[name];
|
|
97
|
+
const value = values?.[name] ?? undefined;
|
|
98
|
+
if (!variable.type.match(Type.of(value))) {
|
|
99
|
+
this._vframes.get(name)?.throwError(`unexpected type ${Type.of(value)} for variable ${name} of type ${variable.type}:\n`);
|
|
100
|
+
}
|
|
101
|
+
variable.value = value;
|
|
102
|
+
}
|
|
103
|
+
return this._root.evaluate();
|
|
104
|
+
}
|
|
105
|
+
_block(state, scope) {
|
|
106
|
+
const frame = state.starts();
|
|
107
|
+
const nodes = [this._unit(state, scope)];
|
|
108
|
+
while (state.isSemicolonSeparator) {
|
|
109
|
+
nodes.push(this._unit(state.next(), scope));
|
|
110
|
+
}
|
|
111
|
+
return new BlockNode(frame.ends(state), nodes);
|
|
112
|
+
}
|
|
113
|
+
_unit(state, scope) {
|
|
114
|
+
return this._disjunction(state, scope);
|
|
115
|
+
}
|
|
116
|
+
_disjunction(state, scope) {
|
|
117
|
+
let node = this._conjunction(state, scope);
|
|
118
|
+
while (state.operator === funcOr) {
|
|
119
|
+
node = this._call(state.starts(), state.operator, [node, this._conjunction(state.next(), scope)]);
|
|
120
|
+
}
|
|
121
|
+
return node;
|
|
122
|
+
}
|
|
123
|
+
_conjunction(state, scope) {
|
|
124
|
+
let node = this._comparison(state, scope);
|
|
125
|
+
while (state.operator === funcAnd) {
|
|
126
|
+
node = this._call(state.starts(), state.operator, [node, this._comparison(state.next(), scope)]);
|
|
127
|
+
}
|
|
128
|
+
return node;
|
|
129
|
+
}
|
|
130
|
+
_comparison(state, scope) {
|
|
131
|
+
const frame = state.starts();
|
|
132
|
+
let not = false;
|
|
133
|
+
while (state.operator === funcNot) {
|
|
134
|
+
not = !not;
|
|
135
|
+
frame.starts(state);
|
|
136
|
+
state.next();
|
|
137
|
+
}
|
|
138
|
+
let node = this._aggregate(state, scope);
|
|
139
|
+
while (state.operator === funcGreaterThan || state.operator === funcLessThan
|
|
140
|
+
|| state.operator === funcGreaterOrEqual || state.operator === funcLessOrEqual
|
|
141
|
+
|| state.operator === funcEqual || state.operator === funcNotEqual) {
|
|
142
|
+
node = this._call(state.starts(), state.operator, [node, this._aggregate(state.next(), scope)]);
|
|
143
|
+
}
|
|
144
|
+
if (not) {
|
|
145
|
+
node = this._call(frame.ends(state), funcNot, [node]);
|
|
146
|
+
}
|
|
147
|
+
return node;
|
|
148
|
+
}
|
|
149
|
+
_aggregate(state, scope) {
|
|
150
|
+
let node = this._product(state, scope);
|
|
151
|
+
while (state.operator === funcAdd || state.operator === funcSubtract) {
|
|
152
|
+
node = this._call(state.starts(), state.operator, [node, this._product(state.next(), scope)]);
|
|
153
|
+
}
|
|
154
|
+
return node;
|
|
155
|
+
}
|
|
156
|
+
_product(state, scope) {
|
|
157
|
+
let node = this._factor(state, scope);
|
|
158
|
+
while (state.operator === funcMultiply || state.operator === funcDivide || state.operator === funcRemainder) {
|
|
159
|
+
node = this._call(state.starts(), state.operator, [node, this._factor(state.next(), scope)]);
|
|
160
|
+
}
|
|
161
|
+
return node;
|
|
162
|
+
}
|
|
163
|
+
_factor(state, scope) {
|
|
164
|
+
const frame = state.starts();
|
|
165
|
+
let neg = false;
|
|
166
|
+
while (state.operator === funcSubtract) {
|
|
167
|
+
neg = !neg;
|
|
168
|
+
frame.starts(state);
|
|
169
|
+
state.next();
|
|
170
|
+
}
|
|
171
|
+
let node = this._coalescence(state, scope);
|
|
172
|
+
while (state.operator === funcPower) {
|
|
173
|
+
node = this._call(state.starts(), state.operator, [node, this._coalescence(state.next(), scope)]);
|
|
174
|
+
}
|
|
175
|
+
if (neg) {
|
|
176
|
+
node = this._call(frame.ends(state), funcNegate, [node]);
|
|
177
|
+
}
|
|
178
|
+
return node;
|
|
179
|
+
}
|
|
180
|
+
_coalescence(state, scope) {
|
|
181
|
+
let node = this._accessor(state, scope);
|
|
182
|
+
while (state.operator === funcCoalesce) {
|
|
183
|
+
node = this._call(state.starts(), state.operator, [node, this._accessor(state.next(), scope)]);
|
|
184
|
+
}
|
|
185
|
+
return node;
|
|
186
|
+
}
|
|
187
|
+
_accessor(state, scope) {
|
|
188
|
+
let node = this._term(state, scope);
|
|
189
|
+
while (state.operator === funcAt || state.isParenthesesOpen || state.isBracketsOpen) {
|
|
190
|
+
const frame = state.starts();
|
|
191
|
+
if (state.operator === funcAt) {
|
|
192
|
+
if (state.next().isLiteral && (typeof state.literalValue === 'string' || typeof state.literalValue === 'bigint')) {
|
|
193
|
+
node = this._call(frame.ends(state), funcAt, [node, new ConstantNode(state, new Constant(state.literalValue))]);
|
|
194
|
+
state.next();
|
|
195
|
+
}
|
|
196
|
+
else if (state.isToken) {
|
|
197
|
+
frame.ends(state);
|
|
198
|
+
const func = this._functions.get(state.token);
|
|
199
|
+
if (func) {
|
|
200
|
+
if (state.next().isParenthesesOpen) {
|
|
201
|
+
const subnodes = [node];
|
|
202
|
+
while (!state.next().isParenthesesClose) {
|
|
203
|
+
subnodes.push(this._unit(state, scope));
|
|
204
|
+
if (!state.isCommaSeparator) {
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
node = this._call(frame.ends(state), func, subnodes);
|
|
209
|
+
state.closeParentheses().next();
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
node = this._call(frame, func, [node]);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
node = this._call(frame, funcAt, [node, new ConstantNode(state, new Constant(state.token))]);
|
|
217
|
+
state.next();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
state.throwError('missing array or object index');
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
else if (state.isParenthesesOpen) {
|
|
225
|
+
const subnodes = [];
|
|
226
|
+
while (!state.next().isParenthesesClose) {
|
|
227
|
+
subnodes.push(this._unit(state, scope));
|
|
228
|
+
if (!state.isCommaSeparator) {
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
node = new CallNode(frame.ends(state), node, subnodes);
|
|
233
|
+
state.closeParentheses().next();
|
|
234
|
+
}
|
|
235
|
+
else if (state.isBracketsOpen) {
|
|
236
|
+
node = this._call(frame, funcAt, [node, this._unit(state.next(), scope)]);
|
|
237
|
+
state.closeBrackets().next();
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return node;
|
|
241
|
+
}
|
|
242
|
+
_term(state, scope) {
|
|
243
|
+
if (state.isLiteral) {
|
|
244
|
+
const frame = state.starts();
|
|
245
|
+
const constant = new Constant(state.literalValue);
|
|
246
|
+
state.next();
|
|
247
|
+
return new ConstantNode(frame, constant);
|
|
248
|
+
}
|
|
249
|
+
else if (state.isToken) {
|
|
250
|
+
const frame = state.starts();
|
|
251
|
+
const constants = this._constants.get(state.token);
|
|
252
|
+
if (constants != null) {
|
|
253
|
+
if (state.next().operator !== funcAt) {
|
|
254
|
+
state.throwError('missing constant accessor operator');
|
|
255
|
+
}
|
|
256
|
+
if (!state.next().isToken) {
|
|
257
|
+
state.throwError('missing constant name');
|
|
258
|
+
}
|
|
259
|
+
const constant = constants[state.token];
|
|
260
|
+
if (!constant) {
|
|
261
|
+
state.throwError(`unknown constant ${state.token}`);
|
|
262
|
+
}
|
|
263
|
+
state.next();
|
|
264
|
+
return new ConstantNode(frame, constant);
|
|
265
|
+
}
|
|
266
|
+
let variable = scope.get(state.token);
|
|
267
|
+
if (variable == null) {
|
|
268
|
+
variable = this._variables.get(state.token);
|
|
269
|
+
if (variable == null) {
|
|
270
|
+
if (this._strict) {
|
|
271
|
+
state.throwError(`undefined variable ${state.token} in strict mode`);
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
variable = new Variable();
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
scope.global(state.token, variable);
|
|
278
|
+
}
|
|
279
|
+
if (!this._vframes.has(state.token)) {
|
|
280
|
+
this._vframes.set(state.token, state.starts());
|
|
281
|
+
}
|
|
282
|
+
if (state.next().isAssignment) {
|
|
283
|
+
if (variable.constant) {
|
|
284
|
+
state.throwError('illegal constant assignment');
|
|
285
|
+
}
|
|
286
|
+
if (state.assignmentOperator) {
|
|
287
|
+
const operator = state.assignmentOperator;
|
|
288
|
+
const subnodes = [new VariableNode(frame, variable), this._unit(state.next(), scope)];
|
|
289
|
+
return new VariableNode(frame, variable, this._call(frame, operator, subnodes));
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
return new VariableNode(frame, variable, this._unit(state.next(), scope));
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return new VariableNode(frame, variable);
|
|
296
|
+
}
|
|
297
|
+
else if (state.isBracesOpen) {
|
|
298
|
+
const node = this._block(state.next(), scope);
|
|
299
|
+
state.closeBraces().next();
|
|
300
|
+
return node;
|
|
301
|
+
}
|
|
302
|
+
else if (state.isBracesClose) {
|
|
303
|
+
state.throwError('unexpected closing braces');
|
|
304
|
+
}
|
|
305
|
+
else if (state.isParenthesesOpen) {
|
|
306
|
+
const node = this._unit(state.next(), scope);
|
|
307
|
+
state.closeParentheses().next();
|
|
308
|
+
return node;
|
|
309
|
+
}
|
|
310
|
+
else if (state.isParenthesesClose) {
|
|
311
|
+
state.throwError('unexpected closing parentheses');
|
|
312
|
+
}
|
|
313
|
+
else if (state.isBracketsOpen) {
|
|
314
|
+
const frame = state.starts();
|
|
315
|
+
const subnodes = [];
|
|
316
|
+
let index = 0, colon = false;
|
|
317
|
+
while (!state.next().isBracketsClose) {
|
|
318
|
+
if (state.isColonSeparator) {
|
|
319
|
+
colon = true;
|
|
320
|
+
state.next();
|
|
321
|
+
break;
|
|
322
|
+
}
|
|
323
|
+
const node = this._unit(state, scope);
|
|
324
|
+
if (state.isColonSeparator) {
|
|
325
|
+
colon = true;
|
|
326
|
+
subnodes.push([node, this._unit(state.next(), scope)]);
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
subnodes.push([index++, node]);
|
|
330
|
+
}
|
|
331
|
+
if (!state.isCommaSeparator) {
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
frame.ends(state);
|
|
336
|
+
state.closeBrackets().next();
|
|
337
|
+
if (colon) {
|
|
338
|
+
return new ObjectNode(frame, subnodes.map(([k, v]) => [typeof k === 'number' ? new ConstantNode(v, new Constant(String(k))) : k, v]));
|
|
339
|
+
}
|
|
340
|
+
return new ArrayNode(frame, subnodes.map(([, v]) => v));
|
|
341
|
+
}
|
|
342
|
+
else if (state.isBracketsClose) {
|
|
343
|
+
state.throwError('unexpected closing brackets');
|
|
344
|
+
}
|
|
345
|
+
else if (state.isVariableDefinition || state.isConstantDefinition) {
|
|
346
|
+
const constant = state.isConstantDefinition;
|
|
347
|
+
if (!state.next().isToken) {
|
|
348
|
+
state.throwError(`missing ${constant ? 'constant' : 'variable'} name`);
|
|
349
|
+
}
|
|
350
|
+
const token = state.token;
|
|
351
|
+
if (scope.has(token)) {
|
|
352
|
+
state.throwError(`illegal redefinition of ${constant ? 'constant' : 'variable'} ${token}`);
|
|
353
|
+
}
|
|
354
|
+
const frame = state.starts();
|
|
355
|
+
let type;
|
|
356
|
+
if (state.next().isColonSeparator) {
|
|
357
|
+
type = this._type(state.next(), scope);
|
|
358
|
+
}
|
|
359
|
+
const variable = new Variable(type, constant);
|
|
360
|
+
scope.local(token, variable);
|
|
361
|
+
if (state.isAssignment) {
|
|
362
|
+
if (state.assignmentOperator) {
|
|
363
|
+
state.throwError(`illegal assignment to ${constant ? 'constant' : 'variable'} ${token}`);
|
|
364
|
+
}
|
|
365
|
+
return new VariableNode(frame, variable, this._unit(state.next(), scope));
|
|
366
|
+
}
|
|
367
|
+
return new VariableNode(frame, variable);
|
|
368
|
+
}
|
|
369
|
+
else if (state.isTildaMark) {
|
|
370
|
+
return this._function(state, scope);
|
|
371
|
+
}
|
|
372
|
+
else if (state.isWhile) {
|
|
373
|
+
return this._loop(state, scope);
|
|
374
|
+
}
|
|
375
|
+
else if (state.isIf) {
|
|
376
|
+
return this._switch(state, scope);
|
|
377
|
+
}
|
|
378
|
+
else if (state.isVoid) {
|
|
379
|
+
state.throwError('unexpected end of expression');
|
|
380
|
+
}
|
|
381
|
+
state.throwError('unexpected expression token');
|
|
382
|
+
}
|
|
383
|
+
_function(state, scope) {
|
|
384
|
+
const frame = state.starts();
|
|
385
|
+
let retType = undefined;
|
|
386
|
+
if (!state.next().isParenthesesOpen) {
|
|
387
|
+
retType = this._type(state, scope);
|
|
388
|
+
state.openParentheses();
|
|
389
|
+
}
|
|
390
|
+
let variadic = false;
|
|
391
|
+
const variables = new Map();
|
|
392
|
+
while (!state.next().isParenthesesClose) {
|
|
393
|
+
if (!state.isToken) {
|
|
394
|
+
state.throwError('missing function argument name');
|
|
395
|
+
}
|
|
396
|
+
const token = state.token;
|
|
397
|
+
if (scope.get(token)) {
|
|
398
|
+
state.throwError('variable redefinition');
|
|
399
|
+
}
|
|
400
|
+
let argType = Type.Unknown;
|
|
401
|
+
if (state.next().isColonSeparator) {
|
|
402
|
+
argType = this._type(state.next(), scope);
|
|
403
|
+
}
|
|
404
|
+
variables.set(token, new Variable(argType));
|
|
405
|
+
if (argType.isArray && state.isVariadicFunction) {
|
|
406
|
+
variadic = true;
|
|
407
|
+
state.next();
|
|
408
|
+
break;
|
|
409
|
+
}
|
|
410
|
+
if (!state.isCommaSeparator) {
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
state.closeParentheses();
|
|
415
|
+
frame.ends(state);
|
|
416
|
+
state.next().openBraces().next();
|
|
417
|
+
const subnode = this._block(state, scope.subscope(variables));
|
|
418
|
+
state.closeBraces().next();
|
|
419
|
+
const args = Array.from(variables.values());
|
|
420
|
+
const value = (...values) => {
|
|
421
|
+
args.forEach((arg, ix) => arg.value = values[ix]);
|
|
422
|
+
return subnode.evaluate();
|
|
423
|
+
};
|
|
424
|
+
if (!retType && args.length) {
|
|
425
|
+
retType = Type.Unknown;
|
|
426
|
+
}
|
|
427
|
+
const constant = new Constant(value, Type.functionType(retType, args.map((v) => v.type), variadic));
|
|
428
|
+
return new ConstantNode(frame, constant, subnode);
|
|
429
|
+
}
|
|
430
|
+
_loop(state, scope) {
|
|
431
|
+
const frame = state.starts();
|
|
432
|
+
const cnode = this._unit(state.next(), scope);
|
|
433
|
+
frame.ends(state);
|
|
434
|
+
state.openBraces().next();
|
|
435
|
+
const subnode = this._block(state, scope);
|
|
436
|
+
state.closeBraces().next();
|
|
437
|
+
return new LoopNode(frame, cnode, subnode);
|
|
438
|
+
}
|
|
439
|
+
_switch(state, scope) {
|
|
440
|
+
const frame = state.starts();
|
|
441
|
+
const cnode = this._unit(state.next(), scope);
|
|
442
|
+
frame.ends(state);
|
|
443
|
+
const subnodes = [];
|
|
444
|
+
state.openBraces().next();
|
|
445
|
+
subnodes.push(this._block(state, scope));
|
|
446
|
+
state.closeBraces().next();
|
|
447
|
+
if (state.isElse) {
|
|
448
|
+
state.next().openBraces().next();
|
|
449
|
+
subnodes.push(this._block(state, scope));
|
|
450
|
+
state.closeBraces().next();
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
subnodes.push(new ConstantNode(state.starts(), Constant.Null));
|
|
454
|
+
}
|
|
455
|
+
return new SwitchNode(frame, cnode, subnodes);
|
|
456
|
+
}
|
|
457
|
+
_call(frame, func, subnodes) {
|
|
458
|
+
return new CallNode(frame, new ConstantNode(frame, func), subnodes);
|
|
459
|
+
}
|
|
460
|
+
_type(state, scope) {
|
|
461
|
+
if (state.isType) {
|
|
462
|
+
let type = state.type;
|
|
463
|
+
if (state.next().isOptionalType) {
|
|
464
|
+
type = type.toOptional();
|
|
465
|
+
state.next();
|
|
466
|
+
}
|
|
467
|
+
if (state.operator === funcOr) { // type union
|
|
468
|
+
return Type.union(type, this._type(state.next(), scope));
|
|
469
|
+
}
|
|
470
|
+
return type;
|
|
471
|
+
}
|
|
472
|
+
else if (state.isBracketsOpen) { // array or object type
|
|
473
|
+
const itemPropTypes = [];
|
|
474
|
+
let index = 0, colon = false;
|
|
475
|
+
while (!state.next().isBracketsClose) {
|
|
476
|
+
if (state.isColonSeparator) { // default object type
|
|
477
|
+
colon = true;
|
|
478
|
+
state.next();
|
|
479
|
+
break;
|
|
480
|
+
}
|
|
481
|
+
if (state.isType) {
|
|
482
|
+
itemPropTypes.push([index++, this._type(state, scope)]);
|
|
483
|
+
}
|
|
484
|
+
else if (state.isToken) {
|
|
485
|
+
const token = state.token;
|
|
486
|
+
state.next().separateByColon().next();
|
|
487
|
+
itemPropTypes.push([token, this._type(state, scope)]);
|
|
488
|
+
}
|
|
489
|
+
else {
|
|
490
|
+
state.throwError('missing type or property name');
|
|
491
|
+
}
|
|
492
|
+
if (!state.isCommaSeparator) {
|
|
493
|
+
break;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
state.closeBrackets().next();
|
|
497
|
+
return colon
|
|
498
|
+
? Type.objectType(Object.fromEntries(itemPropTypes.map(([prop, type]) => [prop, type])))
|
|
499
|
+
: Type.arrayType(itemPropTypes.map(([, v]) => v));
|
|
500
|
+
}
|
|
501
|
+
else if (state.isTildaMark) { // function type
|
|
502
|
+
let retType = undefined;
|
|
503
|
+
if (!state.next().isParenthesesOpen) {
|
|
504
|
+
retType = this._type(state, scope);
|
|
505
|
+
state.openParentheses();
|
|
506
|
+
}
|
|
507
|
+
let variadic = false;
|
|
508
|
+
const argTypes = [];
|
|
509
|
+
while (!state.next().isParenthesesClose) {
|
|
510
|
+
const argType = this._type(state, scope);
|
|
511
|
+
argTypes.push(argType);
|
|
512
|
+
if (state.isVariadicFunction) {
|
|
513
|
+
if (argType.isArray) {
|
|
514
|
+
variadic = true;
|
|
515
|
+
state.next();
|
|
516
|
+
break;
|
|
517
|
+
}
|
|
518
|
+
else {
|
|
519
|
+
state.throwError('variadic function argument must be an array type');
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
if (!state.isCommaSeparator) {
|
|
523
|
+
break;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
state.closeParentheses().next();
|
|
527
|
+
if (!retType && argTypes.length) {
|
|
528
|
+
retType = Type.Unknown;
|
|
529
|
+
}
|
|
530
|
+
return Type.functionType(retType, argTypes, variadic);
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
state.throwError('missing type name');
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Value } from './Value.js';
|
|
2
|
+
import { Type } from './Type.js';
|
|
3
|
+
export declare class Constant {
|
|
4
|
+
protected readonly _value: Value;
|
|
5
|
+
protected _type: Type;
|
|
6
|
+
protected readonly _deterministic: boolean;
|
|
7
|
+
constructor(_value: Value, _type?: Type, _deterministic?: boolean);
|
|
8
|
+
get value(): Value;
|
|
9
|
+
get type(): Type;
|
|
10
|
+
set type(type: Type);
|
|
11
|
+
get deterministic(): boolean;
|
|
12
|
+
static Null: Constant;
|
|
13
|
+
static EmptyArray: Constant;
|
|
14
|
+
static EmptyObject: Constant;
|
|
15
|
+
}
|
package/dst/Constant.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Type } from './Type.js';
|
|
2
|
+
export class Constant {
|
|
3
|
+
_value;
|
|
4
|
+
_type;
|
|
5
|
+
_deterministic;
|
|
6
|
+
constructor(_value, _type = Type.of(_value), _deterministic = true) {
|
|
7
|
+
this._value = _value;
|
|
8
|
+
this._type = _type;
|
|
9
|
+
this._deterministic = _deterministic;
|
|
10
|
+
}
|
|
11
|
+
get value() {
|
|
12
|
+
return this._value;
|
|
13
|
+
}
|
|
14
|
+
get type() {
|
|
15
|
+
return Type.isPrimitiveType(this._value) ? Type.of(this._value) : this._type;
|
|
16
|
+
}
|
|
17
|
+
set type(type) {
|
|
18
|
+
this._type = type;
|
|
19
|
+
}
|
|
20
|
+
get deterministic() {
|
|
21
|
+
return this._deterministic;
|
|
22
|
+
}
|
|
23
|
+
static Null = new Constant(undefined);
|
|
24
|
+
static EmptyArray = new Constant([]);
|
|
25
|
+
static EmptyObject = new Constant({});
|
|
26
|
+
}
|
package/dst/Constants.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { constArray } from './constant/Array.js';
|
|
2
|
+
import { constBoolean } from './constant/Boolean.js';
|
|
3
|
+
import { constBuffer } from './constant/Buffer.js';
|
|
4
|
+
import { constInteger } from './constant/Integer.js';
|
|
5
|
+
import { constFloat } from './constant/Float.js';
|
|
6
|
+
import { constObject } from './constant/Object.js';
|
|
7
|
+
import { constString } from './constant/String.js';
|
|
8
|
+
import { constTimestamp } from './constant/Timestamp.js';
|
|
9
|
+
import { constAN } from './constant/notation/AN.js';
|
|
10
|
+
import { constJSON } from './constant/notation/JSON.js';
|
|
11
|
+
export const Constants = [
|
|
12
|
+
['Array', constArray],
|
|
13
|
+
['Boolean', constBoolean],
|
|
14
|
+
['Buffer', constBuffer],
|
|
15
|
+
['Float', constFloat],
|
|
16
|
+
['Integer', constInteger],
|
|
17
|
+
['Object', constObject],
|
|
18
|
+
['String', constString],
|
|
19
|
+
['Timestamp', constTimestamp],
|
|
20
|
+
['AN', constAN],
|
|
21
|
+
['JSON', constJSON],
|
|
22
|
+
];
|
package/dst/Functions.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { funcFirst, funcLast, funcFirstIndex, funcLastIndex, funcEvery, funcAny, funcFlatten, funcReverse, funcMutate, funcFilter, funcReduce, funcCompose, funcPrepend, funcAppend } from './constant/Array.js';
|
|
2
|
+
import { funcByte } from './constant/Buffer.js';
|
|
3
|
+
import { funcAdd, funcSlice, funcSplice, funcInject } from './constant/Enumerable.js';
|
|
4
|
+
import { funcLength, funcAt } from './constant/Iterable.js';
|
|
5
|
+
import { funcGreaterThan, funcLessThan, funcGreaterOrEqual, funcLessOrEqual, funcSubtract, funcMultiply, funcDivide, funcRemainder, funcModulo, funcPower, funcRoot, funcNegate, funcCast } from './constant/Number.js';
|
|
6
|
+
import { funcEntries, funcKeys, funcValues } from './constant/Object.js';
|
|
7
|
+
import { funcLike, funcUnlike, funcContains, funcStartsWith, funcEndsWith, funcChar, funcCharCode, funcTrim, funcTrimStart, funcTrimEnd, funcLowerCase, funcUpperCase, funcSplit } from './constant/String.js';
|
|
8
|
+
import { funcYear, funcMonth, funcMonthIndex, funcWeekdayIndex, funcDay, funcHour, funcMinute, funcSecond, funcMillisecond, funcEpochTime } from './constant/Timestamp.js';
|
|
9
|
+
import { funcCoalesce, funcEqual, funcNotEqual, funcEncode, funcFormat } from './constant/Unknown.js';
|
|
10
|
+
export const Functions = [
|
|
11
|
+
// Array
|
|
12
|
+
['First', funcFirst],
|
|
13
|
+
['Last', funcLast],
|
|
14
|
+
['FirstIndex', funcFirstIndex],
|
|
15
|
+
['LastIndex', funcLastIndex],
|
|
16
|
+
['Any', funcAny],
|
|
17
|
+
['Every', funcEvery],
|
|
18
|
+
['Flatten', funcFlatten],
|
|
19
|
+
['Reverse', funcReverse],
|
|
20
|
+
['Mutate', funcMutate],
|
|
21
|
+
['Filter', funcFilter],
|
|
22
|
+
['Reduce', funcReduce],
|
|
23
|
+
['Compose', funcCompose],
|
|
24
|
+
['Prepend', funcPrepend],
|
|
25
|
+
['Append', funcAppend],
|
|
26
|
+
// Buffer
|
|
27
|
+
['Byte', funcByte],
|
|
28
|
+
// Enumerable
|
|
29
|
+
['Add', funcAdd],
|
|
30
|
+
['Slice', funcSlice],
|
|
31
|
+
['Splice', funcSplice],
|
|
32
|
+
['Inject', funcInject],
|
|
33
|
+
// Iterable
|
|
34
|
+
['Length', funcLength],
|
|
35
|
+
['At', funcAt],
|
|
36
|
+
// Number
|
|
37
|
+
['GreaterThan', funcGreaterThan],
|
|
38
|
+
['LessThan', funcLessThan],
|
|
39
|
+
['GreaterOrEqual', funcGreaterOrEqual],
|
|
40
|
+
['LessOrEqual', funcLessOrEqual],
|
|
41
|
+
['Subtract', funcSubtract],
|
|
42
|
+
['Multiply', funcMultiply],
|
|
43
|
+
['Divide', funcDivide],
|
|
44
|
+
['Remainder', funcRemainder],
|
|
45
|
+
['Modulo', funcModulo],
|
|
46
|
+
['Power', funcPower],
|
|
47
|
+
['Root', funcRoot],
|
|
48
|
+
['Negate', funcNegate],
|
|
49
|
+
['Cast', funcCast],
|
|
50
|
+
// Object
|
|
51
|
+
['Entries', funcEntries],
|
|
52
|
+
['Keys', funcKeys],
|
|
53
|
+
['Values', funcValues],
|
|
54
|
+
// String
|
|
55
|
+
['Like', funcLike],
|
|
56
|
+
['Unlike', funcUnlike],
|
|
57
|
+
['Contains', funcContains],
|
|
58
|
+
['StartsWith', funcStartsWith],
|
|
59
|
+
['EndsWith', funcEndsWith],
|
|
60
|
+
['Char', funcChar],
|
|
61
|
+
['CharCode', funcCharCode],
|
|
62
|
+
['Trim', funcTrim],
|
|
63
|
+
['TrimStart', funcTrimStart],
|
|
64
|
+
['TrimEnd', funcTrimEnd],
|
|
65
|
+
['LowerCase', funcLowerCase],
|
|
66
|
+
['UpperCase', funcUpperCase],
|
|
67
|
+
['Split', funcSplit],
|
|
68
|
+
// Timestamp
|
|
69
|
+
['Year', funcYear],
|
|
70
|
+
['Month', funcMonth],
|
|
71
|
+
['MonthIndex', funcMonthIndex],
|
|
72
|
+
['WeekdayIndex', funcWeekdayIndex],
|
|
73
|
+
['Day', funcDay],
|
|
74
|
+
['Hour', funcHour],
|
|
75
|
+
['Minute', funcMinute],
|
|
76
|
+
['Second', funcSecond],
|
|
77
|
+
['Millisecond', funcMillisecond],
|
|
78
|
+
['EpochTime', funcEpochTime],
|
|
79
|
+
// Unknown
|
|
80
|
+
['Coalesce', funcCoalesce],
|
|
81
|
+
['Equal', funcEqual],
|
|
82
|
+
['Unequal', funcNotEqual],
|
|
83
|
+
['Encode', funcEncode],
|
|
84
|
+
['Format', funcFormat],
|
|
85
|
+
];
|