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,29 +1,34 @@
1
- import { inspect } from "util";
2
- import { AST } from "yukigo-ast";
3
- import { Function } from "yukigo-ast";
4
-
5
- export function groupFunctionDeclarations(ast: AST): AST {
6
- const groups: Record<string, Function[]> = {};
7
- const others: AST = [];
8
-
9
- for (const node of ast) {
10
- if (node instanceof Function) {
11
- const name = node.identifier.value;
12
- if (!groups[name]) {
13
- groups[name] = [];
14
- }
15
- groups[name].push(node);
16
- } else {
17
- others.push(node);
18
- }
19
- }
20
-
21
- // Merge equations for each function name
22
- const functionGroups: Function[] = Object.values(groups).map((functions) => {
23
- const identifier = functions[0].identifier;
24
- const allEquations = functions.flatMap((f) => f.equations);
25
- return new Function(identifier, allEquations, identifier.loc);
26
- });
27
-
28
- return [...others, ...functionGroups];
29
- }
1
+ import { AST } from "yukigo-ast";
2
+ import { Function } from "yukigo-ast";
3
+
4
+ export class UnexpectedNode extends Error {
5
+ constructor(nodeCons: string, context: string) {
6
+ super(`${nodeCons} not expected in ${context}.`)
7
+ }
8
+ }
9
+
10
+ export function groupFunctionDeclarations(ast: AST): AST {
11
+ const groups: Record<string, Function[]> = {};
12
+ const others: AST = [];
13
+
14
+ for (const node of ast) {
15
+ if (node instanceof Function) {
16
+ const name = node.identifier.value;
17
+ if (!groups[name]) {
18
+ groups[name] = [];
19
+ }
20
+ groups[name].push(node);
21
+ } else {
22
+ others.push(node);
23
+ }
24
+ }
25
+
26
+ // Merge equations for each function name
27
+ const functionGroups: Function[] = Object.values(groups).map((functions) => {
28
+ const identifier = functions[0].identifier;
29
+ const allEquations = functions.flatMap((f) => f.equations);
30
+ return new Function(identifier, allEquations, identifier.loc);
31
+ });
32
+
33
+ return [...others, ...functionGroups];
34
+ }
@@ -1,63 +1,75 @@
1
- import { YukigoPrimitive } from "yukigo-ast";
2
-
3
- export const keywords = [
4
- "type",
5
- "where",
6
- "in",
7
- "if",
8
- "else",
9
- "then",
10
- "data",
11
- "case",
12
- "error",
13
- "class",
14
- "do",
15
- "default",
16
- "deriving",
17
- "import",
18
- "infix",
19
- "infixl",
20
- "infixr",
21
- "instance",
22
- "let",
23
- "module",
24
- "newtype",
25
- "of",
26
- "qualified",
27
- "hiding",
28
- "foreign",
29
- "describe",
30
- "it",
31
- "shouldBe",
32
- "shouldNotBe",
33
- "shouldSatisfy",
34
- "shouldThrow"
35
- ];
36
-
37
- export const typeMappings: { [key: string]: YukigoPrimitive } = {
38
- Float: "YuNumber",
39
- Double: "YuNumber",
40
- Int: "YuNumber",
41
- Integer: "YuNumber",
42
- String: "YuString",
43
- Char: "YuChar",
44
- Boolean: "YuBoolean",
45
- Bool: "YuBoolean",
46
- };
47
-
48
- export const typeClasses: Map<string, string[]> = new Map([
49
- ["Bounded", ["YuChar", "YuNumber"]],
50
- ["Enum", ["YuChar", "YuNumber"]],
51
- ["Ord", ["YuNumber", "YuString", "YuChar"]],
52
- ["Eq", ["YuNumber", "YuString", "YuChar", "YuBoolean"]],
53
- ["Floating", ["YuNumber"]],
54
- ["Fractional", ["YuNumber"]],
55
- ["Integral", ["YuNumber"]],
56
- ["Num", ["YuNumber"]],
57
- ["Random", ["YuBoolean", "YuChar", "YuNumber"]],
58
- ["Read", ["YuList", "YuChar", "YuNumber"]],
59
- ["Real", ["YuNumber"]],
60
- ["RealFloat", ["YuNumber"]],
61
- ["RealFrac", ["YuNumber"]],
62
- ["Show", ["YuNumber", "YuString", "YuChar", "YuList"]],
63
- ]);
1
+ export const keywords = [
2
+ "type",
3
+ "where",
4
+ "in",
5
+ "if",
6
+ "else",
7
+ "then",
8
+ "data",
9
+ "case",
10
+ "error",
11
+ "class",
12
+ "do",
13
+ "default",
14
+ "deriving",
15
+ "import",
16
+ "infix",
17
+ "infixl",
18
+ "infixr",
19
+ "instance",
20
+ "let",
21
+ "module",
22
+ "newtype",
23
+ "of",
24
+ "qualified",
25
+ "hiding",
26
+ "foreign",
27
+ "describe",
28
+ "it",
29
+ "shouldBe",
30
+ "shouldNotBe",
31
+ "shouldSatisfy",
32
+ "shouldThrow"
33
+ ];
34
+
35
+ export enum YUTYPES { // primitivos
36
+ YuNumber = "YuNumber",
37
+ YuString = "YuString",
38
+ YuChar = "YuChar",
39
+ YuBoolean = "YuBoolean",
40
+ YuNil = "YuNil",
41
+ // constructores
42
+ Tuple = "Tuple",
43
+ List = "List",
44
+ Arrow = "->"
45
+ }
46
+
47
+ export const typeMappings: { [key: string]: YUTYPES } = {
48
+ Float: YUTYPES.YuNumber,
49
+ Double: YUTYPES.YuNumber,
50
+ Int: YUTYPES.YuNumber,
51
+ Integer: YUTYPES.YuNumber,
52
+ String: YUTYPES.YuString,
53
+ Char: YUTYPES.YuChar,
54
+ Boolean: YUTYPES.YuBoolean,
55
+ Bool: YUTYPES.YuBoolean,
56
+ };
57
+
58
+ export const typeClasses: Map<string, YUTYPES[]> = new Map([
59
+ ["Bounded", [YUTYPES.YuChar, YUTYPES.YuNumber]],
60
+ ["Enum", [YUTYPES.YuChar, YUTYPES.YuNumber]],
61
+ ["Ord", [YUTYPES.YuNumber, YUTYPES.YuString, YUTYPES.YuChar]],
62
+ ["Eq", [YUTYPES.YuNumber, YUTYPES.YuString, YUTYPES.YuChar, YUTYPES.YuBoolean]],
63
+ ["Floating", [YUTYPES.YuNumber]],
64
+ ["Fractional", [YUTYPES.YuNumber]],
65
+ ["Integral", [YUTYPES.YuNumber]],
66
+ ["Num", [YUTYPES.YuNumber]],
67
+ ["Random", [YUTYPES.YuBoolean, YUTYPES.YuChar, YUTYPES.YuNumber]],
68
+ ["Read", [YUTYPES.YuChar, YUTYPES.YuNumber]],
69
+
70
+ ["Real", [YUTYPES.YuNumber]],
71
+ ["RealFloat", [YUTYPES.YuNumber]],
72
+ ["RealFrac", [YUTYPES.YuNumber]],
73
+ ["Show", [YUTYPES.YuNumber, YUTYPES.YuString, YUTYPES.YuChar]],
74
+
75
+ ]);
@@ -1,92 +1,95 @@
1
- import { expect } from "chai";
2
- import { YukigoHaskellParser } from "../src/index.js";
3
- import {
4
- TestGroup,
5
- Test,
6
- Assert,
7
- Equality,
8
- Truth,
9
- Failure,
10
- Raise,
11
- NumberPrimitive,
12
- SymbolPrimitive,
13
- Application,
14
- StringPrimitive,
15
- BooleanPrimitive
16
- } from "yukigo-ast";
17
-
18
- describe("HSpec Parsing", () => {
19
- const parser = new YukigoHaskellParser("", { typecheck: true, includePrims: false });
20
-
21
- it("should parse describe and it blocks", () => {
22
- const code = `
23
- describe "Math" do
24
- it "adds 1 and 1" do
25
- (1 + 1) shouldBe 2
26
- `;
27
- const ast = parser.parse(code);
28
- expect(ast).to.have.lengthOf(1);
29
- expect(ast[0]).to.be.instanceOf(TestGroup);
30
-
31
- const group = ast[0] as TestGroup;
32
- expect(group.name).to.be.instanceOf(StringPrimitive);
33
- expect((group.name as StringPrimitive).value).to.equal("Math");
34
-
35
- expect(group.group.statements).to.have.lengthOf(1);
36
- expect(group.group.statements[0]).to.be.instanceOf(Test);
37
-
38
- const test = group.group.statements[0] as Test;
39
- expect(test.name).to.be.instanceOf(StringPrimitive);
40
- expect((test.name as StringPrimitive).value).to.equal("adds 1 and 1");
41
-
42
- expect(test.body.statements).to.have.lengthOf(1);
43
- expect(test.body.statements[0]).to.be.instanceOf(Assert);
44
-
45
- const assert = test.body.statements[0] as Assert;
46
- expect(assert.negated).to.be.instanceOf(BooleanPrimitive);
47
- expect((assert.negated as BooleanPrimitive).value).to.be.false;
48
- expect(assert.body).to.be.instanceOf(Equality);
49
-
50
- const equality = assert.body as Equality;
51
- expect(equality.expected).to.be.instanceOf(NumberPrimitive);
52
- expect((equality.expected as NumberPrimitive).value).to.equal(2);
53
- });
54
-
55
- it("should parse shouldNotBe", () => {
56
- const code = `it "negation" do 1 shouldNotBe 2`;
57
- const ast = parser.parse(code);
58
- const test = ast[0] as Test;
59
- const assert = test.body.statements[0] as Assert;
60
- expect((assert.negated as BooleanPrimitive).value).to.be.true;
61
- expect(assert.body).to.be.instanceOf(Equality);
62
- });
63
-
64
- it("should parse shouldSatisfy", () => {
65
- const code = `it "satisfy" do 5 shouldSatisfy odd`;
66
- const ast = parser.parse(code);
67
- const test = ast[0] as Test;
68
- const assert = test.body.statements[0] as Assert;
69
- expect(assert.body).to.be.instanceOf(Truth);
70
-
71
- const truth = assert.body as Truth;
72
- expect(truth.body).to.be.instanceOf(Application);
73
- const app = truth.body as Application;
74
- expect(app.functionExpr).to.be.instanceOf(SymbolPrimitive);
75
- expect((app.functionExpr as SymbolPrimitive).value).to.equal("odd");
76
- expect(app.parameter).to.be.instanceOf(NumberPrimitive);
77
- expect((app.parameter as NumberPrimitive).value).to.equal(5);
78
- });
79
-
80
- it("should parse shouldThrow", () => {
81
- const code = `it "throws" do error "fail" shouldThrow anyException`;
82
- const ast = parser.parse(code);
83
- const test = ast[0] as Test;
84
- const assert = test.body.statements[0] as Assert;
85
- expect(assert.body).to.be.instanceOf(Failure);
86
-
87
- const failure = assert.body as Failure;
88
- expect(failure.func).to.be.instanceOf(Raise);
89
- expect(failure.message).to.be.instanceOf(SymbolPrimitive);
90
- expect((failure.message as SymbolPrimitive).value).to.equal("anyException");
91
- });
92
- });
1
+ import { expect } from "chai";
2
+ import { YukigoHaskellParser } from "../src/index.js";
3
+ import {
4
+ TestGroup,
5
+ Test,
6
+ Assert,
7
+ Equality,
8
+ Truth,
9
+ Failure,
10
+ Raise,
11
+ NumberPrimitive,
12
+ SymbolPrimitive,
13
+ Application,
14
+ StringPrimitive,
15
+ BooleanPrimitive,
16
+ } from "yukigo-ast";
17
+
18
+ describe("HSpec Parsing", () => {
19
+ const parser = new YukigoHaskellParser("", {
20
+ typecheck: true,
21
+ includePrims: false,
22
+ });
23
+
24
+ it("should parse describe and it blocks", () => {
25
+ const code = `
26
+ describe "Math" do
27
+ it "adds 1 and 1" do
28
+ (1 + 1) shouldBe 2
29
+ `;
30
+ const ast = parser.parse(code);
31
+ expect(ast).to.have.lengthOf(1);
32
+ expect(ast[0]).to.be.instanceOf(TestGroup);
33
+
34
+ const group = ast[0] as TestGroup;
35
+ expect(group.name).to.be.instanceOf(StringPrimitive);
36
+ expect((group.name as StringPrimitive).value).to.equal("Math");
37
+
38
+ expect(group.group.statements).to.have.lengthOf(1);
39
+ expect(group.group.statements[0]).to.be.instanceOf(Test);
40
+
41
+ const test = group.group.statements[0] as Test;
42
+ expect(test.name).to.be.instanceOf(StringPrimitive);
43
+ expect((test.name as StringPrimitive).value).to.equal("adds 1 and 1");
44
+
45
+ expect(test.body.statements).to.have.lengthOf(1);
46
+ expect(test.body.statements[0]).to.be.instanceOf(Assert);
47
+
48
+ const assert = test.body.statements[0] as Assert;
49
+ expect(assert.negated).to.be.instanceOf(BooleanPrimitive);
50
+ expect((assert.negated as BooleanPrimitive).value).to.be.false;
51
+ expect(assert.body).to.be.instanceOf(Equality);
52
+
53
+ const equality = assert.body as Equality;
54
+ expect(equality.expected).to.be.instanceOf(NumberPrimitive);
55
+ expect((equality.expected as NumberPrimitive).value).to.equal(2);
56
+ });
57
+
58
+ it("should parse shouldNotBe", () => {
59
+ const code = `it "negation" do 1 shouldNotBe 2`;
60
+ const ast = parser.parse(code);
61
+ const test = ast[0] as Test;
62
+ const assert = test.body.statements[0] as Assert;
63
+ expect((assert.negated as BooleanPrimitive).value).to.be.true;
64
+ expect(assert.body).to.be.instanceOf(Equality);
65
+ });
66
+
67
+ it("should parse shouldSatisfy", () => {
68
+ const code = `it "satisfy" do 5 shouldSatisfy odd`;
69
+ const ast = parser.parse(code);
70
+ const test = ast[0] as Test;
71
+ const assert = test.body.statements[0] as Assert;
72
+ expect(assert.body).to.be.instanceOf(Truth);
73
+
74
+ const truth = assert.body as Truth;
75
+ expect(truth.body).to.be.instanceOf(Application);
76
+ const app = truth.body as Application;
77
+ expect(app.functionExpr).to.be.instanceOf(SymbolPrimitive);
78
+ expect((app.functionExpr as SymbolPrimitive).value).to.equal("odd");
79
+ expect(app.parameter).to.be.instanceOf(NumberPrimitive);
80
+ expect((app.parameter as NumberPrimitive).value).to.equal(5);
81
+ });
82
+
83
+ it("should parse shouldThrow", () => {
84
+ const code = `it "throws" do error "fail" shouldThrow anyException`;
85
+ const ast = parser.parse(code);
86
+ const test = ast[0] as Test;
87
+ const assert = test.body.statements[0] as Assert;
88
+ expect(assert.body).to.be.instanceOf(Failure);
89
+
90
+ const failure = assert.body as Failure;
91
+ expect(failure.func).to.be.instanceOf(Raise);
92
+ expect(failure.message).to.be.instanceOf(SymbolPrimitive);
93
+ expect((failure.message as SymbolPrimitive).value).to.equal("anyException");
94
+ });
95
+ });