yukigo-haskell-parser 0.1.3 → 0.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.
Files changed (57) hide show
  1. package/.mocharc.json +3 -3
  2. package/CHANGELOG.md +51 -9
  3. package/README.md +5 -5
  4. package/dist/index.js +21 -13
  5. package/dist/index.js.map +1 -1
  6. package/dist/parser/grammar.cjs +397 -0
  7. package/dist/parser/grammar.cjs.map +1 -0
  8. package/dist/parser/grammar.d.cts +38 -0
  9. package/dist/parser/lexer.d.ts +2 -1
  10. package/dist/parser/lexer.js +6 -1
  11. package/dist/parser/lexer.js.map +1 -1
  12. package/dist/prelude.js +279 -279
  13. package/dist/typechecker/DeclarationCollector.d.ts +2 -0
  14. package/dist/typechecker/DeclarationCollector.js +6 -5
  15. package/dist/typechecker/DeclarationCollector.js.map +1 -1
  16. package/dist/typechecker/TypeBuilder.js +9 -7
  17. package/dist/typechecker/TypeBuilder.js.map +1 -1
  18. package/dist/typechecker/checker.d.ts +9 -4
  19. package/dist/typechecker/checker.js +66 -94
  20. package/dist/typechecker/checker.js.map +1 -1
  21. package/dist/typechecker/core.js +8 -7
  22. package/dist/typechecker/core.js.map +1 -1
  23. package/dist/typechecker/inference.d.ts +4 -1
  24. package/dist/typechecker/inference.js +39 -19
  25. package/dist/typechecker/inference.js.map +1 -1
  26. package/dist/utils/helpers.d.ts +3 -0
  27. package/dist/utils/helpers.js +5 -0
  28. package/dist/utils/helpers.js.map +1 -1
  29. package/dist/utils/types.d.ts +12 -3
  30. package/dist/utils/types.js +34 -22
  31. package/dist/utils/types.js.map +1 -1
  32. package/package.json +6 -9
  33. package/src/index.ts +223 -192
  34. package/src/parser/{grammar.ts → grammar.cjs} +191 -223
  35. package/src/parser/grammar.d.ts +3 -0
  36. package/src/parser/grammar.ne +521 -518
  37. package/src/parser/lexer.ts +357 -353
  38. package/src/prelude.ts +281 -281
  39. package/src/typechecker/DeclarationCollector.ts +160 -157
  40. package/src/typechecker/TypeBuilder.ts +153 -150
  41. package/src/typechecker/checker.ts +487 -501
  42. package/src/typechecker/core.ts +195 -192
  43. package/src/typechecker/inference.ts +1442 -1421
  44. package/src/utils/helpers.ts +34 -29
  45. package/src/utils/types.ts +75 -63
  46. package/tests/hspec.spec.ts +95 -92
  47. package/tests/lexer.spec.ts +175 -175
  48. package/tests/parser.spec.ts +838 -829
  49. package/tests/prelude.spec.ts +17 -17
  50. package/tests/typechecker.spec.ts +326 -327
  51. package/tsconfig.build.json +16 -0
  52. package/tsconfig.build.tsbuildinfo +1 -0
  53. package/tsconfig.json +29 -26
  54. package/tsconfig.tsbuildinfo +1 -1
  55. package/dist/parser/grammar.d.ts +0 -28
  56. package/dist/parser/grammar.js +0 -393
  57. package/dist/parser/grammar.js.map +0 -1
