@weborigami/language 0.3.3 → 0.3.4-jse.4
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/main.js +3 -1
- package/package.json +3 -3
- package/src/compiler/compile.js +13 -3
- package/src/compiler/isOrigamiFrontMatter.js +4 -3
- package/src/compiler/optimize.js +273 -106
- package/src/compiler/origami.pegjs +286 -169
- package/src/compiler/parse.js +2069 -1275
- package/src/compiler/parserHelpers.js +155 -146
- package/src/runtime/HandleExtensionsTransform.js +10 -1
- package/src/runtime/evaluate.js +28 -35
- package/src/runtime/expressionObject.js +17 -11
- package/src/runtime/getHandlers.js +10 -0
- package/src/runtime/handlers.js +18 -54
- package/src/runtime/jsGlobals.js +106 -0
- package/src/runtime/mergeTrees.js +0 -5
- package/src/runtime/ops.js +92 -161
- package/src/runtime/symbols.js +1 -0
- package/src/runtime/{taggedTemplateIndent.js → templateIndent.js} +2 -2
- package/test/compiler/codeHelpers.js +3 -1
- package/test/compiler/compile.test.js +60 -30
- package/test/compiler/optimize.test.js +263 -24
- package/test/compiler/parse.test.js +895 -521
- package/test/runtime/evaluate.test.js +4 -20
- package/test/runtime/expressionObject.test.js +6 -5
- package/test/runtime/handlers.test.js +19 -10
- package/test/runtime/mergeTrees.test.js +0 -5
- package/test/runtime/ops.test.js +103 -82
- package/test/runtime/taggedTemplateIndent.test.js +1 -1
|
@@ -1,42 +1,281 @@
|
|
|
1
|
+
import { ObjectTree } from "@weborigami/async-tree";
|
|
1
2
|
import { describe, test } from "node:test";
|
|
2
3
|
import * as compile from "../../src/compiler/compile.js";
|
|
3
4
|
import optimize from "../../src/compiler/optimize.js";
|
|
5
|
+
import { markers } from "../../src/compiler/parserHelpers.js";
|
|
4
6
|
import { ops } from "../../src/runtime/internal.js";
|
|
5
7
|
import { assertCodeEqual, createCode } from "./codeHelpers.js";
|
|
6
8
|
|
|
7
9
|
describe("optimize", () => {
|
|
8
|
-
test("
|
|
9
|
-
const expression = `
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
d: name // local, should be left as ops.scope
|
|
15
|
-
}
|
|
16
|
-
`;
|
|
17
|
-
const fn = compile.expression(expression);
|
|
18
|
-
const code = fn.code;
|
|
19
|
-
assertCodeEqual(code, [
|
|
10
|
+
test("change local references to context references", () => {
|
|
11
|
+
const expression = `(name) => {
|
|
12
|
+
a: name,
|
|
13
|
+
b: a
|
|
14
|
+
}`;
|
|
15
|
+
const expected = [
|
|
20
16
|
ops.lambda,
|
|
21
17
|
[[ops.literal, "name"]],
|
|
22
18
|
[
|
|
23
19
|
ops.object,
|
|
24
|
-
[
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
[
|
|
21
|
+
"a",
|
|
22
|
+
[
|
|
23
|
+
[ops.context, 1],
|
|
24
|
+
[ops.literal, "name"],
|
|
25
|
+
],
|
|
26
|
+
],
|
|
27
|
+
["b", [[ops.context], [ops.literal, "a"]]],
|
|
28
28
|
],
|
|
29
|
-
]
|
|
29
|
+
];
|
|
30
|
+
assertCompile(expression, expected);
|
|
30
31
|
});
|
|
31
32
|
|
|
32
|
-
test("
|
|
33
|
-
// Compilation of `
|
|
33
|
+
test("resolve deeper context references", () => {
|
|
34
|
+
// Compilation of `{ a: 1, more: { a } }`
|
|
34
35
|
const code = createCode([
|
|
35
|
-
ops.
|
|
36
|
-
[ops.
|
|
37
|
-
[
|
|
36
|
+
ops.object,
|
|
37
|
+
["a", [ops.literal, 1]],
|
|
38
|
+
[
|
|
39
|
+
"more",
|
|
40
|
+
[ops.object, ["a", [markers.traverse, [markers.reference, "a"]]]],
|
|
41
|
+
],
|
|
38
42
|
]);
|
|
39
|
-
const
|
|
40
|
-
|
|
43
|
+
const expected = [
|
|
44
|
+
ops.object,
|
|
45
|
+
["a", 1],
|
|
46
|
+
[
|
|
47
|
+
"more",
|
|
48
|
+
[
|
|
49
|
+
ops.object,
|
|
50
|
+
[
|
|
51
|
+
"a",
|
|
52
|
+
[
|
|
53
|
+
[ops.context, 1],
|
|
54
|
+
[ops.literal, "a"],
|
|
55
|
+
],
|
|
56
|
+
],
|
|
57
|
+
],
|
|
58
|
+
],
|
|
59
|
+
];
|
|
60
|
+
assertCodeEqual(optimize(code), expected);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("when defining a property, avoid recursive references", () => {
|
|
64
|
+
const expression = `{
|
|
65
|
+
name: "Alice",
|
|
66
|
+
user: {
|
|
67
|
+
name: name
|
|
68
|
+
}
|
|
69
|
+
}`;
|
|
70
|
+
const expected = [
|
|
71
|
+
ops.object,
|
|
72
|
+
["name", "Alice"],
|
|
73
|
+
[
|
|
74
|
+
"user",
|
|
75
|
+
[
|
|
76
|
+
ops.object,
|
|
77
|
+
[
|
|
78
|
+
"name",
|
|
79
|
+
[
|
|
80
|
+
[ops.context, 1],
|
|
81
|
+
[ops.literal, "name"],
|
|
82
|
+
],
|
|
83
|
+
],
|
|
84
|
+
],
|
|
85
|
+
],
|
|
86
|
+
];
|
|
87
|
+
assertCompile(expression, expected);
|
|
88
|
+
assertCompile(expression, expected, "jse");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe("resolve reference", () => {
|
|
92
|
+
test("external reference", () => {
|
|
93
|
+
// Compilation of `folder` where folder isn't a variable
|
|
94
|
+
const code = createCode([
|
|
95
|
+
markers.traverse,
|
|
96
|
+
[markers.reference, "folder"],
|
|
97
|
+
]);
|
|
98
|
+
const expected = [
|
|
99
|
+
ops.cache,
|
|
100
|
+
{},
|
|
101
|
+
"folder",
|
|
102
|
+
[[ops.scope], [ops.literal, "folder"]],
|
|
103
|
+
];
|
|
104
|
+
const globals = {};
|
|
105
|
+
assertCodeEqual(optimize(code, { globals }), expected);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("external reference", () => {
|
|
109
|
+
// Compilation of `index.html` where `index` isn't a variable
|
|
110
|
+
const code = createCode([
|
|
111
|
+
markers.traverse,
|
|
112
|
+
[markers.reference, "index.html"],
|
|
113
|
+
]);
|
|
114
|
+
const expected = [
|
|
115
|
+
ops.cache,
|
|
116
|
+
{},
|
|
117
|
+
"index.html",
|
|
118
|
+
[[ops.scope], [ops.literal, "index.html"]],
|
|
119
|
+
];
|
|
120
|
+
const globals = {};
|
|
121
|
+
assertCodeEqual(optimize(code, { globals }), expected);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("external reference inside object with matching key", () => {
|
|
125
|
+
// Compilation of `{ (posts) = posts.txt }`
|
|
126
|
+
const code = createCode([
|
|
127
|
+
ops.object,
|
|
128
|
+
[
|
|
129
|
+
"(posts)",
|
|
130
|
+
[ops.getter, [markers.traverse, [markers.reference, "posts.txt"]]],
|
|
131
|
+
],
|
|
132
|
+
]);
|
|
133
|
+
const expected = [
|
|
134
|
+
ops.object,
|
|
135
|
+
[
|
|
136
|
+
"(posts)",
|
|
137
|
+
[
|
|
138
|
+
ops.getter,
|
|
139
|
+
[
|
|
140
|
+
ops.cache,
|
|
141
|
+
{},
|
|
142
|
+
"posts.txt",
|
|
143
|
+
[
|
|
144
|
+
[ops.scope, [ops.context, 1]],
|
|
145
|
+
[ops.literal, "posts.txt"],
|
|
146
|
+
],
|
|
147
|
+
],
|
|
148
|
+
],
|
|
149
|
+
],
|
|
150
|
+
];
|
|
151
|
+
const globals = {};
|
|
152
|
+
assertCodeEqual(optimize(code, { globals }), expected);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("global reference", () => {
|
|
156
|
+
// Compilation of `Math` where Math is a global variable
|
|
157
|
+
const code = createCode([markers.traverse, [markers.reference, "Math"]]);
|
|
158
|
+
const globals = { Math: null }; // value doesn't matter
|
|
159
|
+
const expected = [globals, "Math"];
|
|
160
|
+
assertCodeEqual(optimize(code, { globals }), expected);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test("global reference", () => {
|
|
164
|
+
// Compilation of `Math.PI` where Math is a global variable
|
|
165
|
+
const code = createCode([
|
|
166
|
+
markers.traverse,
|
|
167
|
+
[markers.reference, "Math.PI"],
|
|
168
|
+
]);
|
|
169
|
+
const globals = { Math: { PI: null } }; // value doesn't matter
|
|
170
|
+
const expected = [[globals, "Math"], "PI"];
|
|
171
|
+
assertCodeEqual(optimize(code, { globals }), expected);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("local reference", () => {
|
|
175
|
+
// Compilation of `post` where post is a local variable
|
|
176
|
+
const code = createCode([markers.traverse, [markers.reference, "post"]]);
|
|
177
|
+
const globals = { post: {} }; // local should take precedence
|
|
178
|
+
const locals = [["post"]];
|
|
179
|
+
const actual = optimize(code, { globals, locals });
|
|
180
|
+
const expected = [[ops.context], [ops.literal, "post"]];
|
|
181
|
+
assertCodeEqual(actual, expected);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test("local reference and property", () => {
|
|
185
|
+
// Compilation of `post.author.name` where `post` is a local variable
|
|
186
|
+
const code = createCode([
|
|
187
|
+
markers.traverse,
|
|
188
|
+
[markers.reference, "post.author.name"],
|
|
189
|
+
]);
|
|
190
|
+
const globals = { post: {} }; // local should take precedence
|
|
191
|
+
const locals = [["post"]];
|
|
192
|
+
const actual = optimize(code, { globals, locals });
|
|
193
|
+
const expected = [
|
|
194
|
+
[[[ops.context], [ops.literal, "post"]], "author"],
|
|
195
|
+
"name",
|
|
196
|
+
];
|
|
197
|
+
assertCodeEqual(actual, expected);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test("root directory", () => {
|
|
201
|
+
// Compilation of `</>`
|
|
202
|
+
const code = createCode([markers.traverse, [markers.external, "/"]]);
|
|
203
|
+
const expected = [ops.cache, {}, "/", [ops.rootDirectory]];
|
|
204
|
+
assertCodeEqual(optimize(code), expected);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("home directory", () => {
|
|
208
|
+
// Compilation of `<~>`
|
|
209
|
+
const code = createCode([markers.traverse, [ops.homeDirectory]]);
|
|
210
|
+
const expected = [ops.cache, {}, "~", [ops.homeDirectory]];
|
|
211
|
+
assertCodeEqual(optimize(code), expected);
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
describe("path traversal", () => {
|
|
216
|
+
test("explicit external path", () => {
|
|
217
|
+
// `<path/to/file>`
|
|
218
|
+
const code = createCode([
|
|
219
|
+
markers.traverse,
|
|
220
|
+
[markers.external, "path/"],
|
|
221
|
+
[ops.literal, "to/"],
|
|
222
|
+
[ops.literal, "file"],
|
|
223
|
+
]);
|
|
224
|
+
const expected = [
|
|
225
|
+
ops.cache,
|
|
226
|
+
{},
|
|
227
|
+
"path/to/file",
|
|
228
|
+
[
|
|
229
|
+
[ops.scope],
|
|
230
|
+
[ops.literal, "path/"],
|
|
231
|
+
[ops.literal, "to/"],
|
|
232
|
+
[ops.literal, "file"],
|
|
233
|
+
],
|
|
234
|
+
];
|
|
235
|
+
assertCodeEqual(optimize(code), expected);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
test("implicit external path", () => {
|
|
239
|
+
// Compilation of `package.json/name` where package is neither local nor global
|
|
240
|
+
const code = createCode([
|
|
241
|
+
markers.traverse,
|
|
242
|
+
[markers.reference, "package.json/"],
|
|
243
|
+
[ops.literal, "name"],
|
|
244
|
+
]);
|
|
245
|
+
const globals = {};
|
|
246
|
+
const expected = [
|
|
247
|
+
ops.cache,
|
|
248
|
+
{},
|
|
249
|
+
"package.json/name",
|
|
250
|
+
[[ops.scope], [ops.literal, "package.json/"], [ops.literal, "name"]],
|
|
251
|
+
];
|
|
252
|
+
assertCodeEqual(optimize(code, { globals }), expected);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
test("local path", () => {
|
|
256
|
+
// Compilation of `page/title` where page is a local variable
|
|
257
|
+
const code = createCode([
|
|
258
|
+
markers.traverse,
|
|
259
|
+
[markers.reference, "page/"],
|
|
260
|
+
[ops.literal, "title"],
|
|
261
|
+
]);
|
|
262
|
+
const globals = {};
|
|
263
|
+
const locals = [["page"]];
|
|
264
|
+
const actual = optimize(code, { globals, locals });
|
|
265
|
+
const expected = [
|
|
266
|
+
[ops.context],
|
|
267
|
+
[ops.literal, "page/"],
|
|
268
|
+
[ops.literal, "title"],
|
|
269
|
+
];
|
|
270
|
+
assertCodeEqual(actual, expected);
|
|
271
|
+
});
|
|
41
272
|
});
|
|
42
273
|
});
|
|
274
|
+
|
|
275
|
+
function assertCompile(expression, expected, mode = "shell") {
|
|
276
|
+
const parent = new ObjectTree({});
|
|
277
|
+
const globals = new ObjectTree({});
|
|
278
|
+
const fn = compile.expression(expression, { globals, mode, parent });
|
|
279
|
+
const actual = fn.code;
|
|
280
|
+
assertCodeEqual(actual, expected);
|
|
281
|
+
}
|