@graffiticode/parser 0.1.1 → 0.1.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graffiticode/parser",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -8,7 +8,7 @@
8
8
  "main": "./src/index.js",
9
9
  "devDependencies": {
10
10
  "eslint": "^8.38.0",
11
- "jest": "^29.5.0",
11
+ "jest": "^29.7.0",
12
12
  "supertest": "^6.3.3"
13
13
  },
14
14
  "scripts": {
@@ -18,8 +18,6 @@
18
18
  },
19
19
  "keywords": [],
20
20
  "author": "",
21
- "license": "ISC",
22
- "description": "",
23
- "dependencies": {
24
- }
21
+ "license": "MIT",
22
+ "description": ""
25
23
  }
package/src/ast.js CHANGED
@@ -20,23 +20,23 @@ function nodeToJSON(n) {
20
20
  let obj;
21
21
  if (typeof n === "object") {
22
22
  switch (n.tag) {
23
- case "num":
24
- obj = n.elts[0];
25
- break;
26
- case "str":
27
- obj = n.elts[0];
28
- break;
29
- default:
30
- obj = {};
31
- obj.tag = n.tag;
32
- obj.elts = [];
33
- if (n.coord) {
34
- obj.coord = n.coord;
35
- }
36
- for (let i = 0; i < n.elts.length; i++) {
37
- obj.elts[i] = nodeToJSON(n.elts[i]);
38
- }
39
- break;
23
+ case "num":
24
+ obj = n.elts[0];
25
+ break;
26
+ case "str":
27
+ obj = n.elts[0];
28
+ break;
29
+ default:
30
+ obj = {};
31
+ obj.tag = n.tag;
32
+ obj.elts = [];
33
+ if (n.coord) {
34
+ obj.coord = n.coord;
35
+ }
36
+ for (let i = 0; i < n.elts.length; i++) {
37
+ obj.elts[i] = nodeToJSON(n.elts[i]);
38
+ }
39
+ break;
40
40
  }
41
41
  } else if (typeof n === "string") {
42
42
  obj = n;
@@ -667,6 +667,10 @@ export class Ast {
667
667
  nids.push(word.nid || 0);
668
668
  }
669
669
  const pattern = env.pattern;
670
+ console.log(
671
+ "lambda()",
672
+ "pattern=" + JSON.stringify(pattern, null, 2),
673
+ );
670
674
  Ast.push(ctx, {
671
675
  tag: "LAMBDA",
672
676
  elts: [{
@@ -674,7 +678,9 @@ export class Ast {
674
678
  elts: names
675
679
  }, nid, {
676
680
  tag: "LIST",
677
- elts: pattern
681
+ elts: [
682
+ // pattern // FIXME
683
+ ],
678
684
  }, {
679
685
  tag: "LIST",
680
686
  elts: nids
package/src/env.js CHANGED
@@ -1,5 +1,9 @@
1
1
  // env
2
2
 
3
+ function topEnv(ctx) {
4
+ return ctx.state.env[ctx.state.env.length - 1];
5
+ }
6
+
3
7
  export class Env {
4
8
  static findWord(ctx, lexeme) {
5
9
  const env = ctx.state.env;
@@ -13,12 +17,12 @@ export class Env {
13
17
  }
14
18
 
15
19
  static addWord(ctx, lexeme, entry) {
16
- window.gcexports.topEnv(ctx).lexicon[lexeme] = entry;
20
+ topEnv(ctx).lexicon[lexeme] = entry;
17
21
  return null;
18
22
  }
19
23
 
20
24
  static addPattern(ctx, pattern) {
21
- window.gcexports.topEnv(ctx).pattern.push(pattern);
25
+ topEnv(ctx).pattern.push(pattern);
22
26
  }
23
27
 
24
28
  static enterEnv(ctx, name) {
@@ -31,7 +35,7 @@ export class Env {
31
35
  // return; // just stop recursing
32
36
  throw new Error("runaway recursion");
33
37
  }
34
- window.gcexports.topEnv(ctx).paramc = ctx.state.paramc;
38
+ topEnv(ctx).paramc = ctx.state.paramc;
35
39
  ctx.state.env.push({
36
40
  name,
37
41
  lexicon: {},
@@ -41,6 +45,6 @@ export class Env {
41
45
 
42
46
  static exitEnv(ctx) {
43
47
  ctx.state.env.pop();
44
- ctx.state.paramc = window.gcexports.topEnv(ctx).paramc;
48
+ ctx.state.paramc = topEnv(ctx).paramc;
45
49
  }
46
50
  }
package/src/folder.js CHANGED
@@ -203,11 +203,16 @@ export class Folder {
203
203
  const elts = [];
204
204
  for (let i = 0; i < word.length; i++) {
205
205
  const elt = Ast.pop(ctx);
206
- assertErr(ctx, elt, `Too few arguments for ${word.name}. Expected ${word.length}.`, node.coord);
206
+ assertErr(
207
+ ctx,
208
+ elt,
209
+ `Too few arguments for ${word.name}. Expected ${word.length}.`,
210
+ node.coord
211
+ );
207
212
  elts.push(elt);
208
213
  }
209
214
  if (word.nid) {
210
- Ast.fold(ctx, word, elts);
215
+ Ast.foldApply(ctx, word, elts);
211
216
  } else {
212
217
  Ast.push(ctx, {
213
218
  tag: word.name,
@@ -220,7 +225,7 @@ export class Folder {
220
225
  assert(false);
221
226
  }
222
227
  } else {
223
- // assert(false, "unresolved ident "+name);
228
+ // assertErr(ctx, false, "unresolved ident " + name, node.coord);
224
229
  Ast.push(ctx, node);
225
230
  }
226
231
  }
@@ -0,0 +1,42 @@
1
+ import { Folder } from "./folder.js";
2
+ import { Ast } from "./ast.js";
3
+
4
+ describe("folder", () => {
5
+ it("should fold 'add 2 3' to 5", () => {
6
+ // Arrange
7
+ const ctx = {
8
+ state: {
9
+ nodePool: ["unused"],
10
+ nodeStack: [],
11
+ nodeStackStack: [],
12
+ nodeMap: {},
13
+ env: [{ name: "global", lexicon: {} }]
14
+ }
15
+ };
16
+
17
+ // Create nodes for "add 2 3"
18
+ const n1Id = Ast.intern(ctx, {
19
+ tag: "NUM",
20
+ elts: ["2"]
21
+ });
22
+
23
+ const n2Id = Ast.intern(ctx, {
24
+ tag: "NUM",
25
+ elts: ["3"]
26
+ });
27
+
28
+ const addNodeId = Ast.intern(ctx, {
29
+ tag: "ADD",
30
+ elts: [n1Id, n2Id]
31
+ });
32
+
33
+ // Act
34
+ Folder.fold(ctx, addNodeId);
35
+ const resultId = ctx.state.nodeStack.pop();
36
+ const resultNode = ctx.state.nodePool[resultId];
37
+
38
+ // Assert
39
+ expect(resultNode.tag).toBe("NUM");
40
+ expect(resultNode.elts[0]).toBe("5");
41
+ });
42
+ });
package/src/parse.js CHANGED
@@ -239,7 +239,7 @@ export const parse = (function () {
239
239
  scanTime += (t1 - t0);
240
240
  ctx.state.nextToken = tk;
241
241
  const to = getPos(ctx);
242
- ctx.state.nextTokenCoord = {from, to};
242
+ ctx.state.nextTokenCoord = { from, to };
243
243
  } else {
244
244
  tk = nextToken;
245
245
  }
@@ -1166,7 +1166,7 @@ export const parse = (function () {
1166
1166
  c = nextCC();
1167
1167
  }
1168
1168
  }
1169
- const coord = {from: getPos(ctx) - lexeme.length, to: getPos(ctx)};
1169
+ const coord = { from: getPos(ctx) - lexeme.length, to: getPos(ctx) };
1170
1170
  assertErr(ctx, c !== 0, `Unterminated string: ${lexeme}`, coord);
1171
1171
  if (quoteChar === CC_BACKTICK && c === CC_DOLLAR &&
1172
1172
  peekCC() === CC_LEFTBRACE) {
@@ -0,0 +1,118 @@
1
+ import { buildParser } from "./parser.js";
2
+ import { mockPromiseValue } from "./testing/index.js";
3
+
4
+ describe("parse operations", () => {
5
+ const log = jest.fn();
6
+
7
+ it("should parse 'apply (<a b: add a b>) [10 20]'", async () => {
8
+ // Arrange
9
+ const cache = new Map();
10
+ const getLangAsset = mockPromiseValue("{}");
11
+ const main = {
12
+ parse: mockPromiseValue({
13
+ 1: {
14
+ elts: ["a"],
15
+ tag: "IDENT"
16
+ },
17
+ 2: {
18
+ elts: ["b"],
19
+ tag: "IDENT"
20
+ },
21
+ 3: {
22
+ elts: [1, 2],
23
+ tag: "ADD"
24
+ },
25
+ 4: {
26
+ elts: [3],
27
+ tag: "EXPRS"
28
+ },
29
+ 5: {
30
+ elts: [1, 2],
31
+ tag: "LIST"
32
+ },
33
+ 6: {
34
+ elts: [5, 4],
35
+ tag: "LAMBDA"
36
+ },
37
+ 7: {
38
+ elts: ["10"],
39
+ tag: "NUM"
40
+ },
41
+ 8: {
42
+ elts: ["20"],
43
+ tag: "NUM"
44
+ },
45
+ 9: {
46
+ elts: [7, 8],
47
+ tag: "LIST"
48
+ },
49
+ 10: {
50
+ elts: [6, 9],
51
+ tag: "APPLY"
52
+ },
53
+ 11: {
54
+ elts: [10],
55
+ tag: "EXPRS"
56
+ },
57
+ 12: {
58
+ elts: [11],
59
+ tag: "PROG"
60
+ },
61
+ root: 12
62
+ })
63
+ };
64
+ const parser = buildParser({ log, cache, getLangAsset, main });
65
+ const lang = "0002";
66
+ const src = "apply (<a b: add a b>) [10 20]..";
67
+
68
+ // Act
69
+ const result = await parser.parse(lang, src);
70
+
71
+ // Assert
72
+ expect(result).toHaveProperty("root", 12);
73
+ expect(result[10].tag).toBe("APPLY");
74
+ expect(result[10].elts).toEqual([6, 9]);
75
+ expect(result[6].tag).toBe("LAMBDA");
76
+ expect(result[9].tag).toBe("LIST");
77
+ });
78
+
79
+ it("should parse 'hello, world!'", async () => {
80
+ // Arrange
81
+ const cache = new Map();
82
+ const getLangAsset = mockPromiseValue("{}");
83
+ const main = {
84
+ parse: mockPromiseValue({
85
+ 1: {
86
+ elts: [
87
+ "hello, world"
88
+ ],
89
+ tag: "STR"
90
+ },
91
+ 2: {
92
+ elts: [
93
+ 1
94
+ ],
95
+ tag: "EXPRS"
96
+ },
97
+ 3: {
98
+ elts: [
99
+ 2
100
+ ],
101
+ tag: "PROG"
102
+ },
103
+ root: 3
104
+ })
105
+ };
106
+ const parser = buildParser({ log, cache, getLangAsset, main });
107
+ const lang = "0002";
108
+ const src = "'hello, world'..";
109
+
110
+ // Act
111
+ const result = await parser.parse(lang, src);
112
+
113
+ // Assert
114
+ expect(result).toHaveProperty("root", 3);
115
+ expect(result[1].tag).toBe("STR");
116
+ expect(result[1].elts).toEqual(["hello, world"]);
117
+ });
118
+ });
@@ -0,0 +1,119 @@
1
+ import { jest } from "@jest/globals";
2
+ import { parser, buildParser } from "./parser.js";
3
+ import { mockPromiseValue, mockPromiseError } from "./testing/index.js";
4
+
5
+ describe("parse operations", () => {
6
+ const log = jest.fn();
7
+
8
+ it("should parse 'apply (<a b: add a b>) [10 20]'", async () => {
9
+ // Arrange
10
+ const cache = new Map();
11
+ const getLangAsset = mockPromiseValue("{}");
12
+ const main = {
13
+ parse: mockPromiseValue({
14
+ 1: {
15
+ elts: ["a"],
16
+ tag: "IDENT"
17
+ },
18
+ 2: {
19
+ elts: ["b"],
20
+ tag: "IDENT"
21
+ },
22
+ 3: {
23
+ elts: [1, 2],
24
+ tag: "ADD"
25
+ },
26
+ 4: {
27
+ elts: [3],
28
+ tag: "EXPRS"
29
+ },
30
+ 5: {
31
+ elts: [1, 2],
32
+ tag: "LIST"
33
+ },
34
+ 6: {
35
+ elts: [5, 4],
36
+ tag: "LAMBDA"
37
+ },
38
+ 7: {
39
+ elts: ["10"],
40
+ tag: "NUM"
41
+ },
42
+ 8: {
43
+ elts: ["20"],
44
+ tag: "NUM"
45
+ },
46
+ 9: {
47
+ elts: [7, 8],
48
+ tag: "LIST"
49
+ },
50
+ 10: {
51
+ elts: [6, 9],
52
+ tag: "APPLY"
53
+ },
54
+ 11: {
55
+ elts: [10],
56
+ tag: "EXPRS"
57
+ },
58
+ 12: {
59
+ elts: [11],
60
+ tag: "PROG"
61
+ },
62
+ root: 12
63
+ })
64
+ };
65
+ const parser = buildParser({ log, cache, getLangAsset, main });
66
+ const lang = "0";
67
+ const src = "apply (<a b: add a b>) [10 20]..";
68
+
69
+ // Act
70
+ const result = await parser.parse(lang, src);
71
+
72
+ // Assert
73
+ expect(result).toHaveProperty("root", 12);
74
+ expect(result[10].tag).toBe("APPLY");
75
+ expect(result[10].elts).toEqual([6, 9]);
76
+ expect(result[6].tag).toBe("LAMBDA");
77
+ expect(result[9].tag).toBe("LIST");
78
+ });
79
+
80
+ it("should parse 'hello, world!'", async () => {
81
+ // Arrange
82
+ const cache = new Map();
83
+ const getLangAsset = mockPromiseValue("{}");
84
+ const main = {
85
+ parse: mockPromiseValue({
86
+ 1: {
87
+ elts: [
88
+ "hello, world"
89
+ ],
90
+ tag: "STR"
91
+ },
92
+ 2: {
93
+ elts: [
94
+ 1
95
+ ],
96
+ tag: "EXPRS"
97
+ },
98
+ 3: {
99
+ elts: [
100
+ 2
101
+ ],
102
+ tag: "PROG"
103
+ },
104
+ root: 3
105
+ })
106
+ };
107
+ const parser = buildParser({ log, cache, getLangAsset, main });
108
+ const lang = "0";
109
+ const src = "'hello, world'..";
110
+
111
+ // Act
112
+ const result = await parser.parse(lang, src);
113
+
114
+ // Assert
115
+ expect(result).toHaveProperty("root", 3);
116
+ expect(result[1].tag).toBe("STR");
117
+ expect(result[1].elts).toEqual(["hello, world"]);
118
+ });
119
+ });
@@ -1,5 +1,5 @@
1
1
  import { jest } from "@jest/globals";
2
- import { parser, buildParser } from "./parser.js";
2
+ import { buildParser } from "./parser.js";
3
3
  import { mockPromiseValue, mockPromiseError } from "./testing/index.js";
4
4
 
5
5
  describe("lang/parser", () => {
@@ -135,41 +135,17 @@ describe("lang/parser", () => {
135
135
  expect(vm.createContext).toHaveBeenCalled();
136
136
  expect(vm.runInContext).toHaveBeenCalledWith(rawLexicon, expect.anything());
137
137
  });
138
- it.skip("should parse 'hello, world!'", async () => {
139
- const lang = "0";
140
- const src = "'hello, world'..";
141
- const ast = {
142
- 1: {
143
- elts: [
144
- "hello, world"
145
- ],
146
- tag: "STR"
147
- },
148
- 2: {
149
- elts: [
150
- 1
151
- ],
152
- tag: "EXPRS"
153
- },
154
- 3: {
155
- elts: [
156
- 2
157
- ],
158
- tag: "PROG"
159
- },
160
- root: 3
161
- };
162
- await expect(parser.parse(lang, src)).resolves.toStrictEqual(ast);
163
- });
164
- it.skip("should parse error", async () => {
138
+ it("should parse error", async () => {
139
+ // Arrange
140
+ const cache = new Map();
141
+ const getLangAsset = mockPromiseValue("{}");
142
+ const err = new Error("End of program reached.");
143
+ const main = { parse: mockPromiseError(err) };
144
+ const parser = buildParser({ log, cache, getLangAsset, main });
165
145
  const lang = "0";
166
146
  const src = "'hello, world'";
167
- await expect(parser.parse(lang, src)).rejects.toThrow("End of program reached");
147
+
148
+ // Act & Assert
149
+ await expect(parser.parse(lang, src)).rejects.toBe(err);
168
150
  });
169
- // it("should parse error", async () => {
170
- // const lang = "0";
171
- // const src = "'hello, world..";
172
- // const ast = {};
173
- // await expect(parser.parse(lang, src)).rejects.toThrow("End of program reached");
174
- // });
175
151
  });