@graffiticode/parser 1.2.1 → 1.3.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/.claude/settings.local.json +12 -0
- package/package.json +2 -2
- package/src/ast.js +1 -0
- package/src/folder.js +7 -2
- package/src/index.js +1 -0
- package/src/parser.spec.js +31 -0
- package/src/unparse.js +6 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graffiticode/parser",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -22,6 +22,6 @@
|
|
|
22
22
|
"license": "MIT",
|
|
23
23
|
"description": "",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@graffiticode/basis": "^1.6.
|
|
25
|
+
"@graffiticode/basis": "^1.6.3"
|
|
26
26
|
}
|
|
27
27
|
}
|
package/src/ast.js
CHANGED
package/src/folder.js
CHANGED
|
@@ -28,6 +28,7 @@ export class Folder {
|
|
|
28
28
|
// "MUL": mul,
|
|
29
29
|
// "DIV": div,
|
|
30
30
|
// "SUB": sub,
|
|
31
|
+
TAG: Folder.tag,
|
|
31
32
|
ADD: Folder.add,
|
|
32
33
|
POW: Folder.pow,
|
|
33
34
|
MOD: Folder.mod,
|
|
@@ -227,8 +228,8 @@ export class Folder {
|
|
|
227
228
|
} else {
|
|
228
229
|
// Tag value.
|
|
229
230
|
Ast.push(ctx, {
|
|
230
|
-
tag:
|
|
231
|
-
elts: [],
|
|
231
|
+
tag: "TAG",
|
|
232
|
+
elts: [name],
|
|
232
233
|
coord: node.coord,
|
|
233
234
|
});
|
|
234
235
|
}
|
|
@@ -245,6 +246,10 @@ export class Folder {
|
|
|
245
246
|
static bool(node) {
|
|
246
247
|
Ast.bool(Folder.#ctx, node.elts[0]);
|
|
247
248
|
}
|
|
249
|
+
|
|
250
|
+
static tag(node) {
|
|
251
|
+
Ast.push(Folder.#ctx, node);
|
|
252
|
+
}
|
|
248
253
|
}
|
|
249
254
|
|
|
250
255
|
// Keep backward compatibility export
|
package/src/index.js
CHANGED
package/src/parser.spec.js
CHANGED
|
@@ -2,6 +2,7 @@ import { jest } from "@jest/globals";
|
|
|
2
2
|
import { buildParser, parser } from "./parser.js";
|
|
3
3
|
import { mockPromiseValue, mockPromiseError } from "./testing/index.js";
|
|
4
4
|
import { lexicon as basisLexicon } from "@graffiticode/basis";
|
|
5
|
+
import { unparse } from "./unparse.js";
|
|
5
6
|
|
|
6
7
|
describe("lang/parser", () => {
|
|
7
8
|
const log = jest.fn();
|
|
@@ -547,6 +548,36 @@ describe("parser integration tests", () => {
|
|
|
547
548
|
expect(strNode.elts[0]).toBe("Price: ${amount}");
|
|
548
549
|
});
|
|
549
550
|
|
|
551
|
+
it("should parse and unparse a tag node", async () => {
|
|
552
|
+
// Arrange - use an empty lexicon so "foo" is not recognized as a function
|
|
553
|
+
const emptyLexicon = {};
|
|
554
|
+
|
|
555
|
+
// Act - parse "foo.." where "foo" is not in the lexicon, producing a TAG node
|
|
556
|
+
const result = await parser.parse(0, "foo..", emptyLexicon);
|
|
557
|
+
|
|
558
|
+
// Assert - find the TAG node
|
|
559
|
+
expect(result).toHaveProperty("root");
|
|
560
|
+
|
|
561
|
+
let tagNode = null;
|
|
562
|
+
for (const key in result) {
|
|
563
|
+
if (key !== "root") {
|
|
564
|
+
const node = result[key];
|
|
565
|
+
if (node.tag === "TAG" && node.elts[0] === "foo") {
|
|
566
|
+
tagNode = node;
|
|
567
|
+
break;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
expect(tagNode).not.toBeNull();
|
|
573
|
+
expect(tagNode.tag).toBe("TAG");
|
|
574
|
+
expect(tagNode.elts).toEqual(["foo"]);
|
|
575
|
+
|
|
576
|
+
// Unparse should reproduce the original source
|
|
577
|
+
const source = unparse(result, emptyLexicon);
|
|
578
|
+
expect(source).toBe("foo..");
|
|
579
|
+
});
|
|
580
|
+
|
|
550
581
|
it("should parse strings with mixed escape sequences", async () => {
|
|
551
582
|
// Arrange & Act
|
|
552
583
|
const result = await parser.parse(0, '"Line 1\\nTab\\t\\"Quote\\""..', basisLexicon);
|
package/src/unparse.js
CHANGED
|
@@ -46,9 +46,9 @@ function unparseNode(node, lexicon, indent = 0, options = {}) {
|
|
|
46
46
|
if (node.elts.length >= 3) {
|
|
47
47
|
const first = node.elts[0];
|
|
48
48
|
// Check if first element is an identifier that could be a function
|
|
49
|
-
if (first && first.tag
|
|
49
|
+
if (first && first.tag === "TAG") {
|
|
50
50
|
// This might be a function name followed by arguments
|
|
51
|
-
const funcName = first.
|
|
51
|
+
const funcName = first.elts[0];
|
|
52
52
|
// Check if this matches a lexicon function
|
|
53
53
|
if (lexicon && lexicon[funcName]) {
|
|
54
54
|
const arity = lexicon[funcName].arity || 0;
|
|
@@ -88,6 +88,9 @@ function unparseNode(node, lexicon, indent = 0, options = {}) {
|
|
|
88
88
|
case "IDENT":
|
|
89
89
|
return node.elts[0];
|
|
90
90
|
|
|
91
|
+
case "TAG":
|
|
92
|
+
return node.elts[0];
|
|
93
|
+
|
|
91
94
|
case "LIST": {
|
|
92
95
|
// Array literal [a, b, c]
|
|
93
96
|
if (!node.elts || node.elts.length === 0) {
|
|
@@ -371,6 +374,7 @@ function reconstructNode(pool, nodeId) {
|
|
|
371
374
|
case "STR":
|
|
372
375
|
case "IDENT":
|
|
373
376
|
case "BOOL":
|
|
377
|
+
case "TAG":
|
|
374
378
|
// These nodes have primitive values in elts[0]
|
|
375
379
|
result.elts = [node.elts[0]];
|
|
376
380
|
break;
|