@weborigami/language 0.0.73 → 0.1.0
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 +2 -2
- package/package.json +3 -3
- package/src/compiler/compile.js +10 -3
- package/src/compiler/origami.pegjs +127 -90
- package/src/compiler/parse.js +686 -688
- package/src/compiler/parserHelpers.js +13 -7
- package/src/runtime/HandleExtensionsTransform.js +1 -1
- package/src/runtime/ImportModulesMixin.js +1 -1
- package/src/runtime/codeFragment.js +2 -2
- package/src/runtime/errors.js +104 -0
- package/src/runtime/evaluate.js +3 -3
- package/src/runtime/expressionObject.js +8 -5
- package/src/runtime/{extensions.js → handlers.js} +6 -24
- package/src/runtime/internal.js +1 -0
- package/src/runtime/ops.js +58 -169
- package/src/runtime/typos.js +71 -0
- package/test/compiler/compile.test.js +4 -4
- package/test/compiler/parse.test.js +273 -145
- package/test/runtime/fixtures/templates/greet.orit +1 -1
- package/test/runtime/{extensions.test.js → handlers.test.js} +2 -2
- package/test/runtime/ops.test.js +11 -12
- package/test/runtime/typos.test.js +21 -0
- package/src/runtime/formatError.js +0 -56
|
@@ -77,11 +77,11 @@ describe("compile", () => {
|
|
|
77
77
|
return strings[0] + values[0] + strings[1];
|
|
78
78
|
},
|
|
79
79
|
});
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
const alice = await
|
|
80
|
+
const program = compile.expression("=tag`Hello, ${_}!`");
|
|
81
|
+
const lambda = await program.call(scope);
|
|
82
|
+
const alice = await lambda("Alice");
|
|
83
83
|
assert.equal(alice, "Hello, Alice!");
|
|
84
|
-
const bob = await
|
|
84
|
+
const bob = await lambda("Bob");
|
|
85
85
|
assert.equal(bob, "Hello, Bob!");
|
|
86
86
|
});
|
|
87
87
|
|
|
@@ -32,74 +32,38 @@ describe("Origami parser", () => {
|
|
|
32
32
|
[ops.array, [ops.literal, 1]],
|
|
33
33
|
[ops.array, [ops.literal, 2], [ops.literal, 3]],
|
|
34
34
|
]);
|
|
35
|
+
assertParse(
|
|
36
|
+
"array",
|
|
37
|
+
`[
|
|
38
|
+
1
|
|
39
|
+
2
|
|
40
|
+
3
|
|
41
|
+
]`,
|
|
42
|
+
[ops.array, [ops.literal, 1], [ops.literal, 2], [ops.literal, 3]]
|
|
43
|
+
);
|
|
35
44
|
});
|
|
36
45
|
|
|
37
|
-
test("
|
|
38
|
-
assertParse("
|
|
39
|
-
assertParse("
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
[ops.
|
|
43
|
-
[ops.
|
|
44
|
-
]);
|
|
45
|
-
assertParse("expr", "foo.bar('hello', 'world')", [
|
|
46
|
-
[ops.scope, "foo.bar"],
|
|
47
|
-
[ops.literal, "hello"],
|
|
48
|
-
[ops.literal, "world"],
|
|
49
|
-
]);
|
|
50
|
-
assertParse("expr", "(fn)('a')", [
|
|
51
|
-
[ops.scope, "fn"],
|
|
52
|
-
[ops.literal, "a"],
|
|
53
|
-
]);
|
|
54
|
-
assertParse("expr", "1", [ops.literal, 1]);
|
|
55
|
-
assertParse("expr", "{ a: 1, b: 2 }", [
|
|
56
|
-
ops.object,
|
|
57
|
-
["a", [ops.literal, 1]],
|
|
58
|
-
["b", [ops.literal, 2]],
|
|
59
|
-
]);
|
|
60
|
-
assertParse("expr", "serve { index.html: 'hello' }", [
|
|
61
|
-
[ops.scope, "serve"],
|
|
62
|
-
[ops.object, ["index.html", [ops.literal, "hello"]]],
|
|
63
|
-
]);
|
|
64
|
-
assertParse("expr", "fn =`x`", [
|
|
65
|
-
[ops.scope, "fn"],
|
|
66
|
-
[ops.lambda, ["_"], [ops.template, [ops.literal, ["x"]]]],
|
|
67
|
-
]);
|
|
68
|
-
assertParse("expr", "copy app(formulas), files 'snapshot'", [
|
|
69
|
-
[ops.scope, "copy"],
|
|
70
|
-
[
|
|
71
|
-
[ops.scope, "app"],
|
|
72
|
-
[ops.scope, "formulas"],
|
|
73
|
-
],
|
|
74
|
-
[
|
|
75
|
-
[ops.scope, "files"],
|
|
76
|
-
[ops.literal, "snapshot"],
|
|
77
|
-
],
|
|
78
|
-
]);
|
|
79
|
-
assertParse("expr", "@map =`<li>${_}</li>`", [
|
|
80
|
-
[ops.scope, "@map"],
|
|
81
|
-
[
|
|
82
|
-
ops.lambda,
|
|
83
|
-
["_"],
|
|
84
|
-
[
|
|
85
|
-
ops.template,
|
|
86
|
-
[ops.literal, ["<li>", "</li>"]],
|
|
87
|
-
[ops.concat, [ops.scope, "_"]],
|
|
88
|
-
],
|
|
89
|
-
],
|
|
46
|
+
test("callTarget", () => {
|
|
47
|
+
assertParse("callTarget", "foo", [ops.builtin, "foo"]);
|
|
48
|
+
assertParse("callTarget", "foo.js", [ops.scope, "foo.js"]);
|
|
49
|
+
assertParse("callTarget", "[1, 2]", [
|
|
50
|
+
ops.array,
|
|
51
|
+
[ops.literal, 1],
|
|
52
|
+
[ops.literal, 2],
|
|
90
53
|
]);
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("doubleSlashPath", () => {
|
|
57
|
+
assertParse("doubleSlashPath", "//example.com", [
|
|
58
|
+
[ops.literal, "example.com"],
|
|
94
59
|
]);
|
|
95
|
-
assertParse("
|
|
96
|
-
[ops.
|
|
97
|
-
[ops.literal, "
|
|
60
|
+
assertParse("doubleSlashPath", "//example.com/index.html", [
|
|
61
|
+
[ops.literal, "example.com/"],
|
|
62
|
+
[ops.literal, "index.html"],
|
|
98
63
|
]);
|
|
99
|
-
assertParse("
|
|
100
|
-
[ops.
|
|
101
|
-
[ops.literal,
|
|
102
|
-
[ops.concat, [ops.scope, "name"]],
|
|
64
|
+
assertParse("doubleSlashPath", "//localhost:5000/foo", [
|
|
65
|
+
[ops.literal, "localhost:5000/"],
|
|
66
|
+
[ops.literal, "foo"],
|
|
103
67
|
]);
|
|
104
68
|
});
|
|
105
69
|
|
|
@@ -109,7 +73,7 @@ describe("Origami parser", () => {
|
|
|
109
73
|
`
|
|
110
74
|
{
|
|
111
75
|
index.html = index.ori(teamData.yaml)
|
|
112
|
-
thumbnails =
|
|
76
|
+
thumbnails = map(images, { value: thumbnail.js })
|
|
113
77
|
}
|
|
114
78
|
`,
|
|
115
79
|
[
|
|
@@ -129,7 +93,7 @@ describe("Origami parser", () => {
|
|
|
129
93
|
[
|
|
130
94
|
ops.getter,
|
|
131
95
|
[
|
|
132
|
-
[ops.
|
|
96
|
+
[ops.builtin, "map"],
|
|
133
97
|
[ops.scope, "images"],
|
|
134
98
|
[ops.object, ["value", [ops.scope, "thumbnail.js"]]],
|
|
135
99
|
],
|
|
@@ -138,44 +102,64 @@ describe("Origami parser", () => {
|
|
|
138
102
|
]
|
|
139
103
|
);
|
|
140
104
|
|
|
105
|
+
// Builtin on its own is the function itself, not a function call
|
|
106
|
+
assertParse("expression", "mdHtml:", [ops.builtin, "mdHtml:"]);
|
|
107
|
+
|
|
141
108
|
// Consecutive slahes in a path are removed
|
|
142
109
|
assertParse("expression", "path//key", [
|
|
143
110
|
ops.traverse,
|
|
144
111
|
[ops.scope, "path/"],
|
|
145
112
|
[ops.literal, "key"],
|
|
146
113
|
]);
|
|
114
|
+
|
|
147
115
|
// Single slash at start of something = absolute file path
|
|
148
116
|
assertParse("expression", "/path", [
|
|
149
117
|
[ops.filesRoot],
|
|
150
118
|
[ops.literal, "path"],
|
|
151
119
|
]);
|
|
120
|
+
|
|
152
121
|
// Consecutive slashes at start of something = comment
|
|
153
122
|
assertParse("expression", "path //comment", [ops.scope, "path"], false);
|
|
123
|
+
assertParse("expression", "page.ori(mdHtml:(about.md))", [
|
|
124
|
+
[ops.scope, "page.ori"],
|
|
125
|
+
[
|
|
126
|
+
[ops.builtin, "mdHtml:"],
|
|
127
|
+
[ops.scope, "about.md"],
|
|
128
|
+
],
|
|
129
|
+
]);
|
|
130
|
+
|
|
131
|
+
assertParse("expression", "'Hello' -> test.orit", [
|
|
132
|
+
[ops.scope, "test.orit"],
|
|
133
|
+
[ops.literal, "Hello"],
|
|
134
|
+
]);
|
|
154
135
|
});
|
|
155
136
|
|
|
156
137
|
test("functionComposition", () => {
|
|
157
|
-
assertParse("functionComposition", "fn()", [
|
|
158
|
-
|
|
159
|
-
|
|
138
|
+
assertParse("functionComposition", "fn()", [
|
|
139
|
+
[ops.builtin, "fn"],
|
|
140
|
+
undefined,
|
|
141
|
+
]);
|
|
142
|
+
assertParse("functionComposition", "foo.js(arg)", [
|
|
143
|
+
[ops.scope, "foo.js"],
|
|
160
144
|
[ops.scope, "arg"],
|
|
161
145
|
]);
|
|
162
146
|
assertParse("functionComposition", "fn(a, b)", [
|
|
163
|
-
[ops.
|
|
147
|
+
[ops.builtin, "fn"],
|
|
164
148
|
[ops.scope, "a"],
|
|
165
149
|
[ops.scope, "b"],
|
|
166
150
|
]);
|
|
167
|
-
assertParse("functionComposition", "
|
|
168
|
-
[ops.scope, "
|
|
151
|
+
assertParse("functionComposition", "foo.js( a , b )", [
|
|
152
|
+
[ops.scope, "foo.js"],
|
|
169
153
|
[ops.scope, "a"],
|
|
170
154
|
[ops.scope, "b"],
|
|
171
155
|
]);
|
|
172
156
|
assertParse("functionComposition", "fn()(arg)", [
|
|
173
|
-
[[ops.
|
|
157
|
+
[[ops.builtin, "fn"], undefined],
|
|
174
158
|
[ops.scope, "arg"],
|
|
175
159
|
]);
|
|
176
|
-
assertParse("functionComposition", "
|
|
160
|
+
assertParse("functionComposition", "foo.js()/key", [
|
|
177
161
|
ops.traverse,
|
|
178
|
-
[[ops.scope, "
|
|
162
|
+
[[ops.scope, "foo.js"], undefined],
|
|
179
163
|
[ops.literal, "key"],
|
|
180
164
|
]);
|
|
181
165
|
assertParse("functionComposition", "tree/key()", [
|
|
@@ -187,22 +171,22 @@ describe("Origami parser", () => {
|
|
|
187
171
|
[ops.scope, "tree"],
|
|
188
172
|
]);
|
|
189
173
|
assertParse("functionComposition", "fn()/key()", [
|
|
190
|
-
[ops.traverse, [[ops.
|
|
174
|
+
[ops.traverse, [[ops.builtin, "fn"], undefined], [ops.literal, "key"]],
|
|
191
175
|
undefined,
|
|
192
176
|
]);
|
|
193
|
-
assertParse("functionComposition", "(
|
|
194
|
-
[[ops.scope, "
|
|
177
|
+
assertParse("functionComposition", "(foo.js())('arg')", [
|
|
178
|
+
[[ops.scope, "foo.js"], undefined],
|
|
195
179
|
[ops.literal, "arg"],
|
|
196
180
|
]);
|
|
197
181
|
assertParse("functionComposition", "fn('a')('b')", [
|
|
198
182
|
[
|
|
199
|
-
[ops.
|
|
183
|
+
[ops.builtin, "fn"],
|
|
200
184
|
[ops.literal, "a"],
|
|
201
185
|
],
|
|
202
186
|
[ops.literal, "b"],
|
|
203
187
|
]);
|
|
204
|
-
assertParse("functionComposition", "(
|
|
205
|
-
[[ops.scope, "
|
|
188
|
+
assertParse("functionComposition", "(foo.js())(a, b)", [
|
|
189
|
+
[[ops.scope, "foo.js"], undefined],
|
|
206
190
|
[ops.scope, "a"],
|
|
207
191
|
[ops.scope, "b"],
|
|
208
192
|
]);
|
|
@@ -212,53 +196,89 @@ describe("Origami parser", () => {
|
|
|
212
196
|
[ops.literal, "b"],
|
|
213
197
|
]);
|
|
214
198
|
assertParse("functionComposition", "fn arg", [
|
|
215
|
-
[ops.
|
|
199
|
+
[ops.builtin, "fn"],
|
|
216
200
|
[ops.scope, "arg"],
|
|
217
201
|
]);
|
|
218
|
-
assertParse("functionComposition", "
|
|
219
|
-
[ops.scope, "
|
|
202
|
+
assertParse("functionComposition", "page.ori 'a', 'b'", [
|
|
203
|
+
[ops.scope, "page.ori"],
|
|
220
204
|
[ops.literal, "a"],
|
|
221
205
|
[ops.literal, "b"],
|
|
222
206
|
]);
|
|
223
207
|
assertParse("functionComposition", "fn a(b), c", [
|
|
224
|
-
[ops.
|
|
208
|
+
[ops.builtin, "fn"],
|
|
225
209
|
[
|
|
226
|
-
[ops.
|
|
210
|
+
[ops.builtin, "a"],
|
|
227
211
|
[ops.scope, "b"],
|
|
228
212
|
],
|
|
229
213
|
[ops.scope, "c"],
|
|
230
214
|
]);
|
|
231
|
-
assertParse("functionComposition", "
|
|
232
|
-
[ops.scope, "
|
|
215
|
+
assertParse("functionComposition", "foo.js bar.ori 'arg'", [
|
|
216
|
+
[ops.scope, "foo.js"],
|
|
233
217
|
[
|
|
234
|
-
[ops.scope, "
|
|
218
|
+
[ops.scope, "bar.ori"],
|
|
235
219
|
[ops.literal, "arg"],
|
|
236
220
|
],
|
|
237
221
|
]);
|
|
238
222
|
assertParse("functionComposition", "(fn()) 'arg'", [
|
|
239
|
-
[[ops.
|
|
223
|
+
[[ops.builtin, "fn"], undefined],
|
|
240
224
|
[ops.literal, "arg"],
|
|
241
225
|
]);
|
|
242
226
|
assertParse("functionComposition", "tree/key arg", [
|
|
243
227
|
[ops.traverse, [ops.scope, "tree/"], [ops.literal, "key"]],
|
|
244
228
|
[ops.scope, "arg"],
|
|
245
229
|
]);
|
|
246
|
-
assertParse("functionComposition", "
|
|
247
|
-
[ops.
|
|
248
|
-
[
|
|
230
|
+
assertParse("functionComposition", "new:(js:Date, '2025-01-01')", [
|
|
231
|
+
[ops.builtin, "new:"],
|
|
232
|
+
[
|
|
233
|
+
[ops.builtin, "js:"],
|
|
234
|
+
[ops.literal, "Date"],
|
|
235
|
+
],
|
|
236
|
+
[ops.literal, "2025-01-01"],
|
|
237
|
+
]);
|
|
238
|
+
assertParse("functionComposition", "map(markdown, mdHtml)", [
|
|
239
|
+
[ops.builtin, "map"],
|
|
240
|
+
[ops.scope, "markdown"],
|
|
241
|
+
[ops.scope, "mdHtml"],
|
|
249
242
|
]);
|
|
243
|
+
assertParse(
|
|
244
|
+
"functionComposition",
|
|
245
|
+
"package:@weborigami/dropbox/auth(creds)",
|
|
246
|
+
[
|
|
247
|
+
[
|
|
248
|
+
[ops.builtin, "package:"],
|
|
249
|
+
[ops.literal, "@weborigami/"],
|
|
250
|
+
[ops.literal, "dropbox/"],
|
|
251
|
+
[ops.literal, "auth"],
|
|
252
|
+
],
|
|
253
|
+
[ops.scope, "creds"],
|
|
254
|
+
]
|
|
255
|
+
);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test("functionReference", () => {
|
|
259
|
+
assertParse("functionReference", "json", [ops.builtin, "json"]);
|
|
260
|
+
assertParse("functionReference", "greet.js", [ops.scope, "greet.js"]);
|
|
250
261
|
});
|
|
251
262
|
|
|
252
263
|
test("group", () => {
|
|
253
264
|
assertParse("group", "(hello)", [ops.scope, "hello"]);
|
|
254
265
|
assertParse("group", "(((nested)))", [ops.scope, "nested"]);
|
|
255
|
-
assertParse("group", "(fn())", [[ops.
|
|
266
|
+
assertParse("group", "(fn())", [[ops.builtin, "fn"], undefined]);
|
|
267
|
+
assertParse("group", "(a -> b)", [
|
|
268
|
+
[ops.builtin, "b"],
|
|
269
|
+
[ops.scope, "a"],
|
|
270
|
+
]);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
test("homeTree", () => {
|
|
274
|
+
assertParse("homeTree", "~", [ops.homeTree]);
|
|
256
275
|
});
|
|
257
276
|
|
|
258
277
|
test("host", () => {
|
|
259
278
|
assertParse("host", "abc", [ops.literal, "abc"]);
|
|
260
279
|
assertParse("host", "abc:123", [ops.literal, "abc:123"]);
|
|
261
280
|
assertParse("host", "foo\\ bar", [ops.literal, "foo bar"]);
|
|
281
|
+
assertParse("host", "example.com/", [ops.literal, "example.com/"]);
|
|
262
282
|
});
|
|
263
283
|
|
|
264
284
|
test("identifier", () => {
|
|
@@ -283,6 +303,14 @@ describe("Origami parser", () => {
|
|
|
283
303
|
[ops.concat, [ops.scope, "name"]],
|
|
284
304
|
],
|
|
285
305
|
]);
|
|
306
|
+
assertParse("lambda", "=indent`hello`", [
|
|
307
|
+
ops.lambda,
|
|
308
|
+
["_"],
|
|
309
|
+
[
|
|
310
|
+
[ops.builtin, "indent"],
|
|
311
|
+
[ops.literal, ["hello"]],
|
|
312
|
+
],
|
|
313
|
+
]);
|
|
286
314
|
});
|
|
287
315
|
|
|
288
316
|
test("leadingSlashPath", () => {
|
|
@@ -324,10 +352,32 @@ describe("Origami parser", () => {
|
|
|
324
352
|
assertParse("multiLineComment", "/*\nHello, world!\n*/", null, false);
|
|
325
353
|
});
|
|
326
354
|
|
|
327
|
-
test("
|
|
328
|
-
assertParse("
|
|
329
|
-
|
|
330
|
-
|
|
355
|
+
test("namespace", () => {
|
|
356
|
+
assertParse("namespace", "js:", [ops.builtin, "js:"]);
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
test("namespacePath", () => {
|
|
360
|
+
assertParse("namespacePath", "js:Date", [
|
|
361
|
+
[ops.builtin, "js:"],
|
|
362
|
+
[ops.literal, "Date"],
|
|
363
|
+
]);
|
|
364
|
+
assertParse("namespacePath", "files:src/assets", [
|
|
365
|
+
[ops.builtin, "files:"],
|
|
366
|
+
[ops.literal, "src/"],
|
|
367
|
+
[ops.literal, "assets"],
|
|
368
|
+
]);
|
|
369
|
+
assertParse("namespacePath", "foo://bar", [
|
|
370
|
+
[ops.builtin, "foo:"],
|
|
371
|
+
[ops.literal, "bar"],
|
|
372
|
+
]);
|
|
373
|
+
assertParse("namespacePath", "http://example.com", [
|
|
374
|
+
[ops.builtin, "http:"],
|
|
375
|
+
[ops.literal, "example.com"],
|
|
376
|
+
]);
|
|
377
|
+
assertParse("namespacePath", "https://example.com/foo/", [
|
|
378
|
+
[ops.builtin, "https:"],
|
|
379
|
+
[ops.literal, "example.com/"],
|
|
380
|
+
[ops.literal, "foo/"],
|
|
331
381
|
]);
|
|
332
382
|
});
|
|
333
383
|
|
|
@@ -366,6 +416,18 @@ describe("Origami parser", () => {
|
|
|
366
416
|
["a", [ops.getter, [ops.scope, "b"]]],
|
|
367
417
|
["b", [ops.literal, 2]],
|
|
368
418
|
]);
|
|
419
|
+
assertParse(
|
|
420
|
+
"object",
|
|
421
|
+
`{
|
|
422
|
+
a = b
|
|
423
|
+
b = 2
|
|
424
|
+
}`,
|
|
425
|
+
[
|
|
426
|
+
ops.object,
|
|
427
|
+
["a", [ops.getter, [ops.scope, "b"]]],
|
|
428
|
+
["b", [ops.literal, 2]],
|
|
429
|
+
]
|
|
430
|
+
);
|
|
369
431
|
assertParse("object", "{ a: { b: 1 } }", [
|
|
370
432
|
ops.object,
|
|
371
433
|
["a", [ops.object, ["b", [ops.literal, 1]]]],
|
|
@@ -376,16 +438,19 @@ describe("Origami parser", () => {
|
|
|
376
438
|
]);
|
|
377
439
|
assertParse("object", "{ a: { b = fn() } }", [
|
|
378
440
|
ops.object,
|
|
379
|
-
[
|
|
441
|
+
[
|
|
442
|
+
"a/",
|
|
443
|
+
[ops.object, ["b", [ops.getter, [[ops.builtin, "fn"], undefined]]]],
|
|
444
|
+
],
|
|
380
445
|
]);
|
|
381
|
-
assertParse("object", "{ x = fn('a') }", [
|
|
446
|
+
assertParse("object", "{ x = fn.js('a') }", [
|
|
382
447
|
ops.object,
|
|
383
448
|
[
|
|
384
449
|
"x",
|
|
385
450
|
[
|
|
386
451
|
ops.getter,
|
|
387
452
|
[
|
|
388
|
-
[ops.scope, "fn"],
|
|
453
|
+
[ops.scope, "fn.js"],
|
|
389
454
|
[ops.literal, "a"],
|
|
390
455
|
],
|
|
391
456
|
],
|
|
@@ -410,10 +475,10 @@ describe("Origami parser", () => {
|
|
|
410
475
|
"a",
|
|
411
476
|
[ops.lambda, ["a"], [ops.scope, "a"]],
|
|
412
477
|
]);
|
|
413
|
-
assertParse("objectEntry", "posts/:
|
|
478
|
+
assertParse("objectEntry", "posts/: map(posts, post.ori)", [
|
|
414
479
|
"posts/",
|
|
415
480
|
[
|
|
416
|
-
[ops.
|
|
481
|
+
[ops.builtin, "map"],
|
|
417
482
|
[ops.inherited, "posts"],
|
|
418
483
|
[ops.scope, "post.ori"],
|
|
419
484
|
],
|
|
@@ -425,12 +490,12 @@ describe("Origami parser", () => {
|
|
|
425
490
|
"data",
|
|
426
491
|
[ops.getter, [ops.scope, "obj.json"]],
|
|
427
492
|
]);
|
|
428
|
-
assertParse("objectGetter", "foo =
|
|
493
|
+
assertParse("objectGetter", "foo = page.ori 'bar'", [
|
|
429
494
|
"foo",
|
|
430
495
|
[
|
|
431
496
|
ops.getter,
|
|
432
497
|
[
|
|
433
|
-
[ops.scope, "
|
|
498
|
+
[ops.scope, "page.ori"],
|
|
434
499
|
[ops.literal, "bar"],
|
|
435
500
|
],
|
|
436
501
|
],
|
|
@@ -446,7 +511,7 @@ describe("Origami parser", () => {
|
|
|
446
511
|
assertParse("objectProperty", "x: fn('a')", [
|
|
447
512
|
"x",
|
|
448
513
|
[
|
|
449
|
-
[ops.
|
|
514
|
+
[ops.builtin, "fn"],
|
|
450
515
|
[ops.literal, "a"],
|
|
451
516
|
],
|
|
452
517
|
]);
|
|
@@ -468,7 +533,7 @@ describe("Origami parser", () => {
|
|
|
468
533
|
ops.lambda,
|
|
469
534
|
["a", "b", "c"],
|
|
470
535
|
[
|
|
471
|
-
[ops.
|
|
536
|
+
[ops.builtin, "fn"],
|
|
472
537
|
[ops.scope, "a"],
|
|
473
538
|
[ops.scope, "b"],
|
|
474
539
|
[ops.scope, "c"],
|
|
@@ -481,7 +546,7 @@ describe("Origami parser", () => {
|
|
|
481
546
|
ops.lambda,
|
|
482
547
|
["b"],
|
|
483
548
|
[
|
|
484
|
-
[ops.
|
|
549
|
+
[ops.builtin, "fn"],
|
|
485
550
|
[ops.scope, "a"],
|
|
486
551
|
[ops.scope, "b"],
|
|
487
552
|
],
|
|
@@ -516,8 +581,9 @@ describe("Origami parser", () => {
|
|
|
516
581
|
});
|
|
517
582
|
|
|
518
583
|
test("pipeline", () => {
|
|
584
|
+
assertParse("pipeline", "foo", [ops.scope, "foo"]);
|
|
519
585
|
assertParse("pipeline", "a -> b", [
|
|
520
|
-
[ops.
|
|
586
|
+
[ops.builtin, "b"],
|
|
521
587
|
[ops.scope, "a"],
|
|
522
588
|
]);
|
|
523
589
|
assertParse("pipeline", "input → one.js → two.js", [
|
|
@@ -528,33 +594,28 @@ describe("Origami parser", () => {
|
|
|
528
594
|
],
|
|
529
595
|
]);
|
|
530
596
|
assertParse("pipeline", "fn a -> b", [
|
|
531
|
-
[ops.
|
|
597
|
+
[ops.builtin, "b"],
|
|
532
598
|
[
|
|
533
|
-
[ops.
|
|
599
|
+
[ops.builtin, "fn"],
|
|
534
600
|
[ops.scope, "a"],
|
|
535
601
|
],
|
|
536
602
|
]);
|
|
537
603
|
});
|
|
538
604
|
|
|
539
|
-
test("
|
|
540
|
-
assertParse("
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
ops.
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
assertParse("protocolCall", "http://localhost:5000/foo", [
|
|
554
|
-
ops.http,
|
|
555
|
-
[ops.literal, "localhost:5000"],
|
|
556
|
-
[ops.literal, "foo"],
|
|
557
|
-
]);
|
|
605
|
+
test("pipelineStep", () => {
|
|
606
|
+
assertParse("pipelineStep", "foo", [ops.builtin, "foo"]);
|
|
607
|
+
assertParse("pipelineStep", "=_", [ops.lambda, ["_"], [ops.scope, "_"]]);
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
test("program", () => {
|
|
611
|
+
assertParse(
|
|
612
|
+
"program",
|
|
613
|
+
`#!/usr/bin/env ori invoke
|
|
614
|
+
'Hello'
|
|
615
|
+
`,
|
|
616
|
+
[ops.literal, "Hello"],
|
|
617
|
+
false
|
|
618
|
+
);
|
|
558
619
|
});
|
|
559
620
|
|
|
560
621
|
test("scopeReference", () => {
|
|
@@ -577,17 +638,6 @@ describe("Origami parser", () => {
|
|
|
577
638
|
]);
|
|
578
639
|
});
|
|
579
640
|
|
|
580
|
-
test("shebang", () => {
|
|
581
|
-
assertParse(
|
|
582
|
-
"expression",
|
|
583
|
-
`#!/usr/bin/env ori @invoke
|
|
584
|
-
'Hello'
|
|
585
|
-
`,
|
|
586
|
-
[ops.literal, "Hello"],
|
|
587
|
-
false
|
|
588
|
-
);
|
|
589
|
-
});
|
|
590
|
-
|
|
591
641
|
test("singleLineComment", () => {
|
|
592
642
|
assertParse("singleLineComment", "// Hello, world!", null, false);
|
|
593
643
|
});
|
|
@@ -612,8 +662,12 @@ describe("Origami parser", () => {
|
|
|
612
662
|
});
|
|
613
663
|
|
|
614
664
|
test("taggedTemplate", () => {
|
|
615
|
-
assertParse("taggedTemplate", "
|
|
616
|
-
[ops.
|
|
665
|
+
assertParse("taggedTemplate", "indent`hello`", [
|
|
666
|
+
[ops.builtin, "indent"],
|
|
667
|
+
[ops.literal, ["hello"]],
|
|
668
|
+
]);
|
|
669
|
+
assertParse("taggedTemplate", "fn.js`Hello, world.`", [
|
|
670
|
+
[ops.scope, "fn.js"],
|
|
617
671
|
[ops.literal, ["Hello, world."]],
|
|
618
672
|
]);
|
|
619
673
|
});
|
|
@@ -650,13 +704,13 @@ describe("Origami parser", () => {
|
|
|
650
704
|
[ops.literal, ["", ""]],
|
|
651
705
|
[ops.concat, [ops.template, [ops.literal, ["nested"]]]],
|
|
652
706
|
]);
|
|
653
|
-
assertParse("templateLiteral", "`${map(people, =`${name}`)}`", [
|
|
707
|
+
assertParse("templateLiteral", "`${ map:(people, =`${name}`) }`", [
|
|
654
708
|
ops.template,
|
|
655
709
|
[ops.literal, ["", ""]],
|
|
656
710
|
[
|
|
657
711
|
ops.concat,
|
|
658
712
|
[
|
|
659
|
-
[ops.
|
|
713
|
+
[ops.builtin, "map:"],
|
|
660
714
|
[ops.scope, "people"],
|
|
661
715
|
[
|
|
662
716
|
ops.lambda,
|
|
@@ -689,6 +743,80 @@ describe("Origami parser", () => {
|
|
|
689
743
|
});
|
|
690
744
|
});
|
|
691
745
|
|
|
746
|
+
test("value", () => {
|
|
747
|
+
assertParse("value", "obj.json", [ops.scope, "obj.json"]);
|
|
748
|
+
assertParse("value", "(fn a, b, c)", [
|
|
749
|
+
[ops.builtin, "fn"],
|
|
750
|
+
[ops.scope, "a"],
|
|
751
|
+
[ops.scope, "b"],
|
|
752
|
+
[ops.scope, "c"],
|
|
753
|
+
]);
|
|
754
|
+
assertParse("value", "foo.bar('hello', 'world')", [
|
|
755
|
+
[ops.scope, "foo.bar"],
|
|
756
|
+
[ops.literal, "hello"],
|
|
757
|
+
[ops.literal, "world"],
|
|
758
|
+
]);
|
|
759
|
+
assertParse("value", "(key)('a')", [
|
|
760
|
+
[ops.scope, "key"],
|
|
761
|
+
[ops.literal, "a"],
|
|
762
|
+
]);
|
|
763
|
+
assertParse("value", "1", [ops.literal, 1]);
|
|
764
|
+
assertParse("value", "{ a: 1, b: 2 }", [
|
|
765
|
+
ops.object,
|
|
766
|
+
["a", [ops.literal, 1]],
|
|
767
|
+
["b", [ops.literal, 2]],
|
|
768
|
+
]);
|
|
769
|
+
assertParse("value", "serve { index.html: 'hello' }", [
|
|
770
|
+
[ops.builtin, "serve"],
|
|
771
|
+
[ops.object, ["index.html", [ops.literal, "hello"]]],
|
|
772
|
+
]);
|
|
773
|
+
assertParse("value", "fn =`x`", [
|
|
774
|
+
[ops.builtin, "fn"],
|
|
775
|
+
[ops.lambda, ["_"], [ops.template, [ops.literal, ["x"]]]],
|
|
776
|
+
]);
|
|
777
|
+
assertParse("value", "copy app.js(formulas), files:snapshot", [
|
|
778
|
+
[ops.builtin, "copy"],
|
|
779
|
+
[
|
|
780
|
+
[ops.scope, "app.js"],
|
|
781
|
+
[ops.scope, "formulas"],
|
|
782
|
+
],
|
|
783
|
+
[
|
|
784
|
+
[ops.builtin, "files:"],
|
|
785
|
+
[ops.literal, "snapshot"],
|
|
786
|
+
],
|
|
787
|
+
]);
|
|
788
|
+
assertParse("value", "map =`<li>${_}</li>`", [
|
|
789
|
+
[ops.builtin, "map"],
|
|
790
|
+
[
|
|
791
|
+
ops.lambda,
|
|
792
|
+
["_"],
|
|
793
|
+
[
|
|
794
|
+
ops.template,
|
|
795
|
+
[ops.literal, ["<li>", "</li>"]],
|
|
796
|
+
[ops.concat, [ops.scope, "_"]],
|
|
797
|
+
],
|
|
798
|
+
],
|
|
799
|
+
]);
|
|
800
|
+
assertParse("value", `"https://example.com"`, [
|
|
801
|
+
ops.literal,
|
|
802
|
+
"https://example.com",
|
|
803
|
+
]);
|
|
804
|
+
assertParse("value", "tag`Hello, ${name}!`", [
|
|
805
|
+
[ops.builtin, "tag"],
|
|
806
|
+
[ops.literal, ["Hello, ", "!"]],
|
|
807
|
+
[ops.concat, [ops.scope, "name"]],
|
|
808
|
+
]);
|
|
809
|
+
assertParse("value", "(post, slug) => fn.js(post, slug)", [
|
|
810
|
+
ops.lambda,
|
|
811
|
+
["post", "slug"],
|
|
812
|
+
[
|
|
813
|
+
[ops.scope, "fn.js"],
|
|
814
|
+
[ops.scope, "post"],
|
|
815
|
+
[ops.scope, "slug"],
|
|
816
|
+
],
|
|
817
|
+
]);
|
|
818
|
+
});
|
|
819
|
+
|
|
692
820
|
function assertParse(startRule, source, expected, checkLocation = true) {
|
|
693
821
|
const code = parse(source, {
|
|
694
822
|
grammarSource: { text: source },
|
|
@@ -699,7 +827,7 @@ function assertParse(startRule, source, expected, checkLocation = true) {
|
|
|
699
827
|
// entire source. We skip this check in cases where the source starts or ends
|
|
700
828
|
// with a comment; the parser will strip those.
|
|
701
829
|
if (checkLocation) {
|
|
702
|
-
assert(code.location);
|
|
830
|
+
assert(code.location, "no location");
|
|
703
831
|
const resultSource = code.location.source.text.slice(
|
|
704
832
|
code.location.start.offset,
|
|
705
833
|
code.location.end.offset
|