@@ -1,829 +1,838 @@
1
- import { YukigoHaskellParser } from "../src/index.js";
2
- import { assert } from "chai";
3
- import { inspect } from "util";
4
- import {
5
- Application,
6
- ArithmeticBinaryOperation,
7
- Assert,
8
- BooleanPrimitive,
9
- CharPrimitive,
10
- ComparisonOperation,
11
- ConsPattern,
12
- Constraint,
13
- Equation,
14
- Failure,
15
- Function,
16
- GuardedBody,
17
- If,
18
- LetInExpression,
19
- ListBinaryOperation,
20
- ListPrimitive,
21
- ListType,
22
- LiteralPattern,
23
- LogicalBinaryOperation,
24
- NumberPrimitive,
25
- Otherwise,
26
- ParameterizedType,
27
- RangeExpression,
28
- Return,
29
- Sequence,
30
- SimpleType,
31
- StringPrimitive,
32
- SymbolPrimitive,
33
- Test,
34
- TypeAlias,
35
- TypeApplication,
36
- TypeCast,
37
- TypeSignature,
38
- UnguardedBody,
39
- VariablePattern,
40
- } from "yukigo-ast";
41
-
42
- const _deepEqual = assert.deepEqual;
43
- assert.deepEqual = function (actual: any, expected: any, ...args: any[]) {
44
- function stripLoc(obj: any): any {
45
- if (Array.isArray(obj)) return obj.map(stripLoc);
46
- if (obj && typeof obj === "object") {
47
- const { loc, ...rest } = obj;
48
- return Object.fromEntries(
49
- Object.entries(rest).map(([k, v]) => [k, stripLoc(v)])
50
- );
51
- }
52
- return obj;
53
- }
54
- return _deepEqual(stripLoc(actual), stripLoc(expected), ...args);
55
- };
56
-
57
- describe("Parser Tests", () => {
58
- let parser: YukigoHaskellParser;
59
- beforeEach(() => {
60
- parser = new YukigoHaskellParser("", { typecheck: false, includePrims: false });
61
- });
62
- it("parses boolean expressions", () => {
63
- const returnExpression = new Return(
64
- new LogicalBinaryOperation(
65
- "And",
66
- new SymbolPrimitive("x"),
67
- new SymbolPrimitive("y")
68
- )
69
- );
70
- assert.deepEqual(parser.parse("f x y = x && y"), [
71
- new Function(new SymbolPrimitive("f"), [
72
- new Equation(
73
- [
74
- new VariablePattern(new SymbolPrimitive("x")),
75
- new VariablePattern(new SymbolPrimitive("y")),
76
- ],
77
- new UnguardedBody(new Sequence([returnExpression])),
78
- returnExpression
79
- ),
80
- ]),
81
- ]);
82
- });
83
- it("parses literal character patterns", () => {
84
- const returnExpression = new Return(new NumberPrimitive(1));
85
- assert.deepEqual(parser.parse("f 'a' = 1"), [
86
- new Function(new SymbolPrimitive("f"), [
87
- new Equation(
88
- [new LiteralPattern(new CharPrimitive("a"))],
89
- new UnguardedBody(new Sequence([returnExpression])),
90
- returnExpression
91
- ),
92
- ]),
93
- ]);
94
- });
95
- it("parses literal string patterns", () => {
96
- const returnExpression = new Return(new NumberPrimitive(1));
97
- assert.deepEqual(parser.parse('f "hello world" = 1'), [
98
- new Function(new SymbolPrimitive("f"), [
99
- new Equation(
100
- [new LiteralPattern(new StringPrimitive("hello world"))],
101
- new UnguardedBody(new Sequence([returnExpression])),
102
- returnExpression
103
- ),
104
- ]),
105
- ]);
106
- });
107
- it("parses literal number patterns", () => {
108
- const returnExpression = new Return(new NumberPrimitive(1));
109
- assert.deepEqual(parser.parse("f 1 = 1"), [
110
- new Function(new SymbolPrimitive("f"), [
111
- new Equation(
112
- [new LiteralPattern(new NumberPrimitive(1))],
113
- new UnguardedBody(new Sequence([returnExpression])),
114
- returnExpression
115
- ),
116
- ]),
117
- ]);
118
- });
119
- it("parses simple cons pattern", () => {
120
- const returnExpression = new Return(
121
- new ArithmeticBinaryOperation(
122
- "Plus",
123
- new SymbolPrimitive("x"),
124
- new Application(new SymbolPrimitive("f"), new SymbolPrimitive("xs"))
125
- )
126
- );
127
- assert.deepEqual(parser.parse("f (x:xs) = x + f xs"), [
128
- new Function(new SymbolPrimitive("f"), [
129
- new Equation(
130
- [
131
- new ConsPattern(
132
- new VariablePattern(new SymbolPrimitive("x")),
133
- new VariablePattern(new SymbolPrimitive("xs"))
134
- ),
135
- ],
136
- new UnguardedBody(new Sequence([returnExpression])),
137
- returnExpression
138
- ),
139
- ]),
140
- ]);
141
- });
142
- it("parses multiple cons pattern", () => {
143
- const returnExpression = new Return(
144
- new ArithmeticBinaryOperation(
145
- "Plus",
146
- new SymbolPrimitive("x"),
147
- new Application(new SymbolPrimitive("f"), new SymbolPrimitive("xs"))
148
- )
149
- );
150
- assert.deepEqual(parser.parse("f (x:y:xs) = x + f xs"), [
151
- new Function(new SymbolPrimitive("f"), [
152
- new Equation(
153
- [
154
- new ConsPattern(
155
- new VariablePattern(new SymbolPrimitive("x")),
156
- new ConsPattern(
157
- new VariablePattern(new SymbolPrimitive("y")),
158
- new VariablePattern(new SymbolPrimitive("xs"))
159
- )
160
- ),
161
- ],
162
- new UnguardedBody(new Sequence([returnExpression])),
163
- returnExpression
164
- ),
165
- ]),
166
- ]);
167
- });
168
- it("parses left infix partial application", () => {
169
- const returnExpression = new Return(
170
- new Application(new SymbolPrimitive("+"), new NumberPrimitive(1))
171
- );
172
- assert.deepEqual(parser.parse("f = (1+)"), [
173
- new Function(new SymbolPrimitive("f"), [
174
- new Equation(
175
- [],
176
- new UnguardedBody(new Sequence([returnExpression])),
177
- returnExpression
178
- ),
179
- ]),
180
- ]);
181
- });
182
- it("parses right infix partial application", () => {
183
- const returnExpression = new Return(
184
- new Application(
185
- new Application(new SymbolPrimitive("flip"), new SymbolPrimitive("+")),
186
- new NumberPrimitive(1)
187
- )
188
- );
189
- assert.deepEqual(parser.parse("f = (+1)"), [
190
- new Function(new SymbolPrimitive("f"), [
191
- new Equation(
192
- [],
193
- new UnguardedBody(new Sequence([returnExpression])),
194
- returnExpression
195
- ),
196
- ]),
197
- ]);
198
- });
199
- it("parses type restrictions", () => {
200
- assert.deepEqual(parser.parse("f :: Num a => [a] -> [a]"), [
201
- new TypeSignature(
202
- new SymbolPrimitive("f"),
203
- new ParameterizedType(
204
- [new ListType(new SimpleType("a", []), [])],
205
- new ListType(new SimpleType("a", []), []),
206
- [new Constraint("Num", [new SimpleType("a", [])])]
207
- )
208
- ),
209
- ]);
210
- });
211
- it("parses multiple type restrictions", () => {
212
- assert.deepEqual(parser.parse("f :: (Num a, Eq b) => [a] -> [b]"), [
213
- new TypeSignature(
214
- new SymbolPrimitive("f"),
215
- new ParameterizedType(
216
- [new ListType(new SimpleType("a", []), [])],
217
- new ListType(new SimpleType("b", []), []),
218
- [
219
- new Constraint("Num", [new SimpleType("a", [])]),
220
- new Constraint("Eq", [new SimpleType("b", [])]),
221
- ]
222
- )
223
- ),
224
- ]);
225
- });
226
- it("parses signatures without type restrictions", () => {
227
- assert.deepEqual(parser.parse("f :: [a] -> [a]"), [
228
- new TypeSignature(
229
- new SymbolPrimitive("f"),
230
- new ParameterizedType(
231
- [new ListType(new SimpleType("a", []), [])],
232
- new ListType(new SimpleType("a", []), []),
233
- []
234
- )
235
- ),
236
- ]);
237
- });
238
- it("parses type alias", () => {
239
- assert.deepEqual(parser.parse("type String = [Char]"), [
240
- new TypeAlias(
241
- new SymbolPrimitive("String"),
242
- [],
243
- new ListType(new SimpleType("Char", []), [])
244
- ),
245
- ]);
246
- });
247
- it("parses type alias with arguments", () => {
248
- assert.deepEqual(parser.parse("type List a = [a]"), [
249
- new TypeAlias(
250
- new SymbolPrimitive("List"),
251
- ["a"],
252
- new ListType(new SimpleType("a", []), [])
253
- ),
254
- ]);
255
- });
256
- it("parses inline type annotations", () => {
257
- const returnExpression = new Return(
258
- new TypeCast(new NumberPrimitive(1), new SimpleType("Int", []))
259
- );
260
- assert.deepEqual(parser.parse("x = 1 :: Int"), [
261
- new Function(new SymbolPrimitive("x"), [
262
- new Equation(
263
- [],
264
- new UnguardedBody(new Sequence([returnExpression])),
265
- returnExpression
266
- ),
267
- ]),
268
- ]);
269
- });
270
- it("parses inline type annotations with restrictions", () => {
271
- const returnExpression = new Return(
272
- new TypeCast(
273
- new NumberPrimitive(1),
274
- new ParameterizedType(
275
- [],
276
- new TypeApplication(new SimpleType("t", []), new SimpleType("a", [])),
277
- [
278
- new Constraint("Num", [new SimpleType("a", [])]),
279
- new Constraint("Foldable", [new SimpleType("t", [])]),
280
- ]
281
- )
282
- )
283
- );
284
- assert.deepEqual(parser.parse("x = 1 :: (Num a, Foldable t) => t a"), [
285
- new Function(new SymbolPrimitive("x"), [
286
- new Equation(
287
- [],
288
- new UnguardedBody(new Sequence([returnExpression])),
289
- returnExpression
290
- ),
291
- ]),
292
- ]);
293
- });
294
- it("parses chars and single char strings differently", () => {
295
- const stringAst: any = parser.parse('x = "a"');
296
- const charAst: any = parser.parse("x = 'a'");
297
- const stringExpr = stringAst[0].equations[0].returnExpr.body;
298
- const charExpr = charAst[0].equations[0].returnExpr.body;
299
- assert.instanceOf(stringExpr, StringPrimitive);
300
- assert.instanceOf(charExpr, CharPrimitive);
301
- });
302
- it("parses chars as YuChars", () => {
303
- const returnExpression = new Return(new CharPrimitive("a"));
304
- assert.deepEqual(parser.parse("x = 'a'"), [
305
- new Function(new SymbolPrimitive("x"), [
306
- new Equation(
307
- [],
308
- new UnguardedBody(new Sequence([returnExpression])),
309
- returnExpression
310
- ),
311
- ]),
312
- ]);
313
- });
314
- it("parses concat of list as ListBinaryOperation", () => {
315
- const returnExpression = new Return(
316
- new ListBinaryOperation(
317
- "Concat",
318
- new ListPrimitive([new NumberPrimitive(2)]),
319
- new ListPrimitive([new NumberPrimitive(4)])
320
- )
321
- );
322
- assert.deepEqual(parser.parse("f = [2] ++ [4]"), [
323
- new Function(new SymbolPrimitive("f"), [
324
- new Equation(
325
- [],
326
- new UnguardedBody(new Sequence([returnExpression])),
327
- returnExpression
328
- ),
329
- ]),
330
- ]);
331
- });
332
- it("parses inline guards correctly", () => {
333
- assert.deepEqual(parser.parse("f x | x > 40 = 2 | otherwise = 1"), [
334
- new Function(new SymbolPrimitive("f"), [
335
- new Equation(
336
- [new VariablePattern(new SymbolPrimitive("x"))],
337
- [
338
- new GuardedBody(
339
- new ComparisonOperation(
340
- "GreaterThan",
341
- new SymbolPrimitive("x"),
342
- new NumberPrimitive(40)
343
- ),
344
- new NumberPrimitive(2)
345
- ),
346
- new GuardedBody(new Otherwise(), new NumberPrimitive(1)),
347
- ]
348
- ),
349
- ]),
350
- ]);
351
- });
352
- it("throws when guarded body is badly indented ", () => {
353
- assert.throw(
354
- () => parser.parse("f x | x < 1 = 10\n| x > 11 = 0"),
355
- "Parser: Unexpected 'pipe' token '|' at line 2 col 1."
356
- );
357
- });
358
- it("parses multi-line guards correctly", () => {
359
- const ast = [
360
- new Function(new SymbolPrimitive("f"), [
361
- new Equation(
362
- [new VariablePattern(new SymbolPrimitive("x"))],
363
- [
364
- new GuardedBody(
365
- new ComparisonOperation(
366
- "GreaterThan",
367
- new SymbolPrimitive("x"),
368
- new NumberPrimitive(40)
369
- ),
370
- new NumberPrimitive(2)
371
- ),
372
- new GuardedBody(new Otherwise(), new NumberPrimitive(1)),
373
- ]
374
- ),
375
- ]),
376
- ];
377
-
378
- assert.deepEqual(parser.parse("f x\n | x > 40 = 2\n | otherwise = 1"), ast);
379
- assert.deepEqual(parser.parse("f x\n | x > 40 = 2 | otherwise = 1"), ast);
380
- assert.deepEqual(parser.parse("f x | x > 40 = 2\n | otherwise = 1"), ast);
381
- assert.deepEqual(
382
- parser.parse("f x\n |\n x > 40 =\n 2 |\n otherwise\n =\n 1"),
383
- ast
384
- );
385
- assert.deepEqual(
386
- parser.parse("f x\n |\n x > 40 =\n 2 | otherwise = 1"),
387
- ast
388
- );
389
- assert.deepEqual(
390
- parser.parse("f x | x > 40 = 2 |\n otherwise\n =\n 1"),
391
- ast
392
- );
393
- });
394
- it("parses inline if then else correctly", () => {
395
- const returnExpression = new Return(
396
- new If(
397
- new ComparisonOperation(
398
- "LessThan",
399
- new SymbolPrimitive("x"),
400
- new NumberPrimitive(4)
401
- ),
402
- new NumberPrimitive(10),
403
- new NumberPrimitive(20)
404
- )
405
- );
406
- assert.deepEqual(parser.parse("f x = if x < 4 then 10 else 20"), [
407
- new Function(new SymbolPrimitive("f"), [
408
- new Equation(
409
- [new VariablePattern(new SymbolPrimitive("x"))],
410
- new UnguardedBody(new Sequence([returnExpression])),
411
- returnExpression
412
- ),
413
- ]),
414
- ]);
415
- });
416
- it("parses multi-line if then else correctly", () => {
417
- const returnExpression = new Return(
418
- new If(
419
- new ComparisonOperation(
420
- "LessThan",
421
- new SymbolPrimitive("x"),
422
- new NumberPrimitive(4)
423
- ),
424
- new NumberPrimitive(10),
425
- new NumberPrimitive(20)
426
- )
427
- );
428
- const ast = [
429
- new Function(new SymbolPrimitive("f"), [
430
- new Equation(
431
- [new VariablePattern(new SymbolPrimitive("x"))],
432
- new UnguardedBody(new Sequence([returnExpression])),
433
- returnExpression
434
- ),
435
- ]),
436
- ];
437
-
438
- assert.deepEqual(parser.parse("f x =\n if x < 4 then 10 else 20"), ast);
439
- assert.deepEqual(parser.parse("f x = if x < 4\n then 10\n else 20"), ast);
440
- assert.deepEqual(parser.parse("f x = \n if x < 4\nthen 10\n else 20"), ast);
441
- assert.deepEqual(parser.parse("f x = \n if x < 4\n then 10\nelse 20"), ast);
442
- assert.deepEqual(
443
- parser.parse("f x = \n if x < 4\n then 10\n else 20"),
444
- ast
445
- );
446
- });
447
- it("parses inline where clause correctly", () => {
448
- const returnExpression = new Return(
449
- new ArithmeticBinaryOperation(
450
- "Multiply",
451
- new SymbolPrimitive("pi"),
452
- new SymbolPrimitive("radius_squared")
453
- )
454
- );
455
- assert.deepEqual(
456
- parser.parse(
457
- "areaOfCircle radius = pi * radius_squared where pi = 3.14159; radius_squared = radius * radius"
458
- ),
459
- [
460
- new Function(new SymbolPrimitive("areaOfCircle"), [
461
- new Equation(
462
- [new VariablePattern(new SymbolPrimitive("radius"))],
463
- new UnguardedBody(
464
- new Sequence([
465
- new Function(new SymbolPrimitive("pi"), [
466
- new Equation(
467
- [],
468
- new UnguardedBody(
469
- new Sequence([new Return(new NumberPrimitive(3.14159))])
470
- ),
471
- new Return(new NumberPrimitive(3.14159))
472
- ),
473
- ]),
474
- new Function(new SymbolPrimitive("radius_squared"), [
475
- new Equation(
476
- [],
477
- new UnguardedBody(
478
- new Sequence([
479
- new Return(
480
- new ArithmeticBinaryOperation(
481
- "Multiply",
482
- new SymbolPrimitive("radius"),
483
- new SymbolPrimitive("radius")
484
- )
485
- ),
486
- ])
487
- ),
488
- new Return(
489
- new ArithmeticBinaryOperation(
490
- "Multiply",
491
- new SymbolPrimitive("radius"),
492
- new SymbolPrimitive("radius")
493
- )
494
- )
495
- ),
496
- ]),
497
- returnExpression,
498
- ])
499
- ),
500
- returnExpression
501
- ),
502
- ]),
503
- ]
504
- );
505
- });
506
- it("parses multi-line where clause correctly", () => {
507
- const returnExpression = new Return(
508
- new ArithmeticBinaryOperation(
509
- "Multiply",
510
- new SymbolPrimitive("pi"),
511
- new SymbolPrimitive("radius_squared")
512
- )
513
- );
514
- const ast = [
515
- new Function(new SymbolPrimitive("areaOfCircle"), [
516
- new Equation(
517
- [new VariablePattern(new SymbolPrimitive("radius"))],
518
- new UnguardedBody(
519
- new Sequence([
520
- new Function(new SymbolPrimitive("pi"), [
521
- new Equation(
522
- [],
523
- new UnguardedBody(
524
- new Sequence([new Return(new NumberPrimitive(3.14159))])
525
- ),
526
- new Return(new NumberPrimitive(3.14159))
527
- ),
528
- ]),
529
- new Function(new SymbolPrimitive("radius_squared"), [
530
- new Equation(
531
- [],
532
- new UnguardedBody(
533
- new Sequence([
534
- new Return(
535
- new ArithmeticBinaryOperation(
536
- "Multiply",
537
- new SymbolPrimitive("radius"),
538
- new SymbolPrimitive("radius")
539
- )
540
- ),
541
- ])
542
- ),
543
- new Return(
544
- new ArithmeticBinaryOperation(
545
- "Multiply",
546
- new SymbolPrimitive("radius"),
547
- new SymbolPrimitive("radius")
548
- )
549
- )
550
- ),
551
- ]),
552
- returnExpression,
553
- ])
554
- ),
555
- returnExpression
556
- ),
557
- ]),
558
- ];
559
-
560
- assert.deepEqual(
561
- parser.parse(
562
- `areaOfCircle radius = pi * radius_squared
563
- where pi = 3.14159
564
- radius_squared = radius * radius`
565
- ),
566
- ast
567
- );
568
- });
569
- it("parses inline let...in expression correctly", () => {
570
- const returnExpression = new Return(
571
- new LetInExpression(
572
- new Sequence([
573
- new Function(new SymbolPrimitive("a_sq"), [
574
- new Equation(
575
- [],
576
- new UnguardedBody(
577
- new Sequence([
578
- new Return(
579
- new ArithmeticBinaryOperation(
580
- "Multiply",
581
- new SymbolPrimitive("a"),
582
- new SymbolPrimitive("a")
583
- )
584
- ),
585
- ])
586
- ),
587
- new Return(
588
- new ArithmeticBinaryOperation(
589
- "Multiply",
590
- new SymbolPrimitive("a"),
591
- new SymbolPrimitive("a")
592
- )
593
- )
594
- ),
595
- ]),
596
- new Function(new SymbolPrimitive("b_sq"), [
597
- new Equation(
598
- [],
599
- new UnguardedBody(
600
- new Sequence([
601
- new Return(
602
- new ArithmeticBinaryOperation(
603
- "Multiply",
604
- new SymbolPrimitive("b"),
605
- new SymbolPrimitive("b")
606
- )
607
- ),
608
- ])
609
- ),
610
- new Return(
611
- new ArithmeticBinaryOperation(
612
- "Multiply",
613
- new SymbolPrimitive("b"),
614
- new SymbolPrimitive("b")
615
- )
616
- )
617
- ),
618
- ]),
619
- new Function(new SymbolPrimitive("sum_sq"), [
620
- new Equation(
621
- [],
622
- new UnguardedBody(
623
- new Sequence([
624
- new Return(
625
- new ArithmeticBinaryOperation(
626
- "Plus",
627
- new SymbolPrimitive("a_sq"),
628
- new SymbolPrimitive("b_sq")
629
- )
630
- ),
631
- ])
632
- ),
633
- new Return(
634
- new ArithmeticBinaryOperation(
635
- "Plus",
636
- new SymbolPrimitive("a_sq"),
637
- new SymbolPrimitive("b_sq")
638
- )
639
- )
640
- ),
641
- ]),
642
- ]),
643
- new Application(
644
- new SymbolPrimitive("sqrt"),
645
- new SymbolPrimitive("sum_sq")
646
- )
647
- )
648
- );
649
- assert.deepEqual(
650
- parser.parse(
651
- `hypotenuse a b = let a_sq = a * a; b_sq = b * b; sum_sq = a_sq + b_sq in sqrt sum_sq`
652
- ),
653
- [
654
- new Function(new SymbolPrimitive("hypotenuse"), [
655
- new Equation(
656
- [
657
- new VariablePattern(new SymbolPrimitive("a")),
658
- new VariablePattern(new SymbolPrimitive("b")),
659
- ],
660
- new UnguardedBody(new Sequence([returnExpression])),
661
- returnExpression
662
- ),
663
- ]),
664
- ]
665
- );
666
- });
667
- it("parses multi-line let...in clause correctly", () => {
668
- const returnExpression = new Return(
669
- new LetInExpression(
670
- new Sequence([
671
- new Function(new SymbolPrimitive("a_sq"), [
672
- new Equation(
673
- [],
674
- new UnguardedBody(
675
- new Sequence([
676
- new Return(
677
- new ArithmeticBinaryOperation(
678
- "Multiply",
679
- new SymbolPrimitive("a"),
680
- new SymbolPrimitive("a")
681
- )
682
- ),
683
- ])
684
- ),
685
- new Return(
686
- new ArithmeticBinaryOperation(
687
- "Multiply",
688
- new SymbolPrimitive("a"),
689
- new SymbolPrimitive("a")
690
- )
691
- )
692
- ),
693
- ]),
694
- new Function(new SymbolPrimitive("b_sq"), [
695
- new Equation(
696
- [],
697
- new UnguardedBody(
698
- new Sequence([
699
- new Return(
700
- new ArithmeticBinaryOperation(
701
- "Multiply",
702
- new SymbolPrimitive("b"),
703
- new SymbolPrimitive("b")
704
- )
705
- ),
706
- ])
707
- ),
708
- new Return(
709
- new ArithmeticBinaryOperation(
710
- "Multiply",
711
- new SymbolPrimitive("b"),
712
- new SymbolPrimitive("b")
713
- )
714
- )
715
- ),
716
- ]),
717
- new Function(new SymbolPrimitive("sum_sq"), [
718
- new Equation(
719
- [],
720
- new UnguardedBody(
721
- new Sequence([
722
- new Return(
723
- new ArithmeticBinaryOperation(
724
- "Plus",
725
- new SymbolPrimitive("a_sq"),
726
- new SymbolPrimitive("b_sq")
727
- )
728
- ),
729
- ])
730
- ),
731
- new Return(
732
- new ArithmeticBinaryOperation(
733
- "Plus",
734
- new SymbolPrimitive("a_sq"),
735
- new SymbolPrimitive("b_sq")
736
- )
737
- )
738
- ),
739
- ]),
740
- ]),
741
- new Application(
742
- new SymbolPrimitive("sqrt"),
743
- new SymbolPrimitive("sum_sq")
744
- )
745
- )
746
- );
747
- const ast = [
748
- new Function(new SymbolPrimitive("hypotenuse"), [
749
- new Equation(
750
- [
751
- new VariablePattern(new SymbolPrimitive("a")),
752
- new VariablePattern(new SymbolPrimitive("b")),
753
- ],
754
- new UnguardedBody(new Sequence([returnExpression])),
755
- returnExpression
756
- ),
757
- ]),
758
- ];
759
-
760
- assert.deepEqual(
761
- parser.parse(
762
- "hypotenuse a b =\n let a_sq = a * a; b_sq = b * b; sum_sq = a_sq + b_sq in sqrt sum_sq"
763
- ),
764
- ast
765
- );
766
- assert.deepEqual(
767
- parser.parse(
768
- "hypotenuse a b =\n let\n a_sq = a * a; b_sq = b * b; sum_sq = a_sq + b_sq in sqrt sum_sq"
769
- ),
770
- ast
771
- );
772
- assert.deepEqual(
773
- parser.parse(
774
- "hypotenuse a b =\n let\n a_sq = a * a\n b_sq = b * b\n sum_sq = a_sq + b_sq in sqrt sum_sq"
775
- ),
776
- ast
777
- );
778
- assert.deepEqual(
779
- parser.parse(
780
- "hypotenuse a b =\n let\n a_sq = a * a\n b_sq = b * b\n sum_sq = a_sq + b_sq\n in sqrt sum_sq"
781
- ),
782
- ast
783
- );
784
- assert.deepEqual(
785
- parser.parse(
786
- "hypotenuse a b =\n let\n a_sq = a * a\n b_sq = b * b\n sum_sq = a_sq + b_sq\n in\n sqrt sum_sq"
787
- ),
788
- ast
789
- );
790
- });
791
- it("parses partial infix operators", () => {
792
- const code = `doble numero = (*) numero 2`;
793
- const ast = parser.parse(code);
794
- const returnExpression = new Return(
795
- new Application(
796
- new Application(
797
- new SymbolPrimitive("*"),
798
- new SymbolPrimitive("numero")
799
- ),
800
- new NumberPrimitive(2)
801
- )
802
- );
803
- assert.deepEqual(ast, [
804
- new Function(new SymbolPrimitive("doble"), [
805
- new Equation(
806
- [new VariablePattern(new SymbolPrimitive("numero"))],
807
- new UnguardedBody(new Sequence([returnExpression])),
808
- returnExpression
809
- ),
810
- ]),
811
- ]);
812
- });
813
- it("parses range expression", () => {
814
- const code = `oneToTen = [1..10]`;
815
- const ast = parser.parse(code);
816
- const returnExpression = new Return(
817
- new RangeExpression(new NumberPrimitive(1), new NumberPrimitive(10))
818
- );
819
- assert.deepEqual(ast, [
820
- new Function(new SymbolPrimitive("oneToTen"), [
821
- new Equation(
822
- [],
823
- new UnguardedBody(new Sequence([returnExpression])),
824
- returnExpression
825
- ),
826
- ]),
827
- ]);
828
- });
829
- });
1
+ import { YukigoHaskellParser } from "../src/index.js";
2
+ import { assert } from "chai";
3
+ import { inspect } from "util";
4
+ import {
5
+ Application,
6
+ ArithmeticBinaryOperation,
7
+ Assert,
8
+ BooleanPrimitive,
9
+ CharPrimitive,
10
+ ComparisonOperation,
11
+ ConsPattern,
12
+ Constraint,
13
+ Equation,
14
+ Failure,
15
+ Function,
16
+ Guard,
17
+ GuardedExpression,
18
+ If,
19
+ LetInExpression,
20
+ ListBinaryOperation,
21
+ ListPrimitive,
22
+ ListType,
23
+ LiteralPattern,
24
+ LogicalBinaryOperation,
25
+ NumberPrimitive,
26
+ Otherwise,
27
+ ParameterizedType,
28
+ RangeExpression,
29
+ Return,
30
+ Sequence,
31
+ SimpleType,
32
+ StringPrimitive,
33
+ SymbolPrimitive,
34
+ Test,
35
+ TypeAlias,
36
+ TypeApplication,
37
+ TypeCast,
38
+ TypeSignature,
39
+ UnguardedBody,
40
+ VariablePattern,
41
+ } from "yukigo-ast";
42
+
43
+ const _deepEqual = assert.deepEqual;
44
+ assert.deepEqual = function (actual: any, expected: any, ...args: any[]) {
45
+ function stripLoc(obj: any): any {
46
+ if (Array.isArray(obj)) return obj.map(stripLoc);
47
+ if (obj && typeof obj === "object") {
48
+ const { loc, ...rest } = obj;
49
+ return Object.fromEntries(
50
+ Object.entries(rest).map(([k, v]) => [k, stripLoc(v)])
51
+ );
52
+ }
53
+ return obj;
54
+ }
55
+ return _deepEqual(stripLoc(actual), stripLoc(expected), ...args);
56
+ };
57
+
58
+ describe("Parser Tests", () => {
59
+ let parser: YukigoHaskellParser;
60
+ beforeEach(() => {
61
+ parser = new YukigoHaskellParser("", { typecheck: false, includePrims: false });
62
+ });
63
+ it("parses boolean expressions", () => {
64
+ const returnExpression = new Return(
65
+ new LogicalBinaryOperation(
66
+ "And",
67
+ new SymbolPrimitive("x"),
68
+ new SymbolPrimitive("y")
69
+ )
70
+ );
71
+ assert.deepEqual(parser.parse("f x y = x && y"), [
72
+ new Function(new SymbolPrimitive("f"), [
73
+ new Equation(
74
+ [
75
+ new VariablePattern(new SymbolPrimitive("x")),
76
+ new VariablePattern(new SymbolPrimitive("y")),
77
+ ],
78
+ new UnguardedBody(new Sequence([returnExpression])),
79
+ returnExpression
80
+ ),
81
+ ]),
82
+ ]);
83
+ });
84
+ it("parses literal character patterns", () => {
85
+ const returnExpression = new Return(new NumberPrimitive(1));
86
+ assert.deepEqual(parser.parse("f 'a' = 1"), [
87
+ new Function(new SymbolPrimitive("f"), [
88
+ new Equation(
89
+ [new LiteralPattern(new CharPrimitive("a"))],
90
+ new UnguardedBody(new Sequence([returnExpression])),
91
+ returnExpression
92
+ ),
93
+ ]),
94
+ ]);
95
+ });
96
+ it("parses literal string patterns", () => {
97
+ const returnExpression = new Return(new NumberPrimitive(1));
98
+ assert.deepEqual(parser.parse('f "hello world" = 1'), [
99
+ new Function(new SymbolPrimitive("f"), [
100
+ new Equation(
101
+ [new LiteralPattern(new StringPrimitive("hello world"))],
102
+ new UnguardedBody(new Sequence([returnExpression])),
103
+ returnExpression
104
+ ),
105
+ ]),
106
+ ]);
107
+ });
108
+ it("parses literal number patterns", () => {
109
+ const returnExpression = new Return(new NumberPrimitive(1));
110
+ assert.deepEqual(parser.parse("f 1 = 1"), [
111
+ new Function(new SymbolPrimitive("f"), [
112
+ new Equation(
113
+ [new LiteralPattern(new NumberPrimitive(1))],
114
+ new UnguardedBody(new Sequence([returnExpression])),
115
+ returnExpression
116
+ ),
117
+ ]),
118
+ ]);
119
+ });
120
+ it("parses simple cons pattern", () => {
121
+ const returnExpression = new Return(
122
+ new ArithmeticBinaryOperation(
123
+ "Plus",
124
+ new SymbolPrimitive("x"),
125
+ new Application(new SymbolPrimitive("f"), new SymbolPrimitive("xs"))
126
+ )
127
+ );
128
+ assert.deepEqual(parser.parse("f (x:xs) = x + f xs"), [
129
+ new Function(new SymbolPrimitive("f"), [
130
+ new Equation(
131
+ [
132
+ new ConsPattern(
133
+ new VariablePattern(new SymbolPrimitive("x")),
134
+ new VariablePattern(new SymbolPrimitive("xs"))
135
+ ),
136
+ ],
137
+ new UnguardedBody(new Sequence([returnExpression])),
138
+ returnExpression
139
+ ),
140
+ ]),
141
+ ]);
142
+ });
143
+ it("parses multiple cons pattern", () => {
144
+ const returnExpression = new Return(
145
+ new ArithmeticBinaryOperation(
146
+ "Plus",
147
+ new SymbolPrimitive("x"),
148
+ new Application(new SymbolPrimitive("f"), new SymbolPrimitive("xs"))
149
+ )
150
+ );
151
+ assert.deepEqual(parser.parse("f (x:y:xs) = x + f xs"), [
152
+ new Function(new SymbolPrimitive("f"), [
153
+ new Equation(
154
+ [
155
+ new ConsPattern(
156
+ new VariablePattern(new SymbolPrimitive("x")),
157
+ new ConsPattern(
158
+ new VariablePattern(new SymbolPrimitive("y")),
159
+ new VariablePattern(new SymbolPrimitive("xs"))
160
+ )
161
+ ),
162
+ ],
163
+ new UnguardedBody(new Sequence([returnExpression])),
164
+ returnExpression
165
+ ),
166
+ ]),
167
+ ]);
168
+ });
169
+ it("parses left infix partial application", () => {
170
+ const returnExpression = new Return(
171
+ new Application(new SymbolPrimitive("+"), new NumberPrimitive(1))
172
+ );
173
+ assert.deepEqual(parser.parse("f = (1+)"), [
174
+ new Function(new SymbolPrimitive("f"), [
175
+ new Equation(
176
+ [],
177
+ new UnguardedBody(new Sequence([returnExpression])),
178
+ returnExpression
179
+ ),
180
+ ]),
181
+ ]);
182
+ });
183
+ it("parses right infix partial application", () => {
184
+ const returnExpression = new Return(
185
+ new Application(
186
+ new Application(new SymbolPrimitive("flip"), new SymbolPrimitive("+")),
187
+ new NumberPrimitive(1)
188
+ )
189
+ );
190
+ assert.deepEqual(parser.parse("f = (+1)"), [
191
+ new Function(new SymbolPrimitive("f"), [
192
+ new Equation(
193
+ [],
194
+ new UnguardedBody(new Sequence([returnExpression])),
195
+ returnExpression
196
+ ),
197
+ ]),
198
+ ]);
199
+ });
200
+ it("parses type restrictions", () => {
201
+ assert.deepEqual(parser.parse("f :: Num a => [a] -> [a]"), [
202
+ new TypeSignature(
203
+ new SymbolPrimitive("f"),
204
+ new ParameterizedType(
205
+ [new ListType(new SimpleType("a", []), [])],
206
+ new ListType(new SimpleType("a", []), []),
207
+ [new Constraint("Num", [new SimpleType("a", [])])]
208
+ )
209
+ ),
210
+ ]);
211
+ });
212
+ it("parses multiple type restrictions", () => {
213
+ assert.deepEqual(parser.parse("f :: (Num a, Eq b) => [a] -> [b]"), [
214
+ new TypeSignature(
215
+ new SymbolPrimitive("f"),
216
+ new ParameterizedType(
217
+ [new ListType(new SimpleType("a", []), [])],
218
+ new ListType(new SimpleType("b", []), []),
219
+ [
220
+ new Constraint("Num", [new SimpleType("a", [])]),
221
+ new Constraint("Eq", [new SimpleType("b", [])]),
222
+ ]
223
+ )
224
+ ),
225
+ ]);
226
+ });
227
+ it("parses signatures without type restrictions", () => {
228
+ assert.deepEqual(parser.parse("f :: [a] -> [a]"), [
229
+ new TypeSignature(
230
+ new SymbolPrimitive("f"),
231
+ new ParameterizedType(
232
+ [new ListType(new SimpleType("a", []), [])],
233
+ new ListType(new SimpleType("a", []), []),
234
+ []
235
+ )
236
+ ),
237
+ ]);
238
+ });
239
+ it("parses type alias", () => {
240
+ assert.deepEqual(parser.parse("type String = [Char]"), [
241
+ new TypeAlias(
242
+ new SymbolPrimitive("String"),
243
+ [],
244
+ new ListType(new SimpleType("Char", []), [])
245
+ ),
246
+ ]);
247
+ });
248
+ it("parses type alias with arguments", () => {
249
+ assert.deepEqual(parser.parse("type List a = [a]"), [
250
+ new TypeAlias(
251
+ new SymbolPrimitive("List"),
252
+ ["a"],
253
+ new ListType(new SimpleType("a", []), [])
254
+ ),
255
+ ]);
256
+ });
257
+ it("parses inline type annotations", () => {
258
+ const returnExpression = new Return(
259
+ new TypeCast(new NumberPrimitive(1), new SimpleType("Int", []))
260
+ );
261
+ assert.deepEqual(parser.parse("x = 1 :: Int"), [
262
+ new Function(new SymbolPrimitive("x"), [
263
+ new Equation(
264
+ [],
265
+ new UnguardedBody(new Sequence([returnExpression])),
266
+ returnExpression
267
+ ),
268
+ ]),
269
+ ]);
270
+ });
271
+ it("parses inline type annotations with restrictions", () => {
272
+ const returnExpression = new Return(
273
+ new TypeCast(
274
+ new NumberPrimitive(1),
275
+ new ParameterizedType(
276
+ [],
277
+ new TypeApplication(new SimpleType("t", []), new SimpleType("a", [])),
278
+ [
279
+ new Constraint("Num", [new SimpleType("a", [])]),
280
+ new Constraint("Foldable", [new SimpleType("t", [])]),
281
+ ]
282
+ )
283
+ )
284
+ );
285
+ assert.deepEqual(parser.parse("x = 1 :: (Num a, Foldable t) => t a"), [
286
+ new Function(new SymbolPrimitive("x"), [
287
+ new Equation(
288
+ [],
289
+ new UnguardedBody(new Sequence([returnExpression])),
290
+ returnExpression
291
+ ),
292
+ ]),
293
+ ]);
294
+ });
295
+ it("parses chars and single char strings differently", () => {
296
+ const stringAst: any = parser.parse('x = "a"');
297
+ const charAst: any = parser.parse("x = 'a'");
298
+ const stringExpr = stringAst[0].equations[0].returnExpr.body;
299
+ const charExpr = charAst[0].equations[0].returnExpr.body;
300
+ assert.instanceOf(stringExpr, StringPrimitive);
301
+ assert.instanceOf(charExpr, CharPrimitive);
302
+ });
303
+ it("parses chars as YuChars", () => {
304
+ const returnExpression = new Return(new CharPrimitive("a"));
305
+ assert.deepEqual(parser.parse("x = 'a'"), [
306
+ new Function(new SymbolPrimitive("x"), [
307
+ new Equation(
308
+ [],
309
+ new UnguardedBody(new Sequence([returnExpression])),
310
+ returnExpression
311
+ ),
312
+ ]),
313
+ ]);
314
+ });
315
+ it("parses concat of list as ListBinaryOperation", () => {
316
+ const returnExpression = new Return(
317
+ new ListBinaryOperation(
318
+ "Concat",
319
+ new ListPrimitive([new NumberPrimitive(2)]),
320
+ new ListPrimitive([new NumberPrimitive(4)])
321
+ )
322
+ );
323
+ assert.deepEqual(parser.parse("f = [2] ++ [4]"), [
324
+ new Function(new SymbolPrimitive("f"), [
325
+ new Equation(
326
+ [],
327
+ new UnguardedBody(new Sequence([returnExpression])),
328
+ returnExpression
329
+ ),
330
+ ]),
331
+ ]);
332
+ });
333
+ it("parses inline guards correctly", () => {
334
+ const returnExpr = new Return(
335
+ new GuardedExpression([
336
+ new Guard(
337
+ new ComparisonOperation(
338
+ "GreaterThan",
339
+ new SymbolPrimitive("x"),
340
+ new NumberPrimitive(40)
341
+ ),
342
+ new NumberPrimitive(2)
343
+ ),
344
+ new Guard(new Otherwise(), new NumberPrimitive(1)),
345
+ ])
346
+ );
347
+ assert.deepEqual(parser.parse("f x | x > 40 = 2 | otherwise = 1"), [
348
+ new Function(new SymbolPrimitive("f"), [
349
+ new Equation(
350
+ [new VariablePattern(new SymbolPrimitive("x"))],
351
+ new UnguardedBody(new Sequence([returnExpr])),
352
+ returnExpr
353
+ ),
354
+ ]),
355
+ ]);
356
+ });
357
+ it("throws when guarded body is badly indented ", () => {
358
+ assert.throw(
359
+ () => parser.parse("f x | x < 1 = 10\n| x > 11 = 0"),
360
+ "Parser: Unexpected 'pipe' token '|' at line 2 col 1."
361
+ );
362
+ });
363
+ it("parses multi-line guards correctly", () => {
364
+ const returnExpr = new Return(
365
+ new GuardedExpression([
366
+ new Guard(
367
+ new ComparisonOperation(
368
+ "GreaterThan",
369
+ new SymbolPrimitive("x"),
370
+ new NumberPrimitive(40)
371
+ ),
372
+ new NumberPrimitive(2)
373
+ ),
374
+ new Guard(new Otherwise(), new NumberPrimitive(1)),
375
+ ])
376
+ );
377
+ const ast = [
378
+ new Function(new SymbolPrimitive("f"), [
379
+ new Equation(
380
+ [new VariablePattern(new SymbolPrimitive("x"))],
381
+ new UnguardedBody(new Sequence([returnExpr])),
382
+ returnExpr
383
+ ),
384
+ ]),
385
+ ];
386
+
387
+ assert.deepEqual(parser.parse("f x\n | x > 40 = 2\n | otherwise = 1"), ast);
388
+ assert.deepEqual(parser.parse("f x\n | x > 40 = 2 | otherwise = 1"), ast);
389
+ assert.deepEqual(parser.parse("f x | x > 40 = 2\n | otherwise = 1"), ast);
390
+ assert.deepEqual(
391
+ parser.parse("f x\n |\n x > 40 =\n 2 |\n otherwise\n =\n 1"),
392
+ ast
393
+ );
394
+ assert.deepEqual(
395
+ parser.parse("f x\n |\n x > 40 =\n 2 | otherwise = 1"),
396
+ ast
397
+ );
398
+ assert.deepEqual(
399
+ parser.parse("f x | x > 40 = 2 |\n otherwise\n =\n 1"),
400
+ ast
401
+ );
402
+ });
403
+ it("parses inline if then else correctly", () => {
404
+ const returnExpression = new Return(
405
+ new If(
406
+ new ComparisonOperation(
407
+ "LessThan",
408
+ new SymbolPrimitive("x"),
409
+ new NumberPrimitive(4)
410
+ ),
411
+ new NumberPrimitive(10),
412
+ new NumberPrimitive(20)
413
+ )
414
+ );
415
+ assert.deepEqual(parser.parse("f x = if x < 4 then 10 else 20"), [
416
+ new Function(new SymbolPrimitive("f"), [
417
+ new Equation(
418
+ [new VariablePattern(new SymbolPrimitive("x"))],
419
+ new UnguardedBody(new Sequence([returnExpression])),
420
+ returnExpression
421
+ ),
422
+ ]),
423
+ ]);
424
+ });
425
+ it("parses multi-line if then else correctly", () => {
426
+ const returnExpression = new Return(
427
+ new If(
428
+ new ComparisonOperation(
429
+ "LessThan",
430
+ new SymbolPrimitive("x"),
431
+ new NumberPrimitive(4)
432
+ ),
433
+ new NumberPrimitive(10),
434
+ new NumberPrimitive(20)
435
+ )
436
+ );
437
+ const ast = [
438
+ new Function(new SymbolPrimitive("f"), [
439
+ new Equation(
440
+ [new VariablePattern(new SymbolPrimitive("x"))],
441
+ new UnguardedBody(new Sequence([returnExpression])),
442
+ returnExpression
443
+ ),
444
+ ]),
445
+ ];
446
+
447
+ assert.deepEqual(parser.parse("f x =\n if x < 4 then 10 else 20"), ast);
448
+ assert.deepEqual(parser.parse("f x = if x < 4\n then 10\n else 20"), ast);
449
+ assert.deepEqual(parser.parse("f x = \n if x < 4\nthen 10\n else 20"), ast);
450
+ assert.deepEqual(parser.parse("f x = \n if x < 4\n then 10\nelse 20"), ast);
451
+ assert.deepEqual(
452
+ parser.parse("f x = \n if x < 4\n then 10\n else 20"),
453
+ ast
454
+ );
455
+ });
456
+ it("parses inline where clause correctly", () => {
457
+ const returnExpression = new Return(
458
+ new ArithmeticBinaryOperation(
459
+ "Multiply",
460
+ new SymbolPrimitive("pi"),
461
+ new SymbolPrimitive("radius_squared")
462
+ )
463
+ );
464
+ assert.deepEqual(
465
+ parser.parse(
466
+ "areaOfCircle radius = pi * radius_squared where pi = 3.14159; radius_squared = radius * radius"
467
+ ),
468
+ [
469
+ new Function(new SymbolPrimitive("areaOfCircle"), [
470
+ new Equation(
471
+ [new VariablePattern(new SymbolPrimitive("radius"))],
472
+ new UnguardedBody(
473
+ new Sequence([
474
+ new Function(new SymbolPrimitive("pi"), [
475
+ new Equation(
476
+ [],
477
+ new UnguardedBody(
478
+ new Sequence([new Return(new NumberPrimitive(3.14159))])
479
+ ),
480
+ new Return(new NumberPrimitive(3.14159))
481
+ ),
482
+ ]),
483
+ new Function(new SymbolPrimitive("radius_squared"), [
484
+ new Equation(
485
+ [],
486
+ new UnguardedBody(
487
+ new Sequence([
488
+ new Return(
489
+ new ArithmeticBinaryOperation(
490
+ "Multiply",
491
+ new SymbolPrimitive("radius"),
492
+ new SymbolPrimitive("radius")
493
+ )
494
+ ),
495
+ ])
496
+ ),
497
+ new Return(
498
+ new ArithmeticBinaryOperation(
499
+ "Multiply",
500
+ new SymbolPrimitive("radius"),
501
+ new SymbolPrimitive("radius")
502
+ )
503
+ )
504
+ ),
505
+ ]),
506
+ returnExpression,
507
+ ])
508
+ ),
509
+ returnExpression
510
+ ),
511
+ ]),
512
+ ]
513
+ );
514
+ });
515
+ it("parses multi-line where clause correctly", () => {
516
+ const returnExpression = new Return(
517
+ new ArithmeticBinaryOperation(
518
+ "Multiply",
519
+ new SymbolPrimitive("pi"),
520
+ new SymbolPrimitive("radius_squared")
521
+ )
522
+ );
523
+ const ast = [
524
+ new Function(new SymbolPrimitive("areaOfCircle"), [
525
+ new Equation(
526
+ [new VariablePattern(new SymbolPrimitive("radius"))],
527
+ new UnguardedBody(
528
+ new Sequence([
529
+ new Function(new SymbolPrimitive("pi"), [
530
+ new Equation(
531
+ [],
532
+ new UnguardedBody(
533
+ new Sequence([new Return(new NumberPrimitive(3.14159))])
534
+ ),
535
+ new Return(new NumberPrimitive(3.14159))
536
+ ),
537
+ ]),
538
+ new Function(new SymbolPrimitive("radius_squared"), [
539
+ new Equation(
540
+ [],
541
+ new UnguardedBody(
542
+ new Sequence([
543
+ new Return(
544
+ new ArithmeticBinaryOperation(
545
+ "Multiply",
546
+ new SymbolPrimitive("radius"),
547
+ new SymbolPrimitive("radius")
548
+ )
549
+ ),
550
+ ])
551
+ ),
552
+ new Return(
553
+ new ArithmeticBinaryOperation(
554
+ "Multiply",
555
+ new SymbolPrimitive("radius"),
556
+ new SymbolPrimitive("radius")
557
+ )
558
+ )
559
+ ),
560
+ ]),
561
+ returnExpression,
562
+ ])
563
+ ),
564
+ returnExpression
565
+ ),
566
+ ]),
567
+ ];
568
+
569
+ assert.deepEqual(
570
+ parser.parse(
571
+ `areaOfCircle radius = pi * radius_squared
572
+ where pi = 3.14159
573
+ radius_squared = radius * radius`
574
+ ),
575
+ ast
576
+ );
577
+ });
578
+ it("parses inline let...in expression correctly", () => {
579
+ const returnExpression = new Return(
580
+ new LetInExpression(
581
+ new Sequence([
582
+ new Function(new SymbolPrimitive("a_sq"), [
583
+ new Equation(
584
+ [],
585
+ new UnguardedBody(
586
+ new Sequence([
587
+ new Return(
588
+ new ArithmeticBinaryOperation(
589
+ "Multiply",
590
+ new SymbolPrimitive("a"),
591
+ new SymbolPrimitive("a")
592
+ )
593
+ ),
594
+ ])
595
+ ),
596
+ new Return(
597
+ new ArithmeticBinaryOperation(
598
+ "Multiply",
599
+ new SymbolPrimitive("a"),
600
+ new SymbolPrimitive("a")
601
+ )
602
+ )
603
+ ),
604
+ ]),
605
+ new Function(new SymbolPrimitive("b_sq"), [
606
+ new Equation(
607
+ [],
608
+ new UnguardedBody(
609
+ new Sequence([
610
+ new Return(
611
+ new ArithmeticBinaryOperation(
612
+ "Multiply",
613
+ new SymbolPrimitive("b"),
614
+ new SymbolPrimitive("b")
615
+ )
616
+ ),
617
+ ])
618
+ ),
619
+ new Return(
620
+ new ArithmeticBinaryOperation(
621
+ "Multiply",
622
+ new SymbolPrimitive("b"),
623
+ new SymbolPrimitive("b")
624
+ )
625
+ )
626
+ ),
627
+ ]),
628
+ new Function(new SymbolPrimitive("sum_sq"), [
629
+ new Equation(
630
+ [],
631
+ new UnguardedBody(
632
+ new Sequence([
633
+ new Return(
634
+ new ArithmeticBinaryOperation(
635
+ "Plus",
636
+ new SymbolPrimitive("a_sq"),
637
+ new SymbolPrimitive("b_sq")
638
+ )
639
+ ),
640
+ ])
641
+ ),
642
+ new Return(
643
+ new ArithmeticBinaryOperation(
644
+ "Plus",
645
+ new SymbolPrimitive("a_sq"),
646
+ new SymbolPrimitive("b_sq")
647
+ )
648
+ )
649
+ ),
650
+ ]),
651
+ ]),
652
+ new Application(
653
+ new SymbolPrimitive("sqrt"),
654
+ new SymbolPrimitive("sum_sq")
655
+ )
656
+ )
657
+ );
658
+ assert.deepEqual(
659
+ parser.parse(
660
+ `hypotenuse a b = let a_sq = a * a; b_sq = b * b; sum_sq = a_sq + b_sq in sqrt sum_sq`
661
+ ),
662
+ [
663
+ new Function(new SymbolPrimitive("hypotenuse"), [
664
+ new Equation(
665
+ [
666
+ new VariablePattern(new SymbolPrimitive("a")),
667
+ new VariablePattern(new SymbolPrimitive("b")),
668
+ ],
669
+ new UnguardedBody(new Sequence([returnExpression])),
670
+ returnExpression
671
+ ),
672
+ ]),
673
+ ]
674
+ );
675
+ });
676
+ it("parses multi-line let...in clause correctly", () => {
677
+ const returnExpression = new Return(
678
+ new LetInExpression(
679
+ new Sequence([
680
+ new Function(new SymbolPrimitive("a_sq"), [
681
+ new Equation(
682
+ [],
683
+ new UnguardedBody(
684
+ new Sequence([
685
+ new Return(
686
+ new ArithmeticBinaryOperation(
687
+ "Multiply",
688
+ new SymbolPrimitive("a"),
689
+ new SymbolPrimitive("a")
690
+ )
691
+ ),
692
+ ])
693
+ ),
694
+ new Return(
695
+ new ArithmeticBinaryOperation(
696
+ "Multiply",
697
+ new SymbolPrimitive("a"),
698
+ new SymbolPrimitive("a")
699
+ )
700
+ )
701
+ ),
702
+ ]),
703
+ new Function(new SymbolPrimitive("b_sq"), [
704
+ new Equation(
705
+ [],
706
+ new UnguardedBody(
707
+ new Sequence([
708
+ new Return(
709
+ new ArithmeticBinaryOperation(
710
+ "Multiply",
711
+ new SymbolPrimitive("b"),
712
+ new SymbolPrimitive("b")
713
+ )
714
+ ),
715
+ ])
716
+ ),
717
+ new Return(
718
+ new ArithmeticBinaryOperation(
719
+ "Multiply",
720
+ new SymbolPrimitive("b"),
721
+ new SymbolPrimitive("b")
722
+ )
723
+ )
724
+ ),
725
+ ]),
726
+ new Function(new SymbolPrimitive("sum_sq"), [
727
+ new Equation(
728
+ [],
729
+ new UnguardedBody(
730
+ new Sequence([
731
+ new Return(
732
+ new ArithmeticBinaryOperation(
733
+ "Plus",
734
+ new SymbolPrimitive("a_sq"),
735
+ new SymbolPrimitive("b_sq")
736
+ )
737
+ ),
738
+ ])
739
+ ),
740
+ new Return(
741
+ new ArithmeticBinaryOperation(
742
+ "Plus",
743
+ new SymbolPrimitive("a_sq"),
744
+ new SymbolPrimitive("b_sq")
745
+ )
746
+ )
747
+ ),
748
+ ]),
749
+ ]),
750
+ new Application(
751
+ new SymbolPrimitive("sqrt"),
752
+ new SymbolPrimitive("sum_sq")
753
+ )
754
+ )
755
+ );
756
+ const ast = [
757
+ new Function(new SymbolPrimitive("hypotenuse"), [
758
+ new Equation(
759
+ [
760
+ new VariablePattern(new SymbolPrimitive("a")),
761
+ new VariablePattern(new SymbolPrimitive("b")),
762
+ ],
763
+ new UnguardedBody(new Sequence([returnExpression])),
764
+ returnExpression
765
+ ),
766
+ ]),
767
+ ];
768
+
769
+ assert.deepEqual(
770
+ parser.parse(
771
+ "hypotenuse a b =\n let a_sq = a * a; b_sq = b * b; sum_sq = a_sq + b_sq in sqrt sum_sq"
772
+ ),
773
+ ast
774
+ );
775
+ assert.deepEqual(
776
+ parser.parse(
777
+ "hypotenuse a b =\n let\n a_sq = a * a; b_sq = b * b; sum_sq = a_sq + b_sq in sqrt sum_sq"
778
+ ),
779
+ ast
780
+ );
781
+ assert.deepEqual(
782
+ parser.parse(
783
+ "hypotenuse a b =\n let\n a_sq = a * a\n b_sq = b * b\n sum_sq = a_sq + b_sq in sqrt sum_sq"
784
+ ),
785
+ ast
786
+ );
787
+ assert.deepEqual(
788
+ parser.parse(
789
+ "hypotenuse a b =\n let\n a_sq = a * a\n b_sq = b * b\n sum_sq = a_sq + b_sq\n in sqrt sum_sq"
790
+ ),
791
+ ast
792
+ );
793
+ assert.deepEqual(
794
+ parser.parse(
795
+ "hypotenuse a b =\n let\n a_sq = a * a\n b_sq = b * b\n sum_sq = a_sq + b_sq\n in\n sqrt sum_sq"
796
+ ),
797
+ ast
798
+ );
799
+ });
800
+ it("parses partial infix operators", () => {
801
+ const code = `doble numero = (*) numero 2`;
802
+ const ast = parser.parse(code);
803
+ const returnExpression = new Return(
804
+ new Application(
805
+ new Application(
806
+ new SymbolPrimitive("*"),
807
+ new SymbolPrimitive("numero")
808
+ ),
809
+ new NumberPrimitive(2)
810
+ )
811
+ );
812
+ assert.deepEqual(ast, [
813
+ new Function(new SymbolPrimitive("doble"), [
814
+ new Equation(
815
+ [new VariablePattern(new SymbolPrimitive("numero"))],
816
+ new UnguardedBody(new Sequence([returnExpression])),
817
+ returnExpression
818
+ ),
819
+ ]),
820
+ ]);
821
+ });
822
+ it("parses range expression", () => {
823
+ const code = `oneToTen = [1..10]`;
824
+ const ast = parser.parse(code);
825
+ const returnExpression = new Return(
826
+ new RangeExpression(new NumberPrimitive(1), new NumberPrimitive(10))
827
+ );
828
+ assert.deepEqual(ast, [
829
+ new Function(new SymbolPrimitive("oneToTen"), [
830
+ new Equation(
831
+ [],
832
+ new UnguardedBody(new Sequence([returnExpression])),
833
+ returnExpression
834
+ ),
835
+ ]),
836
+ ]);
837
+ });
838
+ });