@graffiticode/parser 0.1.0 → 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/dist/api/src/config/config.d.ts +3 -0
- package/dist/api/src/config/config.js +4 -0
- package/dist/api/src/config/config.js.map +1 -0
- package/dist/api/src/config/index.d.ts +1 -0
- package/dist/api/src/config/index.js +3 -0
- package/dist/api/src/config/index.js.map +1 -0
- package/dist/api/src/lang/base-url.d.ts +7 -0
- package/dist/api/src/lang/base-url.js +24 -0
- package/dist/api/src/lang/base-url.js.map +1 -0
- package/dist/api/src/lang/compile.d.ts +4 -0
- package/dist/api/src/lang/compile.js +6 -0
- package/dist/api/src/lang/compile.js.map +1 -0
- package/dist/api/src/lang/get-asset.d.ts +4 -0
- package/dist/api/src/lang/get-asset.js +9 -0
- package/dist/api/src/lang/get-asset.js.map +1 -0
- package/dist/api/src/lang/index.d.ts +4 -0
- package/dist/api/src/lang/index.js +22 -0
- package/dist/api/src/lang/index.js.map +1 -0
- package/dist/api/src/lang/ping-lang.d.ts +5 -0
- package/dist/api/src/lang/ping-lang.js +31 -0
- package/dist/api/src/lang/ping-lang.js.map +1 -0
- package/dist/api/src/util.d.ts +23 -0
- package/dist/api/src/util.js +187 -0
- package/dist/api/src/util.js.map +1 -0
- package/dist/parser/src/ast.d.ts +58 -0
- package/dist/parser/src/ast.js +683 -0
- package/dist/parser/src/ast.js.map +1 -0
- package/dist/parser/src/env.d.ts +8 -0
- package/dist/parser/src/env.js +38 -0
- package/dist/parser/src/env.js.map +1 -0
- package/dist/parser/src/fold.d.ts +4 -0
- package/dist/parser/src/fold.js +217 -0
- package/dist/parser/src/fold.js.map +1 -0
- package/dist/parser/src/folder.d.ts +30 -0
- package/dist/parser/src/folder.js +231 -0
- package/dist/parser/src/folder.js.map +1 -0
- package/dist/parser/src/index.d.ts +5 -0
- package/dist/parser/src/index.js +6 -0
- package/dist/parser/src/index.js.map +1 -0
- package/dist/parser/src/parse.d.ts +56 -0
- package/dist/parser/src/parse.js +902 -0
- package/dist/parser/src/parse.js.map +1 -0
- package/dist/parser/src/parser.d.ts +17 -0
- package/dist/parser/src/parser.js +89 -0
- package/dist/parser/src/parser.js.map +1 -0
- package/dist/parser/src/parserForTests.d.ts +3 -0
- package/dist/parser/src/parserForTests.js +4 -0
- package/dist/parser/src/parserForTests.js.map +1 -0
- package/dist/parser/src/stringstream.d.ts +10 -0
- package/dist/parser/src/stringstream.js +21 -0
- package/dist/parser/src/stringstream.js.map +1 -0
- package/dist/parser/src/testing/index.d.ts +2 -0
- package/dist/parser/src/testing/index.js +17 -0
- package/dist/parser/src/testing/index.js.map +1 -0
- package/dist/parser/src/types.d.ts +44 -0
- package/dist/parser/src/types.js +2 -0
- package/dist/parser/src/types.js.map +1 -0
- package/package.json +5 -9
- package/src/ast.js +730 -0
- package/src/env.js +50 -0
- package/src/folder.js +249 -0
- package/src/folder.spec.js +42 -0
- package/src/parse.js +35 -935
- package/src/parse.spec.js +118 -0
- package/src/parse.spec.js~ +119 -0
- package/src/parse.ts~ +1037 -0
- package/src/parser.spec.js +11 -35
- package/src/{parser.js~ → parser.ts~} +25 -7
- package/src/stringstream.js +22 -0
- package/src/fold.js +0 -235
- package/src/parser.spec.js~ +0 -175
package/src/env.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// env
|
|
2
|
+
|
|
3
|
+
function topEnv(ctx) {
|
|
4
|
+
return ctx.state.env[ctx.state.env.length - 1];
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export class Env {
|
|
8
|
+
static findWord(ctx, lexeme) {
|
|
9
|
+
const env = ctx.state.env;
|
|
10
|
+
for (let i = env.length - 1; i >= 0; i--) {
|
|
11
|
+
const word = env[i].lexicon[lexeme];
|
|
12
|
+
if (word) {
|
|
13
|
+
return word;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static addWord(ctx, lexeme, entry) {
|
|
20
|
+
topEnv(ctx).lexicon[lexeme] = entry;
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static addPattern(ctx, pattern) {
|
|
25
|
+
topEnv(ctx).pattern.push(pattern);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static enterEnv(ctx, name) {
|
|
29
|
+
// recursion guard
|
|
30
|
+
if (ctx.state.env.length > 380) {
|
|
31
|
+
console.trace(
|
|
32
|
+
"enterEnv()",
|
|
33
|
+
"name=" + name,
|
|
34
|
+
);
|
|
35
|
+
// return; // just stop recursing
|
|
36
|
+
throw new Error("runaway recursion");
|
|
37
|
+
}
|
|
38
|
+
topEnv(ctx).paramc = ctx.state.paramc;
|
|
39
|
+
ctx.state.env.push({
|
|
40
|
+
name,
|
|
41
|
+
lexicon: {},
|
|
42
|
+
pattern: []
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static exitEnv(ctx) {
|
|
47
|
+
ctx.state.env.pop();
|
|
48
|
+
ctx.state.paramc = topEnv(ctx).paramc;
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/folder.js
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
import { Ast } from "./ast.js";
|
|
3
|
+
import { Env } from "./env.js";
|
|
4
|
+
import { assertErr } from "./parse.js";
|
|
5
|
+
|
|
6
|
+
// Helper to check if a value is a function
|
|
7
|
+
function isFunction(v) {
|
|
8
|
+
return v instanceof Function;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class Folder {
|
|
12
|
+
static #nodePool;
|
|
13
|
+
static #ctx;
|
|
14
|
+
static #table;
|
|
15
|
+
|
|
16
|
+
static {
|
|
17
|
+
Folder.#table = {
|
|
18
|
+
PROG: Folder.program,
|
|
19
|
+
EXPRS: Folder.exprs,
|
|
20
|
+
PAREN: Folder.parenExpr,
|
|
21
|
+
IDENT: Folder.ident,
|
|
22
|
+
BOOL: Folder.bool,
|
|
23
|
+
NUM: Folder.num,
|
|
24
|
+
STR: Folder.str,
|
|
25
|
+
PARENS: Folder.unaryExpr,
|
|
26
|
+
APPLY: Folder.apply,
|
|
27
|
+
LAMBDA: Folder.lambda,
|
|
28
|
+
// "MUL": mul,
|
|
29
|
+
// "DIV": div,
|
|
30
|
+
// "SUB": sub,
|
|
31
|
+
ADD: Folder.add,
|
|
32
|
+
POW: Folder.pow,
|
|
33
|
+
MOD: Folder.mod,
|
|
34
|
+
CONCAT: Folder.concat,
|
|
35
|
+
// "OR": orelse,
|
|
36
|
+
// "AND": andalso,
|
|
37
|
+
// "NE": ne,
|
|
38
|
+
// "EQ": eq,
|
|
39
|
+
// "LT": lt,
|
|
40
|
+
// "GT": gt,
|
|
41
|
+
// "LE": le,
|
|
42
|
+
// "GE": ge,
|
|
43
|
+
NEG: Folder.neg,
|
|
44
|
+
LIST: Folder.list
|
|
45
|
+
// "CASE": caseExpr,
|
|
46
|
+
// "OF": ofClause,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static fold(cx, nid) {
|
|
51
|
+
Folder.#ctx = cx;
|
|
52
|
+
Folder.#nodePool = cx.state.nodePool;
|
|
53
|
+
Folder.#visit(nid);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static #visit(nid) {
|
|
57
|
+
const node = Folder.#nodePool[nid];
|
|
58
|
+
if (!node) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (node.tag === undefined) {
|
|
63
|
+
return []; // clean up stubs;
|
|
64
|
+
} else if (isFunction(Folder.#table[node.tag])) {
|
|
65
|
+
// Have a primitive operation so apply it to construct a new node.
|
|
66
|
+
const ret = Folder.#table[node.tag](node);
|
|
67
|
+
return ret;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
Folder.#expr(node);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// BEGIN VISITOR METHODS
|
|
74
|
+
|
|
75
|
+
static program(node) {
|
|
76
|
+
Folder.#visit(node.elts[0]);
|
|
77
|
+
Ast.program(Folder.#ctx);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
static #pushNodeStack() {
|
|
81
|
+
Folder.#ctx.state.nodeStackStack.push(Folder.#ctx.state.nodeStack);
|
|
82
|
+
Folder.#ctx.state.nodeStack = [];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
static #popNodeStack() {
|
|
86
|
+
const stack = Folder.#ctx.state.nodeStack;
|
|
87
|
+
Folder.#ctx.state.nodeStack = Folder.#ctx.state.nodeStackStack.pop().concat(stack);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
static list(node) {
|
|
91
|
+
Folder.#pushNodeStack();
|
|
92
|
+
for (let i = node.elts.length - 1; i >= 0; i--) {
|
|
93
|
+
Folder.#visit(node.elts[i]); // Keep original order.
|
|
94
|
+
}
|
|
95
|
+
Ast.list(Folder.#ctx, Folder.#ctx.state.nodeStack.length, null, true);
|
|
96
|
+
Folder.#popNodeStack();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
static exprs(node) {
|
|
100
|
+
// Fold exprs in reverse order to get precedence right.
|
|
101
|
+
for (let i = node.elts.length - 1; i >= 0; i--) {
|
|
102
|
+
Folder.#visit(node.elts[i]); // Keep original order.
|
|
103
|
+
}
|
|
104
|
+
Folder.#ctx.state.exprc = node.elts.length;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
static lambda(node) {
|
|
108
|
+
// Fold initializers and apply args.
|
|
109
|
+
const inits = Ast.node(Folder.#ctx, node.elts[3]).elts;
|
|
110
|
+
inits.forEach((init, i) => {
|
|
111
|
+
if (init) {
|
|
112
|
+
// If we have an init then fold it and replace in inits list.
|
|
113
|
+
Folder.fold(Folder.#ctx, Ast.intern(Folder.#ctx, init));
|
|
114
|
+
inits[i] = Ast.pop(Folder.#ctx);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
// FIXME don't patch old node. construct a new one.
|
|
118
|
+
node.elts[3] = Ast.intern(Folder.#ctx, { tag: "LIST", elts: inits });
|
|
119
|
+
const fnId = Ast.intern(Folder.#ctx, node);
|
|
120
|
+
const argc = Folder.#ctx.state.nodeStack.length;
|
|
121
|
+
Ast.apply(Folder.#ctx, fnId, argc);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
static apply(node) {
|
|
125
|
+
for (let i = node.elts.length - 1; i >= 0; i--) {
|
|
126
|
+
Folder.#visit(node.elts[i]);
|
|
127
|
+
}
|
|
128
|
+
Ast.applyLate(Folder.#ctx, node.elts.length);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
static #expr(node) {
|
|
132
|
+
// Construct an expression node for the compiler.
|
|
133
|
+
Ast.name(Folder.#ctx, node.tag, node.coord);
|
|
134
|
+
for (let i = node.elts.length - 1; i >= 0; i--) {
|
|
135
|
+
Folder.#visit(node.elts[i]);
|
|
136
|
+
}
|
|
137
|
+
Ast.expr(Folder.#ctx, node.elts.length, node.coord);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
static neg(node) {
|
|
141
|
+
Folder.#visit(node.elts[0]);
|
|
142
|
+
Ast.neg(Folder.#ctx);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
static parenExpr(node) {
|
|
146
|
+
Folder.#pushNodeStack();
|
|
147
|
+
Folder.#visit(node.elts[0]);
|
|
148
|
+
Ast.parenExpr(Folder.#ctx);
|
|
149
|
+
Folder.#popNodeStack();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
static unaryExpr(node) {
|
|
153
|
+
Folder.#visit(node.elts[0]);
|
|
154
|
+
Ast.unaryExpr(Folder.#ctx, node.tag);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
static add(node) {
|
|
158
|
+
Folder.#visit(node.elts[0]);
|
|
159
|
+
Folder.#visit(node.elts[1]);
|
|
160
|
+
Ast.add(Folder.#ctx);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
static pow(node) {
|
|
164
|
+
Folder.#visit(node.elts[0]);
|
|
165
|
+
Folder.#visit(node.elts[1]);
|
|
166
|
+
Ast.pow(Folder.#ctx);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
static concat(node) {
|
|
170
|
+
Folder.#visit(node.elts[0]);
|
|
171
|
+
Ast.concat(Folder.#ctx);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
static mod(node) {
|
|
175
|
+
Folder.#visit(node.elts[0]);
|
|
176
|
+
Folder.#visit(node.elts[1]);
|
|
177
|
+
Ast.mod(Folder.#ctx);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
static ident(node) {
|
|
181
|
+
const ctx = Folder.#ctx;
|
|
182
|
+
const name = node.elts[0];
|
|
183
|
+
const word = Env.findWord(ctx, name);
|
|
184
|
+
if (word) {
|
|
185
|
+
if (word.cls === "val") {
|
|
186
|
+
if (word.val) {
|
|
187
|
+
Ast.string(ctx, word.val); // strip quotes;
|
|
188
|
+
} else if (word.nid) {
|
|
189
|
+
let wrd;
|
|
190
|
+
if ((wrd = Ast.node(ctx, word.nid)).tag === "LAMBDA") {
|
|
191
|
+
const argc = wrd.elts[0].elts.length;
|
|
192
|
+
Ast.apply(ctx, word.nid, argc);
|
|
193
|
+
} else {
|
|
194
|
+
Ast.push(ctx, word.nid);
|
|
195
|
+
}
|
|
196
|
+
} else if (word.name) {
|
|
197
|
+
Ast.push(ctx, node);
|
|
198
|
+
} else {
|
|
199
|
+
// push the original node to be resolved later.
|
|
200
|
+
Ast.push(ctx, node);
|
|
201
|
+
}
|
|
202
|
+
} else if (word.cls === "function") {
|
|
203
|
+
const elts = [];
|
|
204
|
+
for (let i = 0; i < word.length; i++) {
|
|
205
|
+
const elt = Ast.pop(ctx);
|
|
206
|
+
assertErr(
|
|
207
|
+
ctx,
|
|
208
|
+
elt,
|
|
209
|
+
`Too few arguments for ${word.name}. Expected ${word.length}.`,
|
|
210
|
+
node.coord
|
|
211
|
+
);
|
|
212
|
+
elts.push(elt);
|
|
213
|
+
}
|
|
214
|
+
if (word.nid) {
|
|
215
|
+
Ast.foldApply(ctx, word, elts);
|
|
216
|
+
} else {
|
|
217
|
+
Ast.push(ctx, {
|
|
218
|
+
tag: word.name,
|
|
219
|
+
elts,
|
|
220
|
+
coord: node.coord,
|
|
221
|
+
});
|
|
222
|
+
Folder.fold(ctx, Ast.pop(ctx));
|
|
223
|
+
}
|
|
224
|
+
} else {
|
|
225
|
+
assert(false);
|
|
226
|
+
}
|
|
227
|
+
} else {
|
|
228
|
+
// assertErr(ctx, false, "unresolved ident " + name, node.coord);
|
|
229
|
+
Ast.push(ctx, node);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
static num(node) {
|
|
234
|
+
Ast.number(Folder.#ctx, node.elts[0], node.coord);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
static str(node) {
|
|
238
|
+
Ast.string(Folder.#ctx, node.elts[0]);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
static bool(node) {
|
|
242
|
+
Ast.bool(Folder.#ctx, node.elts[0]);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Keep backward compatibility export
|
|
247
|
+
export const folder = {
|
|
248
|
+
fold: Folder.fold
|
|
249
|
+
};
|
|
@@ -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
|
+
});
|