@graffiticode/parser 0.2.0 → 0.4.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,347 @@
1
+ import { parser } from "./parser.js";
2
+ import { unparse } from "./unparse.js";
3
+
4
+ describe("unparse", () => {
5
+ // Helper function to test round-trip parsing
6
+ async function testRoundTrip(source, lexicon = {}, options = { compact: true }) {
7
+ const ast = await parser.parse(0, source);
8
+ const unparsed = unparse(ast, lexicon, options);
9
+ return unparsed;
10
+ }
11
+
12
+ describe("literals", () => {
13
+ it("should unparse string literals", async () => {
14
+ const source = "'hello, world'..";
15
+ const unparsed = await testRoundTrip(source);
16
+ expect(unparsed).toBe("'hello, world'..");
17
+ });
18
+
19
+ it.skip("should unparse string literals with escaped quotes", async () => {
20
+ // Parser doesn't handle escaped quotes properly yet
21
+ const source = "'it\\'s working'..";
22
+ const unparsed = await testRoundTrip(source);
23
+ expect(unparsed).toBe("'it\\'s working'..");
24
+ });
25
+
26
+ it("should unparse numeric literals", async () => {
27
+ const source = "42..";
28
+ const unparsed = await testRoundTrip(source);
29
+ expect(unparsed).toBe("42..");
30
+ });
31
+
32
+ it("should unparse negative numbers", async () => {
33
+ const source = "-42..";
34
+ const unparsed = await testRoundTrip(source);
35
+ expect(unparsed).toBe("-42..");
36
+ });
37
+
38
+ it("should unparse decimal numbers", async () => {
39
+ const source = "3.14159..";
40
+ const unparsed = await testRoundTrip(source);
41
+ expect(unparsed).toBe("3.14159..");
42
+ });
43
+
44
+ it("should unparse boolean true", async () => {
45
+ const source = "true..";
46
+ const unparsed = await testRoundTrip(source);
47
+ expect(unparsed).toBe("true..");
48
+ });
49
+
50
+ it("should unparse boolean false", async () => {
51
+ const source = "false..";
52
+ const unparsed = await testRoundTrip(source);
53
+ expect(unparsed).toBe("false..");
54
+ });
55
+
56
+ it("should unparse null", async () => {
57
+ const source = "null..";
58
+ const unparsed = await testRoundTrip(source);
59
+ expect(unparsed).toBe("null..");
60
+ });
61
+ });
62
+
63
+ describe("data structures", () => {
64
+ it("should unparse empty list", async () => {
65
+ const source = "[]..";
66
+ const unparsed = await testRoundTrip(source);
67
+ expect(unparsed).toBe("[]..");
68
+ });
69
+
70
+ it("should unparse list with single element", async () => {
71
+ const source = "[42]..";
72
+ const unparsed = await testRoundTrip(source);
73
+ expect(unparsed).toBe("[42]..");
74
+ });
75
+
76
+ it("should unparse list with multiple elements", async () => {
77
+ const source = "[1, 2, 3]..";
78
+ const unparsed = await testRoundTrip(source);
79
+ expect(unparsed).toBe("[1, 2, 3]..");
80
+ });
81
+
82
+ it("should unparse nested lists", async () => {
83
+ const source = "[[1, 2], [3, 4]]..";
84
+ const unparsed = await testRoundTrip(source);
85
+ expect(unparsed).toBe("[[1, 2], [3, 4]]..");
86
+ });
87
+
88
+ it("should unparse empty record", async () => {
89
+ const source = "{}..";
90
+ const unparsed = await testRoundTrip(source);
91
+ expect(unparsed).toBe("{}..");
92
+ });
93
+
94
+ it("should unparse record with single field", async () => {
95
+ const source = "{x: 10}..";
96
+ const unparsed = await testRoundTrip(source);
97
+ expect(unparsed).toBe("{x: 10}..");
98
+ });
99
+
100
+ it.skip("should unparse record with multiple fields", async () => {
101
+ const source = "{x: 10, y: 20}..";
102
+ const unparsed = await testRoundTrip(source);
103
+ expect(unparsed).toBe("{x: 10, y: 20}..");
104
+ });
105
+
106
+ it.skip("should unparse nested records", async () => {
107
+ const source = "{a: {b: 1}, c: 2}..";
108
+ const unparsed = await testRoundTrip(source);
109
+ expect(unparsed).toBe("{a: {b: 1}, c: 2}..");
110
+ });
111
+ });
112
+
113
+ describe("expressions", () => {
114
+ it("should unparse parenthesized expression", async () => {
115
+ const source = "(42)..";
116
+ const unparsed = await testRoundTrip(source);
117
+ expect(unparsed).toBe("(42)..");
118
+ });
119
+
120
+ it.skip("should unparse addition", async () => {
121
+ const source = "add 1 2..";
122
+ const unparsed = await testRoundTrip(source);
123
+ expect(unparsed).toBe("add 1 2..");
124
+ });
125
+
126
+ it.skip("should unparse subtraction", async () => {
127
+ const source = "sub 10 5..";
128
+ const unparsed = await testRoundTrip(source);
129
+ expect(unparsed).toBe("sub 10 5..");
130
+ });
131
+
132
+ it.skip("should unparse multiplication", async () => {
133
+ const source = "mul 3 4..";
134
+ const unparsed = await testRoundTrip(source);
135
+ expect(unparsed).toBe("mul 3 4..");
136
+ });
137
+
138
+ it.skip("should unparse division", async () => {
139
+ const source = "div 10 2..";
140
+ const unparsed = await testRoundTrip(source);
141
+ expect(unparsed).toBe("div 10 2..");
142
+ });
143
+
144
+ it.skip("should unparse modulo", async () => {
145
+ const source = "mod 10 3..";
146
+ const unparsed = await testRoundTrip(source);
147
+ expect(unparsed).toBe("mod 10 3..");
148
+ });
149
+
150
+ it.skip("should unparse power", async () => {
151
+ const source = "pow 2 3..";
152
+ const unparsed = await testRoundTrip(source);
153
+ expect(unparsed).toBe("pow 2 3..");
154
+ });
155
+
156
+ it.skip("should unparse string concatenation", async () => {
157
+ const source = "concat 'hello' ' world'..";
158
+ const unparsed = await testRoundTrip(source);
159
+ expect(unparsed).toBe("concat 'hello' ' world'..");
160
+ });
161
+
162
+ it.skip("should unparse complex arithmetic expression", async () => {
163
+ const source = "mul (add 1 2) 3..";
164
+ const unparsed = await testRoundTrip(source);
165
+ expect(unparsed).toBe("mul (add 1 2) 3..");
166
+ });
167
+ });
168
+
169
+ describe("multiple expressions", () => {
170
+ it("should unparse multiple expressions separated by periods", async () => {
171
+ const source = "1.2.3..";
172
+ const unparsed = await testRoundTrip(source);
173
+ expect(unparsed).toBe("1.2.3..");
174
+ });
175
+
176
+ it.skip("should unparse mixed expressions", async () => {
177
+ const source = "'hello'.[1, 2].{x: 10}..";
178
+ const unparsed = await testRoundTrip(source);
179
+ expect(unparsed).toBe("'hello'.[1, 2].{x: 10}..");
180
+ });
181
+ });
182
+
183
+ describe("identifiers and function calls", () => {
184
+ it("should unparse identifier", async () => {
185
+ const source = "foo..";
186
+ const unparsed = await testRoundTrip(source);
187
+ expect(unparsed).toBe("foo..");
188
+ });
189
+
190
+ it.skip("should unparse function application", async () => {
191
+ const source = "foo 42..";
192
+ const unparsed = await testRoundTrip(source);
193
+ expect(unparsed).toBe("foo 42..");
194
+ });
195
+
196
+ it.skip("should unparse function with multiple arguments", async () => {
197
+ const source = "foo [1, 2, 3]..";
198
+ const unparsed = await testRoundTrip(source);
199
+ expect(unparsed).toBe("foo [1, 2, 3]..");
200
+ });
201
+
202
+ it.skip("should unparse nested function applications", async () => {
203
+ const source = "foo (bar 42)..";
204
+ const unparsed = await testRoundTrip(source);
205
+ expect(unparsed).toBe("foo (bar 42)..");
206
+ });
207
+ });
208
+
209
+ describe("control flow", () => {
210
+ it.skip("should unparse if-then expression", async () => {
211
+ const source = "if true then 1..";
212
+ const unparsed = await testRoundTrip(source);
213
+ expect(unparsed).toBe("if true then 1..");
214
+ });
215
+
216
+ it("should unparse if-then-else expression", async () => {
217
+ const source = "if true then 1 else 2..";
218
+ const unparsed = await testRoundTrip(source);
219
+ expect(unparsed).toBe("if true then 1 else 2..");
220
+ });
221
+
222
+ it("should unparse nested if expressions", async () => {
223
+ const source = "if true then (if false then 1 else 2) else 3..";
224
+ const unparsed = await testRoundTrip(source);
225
+ expect(unparsed).toBe("if true then (if false then 1 else 2) else 3..");
226
+ });
227
+ });
228
+
229
+ describe("lambda expressions", () => {
230
+ it.skip("should unparse lambda with no parameters", async () => {
231
+ const source = "\\. 42..";
232
+ const unparsed = await testRoundTrip(source);
233
+ expect(unparsed).toBe("\\. 42..");
234
+ });
235
+
236
+ it.skip("should unparse lambda with one parameter", async () => {
237
+ const source = "\\x . add x 1..";
238
+ const unparsed = await testRoundTrip(source);
239
+ expect(unparsed).toBe("\\x . add x 1..");
240
+ });
241
+
242
+ it.skip("should unparse lambda with multiple parameters", async () => {
243
+ const source = "\\x y . add x y..";
244
+ const unparsed = await testRoundTrip(source);
245
+ expect(unparsed).toBe("\\x y . add x y..");
246
+ });
247
+
248
+ it.skip("should unparse lambda application", async () => {
249
+ const source = "(\\x . add x 1) 5..";
250
+ const unparsed = await testRoundTrip(source);
251
+ expect(unparsed).toBe("(\\x . add x 1) 5..");
252
+ });
253
+ });
254
+
255
+ describe("let bindings", () => {
256
+ it.skip("should unparse let with single binding", async () => {
257
+ const source = "let x = 10 in add x 1..";
258
+ const unparsed = await testRoundTrip(source);
259
+ expect(unparsed).toBe("let x = 10 in add x 1..");
260
+ });
261
+
262
+ it.skip("should unparse let with multiple bindings", async () => {
263
+ const source = "let x = 10, y = 20 in add x y..";
264
+ const unparsed = await testRoundTrip(source);
265
+ expect(unparsed).toBe("let x = 10, y = 20 in add x y..");
266
+ });
267
+
268
+ it.skip("should unparse nested let bindings", async () => {
269
+ const source = "let x = 10 in (let y = 20 in add x y)..";
270
+ const unparsed = await testRoundTrip(source);
271
+ expect(unparsed).toBe("let x = 10 in (let y = 20 in add x y)..");
272
+ });
273
+ });
274
+
275
+ describe("edge cases", () => {
276
+ it("should handle empty program", async () => {
277
+ const source = "..";
278
+ const unparsed = await testRoundTrip(source);
279
+ expect(unparsed).toBe("..");
280
+ });
281
+
282
+ it("should handle null AST", () => {
283
+ const unparsed = unparse(null);
284
+ expect(unparsed).toBe("");
285
+ });
286
+
287
+ it("should handle AST without root", () => {
288
+ const unparsed = unparse({});
289
+ expect(unparsed).toBe("");
290
+ });
291
+ });
292
+
293
+ describe("parser.reformat", () => {
294
+ it("should reformat simple expressions", async () => {
295
+ const source = "42..";
296
+ const reformatted = await parser.reformat(0, source, {});
297
+ expect(reformatted).toBe("42..");
298
+ });
299
+
300
+ it("should reformat and pretty print lists", async () => {
301
+ const source = "[1,2,3]..";
302
+ const reformatted = await parser.reformat(0, source, {});
303
+ expect(reformatted).toContain("[\n");
304
+ expect(reformatted).toContain(" 1");
305
+ expect(reformatted).toContain(" 2");
306
+ expect(reformatted).toContain(" 3");
307
+ expect(reformatted).toContain("\n]");
308
+ });
309
+
310
+ it("should reformat with provided lexicon", async () => {
311
+ const lexicon = {
312
+ "test": {
313
+ "tk": 1,
314
+ "name": "TEST",
315
+ "cls": "function",
316
+ "length": 1,
317
+ "arity": 1,
318
+ }
319
+ };
320
+ const source = "test 42..";
321
+ const reformatted = await parser.reformat(0, source, lexicon);
322
+ expect(reformatted).toBe("test 42..");
323
+ });
324
+
325
+ it("should reformat multiple expressions", async () => {
326
+ const source = "'hello'.[1, 2].{x: 10}..";
327
+ const reformatted = await parser.reformat(0, source, {});
328
+ expect(reformatted).toContain("'hello'");
329
+ expect(reformatted).toContain("[\n 1");
330
+ expect(reformatted).toContain("{\n x: 10");
331
+ expect(reformatted).toContain("..");
332
+ });
333
+
334
+ it("should support compact option", async () => {
335
+ const source = "[1, 2, 3]..";
336
+ const reformatted = await parser.reformat(0, source, {}, { compact: true });
337
+ expect(reformatted).toBe("[1, 2, 3]..");
338
+ });
339
+
340
+ it("should support custom indent size", async () => {
341
+ const source = "[1, 2]..";
342
+ const reformatted = await parser.reformat(0, source, {}, { indentSize: 4 });
343
+ expect(reformatted).toContain(" 1"); // 4 spaces
344
+ expect(reformatted).toContain(" 2"); // 4 spaces
345
+ });
346
+ });
347
+ });