@graffiticode/parser 1.2.0 → 1.2.2
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 +10 -0
- package/package.json +2 -2
- package/src/ast.js +1 -0
- package/src/folder.js +7 -2
- package/src/unparse.js +38 -11
- package/src/unparse.spec.js +12 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graffiticode/parser",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
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/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,8 +88,10 @@ 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
|
-
console.log("LIST");
|
|
93
95
|
// Array literal [a, b, c]
|
|
94
96
|
if (!node.elts || node.elts.length === 0) {
|
|
95
97
|
return "[]";
|
|
@@ -213,13 +215,15 @@ function unparseNode(node, lexicon, indent = 0, options = {}) {
|
|
|
213
215
|
// If-then-else
|
|
214
216
|
if (node.elts && node.elts.length >= 2) {
|
|
215
217
|
const cond = unparseNode(node.elts[0], lexicon, indent, opts);
|
|
216
|
-
const
|
|
218
|
+
const innerIndent = indent + opts.indentSize;
|
|
219
|
+
const indentStr = " ".repeat(innerIndent);
|
|
220
|
+
const thenExpr = unparseNode(node.elts[1], lexicon, innerIndent, opts);
|
|
217
221
|
|
|
218
222
|
if (node.elts.length >= 3) {
|
|
219
|
-
const elseExpr = unparseNode(node.elts[2], lexicon,
|
|
220
|
-
return `if ${cond} then
|
|
223
|
+
const elseExpr = unparseNode(node.elts[2], lexicon, innerIndent, opts);
|
|
224
|
+
return `if ${cond} then\n${indentStr}${thenExpr}\nelse\n${indentStr}${elseExpr}`;
|
|
221
225
|
} else {
|
|
222
|
-
return `if ${cond} then
|
|
226
|
+
return `if ${cond} then\n${indentStr}${thenExpr}`;
|
|
223
227
|
}
|
|
224
228
|
}
|
|
225
229
|
return "";
|
|
@@ -228,17 +232,39 @@ function unparseNode(node, lexicon, indent = 0, options = {}) {
|
|
|
228
232
|
// Case expression
|
|
229
233
|
if (node.elts && node.elts.length > 0) {
|
|
230
234
|
const expr = unparseNode(node.elts[0], lexicon, indent, opts);
|
|
231
|
-
const
|
|
232
|
-
|
|
235
|
+
const innerIndent = indent + opts.indentSize;
|
|
236
|
+
const indentStr = " ".repeat(innerIndent);
|
|
237
|
+
const endIndentStr = " ".repeat(indent);
|
|
238
|
+
|
|
239
|
+
// Process each case branch with proper indentation
|
|
240
|
+
const cases = node.elts.slice(1).map(c => {
|
|
241
|
+
// Pass the inner indent to OF nodes but don't add extra indentation here
|
|
242
|
+
// The OF node will handle its own formatting
|
|
243
|
+
const caseStr = unparseNode(c, lexicon, indent, opts);
|
|
244
|
+
return caseStr;
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
return `case ${expr} of\n${cases.join("\n")}\n${endIndentStr}end`;
|
|
233
248
|
}
|
|
234
249
|
return "";
|
|
235
250
|
|
|
236
251
|
case "OF":
|
|
237
252
|
// Case branch
|
|
238
253
|
if (node.elts && node.elts.length >= 2) {
|
|
254
|
+
const indentStr = " ".repeat(indent + opts.indentSize);
|
|
239
255
|
const pattern = unparseNode(node.elts[0], lexicon, indent, opts);
|
|
240
|
-
|
|
241
|
-
|
|
256
|
+
|
|
257
|
+
// Check if the expression is a CASE node for proper nested formatting
|
|
258
|
+
const exprNode = node.elts[1];
|
|
259
|
+
let expr;
|
|
260
|
+
if (exprNode && exprNode.tag === "CASE") {
|
|
261
|
+
// For nested case, don't add extra indent to the case keyword itself
|
|
262
|
+
expr = unparseNode(exprNode, lexicon, indent + opts.indentSize, opts);
|
|
263
|
+
} else {
|
|
264
|
+
expr = unparseNode(exprNode, lexicon, indent, opts);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return `${indentStr}${pattern}: ${expr}`;
|
|
242
268
|
}
|
|
243
269
|
return "";
|
|
244
270
|
|
|
@@ -348,6 +374,7 @@ function reconstructNode(pool, nodeId) {
|
|
|
348
374
|
case "STR":
|
|
349
375
|
case "IDENT":
|
|
350
376
|
case "BOOL":
|
|
377
|
+
case "TAG":
|
|
351
378
|
// These nodes have primitive values in elts[0]
|
|
352
379
|
result.elts = [node.elts[0]];
|
|
353
380
|
break;
|
package/src/unparse.spec.js
CHANGED
|
@@ -16,7 +16,7 @@ describe("unparse", () => {
|
|
|
16
16
|
it("should unparse string literals", async () => {
|
|
17
17
|
const source = "'hello, world'..";
|
|
18
18
|
const unparsed = await testRoundTrip(source);
|
|
19
|
-
expect(unparsed).toBe("
|
|
19
|
+
expect(unparsed).toBe('"hello, world"..');
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
it.skip("should unparse string literals with escaped quotes", async () => {
|
|
@@ -218,14 +218,14 @@ describe("unparse", () => {
|
|
|
218
218
|
|
|
219
219
|
it("should unparse if-then-else expression", async () => {
|
|
220
220
|
const source = "if true then 1 else 2..";
|
|
221
|
-
const unparsed = await testRoundTrip(source);
|
|
222
|
-
expect(unparsed).toBe("if true then
|
|
221
|
+
const unparsed = await testRoundTrip(source, {}, { compact: false });
|
|
222
|
+
expect(unparsed).toBe("if true then\n 1\nelse\n 2..");
|
|
223
223
|
});
|
|
224
224
|
|
|
225
225
|
it("should unparse nested if expressions", async () => {
|
|
226
226
|
const source = "if true then (if false then 1 else 2) else 3..";
|
|
227
|
-
const unparsed = await testRoundTrip(source);
|
|
228
|
-
expect(unparsed).toBe("if true then
|
|
227
|
+
const unparsed = await testRoundTrip(source, {}, { compact: false });
|
|
228
|
+
expect(unparsed).toBe("if true then\n (if false then\n 1\nelse\n 2)\nelse\n 3..");
|
|
229
229
|
});
|
|
230
230
|
});
|
|
231
231
|
|
|
@@ -328,7 +328,7 @@ describe("unparse", () => {
|
|
|
328
328
|
it("should reformat multiple expressions", async () => {
|
|
329
329
|
const source = "'hello'.[1, 2].{x: 10}..";
|
|
330
330
|
const reformatted = await parser.reformat(0, source, basisLexicon);
|
|
331
|
-
expect(reformatted).toContain("
|
|
331
|
+
expect(reformatted).toContain('"hello"');
|
|
332
332
|
expect(reformatted).toContain("[\n 1");
|
|
333
333
|
expect(reformatted).toContain("{\n x: 10");
|
|
334
334
|
expect(reformatted).toContain("..");
|
|
@@ -346,5 +346,11 @@ describe("unparse", () => {
|
|
|
346
346
|
expect(reformatted).toContain(" 1"); // 4 spaces
|
|
347
347
|
expect(reformatted).toContain(" 2"); // 4 spaces
|
|
348
348
|
});
|
|
349
|
+
|
|
350
|
+
it("should preserve escaped quotes in strings", async () => {
|
|
351
|
+
const source = '"\\\"hello\\\""..'
|
|
352
|
+
const reformatted = await parser.reformat(0, source, basisLexicon, { compact: true });
|
|
353
|
+
expect(reformatted).toBe('"\\\"hello\\\""..'); // Should produce identical program
|
|
354
|
+
});
|
|
349
355
|
});
|
|
350
356
|
});
|