@graffiticode/parser 0.1.2 → 0.1.3
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/jest.config.js +5 -0
- package/jest.config.js~ +6 -0
- package/package.json +3 -2
- package/src/ast.js +22 -12
- package/src/folder.js +5 -2
- package/src/parse.js +14 -19
- package/src/parser.js +0 -4
- package/src/parser.spec.js +252 -1
- package/src/parse.spec.js +0 -118
package/jest.config.js
ADDED
package/jest.config.js~
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graffiticode/parser",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"scripts": {
|
|
15
15
|
"lint": "eslint src/",
|
|
16
16
|
"lint:fix": "eslint --fix src/",
|
|
17
|
-
"test": "NODE_OPTIONS=--experimental-vm-modules jest"
|
|
17
|
+
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
|
|
18
|
+
"console-test": "node console-output-test.js"
|
|
18
19
|
},
|
|
19
20
|
"keywords": [],
|
|
20
21
|
"author": "",
|
package/src/ast.js
CHANGED
|
@@ -148,13 +148,27 @@ export class Ast {
|
|
|
148
148
|
|
|
149
149
|
static poolToJSON(ctx) {
|
|
150
150
|
const nodePool = ctx.state.nodePool;
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
151
|
+
const rootId = nodePool.length - 1;
|
|
152
|
+
// Find all nodes transitively referenced from root
|
|
153
|
+
const referencedNodes = new Set();
|
|
154
|
+
const collectReferences = (nodeId) => {
|
|
155
|
+
if (!nodeId || referencedNodes.has(nodeId)) return;
|
|
156
|
+
referencedNodes.add(nodeId);
|
|
157
|
+
|
|
158
|
+
const node = nodePool[nodeId];
|
|
159
|
+
if (node && node.elts) {
|
|
160
|
+
node.elts.forEach(elt => {
|
|
161
|
+
if (typeof elt === "number") collectReferences(elt);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
collectReferences(rootId);
|
|
166
|
+
return {
|
|
167
|
+
...Object.fromEntries(
|
|
168
|
+
Array.from(referencedNodes).map(id => [id, nodeToJSON(nodePool[id])])
|
|
169
|
+
),
|
|
170
|
+
root: rootId
|
|
171
|
+
};
|
|
158
172
|
}
|
|
159
173
|
|
|
160
174
|
static dump(n) {
|
|
@@ -666,11 +680,7 @@ export class Ast {
|
|
|
666
680
|
});
|
|
667
681
|
nids.push(word.nid || 0);
|
|
668
682
|
}
|
|
669
|
-
const pattern = env.pattern;
|
|
670
|
-
console.log(
|
|
671
|
-
"lambda()",
|
|
672
|
-
"pattern=" + JSON.stringify(pattern, null, 2),
|
|
673
|
-
);
|
|
683
|
+
// const pattern = env.pattern;
|
|
674
684
|
Ast.push(ctx, {
|
|
675
685
|
tag: "LAMBDA",
|
|
676
686
|
elts: [{
|
package/src/folder.js
CHANGED
|
@@ -225,8 +225,11 @@ export class Folder {
|
|
|
225
225
|
assert(false);
|
|
226
226
|
}
|
|
227
227
|
} else {
|
|
228
|
-
//
|
|
229
|
-
Ast.push(ctx,
|
|
228
|
+
// Tag value.
|
|
229
|
+
Ast.push(ctx, {
|
|
230
|
+
tag: name,
|
|
231
|
+
elts: [],
|
|
232
|
+
});
|
|
230
233
|
}
|
|
231
234
|
}
|
|
232
235
|
|
package/src/parse.js
CHANGED
|
@@ -266,7 +266,7 @@ export const parse = (function () {
|
|
|
266
266
|
function number(ctx, cc) {
|
|
267
267
|
eat(ctx, TK_NUM);
|
|
268
268
|
cc.cls = "number";
|
|
269
|
-
Ast.number(ctx, lexeme
|
|
269
|
+
Ast.number(ctx, lexeme);
|
|
270
270
|
return cc;
|
|
271
271
|
}
|
|
272
272
|
|
|
@@ -281,28 +281,24 @@ export const parse = (function () {
|
|
|
281
281
|
*/
|
|
282
282
|
|
|
283
283
|
function str(ctx, cc) {
|
|
284
|
-
let coord;
|
|
285
284
|
if (match(ctx, TK_STR)) {
|
|
286
285
|
eat(ctx, TK_STR);
|
|
287
|
-
|
|
288
|
-
Ast.string(ctx, lexeme, coord); // strip quotes;
|
|
286
|
+
Ast.string(ctx, lexeme); // strip quotes;
|
|
289
287
|
cc.cls = "string";
|
|
290
288
|
return cc;
|
|
291
289
|
} else if (match(ctx, TK_STRPREFIX)) {
|
|
292
290
|
ctx.state.inStr++;
|
|
293
291
|
eat(ctx, TK_STRPREFIX);
|
|
294
292
|
startCounter(ctx);
|
|
295
|
-
|
|
296
|
-
Ast.string(ctx, lexeme, coord); // strip quotes;
|
|
293
|
+
Ast.string(ctx, lexeme); // strip quotes;
|
|
297
294
|
countCounter(ctx);
|
|
298
295
|
const ret = function (ctx) {
|
|
299
296
|
return strSuffix(ctx, function (ctx) {
|
|
300
297
|
ctx.state.inStr--;
|
|
301
298
|
eat(ctx, TK_STRSUFFIX);
|
|
302
|
-
|
|
303
|
-
Ast.string(ctx, lexeme, coord); // strip quotes;
|
|
299
|
+
Ast.string(ctx, lexeme); // strip quotes;
|
|
304
300
|
countCounter(ctx);
|
|
305
|
-
Ast.list(ctx, ctx.state.exprc
|
|
301
|
+
Ast.list(ctx, ctx.state.exprc);
|
|
306
302
|
stopCounter(ctx);
|
|
307
303
|
Ast.concat(ctx);
|
|
308
304
|
cc.cls = "string";
|
|
@@ -324,8 +320,7 @@ export const parse = (function () {
|
|
|
324
320
|
if (match(ctx, TK_STRMIDDLE)) {
|
|
325
321
|
// Not done yet.
|
|
326
322
|
eat(ctx, TK_STRMIDDLE);
|
|
327
|
-
|
|
328
|
-
Ast.string(ctx, lexeme, coord); // strip quotes;
|
|
323
|
+
Ast.string(ctx, lexeme); // strip quotes;
|
|
329
324
|
countCounter(ctx);
|
|
330
325
|
ret = function (ctx) {
|
|
331
326
|
return strSuffix(ctx, resume);
|
|
@@ -355,7 +350,7 @@ export const parse = (function () {
|
|
|
355
350
|
function bindingName(ctx, cc) {
|
|
356
351
|
if (match(ctx, TK_IDENT)) {
|
|
357
352
|
eat(ctx, TK_IDENT);
|
|
358
|
-
Ast.string(ctx, lexeme
|
|
353
|
+
Ast.string(ctx, lexeme);
|
|
359
354
|
cc.cls = "variable";
|
|
360
355
|
return cc;
|
|
361
356
|
}
|
|
@@ -389,7 +384,7 @@ export const parse = (function () {
|
|
|
389
384
|
offset: ctx.state.paramc,
|
|
390
385
|
nid: 0
|
|
391
386
|
});
|
|
392
|
-
Ast.name(ctx, lexeme
|
|
387
|
+
Ast.name(ctx, lexeme);
|
|
393
388
|
cc.cls = "val";
|
|
394
389
|
return cc;
|
|
395
390
|
}
|
|
@@ -414,8 +409,10 @@ export const parse = (function () {
|
|
|
414
409
|
}
|
|
415
410
|
}
|
|
416
411
|
} else {
|
|
417
|
-
cc.cls = "error";
|
|
418
|
-
Ast.error(ctx, "Name '" + lexeme + "' not found.", coord);
|
|
412
|
+
// cc.cls = "error";
|
|
413
|
+
// Ast.error(ctx, "Name '" + lexeme + "' not found.", coord);
|
|
414
|
+
// Create a tag value.
|
|
415
|
+
Ast.name(ctx, lexeme, coord);
|
|
419
416
|
}
|
|
420
417
|
// assert(cc, "name");
|
|
421
418
|
return cc;
|
|
@@ -505,14 +502,12 @@ export const parse = (function () {
|
|
|
505
502
|
return ret;
|
|
506
503
|
}
|
|
507
504
|
function list(ctx, cc) {
|
|
508
|
-
const coord = getCoord(ctx);
|
|
509
505
|
eat(ctx, TK_LEFTBRACKET);
|
|
510
506
|
startCounter(ctx);
|
|
511
507
|
const ret = function (ctx) {
|
|
512
508
|
return elements(ctx, function (ctx) {
|
|
513
509
|
eat(ctx, TK_RIGHTBRACKET);
|
|
514
|
-
|
|
515
|
-
Ast.list(ctx, ctx.state.exprc, coord);
|
|
510
|
+
Ast.list(ctx, ctx.state.exprc);
|
|
516
511
|
stopCounter(ctx);
|
|
517
512
|
cc.cls = "punc";
|
|
518
513
|
return cc;
|
|
@@ -984,7 +979,7 @@ export const parse = (function () {
|
|
|
984
979
|
stream.next();
|
|
985
980
|
}
|
|
986
981
|
if (cc && !stream.peek()) {
|
|
987
|
-
assertErr(ctx, false, "
|
|
982
|
+
assertErr(ctx, false, "Missing program terminator.", getCoord(ctx));
|
|
988
983
|
}
|
|
989
984
|
} catch (x) {
|
|
990
985
|
// console.log("catch() x=" + x);
|
package/src/parser.js
CHANGED
package/src/parser.spec.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jest } from "@jest/globals";
|
|
2
|
-
import { buildParser } from "./parser.js";
|
|
2
|
+
import { buildParser, parser } from "./parser.js";
|
|
3
3
|
import { mockPromiseValue, mockPromiseError } from "./testing/index.js";
|
|
4
|
+
import vm from "vm";
|
|
4
5
|
|
|
5
6
|
describe("lang/parser", () => {
|
|
6
7
|
const log = jest.fn();
|
|
@@ -149,3 +150,253 @@ describe("lang/parser", () => {
|
|
|
149
150
|
await expect(parser.parse(lang, src)).rejects.toBe(err);
|
|
150
151
|
});
|
|
151
152
|
});
|
|
153
|
+
|
|
154
|
+
describe("parser integration tests", () => {
|
|
155
|
+
// Tests using the actual parser
|
|
156
|
+
it("should parse string literals", async () => {
|
|
157
|
+
// Arrange & Act
|
|
158
|
+
const result = await parser.parse(0, "'hello, world'..");
|
|
159
|
+
|
|
160
|
+
// Assert
|
|
161
|
+
expect(result).toHaveProperty("root");
|
|
162
|
+
|
|
163
|
+
// Find the STR node
|
|
164
|
+
let strNode = null;
|
|
165
|
+
for (const key in result) {
|
|
166
|
+
if (key !== "root") {
|
|
167
|
+
const node = result[key];
|
|
168
|
+
if (node.tag === "STR" && node.elts[0] === "hello, world") {
|
|
169
|
+
strNode = node;
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
expect(strNode).not.toBeNull();
|
|
176
|
+
expect(strNode.tag).toBe("STR");
|
|
177
|
+
expect(strNode.elts).toEqual(["hello, world"]);
|
|
178
|
+
|
|
179
|
+
// Program structure verification
|
|
180
|
+
const rootId = result.root;
|
|
181
|
+
const rootNode = result[rootId];
|
|
182
|
+
expect(rootNode.tag).toBe("PROG");
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it("should parse numeric literals", async () => {
|
|
186
|
+
// Arrange & Act
|
|
187
|
+
const result = await parser.parse(0, "42..");
|
|
188
|
+
|
|
189
|
+
// Assert
|
|
190
|
+
expect(result).toHaveProperty("root");
|
|
191
|
+
|
|
192
|
+
// Find the NUM node
|
|
193
|
+
let numNode = null;
|
|
194
|
+
for (const key in result) {
|
|
195
|
+
if (key !== "root") {
|
|
196
|
+
const node = result[key];
|
|
197
|
+
if (node.tag === "NUM" && node.elts[0] === "42") {
|
|
198
|
+
numNode = node;
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
expect(numNode).not.toBeNull();
|
|
205
|
+
expect(numNode.tag).toBe("NUM");
|
|
206
|
+
expect(numNode.elts).toEqual(["42"]);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it("should have a PROG node at the root", async () => {
|
|
210
|
+
// Let's test the most basic structure that should always work
|
|
211
|
+
const result = await parser.parse(0, "123..");
|
|
212
|
+
|
|
213
|
+
// Assert
|
|
214
|
+
expect(result).toHaveProperty("root");
|
|
215
|
+
|
|
216
|
+
// Verify the structure: we need to have a PROG node at the root
|
|
217
|
+
const rootId = result.root;
|
|
218
|
+
const rootNode = result[rootId];
|
|
219
|
+
expect(rootNode.tag).toBe("PROG");
|
|
220
|
+
|
|
221
|
+
// Find the NUM node for 123
|
|
222
|
+
let numNode = null;
|
|
223
|
+
for (const key in result) {
|
|
224
|
+
if (key !== "root") {
|
|
225
|
+
const node = result[key];
|
|
226
|
+
if (node.tag === "NUM" && node.elts[0] === "123") {
|
|
227
|
+
numNode = node;
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
expect(numNode).not.toBeNull();
|
|
234
|
+
expect(numNode.tag).toBe("NUM");
|
|
235
|
+
expect(numNode.elts[0]).toBe("123");
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it("should parse complex program: apply (<a b: add a b>) [10 20]..", async () => {
|
|
239
|
+
// Create parser with custom lexicon
|
|
240
|
+
const customLexiconCache = new Map();
|
|
241
|
+
customLexiconCache.set(0, {
|
|
242
|
+
add: {
|
|
243
|
+
tk: 2,
|
|
244
|
+
name: "add",
|
|
245
|
+
cls: "function",
|
|
246
|
+
length: 2
|
|
247
|
+
},
|
|
248
|
+
apply: {
|
|
249
|
+
tk: 40,
|
|
250
|
+
name: "apply",
|
|
251
|
+
cls: "function",
|
|
252
|
+
length: 2
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// Use the parser with our custom cache
|
|
257
|
+
const customParser = buildParser({
|
|
258
|
+
log: console.log,
|
|
259
|
+
cache: customLexiconCache,
|
|
260
|
+
getLangAsset: async () => ({}),
|
|
261
|
+
main: {
|
|
262
|
+
parse: (src, lexicon) => {
|
|
263
|
+
return Promise.resolve(parser.parse(0, src));
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
vm
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// Act
|
|
270
|
+
const result = await customParser.parse(0, "apply (<a b: add a b>) [10 20]..");
|
|
271
|
+
|
|
272
|
+
// Assert
|
|
273
|
+
expect(result).toHaveProperty("root");
|
|
274
|
+
|
|
275
|
+
// Verify basic structure
|
|
276
|
+
const rootId = result.root;
|
|
277
|
+
const rootNode = result[rootId];
|
|
278
|
+
expect(rootNode.tag).toBe("PROG");
|
|
279
|
+
|
|
280
|
+
// Find NUM nodes with values 10 and 20
|
|
281
|
+
let num10Node = null;
|
|
282
|
+
let num20Node = null;
|
|
283
|
+
|
|
284
|
+
for (const key in result) {
|
|
285
|
+
if (key !== "root") {
|
|
286
|
+
const node = result[key];
|
|
287
|
+
if (node.tag === "NUM") {
|
|
288
|
+
if (node.elts[0] === "10") {
|
|
289
|
+
num10Node = node;
|
|
290
|
+
} else if (node.elts[0] === "20") {
|
|
291
|
+
num20Node = node;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// At minimum, we should be able to find the number values
|
|
298
|
+
expect(num10Node).not.toBeNull();
|
|
299
|
+
expect(num20Node).not.toBeNull();
|
|
300
|
+
|
|
301
|
+
// Find IDENT nodes with names 'a' and 'b'
|
|
302
|
+
let identNodeA = null;
|
|
303
|
+
let identNodeB = null;
|
|
304
|
+
|
|
305
|
+
for (const key in result) {
|
|
306
|
+
if (key !== "root") {
|
|
307
|
+
const node = result[key];
|
|
308
|
+
if (node.tag === "IDENT") {
|
|
309
|
+
if (node.elts[0] === "a") {
|
|
310
|
+
identNodeA = node;
|
|
311
|
+
} else if (node.elts[0] === "b") {
|
|
312
|
+
identNodeB = node;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Check that we found the identifiers
|
|
319
|
+
expect(identNodeA).not.toBeNull();
|
|
320
|
+
expect(identNodeB).not.toBeNull();
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it("should perform parse-time evaluation for adding two numbers", async () => {
|
|
324
|
+
// Create parser with custom lexicon that defines 'add' function
|
|
325
|
+
const customLexiconCache = new Map();
|
|
326
|
+
customLexiconCache.set(0, {
|
|
327
|
+
add: {
|
|
328
|
+
tk: 2,
|
|
329
|
+
name: "add",
|
|
330
|
+
cls: "function",
|
|
331
|
+
length: 2
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// Use the parser with our custom cache
|
|
336
|
+
const customParser = buildParser({
|
|
337
|
+
log: console.log,
|
|
338
|
+
cache: customLexiconCache,
|
|
339
|
+
getLangAsset: async () => ({}),
|
|
340
|
+
main: {
|
|
341
|
+
parse: (src, lexicon) => {
|
|
342
|
+
return Promise.resolve(parser.parse(0, src));
|
|
343
|
+
}
|
|
344
|
+
},
|
|
345
|
+
vm
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
// Act - parse a simple addition expression
|
|
349
|
+
const result = await customParser.parse(0, "add 123 456..");
|
|
350
|
+
console.log(
|
|
351
|
+
"TEST",
|
|
352
|
+
"result=" + JSON.stringify(result, null, 2),
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
// Assert
|
|
356
|
+
expect(result).toHaveProperty("root");
|
|
357
|
+
|
|
358
|
+
// Verify basic structure
|
|
359
|
+
const rootId = result.root;
|
|
360
|
+
const rootNode = result[rootId];
|
|
361
|
+
expect(rootNode.tag).toBe("PROG");
|
|
362
|
+
|
|
363
|
+
// Find the result node - expecting a single NUM node with the sum
|
|
364
|
+
let resultNode = null;
|
|
365
|
+
|
|
366
|
+
// Check all nodes for the result of evaluation (123 + 456 = 579)
|
|
367
|
+
for (const key in result) {
|
|
368
|
+
if (key !== "root") {
|
|
369
|
+
const node = result[key];
|
|
370
|
+
if (node.tag === "NUM" && node.elts[0] === "579") {
|
|
371
|
+
resultNode = node;
|
|
372
|
+
break;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// We should find a node with the computed value (579)
|
|
378
|
+
expect(resultNode).not.toBeNull();
|
|
379
|
+
expect(resultNode.tag).toBe("NUM");
|
|
380
|
+
expect(resultNode.elts[0]).toBe("579");
|
|
381
|
+
|
|
382
|
+
// The original numbers should not be in the final AST
|
|
383
|
+
// if parse-time evaluation is working correctly
|
|
384
|
+
let found123 = false;
|
|
385
|
+
let found456 = false;
|
|
386
|
+
|
|
387
|
+
for (const key in result) {
|
|
388
|
+
if (key !== "root") {
|
|
389
|
+
const node = result[key];
|
|
390
|
+
if (node.tag === "NUM") {
|
|
391
|
+
if (node.elts[0] === "123") found123 = true;
|
|
392
|
+
if (node.elts[0] === "456") found456 = true;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// The original operands should not be in the final AST
|
|
398
|
+
// if they were properly evaluated at parse time
|
|
399
|
+
expect(found123).toBe(false);
|
|
400
|
+
expect(found456).toBe(false);
|
|
401
|
+
});
|
|
402
|
+
});
|
package/src/parse.spec.js
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import { buildParser } from "./parser.js";
|
|
2
|
-
import { mockPromiseValue } from "./testing/index.js";
|
|
3
|
-
|
|
4
|
-
describe("parse operations", () => {
|
|
5
|
-
const log = jest.fn();
|
|
6
|
-
|
|
7
|
-
it("should parse 'apply (<a b: add a b>) [10 20]'", async () => {
|
|
8
|
-
// Arrange
|
|
9
|
-
const cache = new Map();
|
|
10
|
-
const getLangAsset = mockPromiseValue("{}");
|
|
11
|
-
const main = {
|
|
12
|
-
parse: mockPromiseValue({
|
|
13
|
-
1: {
|
|
14
|
-
elts: ["a"],
|
|
15
|
-
tag: "IDENT"
|
|
16
|
-
},
|
|
17
|
-
2: {
|
|
18
|
-
elts: ["b"],
|
|
19
|
-
tag: "IDENT"
|
|
20
|
-
},
|
|
21
|
-
3: {
|
|
22
|
-
elts: [1, 2],
|
|
23
|
-
tag: "ADD"
|
|
24
|
-
},
|
|
25
|
-
4: {
|
|
26
|
-
elts: [3],
|
|
27
|
-
tag: "EXPRS"
|
|
28
|
-
},
|
|
29
|
-
5: {
|
|
30
|
-
elts: [1, 2],
|
|
31
|
-
tag: "LIST"
|
|
32
|
-
},
|
|
33
|
-
6: {
|
|
34
|
-
elts: [5, 4],
|
|
35
|
-
tag: "LAMBDA"
|
|
36
|
-
},
|
|
37
|
-
7: {
|
|
38
|
-
elts: ["10"],
|
|
39
|
-
tag: "NUM"
|
|
40
|
-
},
|
|
41
|
-
8: {
|
|
42
|
-
elts: ["20"],
|
|
43
|
-
tag: "NUM"
|
|
44
|
-
},
|
|
45
|
-
9: {
|
|
46
|
-
elts: [7, 8],
|
|
47
|
-
tag: "LIST"
|
|
48
|
-
},
|
|
49
|
-
10: {
|
|
50
|
-
elts: [6, 9],
|
|
51
|
-
tag: "APPLY"
|
|
52
|
-
},
|
|
53
|
-
11: {
|
|
54
|
-
elts: [10],
|
|
55
|
-
tag: "EXPRS"
|
|
56
|
-
},
|
|
57
|
-
12: {
|
|
58
|
-
elts: [11],
|
|
59
|
-
tag: "PROG"
|
|
60
|
-
},
|
|
61
|
-
root: 12
|
|
62
|
-
})
|
|
63
|
-
};
|
|
64
|
-
const parser = buildParser({ log, cache, getLangAsset, main });
|
|
65
|
-
const lang = "0002";
|
|
66
|
-
const src = "apply (<a b: add a b>) [10 20]..";
|
|
67
|
-
|
|
68
|
-
// Act
|
|
69
|
-
const result = await parser.parse(lang, src);
|
|
70
|
-
|
|
71
|
-
// Assert
|
|
72
|
-
expect(result).toHaveProperty("root", 12);
|
|
73
|
-
expect(result[10].tag).toBe("APPLY");
|
|
74
|
-
expect(result[10].elts).toEqual([6, 9]);
|
|
75
|
-
expect(result[6].tag).toBe("LAMBDA");
|
|
76
|
-
expect(result[9].tag).toBe("LIST");
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it("should parse 'hello, world!'", async () => {
|
|
80
|
-
// Arrange
|
|
81
|
-
const cache = new Map();
|
|
82
|
-
const getLangAsset = mockPromiseValue("{}");
|
|
83
|
-
const main = {
|
|
84
|
-
parse: mockPromiseValue({
|
|
85
|
-
1: {
|
|
86
|
-
elts: [
|
|
87
|
-
"hello, world"
|
|
88
|
-
],
|
|
89
|
-
tag: "STR"
|
|
90
|
-
},
|
|
91
|
-
2: {
|
|
92
|
-
elts: [
|
|
93
|
-
1
|
|
94
|
-
],
|
|
95
|
-
tag: "EXPRS"
|
|
96
|
-
},
|
|
97
|
-
3: {
|
|
98
|
-
elts: [
|
|
99
|
-
2
|
|
100
|
-
],
|
|
101
|
-
tag: "PROG"
|
|
102
|
-
},
|
|
103
|
-
root: 3
|
|
104
|
-
})
|
|
105
|
-
};
|
|
106
|
-
const parser = buildParser({ log, cache, getLangAsset, main });
|
|
107
|
-
const lang = "0002";
|
|
108
|
-
const src = "'hello, world'..";
|
|
109
|
-
|
|
110
|
-
// Act
|
|
111
|
-
const result = await parser.parse(lang, src);
|
|
112
|
-
|
|
113
|
-
// Assert
|
|
114
|
-
expect(result).toHaveProperty("root", 3);
|
|
115
|
-
expect(result[1].tag).toBe("STR");
|
|
116
|
-
expect(result[1].elts).toEqual(["hello, world"]);
|
|
117
|
-
});
|
|
118
|
-
});
|