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