funcity 0.1.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 +21 -0
- package/README.md +109 -0
- package/dist/index.cjs +2005 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +2005 -0
- package/dist/index.mjs.map +1 -0
- package/dist/parser.d.ts +220 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/reducer.d.ts +125 -0
- package/dist/reducer.d.ts.map +1 -0
- package/dist/scripting.d.ts +36 -0
- package/dist/scripting.d.ts.map +1 -0
- package/dist/standards.d.ts +53 -0
- package/dist/standards.d.ts.map +1 -0
- package/dist/tokenizer.d.ts +96 -0
- package/dist/tokenizer.d.ts.map +1 -0
- package/package.json +64 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2005 @@
|
|
|
1
|
+
const emptyLocation = {
|
|
2
|
+
line: 0,
|
|
3
|
+
column: 0
|
|
4
|
+
};
|
|
5
|
+
const emptyRange = {
|
|
6
|
+
start: emptyLocation,
|
|
7
|
+
end: emptyLocation
|
|
8
|
+
};
|
|
9
|
+
const specialFunctionMarker = /* @__PURE__ */ Symbol("$$special$$");
|
|
10
|
+
const makeSpecialFunction = (f) => {
|
|
11
|
+
f[specialFunctionMarker] = true;
|
|
12
|
+
return f;
|
|
13
|
+
};
|
|
14
|
+
const isSpecialFunction = (f) => {
|
|
15
|
+
var _a;
|
|
16
|
+
return (_a = f[specialFunctionMarker]) != null ? _a : false;
|
|
17
|
+
};
|
|
18
|
+
const isConditionalTrue = (v) => {
|
|
19
|
+
if (v === void 0 || v === null) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
switch (typeof v) {
|
|
23
|
+
case "boolean":
|
|
24
|
+
return v;
|
|
25
|
+
case "number":
|
|
26
|
+
case "bigint":
|
|
27
|
+
return v !== 0;
|
|
28
|
+
default:
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const asIterable = (v) => {
|
|
33
|
+
if (typeof v[Symbol.iterator] === "function") {
|
|
34
|
+
return v;
|
|
35
|
+
} else {
|
|
36
|
+
return void 0;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
const combineVariables = (...variablesList) => {
|
|
40
|
+
const variables = /* @__PURE__ */ new Map();
|
|
41
|
+
const appendVariables = (vs) => vs.forEach((v, k) => variables.set(k, v));
|
|
42
|
+
const appendRecord = (vs) => Object.keys(vs).forEach((k) => variables.set(k, vs[k]));
|
|
43
|
+
variablesList.forEach((vs) => {
|
|
44
|
+
if (vs["forEach"] !== void 0) {
|
|
45
|
+
appendVariables(vs);
|
|
46
|
+
} else {
|
|
47
|
+
appendRecord(vs);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
return variables;
|
|
51
|
+
};
|
|
52
|
+
const fromError = (error) => {
|
|
53
|
+
var _a;
|
|
54
|
+
if (error.message) {
|
|
55
|
+
return error.message;
|
|
56
|
+
} else if (typeof error.toString === "function") {
|
|
57
|
+
return (_a = error.toString()) != null ? _a : "unknown";
|
|
58
|
+
} else {
|
|
59
|
+
return "unknown";
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
const widerRange = (...ranges) => {
|
|
63
|
+
let start = emptyRange.start;
|
|
64
|
+
let end = emptyRange.end;
|
|
65
|
+
for (const range of ranges) {
|
|
66
|
+
if (range.start.line >= 1 && range.start.column >= 1) {
|
|
67
|
+
if (start.line === 0 || start.column === 0) {
|
|
68
|
+
start = range.start;
|
|
69
|
+
} else if (range.start.line < start.line) {
|
|
70
|
+
start = range.start;
|
|
71
|
+
} else if (range.start.line === start.line && range.start.column < start.column) {
|
|
72
|
+
start = range.start;
|
|
73
|
+
}
|
|
74
|
+
if (end.line === 0 || end.column === 0) {
|
|
75
|
+
end = range.end;
|
|
76
|
+
} else if (range.end.line > end.line) {
|
|
77
|
+
end = range.end;
|
|
78
|
+
} else if (range.end.line === end.line && range.end.column > end.column) {
|
|
79
|
+
end = range.end;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return { start, end };
|
|
84
|
+
};
|
|
85
|
+
const locationEquals = (lhs, rhs) => lhs.line === rhs.line && lhs.column === rhs.column;
|
|
86
|
+
const getLocationString = (range) => locationEquals(range.start, range.end) ? `${range.start.line}:${range.start.column}` : `${range.start.line}:${range.start.column}:${range.end.line}:${range.end.column}`;
|
|
87
|
+
const printErrorString = (path, error) => {
|
|
88
|
+
switch (error.type) {
|
|
89
|
+
case "warning":
|
|
90
|
+
console.warn(
|
|
91
|
+
`${path}:${getLocationString(error.range)}: warning: ${error.description}`
|
|
92
|
+
);
|
|
93
|
+
break;
|
|
94
|
+
case "error":
|
|
95
|
+
console.error(
|
|
96
|
+
`${path}:${getLocationString(error.range)}: error: ${error.description}`
|
|
97
|
+
);
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
};
|
|
102
|
+
const outputErrors = (path, errors) => {
|
|
103
|
+
let isError = false;
|
|
104
|
+
for (const error of errors) {
|
|
105
|
+
const result = printErrorString(path, error);
|
|
106
|
+
isError || (isError = result);
|
|
107
|
+
}
|
|
108
|
+
return isError;
|
|
109
|
+
};
|
|
110
|
+
const tokenizeString = (context) => {
|
|
111
|
+
const start = context.cursor.location("start");
|
|
112
|
+
context.cursor.skip(1);
|
|
113
|
+
let value = context.cursor.getUntil("'");
|
|
114
|
+
if (value !== void 0) {
|
|
115
|
+
context.cursor.skip(1);
|
|
116
|
+
} else {
|
|
117
|
+
const location = context.cursor.location("start");
|
|
118
|
+
context.errors.push({
|
|
119
|
+
type: "error",
|
|
120
|
+
description: "string close quote is not found",
|
|
121
|
+
range: { start: location, end: location }
|
|
122
|
+
});
|
|
123
|
+
value = "";
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
kind: "string",
|
|
127
|
+
value,
|
|
128
|
+
range: { start, end: context.cursor.location("end") }
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
const firstNumericChars = "0123456789-+";
|
|
132
|
+
const numericCharsWithDot = "0123456789.";
|
|
133
|
+
const numericChars = "0123456789";
|
|
134
|
+
const tokenizeNumber = (context) => {
|
|
135
|
+
const start = context.cursor.location("start");
|
|
136
|
+
let index = 1;
|
|
137
|
+
while (true) {
|
|
138
|
+
if (context.cursor.eot()) {
|
|
139
|
+
return {
|
|
140
|
+
kind: "number",
|
|
141
|
+
value: Number(context.cursor.getRangeAndSkip(index)),
|
|
142
|
+
range: { start, end: context.cursor.location("end") }
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const ch = context.cursor.getChar(index);
|
|
146
|
+
if (numericCharsWithDot.indexOf(ch) < 0) {
|
|
147
|
+
if (ch === ".") {
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
kind: "number",
|
|
152
|
+
value: Number(context.cursor.getRangeAndSkip(index)),
|
|
153
|
+
range: { start, end: context.cursor.location("end") }
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
index++;
|
|
157
|
+
}
|
|
158
|
+
while (true) {
|
|
159
|
+
if (context.cursor.eot()) {
|
|
160
|
+
return {
|
|
161
|
+
kind: "number",
|
|
162
|
+
value: Number(context.cursor.getRangeAndSkip(index)),
|
|
163
|
+
range: { start, end: context.cursor.location("end") }
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
const ch = context.cursor.getChar(index);
|
|
167
|
+
if (numericChars.indexOf(ch) < 0) {
|
|
168
|
+
return {
|
|
169
|
+
kind: "number",
|
|
170
|
+
value: Number(context.cursor.getRangeAndSkip(index)),
|
|
171
|
+
range: { start, end: context.cursor.location("end") }
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
index++;
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
const firstVariableChars = "_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
178
|
+
const variableChars = "_-.?0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
179
|
+
const tokenizeIdentity = (context) => {
|
|
180
|
+
const start = context.cursor.location("start");
|
|
181
|
+
let index = 1;
|
|
182
|
+
let lastCh = "";
|
|
183
|
+
while (true) {
|
|
184
|
+
if (context.cursor.eot()) {
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
const ch = context.cursor.getChar(index);
|
|
188
|
+
if (variableChars.indexOf(ch) < 0) {
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
if (lastCh === "?") {
|
|
192
|
+
index--;
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
lastCh = ch;
|
|
196
|
+
index++;
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
kind: "identity",
|
|
200
|
+
name: context.cursor.getRangeAndSkip(index),
|
|
201
|
+
range: { start, end: context.cursor.location("end") }
|
|
202
|
+
};
|
|
203
|
+
};
|
|
204
|
+
const tokenizeCodeBlock = (context) => {
|
|
205
|
+
const openStart = context.cursor.location("start");
|
|
206
|
+
context.cursor.skip(2);
|
|
207
|
+
const tokens = [
|
|
208
|
+
{
|
|
209
|
+
kind: "open",
|
|
210
|
+
symbol: "{{",
|
|
211
|
+
range: { start: openStart, end: context.cursor.location("end") }
|
|
212
|
+
}
|
|
213
|
+
];
|
|
214
|
+
let unknownStartLocation;
|
|
215
|
+
const finalizeUnknown = () => {
|
|
216
|
+
if (unknownStartLocation) {
|
|
217
|
+
context.errors.push({
|
|
218
|
+
type: "warning",
|
|
219
|
+
description: "unknown words",
|
|
220
|
+
range: {
|
|
221
|
+
start: unknownStartLocation,
|
|
222
|
+
end: context.cursor.location("end")
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
unknownStartLocation = void 0;
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
while (!context.cursor.eot()) {
|
|
229
|
+
if (context.cursor.assert("}}")) {
|
|
230
|
+
finalizeUnknown();
|
|
231
|
+
const location = context.cursor.location("start");
|
|
232
|
+
context.cursor.skip(2);
|
|
233
|
+
tokens.push({
|
|
234
|
+
kind: "close",
|
|
235
|
+
symbol: "}}",
|
|
236
|
+
range: { start: location, end: context.cursor.location("end") }
|
|
237
|
+
});
|
|
238
|
+
return tokens;
|
|
239
|
+
}
|
|
240
|
+
const ch = context.cursor.getChar();
|
|
241
|
+
if (ch === "\\") {
|
|
242
|
+
const next = context.cursor.getChar(1);
|
|
243
|
+
if (next === "{" || next === "}") {
|
|
244
|
+
context.cursor.skip(2);
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (ch === "'") {
|
|
249
|
+
finalizeUnknown();
|
|
250
|
+
tokens.push(tokenizeString(context));
|
|
251
|
+
} else if (firstNumericChars.indexOf(ch) >= 0) {
|
|
252
|
+
finalizeUnknown();
|
|
253
|
+
tokens.push(tokenizeNumber(context));
|
|
254
|
+
} else if (ch === "(") {
|
|
255
|
+
finalizeUnknown();
|
|
256
|
+
const location = context.cursor.location("start");
|
|
257
|
+
tokens.push({
|
|
258
|
+
kind: "open",
|
|
259
|
+
symbol: "(",
|
|
260
|
+
range: { start: location, end: location }
|
|
261
|
+
});
|
|
262
|
+
context.cursor.skip(1);
|
|
263
|
+
} else if (ch === ")") {
|
|
264
|
+
finalizeUnknown();
|
|
265
|
+
const location = context.cursor.location("start");
|
|
266
|
+
tokens.push({
|
|
267
|
+
kind: "close",
|
|
268
|
+
symbol: ")",
|
|
269
|
+
range: { start: location, end: location }
|
|
270
|
+
});
|
|
271
|
+
context.cursor.skip(1);
|
|
272
|
+
} else if (ch === "[") {
|
|
273
|
+
finalizeUnknown();
|
|
274
|
+
const location = context.cursor.location("start");
|
|
275
|
+
tokens.push({
|
|
276
|
+
kind: "open",
|
|
277
|
+
symbol: "[",
|
|
278
|
+
range: { start: location, end: location }
|
|
279
|
+
});
|
|
280
|
+
context.cursor.skip(1);
|
|
281
|
+
} else if (ch === "]") {
|
|
282
|
+
finalizeUnknown();
|
|
283
|
+
const location = context.cursor.location("start");
|
|
284
|
+
tokens.push({
|
|
285
|
+
kind: "close",
|
|
286
|
+
symbol: "]",
|
|
287
|
+
range: { start: location, end: location }
|
|
288
|
+
});
|
|
289
|
+
context.cursor.skip(1);
|
|
290
|
+
} else if (firstVariableChars.indexOf(ch) >= 0) {
|
|
291
|
+
finalizeUnknown();
|
|
292
|
+
tokens.push(tokenizeIdentity(context));
|
|
293
|
+
} else if (ch === "\n") {
|
|
294
|
+
finalizeUnknown();
|
|
295
|
+
const location = context.cursor.location("start");
|
|
296
|
+
tokens.push({
|
|
297
|
+
kind: "eol",
|
|
298
|
+
range: { start: location, end: location }
|
|
299
|
+
});
|
|
300
|
+
context.cursor.skip(1);
|
|
301
|
+
} else if (ch === "\r") {
|
|
302
|
+
finalizeUnknown();
|
|
303
|
+
context.cursor.skip(1);
|
|
304
|
+
} else if (ch === " ") {
|
|
305
|
+
finalizeUnknown();
|
|
306
|
+
context.cursor.skip(1);
|
|
307
|
+
} else if (!unknownStartLocation) {
|
|
308
|
+
unknownStartLocation = context.cursor.location("start");
|
|
309
|
+
context.cursor.skip(1);
|
|
310
|
+
}
|
|
311
|
+
context.cursor.skipChars(" ");
|
|
312
|
+
}
|
|
313
|
+
const causeLocation = context.cursor.location("start");
|
|
314
|
+
context.errors.push({
|
|
315
|
+
type: "error",
|
|
316
|
+
description: "required code block closer: `}}`",
|
|
317
|
+
range: { start: causeLocation, end: causeLocation }
|
|
318
|
+
});
|
|
319
|
+
return tokens;
|
|
320
|
+
};
|
|
321
|
+
const createTokenizerCursor = (text) => {
|
|
322
|
+
let currentIndex = 0;
|
|
323
|
+
let rawLine = 0;
|
|
324
|
+
let rawColumn = 0;
|
|
325
|
+
let lastLine = 0;
|
|
326
|
+
let lastColumn = 0;
|
|
327
|
+
const eot = () => currentIndex >= text.length;
|
|
328
|
+
const getChar = (index) => text[(index != null ? index : 0) + currentIndex];
|
|
329
|
+
const skip = (length) => {
|
|
330
|
+
let lastch = "\0";
|
|
331
|
+
while (length > 0) {
|
|
332
|
+
lastColumn = rawColumn;
|
|
333
|
+
lastLine = rawLine;
|
|
334
|
+
const ch = text[currentIndex];
|
|
335
|
+
if (ch === "\r") {
|
|
336
|
+
rawColumn = 0;
|
|
337
|
+
} else if (ch === "\n" && lastch !== "\r") {
|
|
338
|
+
rawColumn = 0;
|
|
339
|
+
rawLine++;
|
|
340
|
+
} else {
|
|
341
|
+
rawColumn++;
|
|
342
|
+
}
|
|
343
|
+
currentIndex++;
|
|
344
|
+
length--;
|
|
345
|
+
lastch = ch;
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
const skipChars = (ch) => {
|
|
349
|
+
while (!eot()) {
|
|
350
|
+
if (getChar() !== ch) {
|
|
351
|
+
return true;
|
|
352
|
+
}
|
|
353
|
+
skip(1);
|
|
354
|
+
}
|
|
355
|
+
return false;
|
|
356
|
+
};
|
|
357
|
+
const skipUntil = (word) => {
|
|
358
|
+
while (!eot()) {
|
|
359
|
+
const index = text.indexOf(word, currentIndex);
|
|
360
|
+
if (index === currentIndex) {
|
|
361
|
+
skip(word.length);
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
skip(1);
|
|
365
|
+
}
|
|
366
|
+
return false;
|
|
367
|
+
};
|
|
368
|
+
const assert = (word) => {
|
|
369
|
+
if (text.length - currentIndex < word.length) {
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
if (text.substring(currentIndex, currentIndex + word.length) === word) {
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
return false;
|
|
376
|
+
};
|
|
377
|
+
const getRangeAndSkip = (length) => {
|
|
378
|
+
const result = text.substring(currentIndex, currentIndex + length);
|
|
379
|
+
skip(result.length);
|
|
380
|
+
return result;
|
|
381
|
+
};
|
|
382
|
+
const getUntil = (word) => {
|
|
383
|
+
if (currentIndex >= text.length) {
|
|
384
|
+
return void 0;
|
|
385
|
+
}
|
|
386
|
+
const index = text.indexOf(word, currentIndex);
|
|
387
|
+
if (index >= 0) {
|
|
388
|
+
const result = text.substring(currentIndex, index);
|
|
389
|
+
skip(index - currentIndex);
|
|
390
|
+
return result;
|
|
391
|
+
} else {
|
|
392
|
+
const result = text.substring(currentIndex, text.length);
|
|
393
|
+
skip(text.length - currentIndex);
|
|
394
|
+
return result;
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
const location = (type) => type === "end" ? {
|
|
398
|
+
line: lastLine + 1,
|
|
399
|
+
column: lastColumn + 1
|
|
400
|
+
} : {
|
|
401
|
+
line: rawLine + 1,
|
|
402
|
+
column: rawColumn + 1
|
|
403
|
+
};
|
|
404
|
+
return {
|
|
405
|
+
eot,
|
|
406
|
+
getChar,
|
|
407
|
+
skip,
|
|
408
|
+
skipChars,
|
|
409
|
+
skipUntil,
|
|
410
|
+
assert,
|
|
411
|
+
getRangeAndSkip,
|
|
412
|
+
getUntil,
|
|
413
|
+
location
|
|
414
|
+
};
|
|
415
|
+
};
|
|
416
|
+
const runTokenizer = (script, errors) => {
|
|
417
|
+
const context = {
|
|
418
|
+
cursor: createTokenizerCursor(script),
|
|
419
|
+
errors
|
|
420
|
+
};
|
|
421
|
+
const tokens = [];
|
|
422
|
+
const readTextBlock = () => {
|
|
423
|
+
if (context.cursor.eot()) {
|
|
424
|
+
return void 0;
|
|
425
|
+
}
|
|
426
|
+
let text = "";
|
|
427
|
+
while (!context.cursor.eot()) {
|
|
428
|
+
if (context.cursor.assert("{{")) {
|
|
429
|
+
return text;
|
|
430
|
+
}
|
|
431
|
+
const ch = context.cursor.getChar();
|
|
432
|
+
if (ch === "\\") {
|
|
433
|
+
const next = context.cursor.getChar(1);
|
|
434
|
+
if (next === "{" || next === "}") {
|
|
435
|
+
text += next;
|
|
436
|
+
context.cursor.skip(2);
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
text += ch;
|
|
441
|
+
context.cursor.skip(1);
|
|
442
|
+
}
|
|
443
|
+
return text;
|
|
444
|
+
};
|
|
445
|
+
while (!context.cursor.eot()) {
|
|
446
|
+
const start = context.cursor.location("start");
|
|
447
|
+
const text = readTextBlock();
|
|
448
|
+
if (text) {
|
|
449
|
+
tokens.push({
|
|
450
|
+
kind: "text",
|
|
451
|
+
text,
|
|
452
|
+
range: { start, end: context.cursor.location("end") }
|
|
453
|
+
});
|
|
454
|
+
} else {
|
|
455
|
+
tokens.push(...tokenizeCodeBlock(context));
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return tokens;
|
|
459
|
+
};
|
|
460
|
+
const parseNumber = (cursor, _errors) => {
|
|
461
|
+
const token = cursor.takeToken();
|
|
462
|
+
return {
|
|
463
|
+
kind: "number",
|
|
464
|
+
value: token.value,
|
|
465
|
+
range: token.range
|
|
466
|
+
};
|
|
467
|
+
};
|
|
468
|
+
const parseString = (cursor, _errors) => {
|
|
469
|
+
const token = cursor.takeToken();
|
|
470
|
+
return {
|
|
471
|
+
kind: "string",
|
|
472
|
+
value: token.value,
|
|
473
|
+
range: token.range
|
|
474
|
+
};
|
|
475
|
+
};
|
|
476
|
+
const parseIdentity = (cursor, _errors) => {
|
|
477
|
+
const token = cursor.takeToken();
|
|
478
|
+
return {
|
|
479
|
+
kind: "variable",
|
|
480
|
+
name: token.name,
|
|
481
|
+
range: token.range
|
|
482
|
+
};
|
|
483
|
+
};
|
|
484
|
+
const unitKind = "'()'";
|
|
485
|
+
const combineIntoScopeMultipleExpressions = (expressionList, ...outerRanges) => {
|
|
486
|
+
switch (expressionList.length) {
|
|
487
|
+
case 0: {
|
|
488
|
+
return void 0;
|
|
489
|
+
}
|
|
490
|
+
case 1: {
|
|
491
|
+
return expressionList[0];
|
|
492
|
+
}
|
|
493
|
+
default: {
|
|
494
|
+
return {
|
|
495
|
+
kind: "scope",
|
|
496
|
+
nodes: expressionList,
|
|
497
|
+
range: widerRange(
|
|
498
|
+
...expressionList.map((node) => node.range),
|
|
499
|
+
...outerRanges
|
|
500
|
+
)
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
const parseLambdaExpression = (cursor, errors) => {
|
|
506
|
+
const funToken = cursor.takeToken();
|
|
507
|
+
const namesPartial = parsePartialExpression(cursor, errors);
|
|
508
|
+
const bodyPartial = namesPartial ? parsePartialExpression(cursor, errors) : void 0;
|
|
509
|
+
if (!namesPartial || !bodyPartial) {
|
|
510
|
+
const ranges = [funToken.range];
|
|
511
|
+
if (namesPartial) {
|
|
512
|
+
ranges.push(namesPartial.range);
|
|
513
|
+
}
|
|
514
|
+
if (bodyPartial) {
|
|
515
|
+
ranges.push(bodyPartial.range);
|
|
516
|
+
}
|
|
517
|
+
errors.push({
|
|
518
|
+
type: "error",
|
|
519
|
+
description: "Required `fun` parameter identity and expression",
|
|
520
|
+
range: widerRange(...ranges)
|
|
521
|
+
});
|
|
522
|
+
return void 0;
|
|
523
|
+
}
|
|
524
|
+
const namesNode = normalizePartialUnitNode(namesPartial, errors);
|
|
525
|
+
if (namesNode.kind !== "variable" && namesNode.kind !== "list") {
|
|
526
|
+
errors.push({
|
|
527
|
+
type: "error",
|
|
528
|
+
description: "Required `fun` parameter identity",
|
|
529
|
+
range: namesNode.range
|
|
530
|
+
});
|
|
531
|
+
return void 0;
|
|
532
|
+
}
|
|
533
|
+
const nameNodes = extractParameterArguments(namesNode, errors);
|
|
534
|
+
if (!nameNodes) {
|
|
535
|
+
return void 0;
|
|
536
|
+
}
|
|
537
|
+
const bodyNode = normalizePartialUnitNode(bodyPartial, errors);
|
|
538
|
+
return {
|
|
539
|
+
kind: "lambda",
|
|
540
|
+
names: nameNodes,
|
|
541
|
+
body: bodyNode,
|
|
542
|
+
range: widerRange(funToken.range, namesNode.range, bodyNode.range)
|
|
543
|
+
};
|
|
544
|
+
};
|
|
545
|
+
const parsePartialExpression = (cursor, errors) => {
|
|
546
|
+
const token = cursor.peekToken();
|
|
547
|
+
switch (token.kind) {
|
|
548
|
+
case "number": {
|
|
549
|
+
const node = parseNumber(cursor);
|
|
550
|
+
return node;
|
|
551
|
+
}
|
|
552
|
+
case "string": {
|
|
553
|
+
const node = parseString(cursor);
|
|
554
|
+
return node;
|
|
555
|
+
}
|
|
556
|
+
case "identity": {
|
|
557
|
+
if (token.name === "fun") {
|
|
558
|
+
const node2 = parseLambdaExpression(cursor, errors);
|
|
559
|
+
return node2;
|
|
560
|
+
}
|
|
561
|
+
const node = parseIdentity(cursor);
|
|
562
|
+
return node;
|
|
563
|
+
}
|
|
564
|
+
case "open": {
|
|
565
|
+
cursor.takeToken();
|
|
566
|
+
switch (token.symbol) {
|
|
567
|
+
// Parenthesis surrounding expression list `( ... )` (Scope)
|
|
568
|
+
case "(": {
|
|
569
|
+
const innerNodes = parseMultipleApplicationExpressions(
|
|
570
|
+
cursor,
|
|
571
|
+
errors
|
|
572
|
+
);
|
|
573
|
+
const closeToken = cursor.peekToken();
|
|
574
|
+
let range;
|
|
575
|
+
if (!closeToken) {
|
|
576
|
+
range = widerRange(
|
|
577
|
+
token.range,
|
|
578
|
+
...innerNodes.map((node) => node.range)
|
|
579
|
+
);
|
|
580
|
+
errors.push({
|
|
581
|
+
type: "error",
|
|
582
|
+
description: "Could not find close parenthesis",
|
|
583
|
+
range: widerRange(
|
|
584
|
+
token.range,
|
|
585
|
+
...innerNodes.map((node) => node.range)
|
|
586
|
+
)
|
|
587
|
+
});
|
|
588
|
+
} else {
|
|
589
|
+
cursor.takeToken();
|
|
590
|
+
range = widerRange(
|
|
591
|
+
token.range,
|
|
592
|
+
...innerNodes.map((node) => node.range),
|
|
593
|
+
closeToken.range
|
|
594
|
+
);
|
|
595
|
+
if (closeToken.kind !== "close" || closeToken.symbol !== ")") {
|
|
596
|
+
errors.push({
|
|
597
|
+
type: "error",
|
|
598
|
+
description: `Mismatched close parenthesis`,
|
|
599
|
+
range
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
if (innerNodes.length === 0) {
|
|
604
|
+
return {
|
|
605
|
+
kind: unitKind,
|
|
606
|
+
range
|
|
607
|
+
};
|
|
608
|
+
} else {
|
|
609
|
+
const node = combineIntoScopeMultipleExpressions(innerNodes, range);
|
|
610
|
+
return node;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
// Bracket surrounding expression list `[ ... ]` (Iterable list)
|
|
614
|
+
case "[": {
|
|
615
|
+
const itemNodes = parseListExpression(cursor, errors);
|
|
616
|
+
const closeToken = cursor.peekToken();
|
|
617
|
+
let range;
|
|
618
|
+
if (!closeToken) {
|
|
619
|
+
range = widerRange(
|
|
620
|
+
token.range,
|
|
621
|
+
...itemNodes.map((node) => node.range)
|
|
622
|
+
);
|
|
623
|
+
errors.push({
|
|
624
|
+
type: "error",
|
|
625
|
+
description: "Could not find close bracket",
|
|
626
|
+
range
|
|
627
|
+
});
|
|
628
|
+
} else {
|
|
629
|
+
cursor.takeToken();
|
|
630
|
+
range = widerRange(
|
|
631
|
+
token.range,
|
|
632
|
+
...itemNodes.map((node) => node.range),
|
|
633
|
+
closeToken.range
|
|
634
|
+
);
|
|
635
|
+
if (closeToken.kind !== "close" || closeToken.symbol !== "]") {
|
|
636
|
+
errors.push({
|
|
637
|
+
type: "error",
|
|
638
|
+
description: `Mismatched close bracket`,
|
|
639
|
+
range
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
return {
|
|
644
|
+
kind: "list",
|
|
645
|
+
items: itemNodes,
|
|
646
|
+
range
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
default: {
|
|
650
|
+
errors.push({
|
|
651
|
+
type: "error",
|
|
652
|
+
description: `Invalid open parenthesis/bracket`,
|
|
653
|
+
range: token.range
|
|
654
|
+
});
|
|
655
|
+
return void 0;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
return void 0;
|
|
661
|
+
};
|
|
662
|
+
const normalizePartialUnitNode = (node, errors) => {
|
|
663
|
+
if (node.kind === unitKind) {
|
|
664
|
+
errors.push({
|
|
665
|
+
type: "error",
|
|
666
|
+
description: `Invalid ${unitKind} at this location`,
|
|
667
|
+
range: node.range
|
|
668
|
+
});
|
|
669
|
+
return {
|
|
670
|
+
kind: "variable",
|
|
671
|
+
name: "undefined",
|
|
672
|
+
range: node.range
|
|
673
|
+
};
|
|
674
|
+
} else {
|
|
675
|
+
return node;
|
|
676
|
+
}
|
|
677
|
+
};
|
|
678
|
+
const finalizeApplicationException = (partialNodes, errors) => {
|
|
679
|
+
if (partialNodes.length === 0) {
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
if (partialNodes.length === 1) {
|
|
683
|
+
return normalizePartialUnitNode(partialNodes[0], errors);
|
|
684
|
+
}
|
|
685
|
+
const func = partialNodes[0];
|
|
686
|
+
if (func.kind !== "variable" && func.kind !== "lambda") {
|
|
687
|
+
errors.push({
|
|
688
|
+
type: "error",
|
|
689
|
+
description: `Invalid ${func.kind} at this location`,
|
|
690
|
+
range: func.range
|
|
691
|
+
});
|
|
692
|
+
return void 0;
|
|
693
|
+
}
|
|
694
|
+
const arg0 = partialNodes[1];
|
|
695
|
+
if (arg0.kind === unitKind) {
|
|
696
|
+
return {
|
|
697
|
+
kind: "apply",
|
|
698
|
+
func,
|
|
699
|
+
args: [],
|
|
700
|
+
// Unit application: `foobar ()`
|
|
701
|
+
range: widerRange(func.range, arg0.range)
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
const args = partialNodes.slice(1).map((node) => normalizePartialUnitNode(node, errors));
|
|
705
|
+
return {
|
|
706
|
+
kind: "apply",
|
|
707
|
+
func,
|
|
708
|
+
args,
|
|
709
|
+
range: widerRange(func.range, ...args.map((node) => node.range))
|
|
710
|
+
};
|
|
711
|
+
};
|
|
712
|
+
const parseMultipleApplicationExpressions = (cursor, errors) => {
|
|
713
|
+
const expressionList = [];
|
|
714
|
+
const partialNodes = [];
|
|
715
|
+
while (true) {
|
|
716
|
+
const token = cursor.peekToken();
|
|
717
|
+
if (!token) {
|
|
718
|
+
break;
|
|
719
|
+
}
|
|
720
|
+
switch (token.kind) {
|
|
721
|
+
case "eol": {
|
|
722
|
+
cursor.takeToken();
|
|
723
|
+
const expr2 = finalizeApplicationException(partialNodes, errors);
|
|
724
|
+
if (expr2) {
|
|
725
|
+
expressionList.push(expr2);
|
|
726
|
+
}
|
|
727
|
+
partialNodes.length = 0;
|
|
728
|
+
continue;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
const partialNode = parsePartialExpression(cursor, errors);
|
|
732
|
+
if (!partialNode) {
|
|
733
|
+
break;
|
|
734
|
+
}
|
|
735
|
+
partialNodes.push(partialNode);
|
|
736
|
+
}
|
|
737
|
+
const expr = finalizeApplicationException(partialNodes, errors);
|
|
738
|
+
if (expr) {
|
|
739
|
+
expressionList.push(expr);
|
|
740
|
+
}
|
|
741
|
+
return expressionList;
|
|
742
|
+
};
|
|
743
|
+
const parseListExpression = (cursor, errors) => {
|
|
744
|
+
const itemNodes = [];
|
|
745
|
+
while (true) {
|
|
746
|
+
const token = cursor.peekToken();
|
|
747
|
+
if (!token) {
|
|
748
|
+
break;
|
|
749
|
+
}
|
|
750
|
+
switch (token.kind) {
|
|
751
|
+
case "eol": {
|
|
752
|
+
continue;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
const partialNode = parsePartialExpression(cursor, errors);
|
|
756
|
+
if (!partialNode) {
|
|
757
|
+
break;
|
|
758
|
+
}
|
|
759
|
+
itemNodes.push(normalizePartialUnitNode(partialNode, errors));
|
|
760
|
+
}
|
|
761
|
+
return itemNodes;
|
|
762
|
+
};
|
|
763
|
+
const drainEndOfLineAndPeek = (cursor) => {
|
|
764
|
+
let token = cursor.peekToken();
|
|
765
|
+
while (token) {
|
|
766
|
+
if (token.kind === "eol") {
|
|
767
|
+
cursor.takeToken();
|
|
768
|
+
} else {
|
|
769
|
+
break;
|
|
770
|
+
}
|
|
771
|
+
token = cursor.peekToken();
|
|
772
|
+
}
|
|
773
|
+
return token;
|
|
774
|
+
};
|
|
775
|
+
const parseExpression = (cursor, errors) => {
|
|
776
|
+
let token = drainEndOfLineAndPeek(cursor);
|
|
777
|
+
if (!token) {
|
|
778
|
+
return void 0;
|
|
779
|
+
}
|
|
780
|
+
const partialNodes = [];
|
|
781
|
+
while (true) {
|
|
782
|
+
const partialNode = parsePartialExpression(cursor, errors);
|
|
783
|
+
if (!partialNode) {
|
|
784
|
+
break;
|
|
785
|
+
}
|
|
786
|
+
partialNodes.push(partialNode);
|
|
787
|
+
token = cursor.peekToken();
|
|
788
|
+
if (!token) {
|
|
789
|
+
break;
|
|
790
|
+
}
|
|
791
|
+
if (token.kind === "eol") {
|
|
792
|
+
cursor.takeToken();
|
|
793
|
+
token = cursor.peekToken();
|
|
794
|
+
break;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
drainEndOfLineAndPeek(cursor);
|
|
798
|
+
const expr = finalizeApplicationException(partialNodes, errors);
|
|
799
|
+
return expr;
|
|
800
|
+
};
|
|
801
|
+
const createBranchState = () => ({
|
|
802
|
+
blocks: [],
|
|
803
|
+
exprBuffer: []
|
|
804
|
+
});
|
|
805
|
+
const getBranchState = (statementState) => {
|
|
806
|
+
switch (statementState.kind) {
|
|
807
|
+
case "root": {
|
|
808
|
+
return statementState.branch;
|
|
809
|
+
}
|
|
810
|
+
case "if": {
|
|
811
|
+
return statementState.currentBlock === "then" ? statementState.then : statementState.else;
|
|
812
|
+
}
|
|
813
|
+
case "while":
|
|
814
|
+
case "for": {
|
|
815
|
+
return statementState.repeat;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
};
|
|
819
|
+
const flushExpressions = (branch) => {
|
|
820
|
+
if (branch.exprBuffer.length === 0) {
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
if (branch.exprBuffer.length === 1) {
|
|
824
|
+
branch.blocks.push(branch.exprBuffer[0]);
|
|
825
|
+
} else {
|
|
826
|
+
branch.blocks.push({
|
|
827
|
+
kind: "scope",
|
|
828
|
+
nodes: branch.exprBuffer,
|
|
829
|
+
range: widerRange(...branch.exprBuffer.map((node) => node.range))
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
branch.exprBuffer = [];
|
|
833
|
+
};
|
|
834
|
+
const flushCurrentBranch = (statementStates) => {
|
|
835
|
+
const statementState = statementStates[statementStates.length - 1];
|
|
836
|
+
flushExpressions(getBranchState(statementState));
|
|
837
|
+
};
|
|
838
|
+
const flushStatementState = (statementState) => {
|
|
839
|
+
switch (statementState.kind) {
|
|
840
|
+
case "root": {
|
|
841
|
+
flushExpressions(statementState.branch);
|
|
842
|
+
break;
|
|
843
|
+
}
|
|
844
|
+
case "if": {
|
|
845
|
+
flushExpressions(statementState.then);
|
|
846
|
+
flushExpressions(statementState.else);
|
|
847
|
+
break;
|
|
848
|
+
}
|
|
849
|
+
case "while":
|
|
850
|
+
case "for": {
|
|
851
|
+
flushExpressions(statementState.repeat);
|
|
852
|
+
break;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
};
|
|
856
|
+
const pushExpressionNode = (statementStates, node) => {
|
|
857
|
+
const statementState = statementStates[statementStates.length - 1];
|
|
858
|
+
const branch = getBranchState(statementState);
|
|
859
|
+
branch.exprBuffer.push(node);
|
|
860
|
+
};
|
|
861
|
+
const pushBlockNode = (statementStates, node) => {
|
|
862
|
+
const statementState = statementStates[statementStates.length - 1];
|
|
863
|
+
const branch = getBranchState(statementState);
|
|
864
|
+
flushExpressions(branch);
|
|
865
|
+
branch.blocks.push(node);
|
|
866
|
+
};
|
|
867
|
+
const pushNode = (statementStates, node) => {
|
|
868
|
+
switch (node.kind) {
|
|
869
|
+
case "text":
|
|
870
|
+
case "if":
|
|
871
|
+
case "while":
|
|
872
|
+
case "for": {
|
|
873
|
+
pushBlockNode(statementStates, node);
|
|
874
|
+
break;
|
|
875
|
+
}
|
|
876
|
+
default: {
|
|
877
|
+
pushExpressionNode(statementStates, node);
|
|
878
|
+
break;
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
};
|
|
882
|
+
const parseStatementArguments = (cursor, errors) => {
|
|
883
|
+
const args = [];
|
|
884
|
+
while (true) {
|
|
885
|
+
const token = cursor.peekToken();
|
|
886
|
+
if (!token) {
|
|
887
|
+
break;
|
|
888
|
+
}
|
|
889
|
+
if (token.kind === "eol") {
|
|
890
|
+
cursor.takeToken();
|
|
891
|
+
break;
|
|
892
|
+
}
|
|
893
|
+
const partialNode = parsePartialExpression(cursor, errors);
|
|
894
|
+
if (!partialNode) {
|
|
895
|
+
break;
|
|
896
|
+
}
|
|
897
|
+
args.push(normalizePartialUnitNode(partialNode, errors));
|
|
898
|
+
}
|
|
899
|
+
return args;
|
|
900
|
+
};
|
|
901
|
+
const extractParameterArguments = (namesNode, errors) => {
|
|
902
|
+
switch (namesNode.kind) {
|
|
903
|
+
case "variable": {
|
|
904
|
+
return [namesNode];
|
|
905
|
+
}
|
|
906
|
+
case "list": {
|
|
907
|
+
const nameNodes = [];
|
|
908
|
+
for (const nameNode of namesNode.items) {
|
|
909
|
+
if (nameNode.kind !== "variable") {
|
|
910
|
+
errors.push({
|
|
911
|
+
type: "error",
|
|
912
|
+
description: "Required lambda parameter identity",
|
|
913
|
+
range: nameNode.range
|
|
914
|
+
});
|
|
915
|
+
} else {
|
|
916
|
+
nameNodes.push(nameNode);
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
return nameNodes;
|
|
920
|
+
}
|
|
921
|
+
default: {
|
|
922
|
+
errors.push({
|
|
923
|
+
type: "error",
|
|
924
|
+
description: "Required lambda parameter identity",
|
|
925
|
+
range: namesNode.range
|
|
926
|
+
});
|
|
927
|
+
return void 0;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
};
|
|
931
|
+
const parseBlock = (cursor, errors) => {
|
|
932
|
+
const rootState = {
|
|
933
|
+
kind: "root",
|
|
934
|
+
startRange: emptyRange,
|
|
935
|
+
branch: createBranchState()
|
|
936
|
+
};
|
|
937
|
+
const statementStates = [rootState];
|
|
938
|
+
let isInExpressionBlock = false;
|
|
939
|
+
while (true) {
|
|
940
|
+
const token = drainEndOfLineAndPeek(cursor);
|
|
941
|
+
if (!token) {
|
|
942
|
+
break;
|
|
943
|
+
}
|
|
944
|
+
switch (token.kind) {
|
|
945
|
+
case "text": {
|
|
946
|
+
cursor.takeToken();
|
|
947
|
+
if (isInExpressionBlock) {
|
|
948
|
+
errors.push({
|
|
949
|
+
type: "error",
|
|
950
|
+
description: `Already opened expression block (tokenizer bug?)`,
|
|
951
|
+
range: token.range
|
|
952
|
+
});
|
|
953
|
+
break;
|
|
954
|
+
}
|
|
955
|
+
pushNode(statementStates, {
|
|
956
|
+
kind: "text",
|
|
957
|
+
text: token.text,
|
|
958
|
+
range: token.range
|
|
959
|
+
});
|
|
960
|
+
break;
|
|
961
|
+
}
|
|
962
|
+
case "open": {
|
|
963
|
+
if (token.symbol === "{{") {
|
|
964
|
+
cursor.takeToken();
|
|
965
|
+
if (isInExpressionBlock) {
|
|
966
|
+
errors.push({
|
|
967
|
+
type: "error",
|
|
968
|
+
description: `Already opened expression block`,
|
|
969
|
+
range: token.range
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
isInExpressionBlock = true;
|
|
973
|
+
} else {
|
|
974
|
+
const node = parseExpression(cursor, errors);
|
|
975
|
+
if (node) {
|
|
976
|
+
pushNode(statementStates, node);
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
break;
|
|
980
|
+
}
|
|
981
|
+
case "close": {
|
|
982
|
+
cursor.takeToken();
|
|
983
|
+
if (!isInExpressionBlock) {
|
|
984
|
+
errors.push({
|
|
985
|
+
type: "error",
|
|
986
|
+
description: `Mismatched close bracket`,
|
|
987
|
+
range: token.range
|
|
988
|
+
});
|
|
989
|
+
break;
|
|
990
|
+
}
|
|
991
|
+
flushCurrentBranch(statementStates);
|
|
992
|
+
isInExpressionBlock = false;
|
|
993
|
+
break;
|
|
994
|
+
}
|
|
995
|
+
case "identity": {
|
|
996
|
+
if (!isInExpressionBlock) {
|
|
997
|
+
errors.push({
|
|
998
|
+
type: "error",
|
|
999
|
+
description: `Invalid identity (tokenizer bug?)`,
|
|
1000
|
+
range: token.range
|
|
1001
|
+
});
|
|
1002
|
+
break;
|
|
1003
|
+
}
|
|
1004
|
+
switch (token.name) {
|
|
1005
|
+
case "if": {
|
|
1006
|
+
cursor.takeToken();
|
|
1007
|
+
const args = parseStatementArguments(cursor, errors);
|
|
1008
|
+
if (args.length !== 1) {
|
|
1009
|
+
errors.push({
|
|
1010
|
+
type: "error",
|
|
1011
|
+
description: "Required `if` condition",
|
|
1012
|
+
range: widerRange(
|
|
1013
|
+
token.range,
|
|
1014
|
+
...args.map((node) => node.range)
|
|
1015
|
+
)
|
|
1016
|
+
});
|
|
1017
|
+
break;
|
|
1018
|
+
}
|
|
1019
|
+
const conditionNode = args[0];
|
|
1020
|
+
statementStates.push({
|
|
1021
|
+
kind: "if",
|
|
1022
|
+
startRange: token.range,
|
|
1023
|
+
condition: conditionNode,
|
|
1024
|
+
then: createBranchState(),
|
|
1025
|
+
else: createBranchState(),
|
|
1026
|
+
currentBlock: "then"
|
|
1027
|
+
});
|
|
1028
|
+
break;
|
|
1029
|
+
}
|
|
1030
|
+
case "else": {
|
|
1031
|
+
cursor.takeToken();
|
|
1032
|
+
const args = parseStatementArguments(cursor, errors);
|
|
1033
|
+
if (args.length !== 0) {
|
|
1034
|
+
errors.push({
|
|
1035
|
+
type: "error",
|
|
1036
|
+
description: "Could not take any arguments in `else` statement",
|
|
1037
|
+
range: widerRange(
|
|
1038
|
+
token.range,
|
|
1039
|
+
...args.map((node) => node.range)
|
|
1040
|
+
)
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
if (statementStates.length <= 1) {
|
|
1044
|
+
errors.push({
|
|
1045
|
+
type: "error",
|
|
1046
|
+
description: "Cound not find pair of `if` statement",
|
|
1047
|
+
range: token.range
|
|
1048
|
+
});
|
|
1049
|
+
break;
|
|
1050
|
+
}
|
|
1051
|
+
const lastState = statementStates[statementStates.length - 1];
|
|
1052
|
+
if (lastState.kind !== "if") {
|
|
1053
|
+
errors.push({
|
|
1054
|
+
type: "error",
|
|
1055
|
+
description: "Cound not find pair of `if` statement",
|
|
1056
|
+
range: token.range
|
|
1057
|
+
});
|
|
1058
|
+
break;
|
|
1059
|
+
}
|
|
1060
|
+
if (lastState.currentBlock === "else") {
|
|
1061
|
+
errors.push({
|
|
1062
|
+
type: "error",
|
|
1063
|
+
description: "Duplicated `else` statement",
|
|
1064
|
+
range: token.range
|
|
1065
|
+
});
|
|
1066
|
+
break;
|
|
1067
|
+
}
|
|
1068
|
+
flushExpressions(lastState.then);
|
|
1069
|
+
lastState.currentBlock = "else";
|
|
1070
|
+
break;
|
|
1071
|
+
}
|
|
1072
|
+
case "while": {
|
|
1073
|
+
cursor.takeToken();
|
|
1074
|
+
const args = parseStatementArguments(cursor, errors);
|
|
1075
|
+
if (args.length !== 1) {
|
|
1076
|
+
errors.push({
|
|
1077
|
+
type: "error",
|
|
1078
|
+
description: "Required `while` condition",
|
|
1079
|
+
range: widerRange(
|
|
1080
|
+
token.range,
|
|
1081
|
+
...args.map((node) => node.range)
|
|
1082
|
+
)
|
|
1083
|
+
});
|
|
1084
|
+
break;
|
|
1085
|
+
}
|
|
1086
|
+
const conditionNode = args[0];
|
|
1087
|
+
statementStates.push({
|
|
1088
|
+
kind: "while",
|
|
1089
|
+
startRange: token.range,
|
|
1090
|
+
condition: conditionNode,
|
|
1091
|
+
repeat: createBranchState()
|
|
1092
|
+
});
|
|
1093
|
+
break;
|
|
1094
|
+
}
|
|
1095
|
+
case "for": {
|
|
1096
|
+
cursor.takeToken();
|
|
1097
|
+
const args = parseStatementArguments(cursor, errors);
|
|
1098
|
+
if (args.length !== 2) {
|
|
1099
|
+
errors.push({
|
|
1100
|
+
type: "error",
|
|
1101
|
+
description: "Required `for` bind identity and iterable expression",
|
|
1102
|
+
range: widerRange(
|
|
1103
|
+
token.range,
|
|
1104
|
+
...args.map((node) => node.range)
|
|
1105
|
+
)
|
|
1106
|
+
});
|
|
1107
|
+
break;
|
|
1108
|
+
}
|
|
1109
|
+
const bindNode = args[0];
|
|
1110
|
+
if (bindNode.kind !== "variable") {
|
|
1111
|
+
errors.push({
|
|
1112
|
+
type: "error",
|
|
1113
|
+
description: "Required `for` bind identity",
|
|
1114
|
+
range: bindNode.range
|
|
1115
|
+
});
|
|
1116
|
+
break;
|
|
1117
|
+
}
|
|
1118
|
+
const iterableNode = args[1];
|
|
1119
|
+
statementStates.push({
|
|
1120
|
+
kind: "for",
|
|
1121
|
+
startRange: token.range,
|
|
1122
|
+
bind: bindNode,
|
|
1123
|
+
iterable: iterableNode,
|
|
1124
|
+
repeat: createBranchState()
|
|
1125
|
+
});
|
|
1126
|
+
break;
|
|
1127
|
+
}
|
|
1128
|
+
case "end": {
|
|
1129
|
+
cursor.takeToken();
|
|
1130
|
+
const args = parseStatementArguments(cursor, errors);
|
|
1131
|
+
if (args.length !== 0) {
|
|
1132
|
+
errors.push({
|
|
1133
|
+
type: "error",
|
|
1134
|
+
description: "Could not take any arguments in `end` statement",
|
|
1135
|
+
range: widerRange(
|
|
1136
|
+
token.range,
|
|
1137
|
+
...args.map((node) => node.range)
|
|
1138
|
+
)
|
|
1139
|
+
});
|
|
1140
|
+
}
|
|
1141
|
+
if (statementStates.length <= 1) {
|
|
1142
|
+
errors.push({
|
|
1143
|
+
type: "error",
|
|
1144
|
+
description: "Cound not find pair of `if`,`while` or `for` statement",
|
|
1145
|
+
range: token.range
|
|
1146
|
+
});
|
|
1147
|
+
break;
|
|
1148
|
+
}
|
|
1149
|
+
const lastState = statementStates.pop();
|
|
1150
|
+
flushStatementState(lastState);
|
|
1151
|
+
switch (lastState.kind) {
|
|
1152
|
+
case "if": {
|
|
1153
|
+
pushNode(statementStates, {
|
|
1154
|
+
kind: "if",
|
|
1155
|
+
condition: lastState.condition,
|
|
1156
|
+
then: lastState.then.blocks,
|
|
1157
|
+
else: lastState.else.blocks,
|
|
1158
|
+
range: widerRange(
|
|
1159
|
+
lastState.startRange,
|
|
1160
|
+
lastState.condition.range,
|
|
1161
|
+
...lastState.then.blocks.map((node) => node.range),
|
|
1162
|
+
...lastState.else.blocks.map((node) => node.range),
|
|
1163
|
+
token.range
|
|
1164
|
+
)
|
|
1165
|
+
});
|
|
1166
|
+
break;
|
|
1167
|
+
}
|
|
1168
|
+
case "while": {
|
|
1169
|
+
pushNode(statementStates, {
|
|
1170
|
+
kind: "while",
|
|
1171
|
+
condition: lastState.condition,
|
|
1172
|
+
repeat: lastState.repeat.blocks,
|
|
1173
|
+
range: widerRange(
|
|
1174
|
+
lastState.startRange,
|
|
1175
|
+
lastState.condition.range,
|
|
1176
|
+
...lastState.repeat.blocks.map((node) => node.range),
|
|
1177
|
+
token.range
|
|
1178
|
+
)
|
|
1179
|
+
});
|
|
1180
|
+
break;
|
|
1181
|
+
}
|
|
1182
|
+
case "for": {
|
|
1183
|
+
pushNode(statementStates, {
|
|
1184
|
+
kind: "for",
|
|
1185
|
+
bind: lastState.bind,
|
|
1186
|
+
iterable: lastState.iterable,
|
|
1187
|
+
repeat: lastState.repeat.blocks,
|
|
1188
|
+
range: widerRange(
|
|
1189
|
+
lastState.startRange,
|
|
1190
|
+
lastState.bind.range,
|
|
1191
|
+
...lastState.repeat.blocks.map((node) => node.range),
|
|
1192
|
+
token.range
|
|
1193
|
+
)
|
|
1194
|
+
});
|
|
1195
|
+
break;
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
break;
|
|
1199
|
+
}
|
|
1200
|
+
case "set": {
|
|
1201
|
+
cursor.takeToken();
|
|
1202
|
+
const args = parseStatementArguments(cursor, errors);
|
|
1203
|
+
if (args.length !== 2) {
|
|
1204
|
+
errors.push({
|
|
1205
|
+
type: "error",
|
|
1206
|
+
description: "Required `set` bind identity and expression",
|
|
1207
|
+
range: widerRange(
|
|
1208
|
+
token.range,
|
|
1209
|
+
...args.map((node) => node.range)
|
|
1210
|
+
)
|
|
1211
|
+
});
|
|
1212
|
+
break;
|
|
1213
|
+
}
|
|
1214
|
+
const bindNode = args[0];
|
|
1215
|
+
if (bindNode.kind !== "variable") {
|
|
1216
|
+
errors.push({
|
|
1217
|
+
type: "error",
|
|
1218
|
+
description: "Required `set` bind identity",
|
|
1219
|
+
range: bindNode.range
|
|
1220
|
+
});
|
|
1221
|
+
break;
|
|
1222
|
+
}
|
|
1223
|
+
const exprNode = args[1];
|
|
1224
|
+
pushNode(statementStates, {
|
|
1225
|
+
kind: "set",
|
|
1226
|
+
name: bindNode,
|
|
1227
|
+
expr: exprNode,
|
|
1228
|
+
range: widerRange(token.range, bindNode.range, exprNode.range)
|
|
1229
|
+
});
|
|
1230
|
+
break;
|
|
1231
|
+
}
|
|
1232
|
+
default: {
|
|
1233
|
+
const node = parseExpression(cursor, errors);
|
|
1234
|
+
if (node) {
|
|
1235
|
+
pushNode(statementStates, node);
|
|
1236
|
+
}
|
|
1237
|
+
break;
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
break;
|
|
1241
|
+
}
|
|
1242
|
+
default: {
|
|
1243
|
+
if (!isInExpressionBlock) {
|
|
1244
|
+
errors.push({
|
|
1245
|
+
type: "error",
|
|
1246
|
+
description: `Invalid ${token.kind} (tokenizer bug?)`,
|
|
1247
|
+
range: token.range
|
|
1248
|
+
});
|
|
1249
|
+
break;
|
|
1250
|
+
}
|
|
1251
|
+
const node = parseExpression(cursor, errors);
|
|
1252
|
+
if (node) {
|
|
1253
|
+
pushNode(statementStates, node);
|
|
1254
|
+
}
|
|
1255
|
+
break;
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
flushStatementState(rootState);
|
|
1260
|
+
if (statementStates.length !== 1) {
|
|
1261
|
+
errors.push({
|
|
1262
|
+
type: "error",
|
|
1263
|
+
description: `Could not find statement closing`,
|
|
1264
|
+
range: widerRange(...statementStates.map((state) => state.startRange))
|
|
1265
|
+
});
|
|
1266
|
+
}
|
|
1267
|
+
return rootState.branch.blocks;
|
|
1268
|
+
};
|
|
1269
|
+
const createParserCursor = (tokens) => {
|
|
1270
|
+
let index = 0;
|
|
1271
|
+
const peekToken = () => {
|
|
1272
|
+
return tokens[index];
|
|
1273
|
+
};
|
|
1274
|
+
const takeToken = () => {
|
|
1275
|
+
if (index >= tokens.length) {
|
|
1276
|
+
return void 0;
|
|
1277
|
+
}
|
|
1278
|
+
const token = tokens[index];
|
|
1279
|
+
index++;
|
|
1280
|
+
return token;
|
|
1281
|
+
};
|
|
1282
|
+
return {
|
|
1283
|
+
peekToken,
|
|
1284
|
+
takeToken
|
|
1285
|
+
};
|
|
1286
|
+
};
|
|
1287
|
+
const runParser = (tokens, errors) => {
|
|
1288
|
+
const cursor = createParserCursor(tokens);
|
|
1289
|
+
const blockNodes = parseBlock(cursor, errors);
|
|
1290
|
+
return blockNodes;
|
|
1291
|
+
};
|
|
1292
|
+
const deconstructConditionalCombine = (name) => {
|
|
1293
|
+
if (name.length >= 1) {
|
|
1294
|
+
const last = name[name.length - 1];
|
|
1295
|
+
if (last === "?") {
|
|
1296
|
+
return {
|
|
1297
|
+
name: name.substring(0, name.length - 1),
|
|
1298
|
+
canIgnore: true
|
|
1299
|
+
};
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
return {
|
|
1303
|
+
name,
|
|
1304
|
+
canIgnore: false
|
|
1305
|
+
};
|
|
1306
|
+
};
|
|
1307
|
+
const traverseVariable = (context, name) => {
|
|
1308
|
+
const names = name.name.split(".");
|
|
1309
|
+
const n0 = names[0];
|
|
1310
|
+
const n0r = deconstructConditionalCombine(n0);
|
|
1311
|
+
const result0 = context.getValue(n0r.name);
|
|
1312
|
+
if (!result0.isFound) {
|
|
1313
|
+
if (!n0r.canIgnore) {
|
|
1314
|
+
context.appendError({
|
|
1315
|
+
type: "error",
|
|
1316
|
+
description: `variable is not bound: ${names[0]}`,
|
|
1317
|
+
range: name.range
|
|
1318
|
+
});
|
|
1319
|
+
}
|
|
1320
|
+
return void 0;
|
|
1321
|
+
}
|
|
1322
|
+
let value = result0.value;
|
|
1323
|
+
for (const n of names.slice(1)) {
|
|
1324
|
+
const nr = deconstructConditionalCombine(n);
|
|
1325
|
+
if (typeof value === "object") {
|
|
1326
|
+
const r = value;
|
|
1327
|
+
const v = r[nr.name];
|
|
1328
|
+
value = v;
|
|
1329
|
+
} else {
|
|
1330
|
+
if (!nr.canIgnore) {
|
|
1331
|
+
context.appendError({
|
|
1332
|
+
type: "error",
|
|
1333
|
+
description: `variable is not bound: ${n}`,
|
|
1334
|
+
range: name.range
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
return void 0;
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
return value;
|
|
1341
|
+
};
|
|
1342
|
+
const fromLambda = (context, lambda) => {
|
|
1343
|
+
return async (...args) => {
|
|
1344
|
+
if (args.length < lambda.names.length) {
|
|
1345
|
+
context.appendError({
|
|
1346
|
+
type: "error",
|
|
1347
|
+
description: `Arguments are not filled: ${args.length} < ${lambda.names.length}`,
|
|
1348
|
+
range: lambda.range
|
|
1349
|
+
});
|
|
1350
|
+
return void 0;
|
|
1351
|
+
} else if (args.length > lambda.names.length) {
|
|
1352
|
+
context.appendError({
|
|
1353
|
+
type: "warning",
|
|
1354
|
+
description: `Too many arguments: ${args.length} > ${lambda.names.length}`,
|
|
1355
|
+
range: lambda.range
|
|
1356
|
+
});
|
|
1357
|
+
}
|
|
1358
|
+
const newContext = context.newScope();
|
|
1359
|
+
for (let index = 0; index < lambda.names.length; index++) {
|
|
1360
|
+
newContext.setValue(lambda.names[index].name, args[index]);
|
|
1361
|
+
}
|
|
1362
|
+
const result = await reduceExpressionNode(newContext, lambda.body);
|
|
1363
|
+
return result;
|
|
1364
|
+
};
|
|
1365
|
+
};
|
|
1366
|
+
const reduceExpressionNode = async (context, node) => {
|
|
1367
|
+
switch (node.kind) {
|
|
1368
|
+
case "number":
|
|
1369
|
+
case "string": {
|
|
1370
|
+
return node.value;
|
|
1371
|
+
}
|
|
1372
|
+
case "variable": {
|
|
1373
|
+
return traverseVariable(context, node);
|
|
1374
|
+
}
|
|
1375
|
+
case "apply": {
|
|
1376
|
+
const func = await reduceExpressionNode(context, node.func);
|
|
1377
|
+
if (typeof func !== "function") {
|
|
1378
|
+
context.appendError({
|
|
1379
|
+
type: "error",
|
|
1380
|
+
description: "could not apply it for function",
|
|
1381
|
+
range: node.range
|
|
1382
|
+
});
|
|
1383
|
+
return void 0;
|
|
1384
|
+
}
|
|
1385
|
+
const thisProxy = context.createFunctionContext(node);
|
|
1386
|
+
if (isSpecialFunction(func)) {
|
|
1387
|
+
const value = await func.call(thisProxy, ...node.args);
|
|
1388
|
+
return value;
|
|
1389
|
+
} else {
|
|
1390
|
+
const args = await Promise.all(
|
|
1391
|
+
node.args.map(async (argNode) => {
|
|
1392
|
+
try {
|
|
1393
|
+
const arg = await reduceExpressionNode(context, argNode);
|
|
1394
|
+
return arg;
|
|
1395
|
+
} catch (e) {
|
|
1396
|
+
context.appendError({
|
|
1397
|
+
type: "error",
|
|
1398
|
+
description: fromError(e),
|
|
1399
|
+
range: argNode.range
|
|
1400
|
+
});
|
|
1401
|
+
return void 0;
|
|
1402
|
+
}
|
|
1403
|
+
})
|
|
1404
|
+
);
|
|
1405
|
+
const value = await func.call(thisProxy, ...args);
|
|
1406
|
+
return value;
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
case "lambda": {
|
|
1410
|
+
return fromLambda(context, node);
|
|
1411
|
+
}
|
|
1412
|
+
case "list": {
|
|
1413
|
+
const results = await Promise.all(
|
|
1414
|
+
node.items.map((item) => reduceExpressionNode(context, item))
|
|
1415
|
+
);
|
|
1416
|
+
return results;
|
|
1417
|
+
}
|
|
1418
|
+
case "set": {
|
|
1419
|
+
const expr = await reduceExpressionNode(context, node.expr);
|
|
1420
|
+
context.setValue(node.name.name, expr);
|
|
1421
|
+
return void 0;
|
|
1422
|
+
}
|
|
1423
|
+
case "scope": {
|
|
1424
|
+
if (node.nodes.length === 0) {
|
|
1425
|
+
return [];
|
|
1426
|
+
}
|
|
1427
|
+
let index = 0;
|
|
1428
|
+
while (!context.isFailed()) {
|
|
1429
|
+
const result = await reduceExpressionNode(context, node.nodes[index]);
|
|
1430
|
+
index++;
|
|
1431
|
+
if (index >= node.nodes.length) {
|
|
1432
|
+
return result;
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
return void 0;
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
};
|
|
1439
|
+
const reduceNode = async (context, node) => {
|
|
1440
|
+
switch (node.kind) {
|
|
1441
|
+
case "text": {
|
|
1442
|
+
return [node.text];
|
|
1443
|
+
}
|
|
1444
|
+
case "for": {
|
|
1445
|
+
const result = await reduceExpressionNode(context, node.iterable);
|
|
1446
|
+
const iterable = asIterable(result);
|
|
1447
|
+
if (!iterable) {
|
|
1448
|
+
context.appendError({
|
|
1449
|
+
type: "error",
|
|
1450
|
+
description: "could not apply it for function",
|
|
1451
|
+
range: node.range
|
|
1452
|
+
});
|
|
1453
|
+
return [];
|
|
1454
|
+
}
|
|
1455
|
+
const resultList = [];
|
|
1456
|
+
for (const item of iterable) {
|
|
1457
|
+
if (context.isFailed()) {
|
|
1458
|
+
break;
|
|
1459
|
+
}
|
|
1460
|
+
context.setValue(node.bind.name, item);
|
|
1461
|
+
for (const repeat of node.repeat) {
|
|
1462
|
+
const results = await reduceNode(context, repeat);
|
|
1463
|
+
resultList.push(...results);
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
return resultList;
|
|
1467
|
+
}
|
|
1468
|
+
case "while": {
|
|
1469
|
+
const resultList = [];
|
|
1470
|
+
while (!context.isFailed()) {
|
|
1471
|
+
const condition = await reduceExpressionNode(context, node.condition);
|
|
1472
|
+
if (!isConditionalTrue(condition)) {
|
|
1473
|
+
break;
|
|
1474
|
+
}
|
|
1475
|
+
for (const repeat of node.repeat) {
|
|
1476
|
+
const results = await reduceNode(context, repeat);
|
|
1477
|
+
resultList.push(...results);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
return resultList;
|
|
1481
|
+
}
|
|
1482
|
+
case "if": {
|
|
1483
|
+
const resultList = [];
|
|
1484
|
+
const condition = await reduceExpressionNode(context, node.condition);
|
|
1485
|
+
if (isConditionalTrue(condition)) {
|
|
1486
|
+
for (const then of node.then) {
|
|
1487
|
+
const results = await reduceNode(context, then);
|
|
1488
|
+
resultList.push(...results);
|
|
1489
|
+
}
|
|
1490
|
+
} else {
|
|
1491
|
+
for (const els of node.else) {
|
|
1492
|
+
const results = await reduceNode(context, els);
|
|
1493
|
+
resultList.push(...results);
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
return resultList;
|
|
1497
|
+
}
|
|
1498
|
+
default: {
|
|
1499
|
+
const result = await reduceExpressionNode(context, node);
|
|
1500
|
+
return [result];
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
};
|
|
1504
|
+
const createReducerContext = (variables, errors) => {
|
|
1505
|
+
let vs = variables;
|
|
1506
|
+
let mvs;
|
|
1507
|
+
let variablesProxy;
|
|
1508
|
+
const getValue = (name) => {
|
|
1509
|
+
if (vs.has(name)) {
|
|
1510
|
+
return { value: vs.get(name), isFound: true };
|
|
1511
|
+
} else {
|
|
1512
|
+
return { value: void 0, isFound: false };
|
|
1513
|
+
}
|
|
1514
|
+
};
|
|
1515
|
+
const setValue = (name, value) => {
|
|
1516
|
+
if (!mvs) {
|
|
1517
|
+
mvs = new Map(vs);
|
|
1518
|
+
vs = mvs;
|
|
1519
|
+
}
|
|
1520
|
+
mvs.set(name, value);
|
|
1521
|
+
if (variablesProxy !== void 0) {
|
|
1522
|
+
Object.defineProperty(variablesProxy, name, {
|
|
1523
|
+
get() {
|
|
1524
|
+
return vs.get(name);
|
|
1525
|
+
},
|
|
1526
|
+
configurable: true,
|
|
1527
|
+
enumerable: true
|
|
1528
|
+
});
|
|
1529
|
+
}
|
|
1530
|
+
};
|
|
1531
|
+
const appendError = (error) => {
|
|
1532
|
+
errors.push(error);
|
|
1533
|
+
};
|
|
1534
|
+
const isFailed = () => {
|
|
1535
|
+
return errors.some((error) => error.type === "error");
|
|
1536
|
+
};
|
|
1537
|
+
const newScope = () => {
|
|
1538
|
+
const newContext = createReducerContext(vs, errors);
|
|
1539
|
+
return newContext;
|
|
1540
|
+
};
|
|
1541
|
+
let context;
|
|
1542
|
+
const reduceByProxy = (node) => reduceExpressionNode(context, node);
|
|
1543
|
+
const getVariablesFromProxy = () => {
|
|
1544
|
+
if (variablesProxy === void 0) {
|
|
1545
|
+
variablesProxy = {};
|
|
1546
|
+
for (const key of vs.keys()) {
|
|
1547
|
+
Object.defineProperty(variablesProxy, key, {
|
|
1548
|
+
get: () => vs.get(key),
|
|
1549
|
+
configurable: true,
|
|
1550
|
+
enumerable: true
|
|
1551
|
+
});
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
return variablesProxy;
|
|
1555
|
+
};
|
|
1556
|
+
const createFunctionContext = (thisNode) => {
|
|
1557
|
+
return {
|
|
1558
|
+
get variables() {
|
|
1559
|
+
return getVariablesFromProxy();
|
|
1560
|
+
},
|
|
1561
|
+
thisNode,
|
|
1562
|
+
appendError,
|
|
1563
|
+
reduce: reduceByProxy
|
|
1564
|
+
};
|
|
1565
|
+
};
|
|
1566
|
+
context = {
|
|
1567
|
+
getValue,
|
|
1568
|
+
setValue,
|
|
1569
|
+
appendError,
|
|
1570
|
+
isFailed,
|
|
1571
|
+
newScope,
|
|
1572
|
+
createFunctionContext
|
|
1573
|
+
};
|
|
1574
|
+
return context;
|
|
1575
|
+
};
|
|
1576
|
+
const unwrap = (results) => {
|
|
1577
|
+
return results.flatMap((result) => {
|
|
1578
|
+
if (result === void 0) {
|
|
1579
|
+
return [];
|
|
1580
|
+
} else {
|
|
1581
|
+
return [result];
|
|
1582
|
+
}
|
|
1583
|
+
});
|
|
1584
|
+
};
|
|
1585
|
+
async function runReducer(nodes, variables, errors) {
|
|
1586
|
+
const context = createReducerContext(variables, errors);
|
|
1587
|
+
if (Array.isArray(nodes)) {
|
|
1588
|
+
const resultList = [];
|
|
1589
|
+
for (const node of nodes) {
|
|
1590
|
+
const results = await reduceNode(context, node);
|
|
1591
|
+
resultList.push(...results);
|
|
1592
|
+
}
|
|
1593
|
+
return unwrap(resultList);
|
|
1594
|
+
} else {
|
|
1595
|
+
const results = await reduceNode(context, nodes);
|
|
1596
|
+
return unwrap(results);
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
const _cond = makeSpecialFunction(async function(arg0, arg1, arg2) {
|
|
1600
|
+
const cond = await this.reduce(arg0);
|
|
1601
|
+
if (isConditionalTrue(cond)) {
|
|
1602
|
+
return await this.reduce(arg1);
|
|
1603
|
+
} else {
|
|
1604
|
+
return await this.reduce(arg2);
|
|
1605
|
+
}
|
|
1606
|
+
});
|
|
1607
|
+
const _typeof = async (arg0) => {
|
|
1608
|
+
if (arg0 === null) {
|
|
1609
|
+
return "null";
|
|
1610
|
+
} else if (typeof arg0 === "string") {
|
|
1611
|
+
return "string";
|
|
1612
|
+
} else if (Array.isArray(arg0)) {
|
|
1613
|
+
return "array";
|
|
1614
|
+
} else if (asIterable(arg0)) {
|
|
1615
|
+
return "iterable";
|
|
1616
|
+
} else {
|
|
1617
|
+
return typeof arg0;
|
|
1618
|
+
}
|
|
1619
|
+
};
|
|
1620
|
+
const funcIds = /* @__PURE__ */ new WeakMap();
|
|
1621
|
+
let nextId = 1;
|
|
1622
|
+
const getFuncId = (fn) => {
|
|
1623
|
+
const cached = funcIds.get(fn);
|
|
1624
|
+
if (cached) return cached;
|
|
1625
|
+
const id = nextId++;
|
|
1626
|
+
funcIds.set(fn, id);
|
|
1627
|
+
return id;
|
|
1628
|
+
};
|
|
1629
|
+
const _toString = async (...args) => {
|
|
1630
|
+
const results = args.map((arg0) => {
|
|
1631
|
+
switch (arg0) {
|
|
1632
|
+
case void 0:
|
|
1633
|
+
return "(undefined)";
|
|
1634
|
+
case null:
|
|
1635
|
+
return "(null)";
|
|
1636
|
+
default:
|
|
1637
|
+
switch (typeof arg0) {
|
|
1638
|
+
case "string":
|
|
1639
|
+
return arg0;
|
|
1640
|
+
case "boolean":
|
|
1641
|
+
return arg0 ? "true" : "false";
|
|
1642
|
+
case "number":
|
|
1643
|
+
case "bigint":
|
|
1644
|
+
case "symbol":
|
|
1645
|
+
return arg0.toString();
|
|
1646
|
+
case "function":
|
|
1647
|
+
if (arg0.name) {
|
|
1648
|
+
return `fun<${arg0.name}:#${getFuncId(arg0)}>`;
|
|
1649
|
+
} else {
|
|
1650
|
+
return `fun<#${getFuncId(arg0)}>`;
|
|
1651
|
+
}
|
|
1652
|
+
default:
|
|
1653
|
+
if (Array.isArray(arg0)) {
|
|
1654
|
+
return JSON.stringify(arg0);
|
|
1655
|
+
}
|
|
1656
|
+
const iterable = asIterable(arg0);
|
|
1657
|
+
if (iterable) {
|
|
1658
|
+
const arr = Array.from(iterable);
|
|
1659
|
+
return JSON.stringify(arr);
|
|
1660
|
+
} else {
|
|
1661
|
+
return JSON.stringify(arg0);
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
});
|
|
1666
|
+
return results.join(",");
|
|
1667
|
+
};
|
|
1668
|
+
const _add = async (arg0, ...args) => {
|
|
1669
|
+
const r = args.reduce((v0, v) => v0 + Number(v), Number(arg0));
|
|
1670
|
+
return r;
|
|
1671
|
+
};
|
|
1672
|
+
const _sub = async (arg0, ...args) => {
|
|
1673
|
+
const r = args.reduce((v0, v) => v0 - Number(v), Number(arg0));
|
|
1674
|
+
return r;
|
|
1675
|
+
};
|
|
1676
|
+
const _mul = async (arg0, ...args) => {
|
|
1677
|
+
const r = args.reduce((v0, v) => v0 * Number(v), Number(arg0));
|
|
1678
|
+
return r;
|
|
1679
|
+
};
|
|
1680
|
+
const _div = async (arg0, ...args) => {
|
|
1681
|
+
const r = args.reduce((v0, v) => v0 / Number(v), Number(arg0));
|
|
1682
|
+
return r;
|
|
1683
|
+
};
|
|
1684
|
+
const _mod = async (arg0, ...args) => {
|
|
1685
|
+
const r = args.reduce((v0, v) => v0 % Number(v), Number(arg0));
|
|
1686
|
+
return r;
|
|
1687
|
+
};
|
|
1688
|
+
const _equal = async (arg0, arg1) => {
|
|
1689
|
+
const r = arg0 === arg1;
|
|
1690
|
+
return r;
|
|
1691
|
+
};
|
|
1692
|
+
const _now = async () => {
|
|
1693
|
+
return Date.now();
|
|
1694
|
+
};
|
|
1695
|
+
const concatInner = (args) => {
|
|
1696
|
+
let v = "";
|
|
1697
|
+
for (const arg of args) {
|
|
1698
|
+
if (typeof arg === "string") {
|
|
1699
|
+
v = v + arg;
|
|
1700
|
+
} else {
|
|
1701
|
+
const iterable = asIterable(arg);
|
|
1702
|
+
if (iterable) {
|
|
1703
|
+
v = v + concatInner(iterable);
|
|
1704
|
+
} else {
|
|
1705
|
+
v = v + String(arg);
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
return v;
|
|
1710
|
+
};
|
|
1711
|
+
const _concat = async (...args) => {
|
|
1712
|
+
const r = concatInner(args);
|
|
1713
|
+
return r;
|
|
1714
|
+
};
|
|
1715
|
+
const _join = async (arg0, ...args) => {
|
|
1716
|
+
const sep = String(arg0);
|
|
1717
|
+
const r = args.map((v) => String(v)).join(sep);
|
|
1718
|
+
return r;
|
|
1719
|
+
};
|
|
1720
|
+
const _trim = async (arg0) => {
|
|
1721
|
+
var _a;
|
|
1722
|
+
let v = arg0;
|
|
1723
|
+
if (v === void 0 || v === null) {
|
|
1724
|
+
v = "";
|
|
1725
|
+
} else if (typeof v !== "string") {
|
|
1726
|
+
v = (_a = v.toString()) != null ? _a : "";
|
|
1727
|
+
}
|
|
1728
|
+
return v.trim();
|
|
1729
|
+
};
|
|
1730
|
+
const _toUpper = async (arg0) => {
|
|
1731
|
+
var _a;
|
|
1732
|
+
let v = arg0;
|
|
1733
|
+
if (typeof v !== "string") {
|
|
1734
|
+
v = (_a = v.toString()) != null ? _a : "";
|
|
1735
|
+
}
|
|
1736
|
+
return v.toUpperCase();
|
|
1737
|
+
};
|
|
1738
|
+
const _toLower = async (arg0) => {
|
|
1739
|
+
var _a;
|
|
1740
|
+
let v = arg0;
|
|
1741
|
+
if (typeof v !== "string") {
|
|
1742
|
+
v = (_a = v.toString()) != null ? _a : "";
|
|
1743
|
+
}
|
|
1744
|
+
return v.toLowerCase();
|
|
1745
|
+
};
|
|
1746
|
+
const _length = async (arg0) => {
|
|
1747
|
+
if (arg0) {
|
|
1748
|
+
if (typeof arg0 === "string") {
|
|
1749
|
+
return arg0.length;
|
|
1750
|
+
} else if (Array.isArray(arg0)) {
|
|
1751
|
+
return arg0.length;
|
|
1752
|
+
} else {
|
|
1753
|
+
const iterable = asIterable(arg0);
|
|
1754
|
+
if (iterable) {
|
|
1755
|
+
let count = 0;
|
|
1756
|
+
for (const _item of arg0) {
|
|
1757
|
+
count++;
|
|
1758
|
+
}
|
|
1759
|
+
return count;
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
return 0;
|
|
1764
|
+
};
|
|
1765
|
+
const _and = async (...args) => {
|
|
1766
|
+
if (args.length === 0) {
|
|
1767
|
+
throw new Error("empty arguments");
|
|
1768
|
+
}
|
|
1769
|
+
const r = args.reduce((v0, v) => v0 && isConditionalTrue(v), true);
|
|
1770
|
+
return r;
|
|
1771
|
+
};
|
|
1772
|
+
const _or = async (...args) => {
|
|
1773
|
+
if (args.length === 0) {
|
|
1774
|
+
throw new Error("empty arguments");
|
|
1775
|
+
}
|
|
1776
|
+
const r = args.reduce((v0, v) => v0 || isConditionalTrue(v), false);
|
|
1777
|
+
return r;
|
|
1778
|
+
};
|
|
1779
|
+
const _not = async (arg0) => {
|
|
1780
|
+
return !isConditionalTrue(arg0);
|
|
1781
|
+
};
|
|
1782
|
+
const _at = async (arg0, arg1) => {
|
|
1783
|
+
const index = Number(arg0);
|
|
1784
|
+
if (arg1) {
|
|
1785
|
+
if (typeof arg1 === "string") {
|
|
1786
|
+
return arg1[index];
|
|
1787
|
+
} else if (Array.isArray(arg1)) {
|
|
1788
|
+
return arg1[index];
|
|
1789
|
+
} else {
|
|
1790
|
+
const iterable = asIterable(arg1);
|
|
1791
|
+
if (iterable) {
|
|
1792
|
+
let current = 0;
|
|
1793
|
+
for (const item of iterable) {
|
|
1794
|
+
if (current >= index) {
|
|
1795
|
+
return item;
|
|
1796
|
+
}
|
|
1797
|
+
current++;
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
return void 0;
|
|
1803
|
+
};
|
|
1804
|
+
const _first = async (arg0) => {
|
|
1805
|
+
if (arg0) {
|
|
1806
|
+
if (typeof arg0 === "string") {
|
|
1807
|
+
return arg0[0];
|
|
1808
|
+
} else if (Array.isArray(arg0)) {
|
|
1809
|
+
return arg0[0];
|
|
1810
|
+
} else {
|
|
1811
|
+
const iterable = asIterable(arg0);
|
|
1812
|
+
if (iterable) {
|
|
1813
|
+
for (const item of iterable) {
|
|
1814
|
+
return item;
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
return void 0;
|
|
1820
|
+
};
|
|
1821
|
+
const _last = async (arg0) => {
|
|
1822
|
+
if (arg0) {
|
|
1823
|
+
if (typeof arg0 === "string") {
|
|
1824
|
+
return arg0[arg0.length - 1];
|
|
1825
|
+
} else if (Array.isArray(arg0)) {
|
|
1826
|
+
return arg0[arg0.length - 1];
|
|
1827
|
+
} else {
|
|
1828
|
+
const iterable = asIterable(arg0);
|
|
1829
|
+
if (iterable) {
|
|
1830
|
+
let lastItem = void 0;
|
|
1831
|
+
for (const item of iterable) {
|
|
1832
|
+
lastItem = item;
|
|
1833
|
+
}
|
|
1834
|
+
return lastItem;
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
return void 0;
|
|
1839
|
+
};
|
|
1840
|
+
const _range = async (arg0, arg1) => {
|
|
1841
|
+
let value = Number(arg0);
|
|
1842
|
+
const count = Number(arg1);
|
|
1843
|
+
const resultList = [];
|
|
1844
|
+
for (let index = 0; index < count; index++) {
|
|
1845
|
+
resultList.push(value++);
|
|
1846
|
+
}
|
|
1847
|
+
return resultList;
|
|
1848
|
+
};
|
|
1849
|
+
const _reverse = async (arg0) => {
|
|
1850
|
+
const iter = arg0;
|
|
1851
|
+
let resultList = [];
|
|
1852
|
+
for (const item of iter) {
|
|
1853
|
+
resultList.push(item);
|
|
1854
|
+
}
|
|
1855
|
+
return resultList.reverse();
|
|
1856
|
+
};
|
|
1857
|
+
const _sort = async (arg0) => {
|
|
1858
|
+
const iter = arg0;
|
|
1859
|
+
let resultList = [];
|
|
1860
|
+
for (const item of iter) {
|
|
1861
|
+
resultList.push(item);
|
|
1862
|
+
}
|
|
1863
|
+
return resultList.sort();
|
|
1864
|
+
};
|
|
1865
|
+
const _map = async (arg0, arg1) => {
|
|
1866
|
+
const predicate = arg0;
|
|
1867
|
+
const iter = arg1;
|
|
1868
|
+
const resultList = [];
|
|
1869
|
+
for (const item of iter) {
|
|
1870
|
+
const result = await predicate(item);
|
|
1871
|
+
resultList.push(result);
|
|
1872
|
+
}
|
|
1873
|
+
return resultList;
|
|
1874
|
+
};
|
|
1875
|
+
const _flatMap = async (arg0, arg1) => {
|
|
1876
|
+
const predicate = arg0;
|
|
1877
|
+
const iter = arg1;
|
|
1878
|
+
const resultList = [];
|
|
1879
|
+
for (const item of iter) {
|
|
1880
|
+
const results = await predicate(item);
|
|
1881
|
+
resultList.push(...results);
|
|
1882
|
+
}
|
|
1883
|
+
return resultList;
|
|
1884
|
+
};
|
|
1885
|
+
const _filter = async (arg0, arg1) => {
|
|
1886
|
+
const predicate = arg0;
|
|
1887
|
+
const iter = arg1;
|
|
1888
|
+
const resultList = [];
|
|
1889
|
+
for (const item of iter) {
|
|
1890
|
+
const result = await predicate(item);
|
|
1891
|
+
if (isConditionalTrue(result)) {
|
|
1892
|
+
resultList.push(item);
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
return resultList;
|
|
1896
|
+
};
|
|
1897
|
+
const _collect = async (arg0) => {
|
|
1898
|
+
const iter = arg0;
|
|
1899
|
+
const resultList = [];
|
|
1900
|
+
for (const item of iter) {
|
|
1901
|
+
if (item !== void 0 && item !== null) {
|
|
1902
|
+
resultList.push(item);
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
return resultList;
|
|
1906
|
+
};
|
|
1907
|
+
const _reduce = async (arg0, arg1, arg2) => {
|
|
1908
|
+
let acc = arg0;
|
|
1909
|
+
const predicate = arg1;
|
|
1910
|
+
const iter = arg2;
|
|
1911
|
+
for (const item of iter) {
|
|
1912
|
+
acc = await predicate(acc, item);
|
|
1913
|
+
}
|
|
1914
|
+
return acc;
|
|
1915
|
+
};
|
|
1916
|
+
const _match = async (arg0, arg1) => {
|
|
1917
|
+
const re = arg0 instanceof RegExp ? arg0 : new RegExp(String(arg0), "g");
|
|
1918
|
+
const results = String(arg1).match(re);
|
|
1919
|
+
return results;
|
|
1920
|
+
};
|
|
1921
|
+
const _replace = async (arg0, arg1, arg2) => {
|
|
1922
|
+
const re = arg0 instanceof RegExp ? arg0 : new RegExp(String(arg0), "g");
|
|
1923
|
+
const replace = String(arg1);
|
|
1924
|
+
const results = String(arg2).replace(re, replace);
|
|
1925
|
+
return results;
|
|
1926
|
+
};
|
|
1927
|
+
const _regex = async (arg0, arg1) => {
|
|
1928
|
+
if (arg1) {
|
|
1929
|
+
const re = new RegExp(String(arg0), String(arg1));
|
|
1930
|
+
return re;
|
|
1931
|
+
} else {
|
|
1932
|
+
const re = new RegExp(String(arg0));
|
|
1933
|
+
return re;
|
|
1934
|
+
}
|
|
1935
|
+
};
|
|
1936
|
+
const _bind = async (arg0, ...args) => {
|
|
1937
|
+
const predicate = arg0;
|
|
1938
|
+
return predicate.bind(void 0, ...args);
|
|
1939
|
+
};
|
|
1940
|
+
const standardVariables = {
|
|
1941
|
+
undefined: void 0,
|
|
1942
|
+
null: null,
|
|
1943
|
+
true: true,
|
|
1944
|
+
false: false,
|
|
1945
|
+
cond: _cond,
|
|
1946
|
+
toString: _toString,
|
|
1947
|
+
typeof: _typeof,
|
|
1948
|
+
add: _add,
|
|
1949
|
+
sub: _sub,
|
|
1950
|
+
mul: _mul,
|
|
1951
|
+
div: _div,
|
|
1952
|
+
mod: _mod,
|
|
1953
|
+
equal: _equal,
|
|
1954
|
+
now: _now,
|
|
1955
|
+
concat: _concat,
|
|
1956
|
+
join: _join,
|
|
1957
|
+
trim: _trim,
|
|
1958
|
+
toUpper: _toUpper,
|
|
1959
|
+
toLower: _toLower,
|
|
1960
|
+
length: _length,
|
|
1961
|
+
and: _and,
|
|
1962
|
+
or: _or,
|
|
1963
|
+
not: _not,
|
|
1964
|
+
at: _at,
|
|
1965
|
+
first: _first,
|
|
1966
|
+
last: _last,
|
|
1967
|
+
range: _range,
|
|
1968
|
+
sort: _sort,
|
|
1969
|
+
reverse: _reverse,
|
|
1970
|
+
map: _map,
|
|
1971
|
+
flatMap: _flatMap,
|
|
1972
|
+
filter: _filter,
|
|
1973
|
+
collect: _collect,
|
|
1974
|
+
reduce: _reduce,
|
|
1975
|
+
match: _match,
|
|
1976
|
+
replace: _replace,
|
|
1977
|
+
regex: _regex,
|
|
1978
|
+
bind: _bind
|
|
1979
|
+
};
|
|
1980
|
+
const buildCandidateVariables = (...variablesList) => {
|
|
1981
|
+
return combineVariables(standardVariables, ...variablesList);
|
|
1982
|
+
};
|
|
1983
|
+
export {
|
|
1984
|
+
asIterable,
|
|
1985
|
+
buildCandidateVariables,
|
|
1986
|
+
combineVariables,
|
|
1987
|
+
createReducerContext,
|
|
1988
|
+
emptyLocation,
|
|
1989
|
+
emptyRange,
|
|
1990
|
+
fromError,
|
|
1991
|
+
isConditionalTrue,
|
|
1992
|
+
isSpecialFunction,
|
|
1993
|
+
makeSpecialFunction,
|
|
1994
|
+
outputErrors,
|
|
1995
|
+
parseBlock,
|
|
1996
|
+
parseExpression,
|
|
1997
|
+
reduceExpressionNode,
|
|
1998
|
+
reduceNode,
|
|
1999
|
+
runParser,
|
|
2000
|
+
runReducer,
|
|
2001
|
+
runTokenizer,
|
|
2002
|
+
standardVariables,
|
|
2003
|
+
widerRange
|
|
2004
|
+
};
|
|
2005
|
+
//# sourceMappingURL=index.mjs.map
|