@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.
@@ -0,0 +1,12 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(NODE_OPTIONS=--experimental-vm-modules npx jest:*)",
5
+ "Bash(npx eslint:*)",
6
+ "Bash(npm test:*)",
7
+ "Bash(npm publish:*)",
8
+ "Bash(grep:*)",
9
+ "Bash(npm install:*)"
10
+ ]
11
+ }
12
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graffiticode/parser",
3
- "version": "1.2.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.2"
25
+ "@graffiticode/basis": "^1.6.3"
26
26
  }
27
27
  }
package/src/ast.js CHANGED
@@ -120,6 +120,7 @@ export class Ast {
120
120
  case "STR":
121
121
  case "IDENT":
122
122
  case "BOOL":
123
+ case "TAG":
123
124
  elts[0] = n.elts[0];
124
125
  break;
125
126
  default:
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: name,
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
@@ -1 +1,2 @@
1
1
  export { parser } from "./parser.js";
2
+ export { unparse } from "./unparse.js";
@@ -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 && first.elts && first.elts.length === 0) {
49
+ if (first && first.tag === "TAG") {
50
50
  // This might be a function name followed by arguments
51
- const funcName = first.tag;
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;