@weborigami/language 0.0.65-beta.1 → 0.0.65
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/index.ts +0 -2
- package/package.json +3 -3
- package/src/compiler/origami.pegjs +45 -23
- package/src/compiler/parse.d.ts +2 -2
- package/src/compiler/parse.js +139 -104
- package/src/compiler/parserHelpers.js +10 -2
- package/src/runtime/codeFragment.js +19 -0
- package/src/runtime/evaluate.js +21 -16
- package/src/runtime/expressionFunction.js +5 -6
- package/src/runtime/formatError.js +6 -6
- package/src/runtime/ops.js +8 -0
- package/test/compiler/parse.test.js +237 -112
- package/test/runtime/evaluate.test.js +20 -7
- package/test/runtime/ops.test.js +28 -10
|
@@ -5,20 +5,19 @@ import { evaluate } from "./internal.js";
|
|
|
5
5
|
/**
|
|
6
6
|
* Given parsed Origami code, return a function that executes that code.
|
|
7
7
|
*
|
|
8
|
-
* @param {import("../../index.js").
|
|
8
|
+
* @param {import("../../index.js").Code} code - parsed Origami expression
|
|
9
9
|
* @param {string} [name] - optional name of the function
|
|
10
10
|
*/
|
|
11
|
-
export function createExpressionFunction(
|
|
11
|
+
export function createExpressionFunction(code, name) {
|
|
12
12
|
/** @this {AsyncTree|null} */
|
|
13
13
|
async function fn() {
|
|
14
|
-
return evaluate.call(this,
|
|
14
|
+
return evaluate.call(this, code);
|
|
15
15
|
}
|
|
16
16
|
if (name) {
|
|
17
17
|
Object.defineProperty(fn, "name", { value: name });
|
|
18
18
|
}
|
|
19
|
-
fn.code =
|
|
20
|
-
fn.toString = () =>
|
|
21
|
-
parsed instanceof Array ? parsed.location.source.text : parsed;
|
|
19
|
+
fn.code = code;
|
|
20
|
+
fn.toString = () => code.location.source.text;
|
|
22
21
|
return fn;
|
|
23
22
|
}
|
|
24
23
|
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
// Text we look for in an error stack to guess whether a given line represents a
|
|
2
|
+
|
|
3
|
+
import codeFragment from "./codeFragment.js";
|
|
4
|
+
|
|
2
5
|
// function in the Origami source code.
|
|
3
6
|
const origamiSourceSignals = [
|
|
4
7
|
"async-tree/src/",
|
|
@@ -35,12 +38,9 @@ export default function formatError(error) {
|
|
|
35
38
|
// Add location
|
|
36
39
|
let location = /** @type {any} */ (error).location;
|
|
37
40
|
if (location) {
|
|
38
|
-
|
|
39
|
-
let
|
|
40
|
-
|
|
41
|
-
// Use entire source.
|
|
42
|
-
fragment = source.text;
|
|
43
|
-
}
|
|
41
|
+
const fragment = codeFragment(location);
|
|
42
|
+
let { source, start } = location;
|
|
43
|
+
|
|
44
44
|
message += `\nevaluating: ${fragment}`;
|
|
45
45
|
if (typeof source === "object" && source.url) {
|
|
46
46
|
message += `\n at ${source.url.href}:${start.line}:${start.column}`;
|
package/src/runtime/ops.js
CHANGED
|
@@ -282,6 +282,14 @@ export function spread(...args) {
|
|
|
282
282
|
}
|
|
283
283
|
spread.toString = () => "«ops.spread»";
|
|
284
284
|
|
|
285
|
+
/**
|
|
286
|
+
* Return a primitive value
|
|
287
|
+
*/
|
|
288
|
+
export async function primitive(value) {
|
|
289
|
+
return value;
|
|
290
|
+
}
|
|
291
|
+
primitive.toString = () => "«ops.primitive»";
|
|
292
|
+
|
|
285
293
|
/**
|
|
286
294
|
* Traverse a path of keys through a tree.
|
|
287
295
|
*/
|
|
@@ -8,19 +8,29 @@ describe("Origami parser", () => {
|
|
|
8
8
|
test("absoluteFilePath", () => {
|
|
9
9
|
assertParse("absoluteFilePath", "/foo/bar", [
|
|
10
10
|
[ops.filesRoot],
|
|
11
|
-
"foo",
|
|
12
|
-
"bar",
|
|
11
|
+
[ops.primitive, "foo"],
|
|
12
|
+
[ops.primitive, "bar"],
|
|
13
13
|
]);
|
|
14
14
|
});
|
|
15
15
|
|
|
16
16
|
test("array", () => {
|
|
17
17
|
assertParse("array", "[]", [ops.array]);
|
|
18
|
-
assertParse("array", "[1, 2, 3]", [
|
|
19
|
-
|
|
18
|
+
assertParse("array", "[1, 2, 3]", [
|
|
19
|
+
ops.array,
|
|
20
|
+
[ops.primitive, 1],
|
|
21
|
+
[ops.primitive, 2],
|
|
22
|
+
[ops.primitive, 3],
|
|
23
|
+
]);
|
|
24
|
+
assertParse("array", "[ 1 , 2 , 3 ]", [
|
|
25
|
+
ops.array,
|
|
26
|
+
[ops.primitive, 1],
|
|
27
|
+
[ops.primitive, 2],
|
|
28
|
+
[ops.primitive, 3],
|
|
29
|
+
]);
|
|
20
30
|
assertParse("array", "[ 1, ...[2, 3]]", [
|
|
21
31
|
ops.merge,
|
|
22
|
-
[ops.array, 1],
|
|
23
|
-
[ops.array, 2, 3],
|
|
32
|
+
[ops.array, [ops.primitive, 1]],
|
|
33
|
+
[ops.array, [ops.primitive, 2], [ops.primitive, 3]],
|
|
24
34
|
]);
|
|
25
35
|
});
|
|
26
36
|
|
|
@@ -34,19 +44,26 @@ describe("Origami parser", () => {
|
|
|
34
44
|
]);
|
|
35
45
|
assertParse("expr", "foo.bar('hello', 'world')", [
|
|
36
46
|
[ops.scope, "foo.bar"],
|
|
37
|
-
"hello",
|
|
38
|
-
"world",
|
|
47
|
+
[ops.primitive, "hello"],
|
|
48
|
+
[ops.primitive, "world"],
|
|
49
|
+
]);
|
|
50
|
+
assertParse("expr", "(fn)('a')", [
|
|
51
|
+
[ops.scope, "fn"],
|
|
52
|
+
[ops.primitive, "a"],
|
|
53
|
+
]);
|
|
54
|
+
assertParse("expr", "1", [ops.primitive, 1]);
|
|
55
|
+
assertParse("expr", "{ a: 1, b: 2 }", [
|
|
56
|
+
ops.object,
|
|
57
|
+
["a", [ops.primitive, 1]],
|
|
58
|
+
["b", [ops.primitive, 2]],
|
|
39
59
|
]);
|
|
40
|
-
assertParse("expr", "(fn)('a')", [[ops.scope, "fn"], "a"]);
|
|
41
|
-
assertParse("expr", "1", 1);
|
|
42
|
-
assertParse("expr", "{ a: 1, b: 2 }", [ops.object, ["a", 1], ["b", 2]]);
|
|
43
60
|
assertParse("expr", "serve { index.html: 'hello' }", [
|
|
44
61
|
[ops.scope, "serve"],
|
|
45
|
-
[ops.object, ["index.html", "hello"]],
|
|
62
|
+
[ops.object, ["index.html", [ops.primitive, "hello"]]],
|
|
46
63
|
]);
|
|
47
64
|
assertParse("expr", "fn =`x`", [
|
|
48
65
|
[ops.scope, "fn"],
|
|
49
|
-
[ops.lambda, null, "x"],
|
|
66
|
+
[ops.lambda, null, [ops.primitive, "x"]],
|
|
50
67
|
]);
|
|
51
68
|
assertParse("expr", "copy app(formulas), files 'snapshot'", [
|
|
52
69
|
[ops.scope, "copy"],
|
|
@@ -54,16 +71,31 @@ describe("Origami parser", () => {
|
|
|
54
71
|
[ops.scope, "app"],
|
|
55
72
|
[ops.scope, "formulas"],
|
|
56
73
|
],
|
|
57
|
-
[
|
|
74
|
+
[
|
|
75
|
+
[ops.scope, "files"],
|
|
76
|
+
[ops.primitive, "snapshot"],
|
|
77
|
+
],
|
|
58
78
|
]);
|
|
59
79
|
assertParse("expr", "@map =`<li>${_}</li>`", [
|
|
60
80
|
[ops.scope, "@map"],
|
|
61
|
-
[
|
|
81
|
+
[
|
|
82
|
+
ops.lambda,
|
|
83
|
+
null,
|
|
84
|
+
[
|
|
85
|
+
ops.concat,
|
|
86
|
+
[ops.primitive, "<li>"],
|
|
87
|
+
[ops.scope, "_"],
|
|
88
|
+
[ops.primitive, "</li>"],
|
|
89
|
+
],
|
|
90
|
+
],
|
|
91
|
+
]);
|
|
92
|
+
assertParse("expr", `"https://example.com"`, [
|
|
93
|
+
ops.primitive,
|
|
94
|
+
"https://example.com",
|
|
62
95
|
]);
|
|
63
|
-
assertParse("expr", `"https://example.com"`, "https://example.com");
|
|
64
96
|
assertParse("expr", "'Hello' -> test.orit", [
|
|
65
97
|
[ops.scope, "test.orit"],
|
|
66
|
-
"Hello",
|
|
98
|
+
[ops.primitive, "Hello"],
|
|
67
99
|
]);
|
|
68
100
|
});
|
|
69
101
|
|
|
@@ -106,11 +138,14 @@ describe("Origami parser", () => {
|
|
|
106
138
|
assertParse("expression", "path//key", [
|
|
107
139
|
ops.traverse,
|
|
108
140
|
[ops.scope, "path"],
|
|
109
|
-
"",
|
|
110
|
-
"key",
|
|
141
|
+
[ops.primitive, ""],
|
|
142
|
+
[ops.primitive, "key"],
|
|
111
143
|
]);
|
|
112
144
|
// Single slash at start of something = absolute file path
|
|
113
|
-
assertParse("expression", "/path", [
|
|
145
|
+
assertParse("expression", "/path", [
|
|
146
|
+
[ops.filesRoot],
|
|
147
|
+
[ops.primitive, "path"],
|
|
148
|
+
]);
|
|
114
149
|
// Consecutive slashes at start of something = comment
|
|
115
150
|
assertParse("expression", "path //comment", [ops.scope, "path"], false);
|
|
116
151
|
});
|
|
@@ -138,39 +173,42 @@ describe("Origami parser", () => {
|
|
|
138
173
|
assertParse("functionComposition", "fn()/key", [
|
|
139
174
|
ops.traverse,
|
|
140
175
|
[[ops.scope, "fn"], undefined],
|
|
141
|
-
"key",
|
|
176
|
+
[ops.primitive, "key"],
|
|
142
177
|
]);
|
|
143
178
|
assertParse("functionComposition", "tree/", [
|
|
144
179
|
ops.traverse,
|
|
145
180
|
[ops.scope, "tree"],
|
|
146
|
-
"",
|
|
181
|
+
[ops.primitive, ""],
|
|
147
182
|
]);
|
|
148
183
|
assertParse("functionComposition", "tree/key", [
|
|
149
184
|
ops.traverse,
|
|
150
185
|
[ops.scope, "tree"],
|
|
151
|
-
"key",
|
|
186
|
+
[ops.primitive, "key"],
|
|
152
187
|
]);
|
|
153
188
|
assertParse("functionComposition", "tree/foo/bar", [
|
|
154
189
|
ops.traverse,
|
|
155
190
|
[ops.scope, "tree"],
|
|
156
|
-
"foo",
|
|
157
|
-
"bar",
|
|
191
|
+
[ops.primitive, "foo"],
|
|
192
|
+
[ops.primitive, "bar"],
|
|
158
193
|
]);
|
|
159
194
|
assertParse("functionComposition", "tree/key()", [
|
|
160
|
-
[ops.traverse, [ops.scope, "tree"], "key"],
|
|
195
|
+
[ops.traverse, [ops.scope, "tree"], [ops.primitive, "key"]],
|
|
161
196
|
undefined,
|
|
162
197
|
]);
|
|
163
198
|
assertParse("functionComposition", "fn()/key()", [
|
|
164
|
-
[ops.traverse, [[ops.scope, "fn"], undefined], "key"],
|
|
199
|
+
[ops.traverse, [[ops.scope, "fn"], undefined], [ops.primitive, "key"]],
|
|
165
200
|
undefined,
|
|
166
201
|
]);
|
|
167
202
|
assertParse("functionComposition", "(fn())('arg')", [
|
|
168
203
|
[[ops.scope, "fn"], undefined],
|
|
169
|
-
"arg",
|
|
204
|
+
[ops.primitive, "arg"],
|
|
170
205
|
]);
|
|
171
206
|
assertParse("functionComposition", "fn('a')('b')", [
|
|
172
|
-
[
|
|
173
|
-
|
|
207
|
+
[
|
|
208
|
+
[ops.scope, "fn"],
|
|
209
|
+
[ops.primitive, "a"],
|
|
210
|
+
],
|
|
211
|
+
[ops.primitive, "b"],
|
|
174
212
|
]);
|
|
175
213
|
assertParse("functionComposition", "(fn())(a, b)", [
|
|
176
214
|
[[ops.scope, "fn"], undefined],
|
|
@@ -179,8 +217,8 @@ describe("Origami parser", () => {
|
|
|
179
217
|
]);
|
|
180
218
|
assertParse("functionComposition", "{ a: 1, b: 2}/b", [
|
|
181
219
|
ops.traverse,
|
|
182
|
-
[ops.object, ["a", 1], ["b", 2]],
|
|
183
|
-
"b",
|
|
220
|
+
[ops.object, ["a", [ops.primitive, 1]], ["b", [ops.primitive, 2]]],
|
|
221
|
+
[ops.primitive, "b"],
|
|
184
222
|
]);
|
|
185
223
|
assertParse("functionComposition", "fn arg", [
|
|
186
224
|
[ops.scope, "fn"],
|
|
@@ -188,8 +226,8 @@ describe("Origami parser", () => {
|
|
|
188
226
|
]);
|
|
189
227
|
assertParse("functionComposition", "fn 'a', 'b'", [
|
|
190
228
|
[ops.scope, "fn"],
|
|
191
|
-
"a",
|
|
192
|
-
"b",
|
|
229
|
+
[ops.primitive, "a"],
|
|
230
|
+
[ops.primitive, "b"],
|
|
193
231
|
]);
|
|
194
232
|
assertParse("functionComposition", "fn a(b), c", [
|
|
195
233
|
[ops.scope, "fn"],
|
|
@@ -201,19 +239,22 @@ describe("Origami parser", () => {
|
|
|
201
239
|
]);
|
|
202
240
|
assertParse("functionComposition", "fn1 fn2 'arg'", [
|
|
203
241
|
[ops.scope, "fn1"],
|
|
204
|
-
[
|
|
242
|
+
[
|
|
243
|
+
[ops.scope, "fn2"],
|
|
244
|
+
[ops.primitive, "arg"],
|
|
245
|
+
],
|
|
205
246
|
]);
|
|
206
247
|
assertParse("functionComposition", "(fn()) 'arg'", [
|
|
207
248
|
[[ops.scope, "fn"], undefined],
|
|
208
|
-
"arg",
|
|
249
|
+
[ops.primitive, "arg"],
|
|
209
250
|
]);
|
|
210
251
|
assertParse("functionComposition", "tree/key arg", [
|
|
211
|
-
[ops.traverse, [ops.scope, "tree"], "key"],
|
|
252
|
+
[ops.traverse, [ops.scope, "tree"], [ops.primitive, "key"]],
|
|
212
253
|
[ops.scope, "arg"],
|
|
213
254
|
]);
|
|
214
255
|
assertParse("functionComposition", "https://example.com/tree.yaml 'key'", [
|
|
215
|
-
[ops.https, "example.com", "tree.yaml"],
|
|
216
|
-
"key",
|
|
256
|
+
[ops.https, [ops.primitive, "example.com"], [ops.primitive, "tree.yaml"]],
|
|
257
|
+
[ops.primitive, "key"],
|
|
217
258
|
]);
|
|
218
259
|
});
|
|
219
260
|
|
|
@@ -224,15 +265,15 @@ describe("Origami parser", () => {
|
|
|
224
265
|
});
|
|
225
266
|
|
|
226
267
|
test("host", () => {
|
|
227
|
-
assertParse("host", "abc", "abc");
|
|
228
|
-
assertParse("host", "abc:123", "abc:123");
|
|
268
|
+
assertParse("host", "abc", [ops.primitive, "abc"]);
|
|
269
|
+
assertParse("host", "abc:123", [ops.primitive, "abc:123"]);
|
|
229
270
|
});
|
|
230
271
|
|
|
231
272
|
test("identifier", () => {
|
|
232
|
-
assertParse("identifier", "abc", "abc");
|
|
233
|
-
assertParse("identifier", "index.html", "index.html");
|
|
234
|
-
assertParse("identifier", "foo\\ bar", "foo bar");
|
|
235
|
-
assertParse("identifier", "x-y-z", "x-y-z");
|
|
273
|
+
assertParse("identifier", "abc", "abc", false);
|
|
274
|
+
assertParse("identifier", "index.html", "index.html", false);
|
|
275
|
+
assertParse("identifier", "foo\\ bar", "foo bar", false);
|
|
276
|
+
assertParse("identifier", "x-y-z", "x-y-z", false);
|
|
236
277
|
});
|
|
237
278
|
|
|
238
279
|
test("lambda", () => {
|
|
@@ -244,71 +285,111 @@ describe("Origami parser", () => {
|
|
|
244
285
|
assertParse("lambda", "=`Hello, ${name}.`", [
|
|
245
286
|
ops.lambda,
|
|
246
287
|
null,
|
|
247
|
-
[
|
|
288
|
+
[
|
|
289
|
+
ops.concat,
|
|
290
|
+
[ops.primitive, "Hello, "],
|
|
291
|
+
[ops.scope, "name"],
|
|
292
|
+
[ops.primitive, "."],
|
|
293
|
+
],
|
|
248
294
|
]);
|
|
249
295
|
});
|
|
250
296
|
|
|
251
297
|
test("leadingSlashPath", () => {
|
|
252
|
-
assertParse("leadingSlashPath", "/tree/", [
|
|
298
|
+
assertParse("leadingSlashPath", "/tree/", [
|
|
299
|
+
[ops.primitive, "tree"],
|
|
300
|
+
[ops.primitive, ""],
|
|
301
|
+
]);
|
|
253
302
|
});
|
|
254
303
|
|
|
255
304
|
test("list", () => {
|
|
256
|
-
assertParse("list", "1", [1]);
|
|
257
|
-
assertParse("list", "1,2,3", [
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
305
|
+
assertParse("list", "1", [[ops.primitive, 1]]);
|
|
306
|
+
assertParse("list", "1,2,3", [
|
|
307
|
+
[ops.primitive, 1],
|
|
308
|
+
[ops.primitive, 2],
|
|
309
|
+
[ops.primitive, 3],
|
|
310
|
+
]);
|
|
311
|
+
assertParse("list", "1, 2, 3,", [
|
|
312
|
+
[ops.primitive, 1],
|
|
313
|
+
[ops.primitive, 2],
|
|
314
|
+
[ops.primitive, 3],
|
|
315
|
+
]);
|
|
316
|
+
assertParse("list", "1 , 2 , 3", [
|
|
317
|
+
[ops.primitive, 1],
|
|
318
|
+
[ops.primitive, 2],
|
|
319
|
+
[ops.primitive, 3],
|
|
320
|
+
]);
|
|
321
|
+
assertParse("list", "1\n2\n3", [
|
|
322
|
+
[ops.primitive, 1],
|
|
323
|
+
[ops.primitive, 2],
|
|
324
|
+
[ops.primitive, 3],
|
|
325
|
+
]);
|
|
326
|
+
assertParse("list", "'a' , 'b' , 'c'", [
|
|
327
|
+
[ops.primitive, "a"],
|
|
328
|
+
[ops.primitive, "b"],
|
|
329
|
+
[ops.primitive, "c"],
|
|
330
|
+
]);
|
|
262
331
|
});
|
|
263
332
|
|
|
264
333
|
test("multiLineComment", () => {
|
|
265
|
-
assertParse("multiLineComment", "/*\nHello, world!\n*/", null);
|
|
334
|
+
assertParse("multiLineComment", "/*\nHello, world!\n*/", null, false);
|
|
266
335
|
});
|
|
267
336
|
|
|
268
337
|
test("new", () => {
|
|
269
338
|
assertParse("expression", "new:@js/Date('2025-01-01')", [
|
|
270
|
-
[ops.constructor, "@js", "Date"],
|
|
271
|
-
"2025-01-01",
|
|
339
|
+
[ops.constructor, [ops.primitive, "@js"], [ops.primitive, "Date"]],
|
|
340
|
+
[ops.primitive, "2025-01-01"],
|
|
272
341
|
]);
|
|
273
342
|
});
|
|
274
343
|
|
|
275
344
|
test("number", () => {
|
|
276
|
-
assertParse("number", "123", 123);
|
|
277
|
-
assertParse("number", "-456", -456);
|
|
278
|
-
assertParse("number", ".5", 0.5);
|
|
279
|
-
assertParse("number", "123.45", 123.45);
|
|
280
|
-
assertParse("number", "-678.90", -678.9);
|
|
281
|
-
assertParse("number", "+123", 123);
|
|
282
|
-
assertParse("number", "+456.78", 456.78);
|
|
345
|
+
assertParse("number", "123", [ops.primitive, 123]);
|
|
346
|
+
assertParse("number", "-456", [ops.primitive, -456]);
|
|
347
|
+
assertParse("number", ".5", [ops.primitive, 0.5]);
|
|
348
|
+
assertParse("number", "123.45", [ops.primitive, 123.45]);
|
|
349
|
+
assertParse("number", "-678.90", [ops.primitive, -678.9]);
|
|
350
|
+
assertParse("number", "+123", [ops.primitive, 123]);
|
|
351
|
+
assertParse("number", "+456.78", [ops.primitive, 456.78]);
|
|
283
352
|
});
|
|
284
353
|
|
|
285
354
|
test("object", () => {
|
|
286
355
|
assertParse("object", "{}", [ops.object]);
|
|
287
356
|
assertParse("object", "{ a: 1, b }", [
|
|
288
357
|
ops.object,
|
|
289
|
-
["a", 1],
|
|
358
|
+
["a", [ops.primitive, 1]],
|
|
290
359
|
["b", [ops.inherited, "b"]],
|
|
291
360
|
]);
|
|
292
361
|
assertParse("object", `{ "a": 1, "b": 2 }`, [
|
|
293
362
|
ops.object,
|
|
294
|
-
["a", 1],
|
|
295
|
-
["b", 2],
|
|
363
|
+
["a", [ops.primitive, 1]],
|
|
364
|
+
["b", [ops.primitive, 2]],
|
|
296
365
|
]);
|
|
297
366
|
assertParse("object", "{ a = b, b: 2 }", [
|
|
298
367
|
ops.object,
|
|
299
368
|
["a", [ops.getter, [ops.scope, "b"]]],
|
|
300
|
-
["b", 2],
|
|
369
|
+
["b", [ops.primitive, 2]],
|
|
301
370
|
]);
|
|
302
371
|
assertParse("object", "{ x = fn('a') }", [
|
|
303
372
|
ops.object,
|
|
304
|
-
[
|
|
373
|
+
[
|
|
374
|
+
"x",
|
|
375
|
+
[
|
|
376
|
+
ops.getter,
|
|
377
|
+
[
|
|
378
|
+
[ops.scope, "fn"],
|
|
379
|
+
[ops.primitive, "a"],
|
|
380
|
+
],
|
|
381
|
+
],
|
|
382
|
+
],
|
|
305
383
|
]);
|
|
306
384
|
assertParse("object", "{ a: 1, ...b }", [
|
|
307
385
|
ops.merge,
|
|
308
|
-
[ops.object, ["a", 1]],
|
|
386
|
+
[ops.object, ["a", [ops.primitive, 1]]],
|
|
309
387
|
[ops.scope, "b"],
|
|
310
388
|
]);
|
|
311
|
-
assertParse("object", "{ (a): 1 }", [
|
|
389
|
+
assertParse("object", "{ (a): 1 }", [
|
|
390
|
+
ops.object,
|
|
391
|
+
["(a)", [ops.primitive, 1]],
|
|
392
|
+
]);
|
|
312
393
|
});
|
|
313
394
|
|
|
314
395
|
test("objectEntry", () => {
|
|
@@ -323,16 +404,28 @@ describe("Origami parser", () => {
|
|
|
323
404
|
]);
|
|
324
405
|
assertParse("objectGetter", "foo = fn 'bar'", [
|
|
325
406
|
"foo",
|
|
326
|
-
[
|
|
407
|
+
[
|
|
408
|
+
ops.getter,
|
|
409
|
+
[
|
|
410
|
+
[ops.scope, "fn"],
|
|
411
|
+
[ops.primitive, "bar"],
|
|
412
|
+
],
|
|
413
|
+
],
|
|
327
414
|
]);
|
|
328
415
|
});
|
|
329
416
|
|
|
330
417
|
test("objectProperty", () => {
|
|
331
|
-
assertParse("objectProperty", "a: 1", ["a", 1]);
|
|
332
|
-
assertParse("objectProperty", "name: 'Alice'", [
|
|
418
|
+
assertParse("objectProperty", "a: 1", ["a", [ops.primitive, 1]]);
|
|
419
|
+
assertParse("objectProperty", "name: 'Alice'", [
|
|
420
|
+
"name",
|
|
421
|
+
[ops.primitive, "Alice"],
|
|
422
|
+
]);
|
|
333
423
|
assertParse("objectProperty", "x: fn('a')", [
|
|
334
424
|
"x",
|
|
335
|
-
[
|
|
425
|
+
[
|
|
426
|
+
[ops.scope, "fn"],
|
|
427
|
+
[ops.primitive, "a"],
|
|
428
|
+
],
|
|
336
429
|
]);
|
|
337
430
|
});
|
|
338
431
|
|
|
@@ -377,9 +470,19 @@ describe("Origami parser", () => {
|
|
|
377
470
|
});
|
|
378
471
|
|
|
379
472
|
test("path", () => {
|
|
380
|
-
assertParse("path", "tree/", [
|
|
381
|
-
|
|
382
|
-
|
|
473
|
+
assertParse("path", "tree/", [
|
|
474
|
+
[ops.primitive, "tree"],
|
|
475
|
+
[ops.primitive, ""],
|
|
476
|
+
]);
|
|
477
|
+
assertParse("path", "month/12", [
|
|
478
|
+
[ops.primitive, "month"],
|
|
479
|
+
[ops.primitive, "12"],
|
|
480
|
+
]);
|
|
481
|
+
assertParse("path", "tree/foo/bar", [
|
|
482
|
+
[ops.primitive, "tree"],
|
|
483
|
+
[ops.primitive, "foo"],
|
|
484
|
+
[ops.primitive, "bar"],
|
|
485
|
+
]);
|
|
383
486
|
});
|
|
384
487
|
|
|
385
488
|
test("pipeline", () => {
|
|
@@ -404,18 +507,24 @@ describe("Origami parser", () => {
|
|
|
404
507
|
});
|
|
405
508
|
|
|
406
509
|
test("protocolCall", () => {
|
|
407
|
-
assertParse("protocolCall", "foo://bar", [
|
|
510
|
+
assertParse("protocolCall", "foo://bar", [
|
|
511
|
+
[ops.scope, "foo"],
|
|
512
|
+
[ops.primitive, "bar"],
|
|
513
|
+
]);
|
|
408
514
|
assertParse("protocolCall", "https://example.com/foo/", [
|
|
409
515
|
ops.https,
|
|
410
|
-
"example.com",
|
|
411
|
-
"foo",
|
|
412
|
-
"",
|
|
516
|
+
[ops.primitive, "example.com"],
|
|
517
|
+
[ops.primitive, "foo"],
|
|
518
|
+
[ops.primitive, ""],
|
|
519
|
+
]);
|
|
520
|
+
assertParse("protocolCall", "http:example.com", [
|
|
521
|
+
ops.http,
|
|
522
|
+
[ops.primitive, "example.com"],
|
|
413
523
|
]);
|
|
414
|
-
assertParse("protocolCall", "http:example.com", [ops.http, "example.com"]);
|
|
415
524
|
assertParse("protocolCall", "http://localhost:5000/foo", [
|
|
416
525
|
ops.http,
|
|
417
|
-
"localhost:5000",
|
|
418
|
-
"foo",
|
|
526
|
+
[ops.primitive, "localhost:5000"],
|
|
527
|
+
[ops.primitive, "foo"],
|
|
419
528
|
]);
|
|
420
529
|
});
|
|
421
530
|
|
|
@@ -425,12 +534,13 @@ describe("Origami parser", () => {
|
|
|
425
534
|
`#!/usr/bin/env ori @invoke
|
|
426
535
|
'Hello'
|
|
427
536
|
`,
|
|
428
|
-
"Hello"
|
|
537
|
+
[ops.primitive, "Hello"],
|
|
538
|
+
false
|
|
429
539
|
);
|
|
430
540
|
});
|
|
431
541
|
|
|
432
542
|
test("singleLineComment", () => {
|
|
433
|
-
assertParse("singleLineComment", "// Hello, world!", null);
|
|
543
|
+
assertParse("singleLineComment", "// Hello, world!", null, false);
|
|
434
544
|
});
|
|
435
545
|
|
|
436
546
|
test("scopeReference", () => {
|
|
@@ -443,38 +553,49 @@ describe("Origami parser", () => {
|
|
|
443
553
|
});
|
|
444
554
|
|
|
445
555
|
test("string", () => {
|
|
446
|
-
assertParse("string", '"foo"', "foo");
|
|
447
|
-
assertParse("string", "'bar'", "bar");
|
|
448
|
-
assertParse("string", '"foo bar"', "foo bar");
|
|
449
|
-
assertParse("string", "'bar baz'", "bar baz");
|
|
450
|
-
assertParse("string", `"foo\\"s bar"`, `foo"s bar`);
|
|
451
|
-
assertParse("string", `'bar\\'s baz'`, `bar's baz`);
|
|
452
|
-
assertParse("string", `«string»`, "string");
|
|
453
|
-
assertParse("string", `"\\0\\b\\f\\n\\r\\t\\v"`,
|
|
556
|
+
assertParse("string", '"foo"', [ops.primitive, "foo"]);
|
|
557
|
+
assertParse("string", "'bar'", [ops.primitive, "bar"]);
|
|
558
|
+
assertParse("string", '"foo bar"', [ops.primitive, "foo bar"]);
|
|
559
|
+
assertParse("string", "'bar baz'", [ops.primitive, "bar baz"]);
|
|
560
|
+
assertParse("string", `"foo\\"s bar"`, [ops.primitive, `foo"s bar`]);
|
|
561
|
+
assertParse("string", `'bar\\'s baz'`, [ops.primitive, `bar's baz`]);
|
|
562
|
+
assertParse("string", `«string»`, [ops.primitive, "string"]);
|
|
563
|
+
assertParse("string", `"\\0\\b\\f\\n\\r\\t\\v"`, [
|
|
564
|
+
ops.primitive,
|
|
565
|
+
"\0\b\f\n\r\t\v",
|
|
566
|
+
]);
|
|
454
567
|
});
|
|
455
568
|
|
|
456
569
|
test("templateDocument", () => {
|
|
457
570
|
assertParse("templateDocument", "hello${foo}world", [
|
|
458
571
|
ops.lambda,
|
|
459
572
|
null,
|
|
460
|
-
[
|
|
573
|
+
[
|
|
574
|
+
ops.concat,
|
|
575
|
+
[ops.primitive, "hello"],
|
|
576
|
+
[ops.scope, "foo"],
|
|
577
|
+
[ops.primitive, "world"],
|
|
578
|
+
],
|
|
461
579
|
]);
|
|
462
580
|
assertParse("templateDocument", "Documents can contain ` backticks", [
|
|
463
581
|
ops.lambda,
|
|
464
582
|
null,
|
|
465
|
-
"Documents can contain ` backticks",
|
|
583
|
+
[ops.primitive, "Documents can contain ` backticks"],
|
|
466
584
|
]);
|
|
467
585
|
});
|
|
468
586
|
|
|
469
587
|
test("templateLiteral", () => {
|
|
470
|
-
assertParse("templateLiteral", "`Hello, world.`",
|
|
588
|
+
assertParse("templateLiteral", "`Hello, world.`", [
|
|
589
|
+
ops.primitive,
|
|
590
|
+
"Hello, world.",
|
|
591
|
+
]);
|
|
471
592
|
assertParse("templateLiteral", "`foo ${x} bar`", [
|
|
472
593
|
ops.concat,
|
|
473
|
-
"foo ",
|
|
594
|
+
[ops.primitive, "foo "],
|
|
474
595
|
[ops.scope, "x"],
|
|
475
|
-
" bar",
|
|
596
|
+
[ops.primitive, " bar"],
|
|
476
597
|
]);
|
|
477
|
-
assertParse("templateLiteral", "`${`nested`}`", "nested");
|
|
598
|
+
assertParse("templateLiteral", "`${`nested`}`", [ops.primitive, "nested"]);
|
|
478
599
|
assertParse("templateLiteral", "`${map(people, =`${name}`)}`", [
|
|
479
600
|
ops.concat,
|
|
480
601
|
[
|
|
@@ -486,14 +607,17 @@ describe("Origami parser", () => {
|
|
|
486
607
|
});
|
|
487
608
|
|
|
488
609
|
test("templateLiteral (JS)", () => {
|
|
489
|
-
assertParse("templateLiteral", "`Hello, world.`",
|
|
610
|
+
assertParse("templateLiteral", "`Hello, world.`", [
|
|
611
|
+
ops.primitive,
|
|
612
|
+
"Hello, world.",
|
|
613
|
+
]);
|
|
490
614
|
assertParse("templateLiteral", "`foo ${x} bar`", [
|
|
491
615
|
ops.concat,
|
|
492
|
-
"foo ",
|
|
616
|
+
[ops.primitive, "foo "],
|
|
493
617
|
[ops.scope, "x"],
|
|
494
|
-
" bar",
|
|
618
|
+
[ops.primitive, " bar"],
|
|
495
619
|
]);
|
|
496
|
-
assertParse("templateLiteral", "`${`nested`}`", "nested");
|
|
620
|
+
assertParse("templateLiteral", "`${`nested`}`", [ops.primitive, "nested"]);
|
|
497
621
|
assertParse("templateLiteral", "`${map(people, =`${name}`)}`", [
|
|
498
622
|
ops.concat,
|
|
499
623
|
[
|
|
@@ -515,13 +639,14 @@ describe("Origami parser", () => {
|
|
|
515
639
|
// First comment
|
|
516
640
|
// Second comment
|
|
517
641
|
`,
|
|
518
|
-
|
|
642
|
+
null,
|
|
643
|
+
false
|
|
519
644
|
);
|
|
520
645
|
});
|
|
521
646
|
});
|
|
522
647
|
|
|
523
648
|
function assertParse(startRule, source, expected, checkLocation = true) {
|
|
524
|
-
const
|
|
649
|
+
const code = parse(source, {
|
|
525
650
|
grammarSource: { text: source },
|
|
526
651
|
startRule,
|
|
527
652
|
});
|
|
@@ -529,16 +654,16 @@ function assertParse(startRule, source, expected, checkLocation = true) {
|
|
|
529
654
|
// Verify that the parser returned a `location` property and that it spans the
|
|
530
655
|
// entire source. We skip this check in cases where the source starts or ends
|
|
531
656
|
// with a comment; the parser will strip those.
|
|
532
|
-
if (checkLocation
|
|
533
|
-
assert(
|
|
534
|
-
const resultSource =
|
|
535
|
-
|
|
536
|
-
|
|
657
|
+
if (checkLocation) {
|
|
658
|
+
assert(code.location);
|
|
659
|
+
const resultSource = code.location.source.text.slice(
|
|
660
|
+
code.location.start.offset,
|
|
661
|
+
code.location.end.offset
|
|
537
662
|
);
|
|
538
663
|
assert.equal(resultSource, source.trim());
|
|
539
664
|
}
|
|
540
665
|
|
|
541
|
-
const actual = stripLocations(
|
|
666
|
+
const actual = stripLocations(code);
|
|
542
667
|
assert.deepEqual(actual, expected);
|
|
543
668
|
}
|
|
544
669
|
|