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.
- package/.mocharc.json +4 -0
- package/CHANGELOG.md +19 -0
- package/README.md +10 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +54 -0
- package/dist/index.js.map +1 -0
- package/dist/parser/grammar.d.ts +28 -0
- package/dist/parser/grammar.js +404 -0
- package/dist/parser/grammar.js.map +1 -0
- package/dist/parser/layoutPreprocessor.d.ts +1 -0
- package/dist/parser/layoutPreprocessor.js +22 -0
- package/dist/parser/layoutPreprocessor.js.map +1 -0
- package/dist/parser/lexer.d.ts +42 -0
- package/dist/parser/lexer.js +75 -0
- package/dist/parser/lexer.js.map +1 -0
- package/dist/parser/preprocessor.d.ts +28 -0
- package/dist/parser/preprocessor.js +453 -0
- package/dist/parser/preprocessor.js.map +1 -0
- package/dist/prelude.d.ts +1 -0
- package/dist/prelude.js +205 -0
- package/dist/prelude.js.map +1 -0
- package/dist/typechecker/DeclarationCollector.d.ts +15 -0
- package/dist/typechecker/DeclarationCollector.js +80 -0
- package/dist/typechecker/DeclarationCollector.js.map +1 -0
- package/dist/typechecker/TypeBuilder.d.ts +12 -0
- package/dist/typechecker/TypeBuilder.js +113 -0
- package/dist/typechecker/TypeBuilder.js.map +1 -0
- package/dist/typechecker/checker.d.ts +80 -0
- package/dist/typechecker/checker.js +269 -0
- package/dist/typechecker/checker.js.map +1 -0
- package/dist/typechecker/core.d.ts +14 -0
- package/dist/typechecker/core.js +142 -0
- package/dist/typechecker/core.js.map +1 -0
- package/dist/typechecker/inference.d.ts +69 -0
- package/dist/typechecker/inference.js +984 -0
- package/dist/typechecker/inference.js.map +1 -0
- package/dist/utils/helpers.d.ts +2 -0
- package/dist/utils/helpers.js +25 -0
- package/dist/utils/helpers.js.map +1 -0
- package/dist/utils/types.d.ts +6 -0
- package/dist/utils/types.js +53 -0
- package/dist/utils/types.js.map +1 -0
- package/package.json +36 -0
- package/src/index.ts +55 -0
- package/src/parser/grammar.ne +463 -0
- package/src/parser/grammar.ts +512 -0
- package/src/parser/layoutPreprocessor.ts +21 -0
- package/src/parser/lexer.ts +77 -0
- package/src/parser/preprocessor.ne +375 -0
- package/src/parser/preprocessor.ts +502 -0
- package/src/prelude.ts +206 -0
- package/src/typechecker/DeclarationCollector.ts +104 -0
- package/src/typechecker/TypeBuilder.ts +148 -0
- package/src/typechecker/checker.ts +395 -0
- package/src/typechecker/core.ts +162 -0
- package/src/typechecker/inference.ts +1327 -0
- package/src/utils/helpers.ts +29 -0
- package/src/utils/types.ts +56 -0
- package/tests/parser.spec.ts +823 -0
- package/tests/prelude.spec.ts +22 -0
- package/tests/preprocessor.spec.ts +69 -0
- package/tests/typechecker.spec.ts +310 -0
- 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
|
+
});
|