@weborigami/language 0.3.3-jse.3 → 0.3.3

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.
@@ -1,7 +1,7 @@
1
1
  import assert from "node:assert";
2
2
  import { describe, test } from "node:test";
3
3
  import { parse } from "../../src/compiler/parse.js";
4
- import { markers } from "../../src/compiler/parserHelpers.js";
4
+ import { undetermined } from "../../src/compiler/parserHelpers.js";
5
5
  import * as ops from "../../src/runtime/ops.js";
6
6
  import { assertCodeEqual } from "./codeHelpers.js";
7
7
 
@@ -19,63 +19,6 @@ describe("Origami parser", () => {
19
19
  ]);
20
20
  });
21
21
 
22
- describe("angleBracketLiteral", () => {
23
- test("with path", () => {
24
- assertParse(
25
- "angleBracketLiteral",
26
- "<index.html>",
27
- [[ops.scope], [ops.literal, "index.html"]],
28
- "jse"
29
- );
30
- assertParse(
31
- "angleBracketLiteral",
32
- "<foo/bar/baz>",
33
- [
34
- [ops.scope],
35
- [ops.literal, "foo/"],
36
- [ops.literal, "bar/"],
37
- [ops.literal, "baz"],
38
- ],
39
- "jse"
40
- );
41
- });
42
-
43
- test("root directory", () => {
44
- assertParse("angleBracketLiteral", "</>", [ops.rootDirectory]);
45
- assertParse("angleBracketLiteral", "</etc/passwd>", [
46
- [ops.rootDirectory],
47
- [ops.literal, "etc/"],
48
- [ops.literal, "passwd"],
49
- ]);
50
- });
51
-
52
- test("home directory", () => {
53
- assertParse("angleBracketLiteral", "<~>", [ops.homeDirectory]);
54
- assertParse("angleBracketLiteral", "<~/.bash_profile>", [
55
- [ops.homeDirectory],
56
- [ops.literal, ".bash_profile"],
57
- ]);
58
- });
59
-
60
- test("with protocol URL", () => {
61
- assertParse("angleBracketLiteral", "<files:src/assets>", [
62
- [markers.global, "files:"],
63
- [ops.literal, "src/"],
64
- [ops.literal, "assets"],
65
- ]);
66
- assertParse(
67
- "angleBracketLiteral",
68
- "<https://example.com/data.yaml>",
69
- [
70
- [markers.global, "https:"],
71
- [ops.literal, "example.com/"],
72
- [ops.literal, "data.yaml"],
73
- ],
74
- "jse"
75
- );
76
- });
77
- });
78
-
79
22
  test("arrayLiteral", () => {
80
23
  assertParse("arrayLiteral", "[]", [ops.array]);
81
24
  assertParse("arrayLiteral", "[1, 2, 3]", [
@@ -98,7 +41,7 @@ describe("Origami parser", () => {
98
41
  [ops.literal, 4],
99
42
  ]);
100
43
  assertParse("arrayLiteral", "[ 1, ...[2, 3]]", [
101
- ops.flat,
44
+ ops.merge,
102
45
  [ops.array, [ops.literal, 1]],
103
46
  [ops.array, [ops.literal, 2], [ops.literal, 3]],
104
47
  ]);
@@ -117,12 +60,12 @@ describe("Origami parser", () => {
117
60
  assertParse("arrowFunction", "() => foo", [
118
61
  ops.lambda,
119
62
  [],
120
- [markers.reference, [ops.literal, "foo"]],
63
+ [ops.scope, "foo"],
121
64
  ]);
122
65
  assertParse("arrowFunction", "x => y", [
123
66
  ops.lambda,
124
67
  [[ops.literal, "x"]],
125
- [markers.reference, [ops.literal, "y"]],
68
+ [ops.scope, "y"],
126
69
  ]);
127
70
  assertParse("arrowFunction", "(a, b, c) ⇒ fn(a, b, c)", [
128
71
  ops.lambda,
@@ -132,10 +75,10 @@ describe("Origami parser", () => {
132
75
  [ops.literal, "c"],
133
76
  ],
134
77
  [
135
- [markers.global, "fn"],
136
- [markers.reference, [ops.literal, "a"]],
137
- [markers.reference, [ops.literal, "b"]],
138
- [markers.reference, [ops.literal, "c"]],
78
+ [ops.builtin, "fn"],
79
+ [ops.scope, "a"],
80
+ [ops.scope, "b"],
81
+ [ops.scope, "c"],
139
82
  ],
140
83
  ]);
141
84
  assertParse("arrowFunction", "a => b => fn(a, b)", [
@@ -145,9 +88,9 @@ describe("Origami parser", () => {
145
88
  ops.lambda,
146
89
  [[ops.literal, "b"]],
147
90
  [
148
- [markers.global, "fn"],
149
- [markers.reference, [ops.literal, "a"]],
150
- [markers.reference, [ops.literal, "b"]],
91
+ [ops.builtin, "fn"],
92
+ [ops.scope, "a"],
93
+ [ops.scope, "b"],
151
94
  ],
152
95
  ],
153
96
  ]);
@@ -177,177 +120,147 @@ describe("Origami parser", () => {
177
120
  ]);
178
121
  });
179
122
 
180
- describe("callExpression", () => {
181
- test("with parentheses arguments", () => {
182
- assertParse("callExpression", "fn()", [
183
- [markers.global, "fn"],
184
- undefined,
185
- ]);
186
- assertParse("callExpression", "foo.js(arg)", [
187
- [markers.reference, [ops.literal, "foo.js"]],
188
- [markers.reference, [ops.literal, "arg"]],
189
- ]);
190
- assertParse("callExpression", "fn(a, b)", [
191
- [markers.global, "fn"],
192
- [markers.reference, [ops.literal, "a"]],
193
- [markers.reference, [ops.literal, "b"]],
194
- ]);
195
- assertParse("callExpression", "foo.js( a , b )", [
196
- [markers.reference, [ops.literal, "foo.js"]],
197
- [markers.reference, [ops.literal, "a"]],
198
- [markers.reference, [ops.literal, "b"]],
199
- ]);
200
- assertParse("callExpression", "fn()(arg)", [
201
- [[markers.global, "fn"], undefined],
202
- [markers.reference, [ops.literal, "arg"]],
203
- ]);
204
- });
205
- test("call chains", () => {
206
- assertParse("callExpression", "(foo.js())('arg')", [
207
- [[markers.reference, [ops.literal, "foo.js"]], undefined],
208
- [ops.literal, "arg"],
209
- ]);
210
- assertParse("callExpression", "fn('a')('b')", [
211
- [
212
- [markers.global, "fn"],
213
- [ops.literal, "a"],
214
- ],
215
- [ops.literal, "b"],
216
- ]);
217
- assertParse("callExpression", "(foo.js())(a, b)", [
218
- [[markers.reference, [ops.literal, "foo.js"]], undefined],
219
- [markers.reference, [ops.literal, "a"]],
220
- [markers.reference, [ops.literal, "b"]],
221
- ]);
222
- });
223
- test("with paths", () => {
224
- assertParse("callExpression", "/", [ops.rootDirectory]);
225
- assertParse("callExpression", "tree/", [
226
- ops.unpack,
227
- [markers.reference, [ops.literal, "tree/"]],
228
- ]);
229
- assertParse("callExpression", "tree/foo/bar", [
230
- markers.reference,
231
- [ops.literal, "tree/"],
232
- [ops.literal, "foo/"],
233
- [ops.literal, "bar"],
234
- ]);
235
- assertParse("callExpression", "tree/foo/bar/", [
236
- markers.reference,
237
- [ops.literal, "tree/"],
238
- [ops.literal, "foo/"],
239
- [ops.literal, "bar/"],
240
- ]);
241
- // Consecutive slahes in a path are removed
242
- assertParse("callExpression", "path//key", [
243
- markers.reference,
244
- [ops.literal, "path/"],
245
- [ops.literal, "key"],
246
- ]);
247
- assertParse("callExpression", "/foo/bar", [
248
- [ops.rootDirectory],
249
- [ops.literal, "foo/"],
250
- [ops.literal, "bar"],
251
- ]);
252
- assertParse("callExpression", "{ a: 1, b: 2}/b", [
253
- [ops.object, ["a", [ops.literal, 1]], ["b", [ops.literal, 2]]],
254
- [ops.literal, "b"],
255
- ]);
256
- assertParse("callExpression", "files:foo/bar", [
257
- [markers.global, "files:"],
258
- [ops.literal, "foo/"],
259
- [ops.literal, "bar"],
260
- ]);
261
- });
262
- test("path and parentheses chains", () => {
263
- assertParse("callExpression", "foo.js()/key", [
264
- [[markers.reference, [ops.literal, "foo.js"]], undefined],
265
- [ops.literal, "key"],
266
- ]);
267
- assertParse("callExpression", "tree/key()", [
268
- [markers.reference, [ops.literal, "tree/"], [ops.literal, "key"]],
269
- undefined,
270
- ]);
271
- assertParse("callExpression", "(tree)/", [
272
- ops.unpack,
273
- [markers.reference, [ops.literal, "tree/"]],
274
- ]);
275
- assertParse("callExpression", "fn()/key()", [
276
- [
277
- [[markers.global, "fn"], undefined],
278
- [ops.literal, "key"],
279
- ],
280
- undefined,
281
- ]);
282
- assertParse("callExpression", "package:@weborigami/dropbox/auth(creds)", [
283
- [
284
- [markers.global, "package:"],
285
- [ops.literal, "@weborigami/"],
286
- [ops.literal, "dropbox/"],
287
- [ops.literal, "auth"],
288
- ],
289
- [markers.reference, [ops.literal, "creds"]],
290
- ]);
291
- });
292
- test("tagged templates", () => {
293
- assertParse("callExpression", "indent`hello`", [
294
- [markers.global, "indent"],
295
- [ops.literal, ["hello"]],
296
- ]);
297
- assertParse("callExpression", "fn.js`Hello, world.`", [
298
- [markers.reference, [ops.literal, "fn.js"]],
299
- [ops.literal, ["Hello, world."]],
300
- ]);
301
- });
302
- test("protocols", () => {
303
- assertParse("callExpression", "files:src/assets", [
304
- [markers.global, "files:"],
123
+ test("callExpression", () => {
124
+ assertParse("callExpression", "fn()", [[ops.builtin, "fn"], undefined]);
125
+ assertParse("callExpression", "foo.js(arg)", [
126
+ [ops.scope, "foo.js"],
127
+ [ops.scope, "arg"],
128
+ ]);
129
+ assertParse("callExpression", "fn(a, b)", [
130
+ [ops.builtin, "fn"],
131
+ [ops.scope, "a"],
132
+ [ops.scope, "b"],
133
+ ]);
134
+ assertParse("callExpression", "foo.js( a , b )", [
135
+ [ops.scope, "foo.js"],
136
+ [ops.scope, "a"],
137
+ [ops.scope, "b"],
138
+ ]);
139
+ assertParse("callExpression", "fn()(arg)", [
140
+ [[ops.builtin, "fn"], undefined],
141
+ [ops.scope, "arg"],
142
+ ]);
143
+ assertParse("callExpression", "tree/", [ops.unpack, [ops.scope, "tree/"]]);
144
+ assertParse("callExpression", "tree/foo/bar", [
145
+ ops.traverse,
146
+ [ops.scope, "tree/"],
147
+ [ops.literal, "foo/"],
148
+ [ops.literal, "bar"],
149
+ ]);
150
+ assertParse("callExpression", "tree/foo/bar/", [
151
+ ops.traverse,
152
+ [ops.scope, "tree/"],
153
+ [ops.literal, "foo/"],
154
+ [ops.literal, "bar/"],
155
+ ]);
156
+ assertParse("callExpression", "/foo/bar", [
157
+ ops.traverse,
158
+ [ops.rootDirectory, [ops.literal, "foo/"]],
159
+ [ops.literal, "bar"],
160
+ ]);
161
+ assertParse("callExpression", "foo.js()/key", [
162
+ ops.traverse,
163
+ [[ops.scope, "foo.js"], undefined],
164
+ [ops.literal, "key"],
165
+ ]);
166
+ assertParse("callExpression", "tree/key()", [
167
+ [ops.traverse, [ops.scope, "tree/"], [ops.literal, "key"]],
168
+ undefined,
169
+ ]);
170
+ assertParse("callExpression", "(tree)/", [ops.unpack, [ops.scope, "tree"]]);
171
+ assertParse("callExpression", "fn()/key()", [
172
+ [ops.traverse, [[ops.builtin, "fn"], undefined], [ops.literal, "key"]],
173
+ undefined,
174
+ ]);
175
+ assertParse("callExpression", "(foo.js())('arg')", [
176
+ [[ops.scope, "foo.js"], undefined],
177
+ [ops.literal, "arg"],
178
+ ]);
179
+ assertParse("callExpression", "fn('a')('b')", [
180
+ [
181
+ [ops.builtin, "fn"],
182
+ [ops.literal, "a"],
183
+ ],
184
+ [ops.literal, "b"],
185
+ ]);
186
+ assertParse("callExpression", "(foo.js())(a, b)", [
187
+ [[ops.scope, "foo.js"], undefined],
188
+ [ops.scope, "a"],
189
+ [ops.scope, "b"],
190
+ ]);
191
+ assertParse("callExpression", "{ a: 1, b: 2}/b", [
192
+ ops.traverse,
193
+ [ops.object, ["a", [ops.literal, 1]], ["b", [ops.literal, 2]]],
194
+ [ops.literal, "b"],
195
+ ]);
196
+ assertParse("callExpression", "indent`hello`", [
197
+ [ops.builtin, "indent"],
198
+ [ops.literal, ["hello"]],
199
+ ]);
200
+ assertParse("callExpression", "fn.js`Hello, world.`", [
201
+ [ops.scope, "fn.js"],
202
+ [ops.literal, ["Hello, world."]],
203
+ ]);
204
+ assertParse("callExpression", "files:src/assets", [
205
+ ops.traverse,
206
+ [
207
+ [ops.builtin, "files:"],
305
208
  [ops.literal, "src/"],
306
- [ops.literal, "assets"],
307
- ]);
308
- assertParse("callExpression", "new:(js:Date, '2025-01-01')", [
309
- [markers.global, "new:"],
209
+ ],
210
+ [ops.literal, "assets"],
211
+ ]);
212
+ assertParse("callExpression", "new:(js:Date, '2025-01-01')", [
213
+ [ops.builtin, "new:"],
214
+ [
215
+ [ops.builtin, "js:"],
216
+ [ops.literal, "Date"],
217
+ ],
218
+ [ops.literal, "2025-01-01"],
219
+ ]);
220
+ assertParse("callExpression", "map(markdown, mdHtml)", [
221
+ [ops.builtin, "map"],
222
+ [ops.scope, "markdown"],
223
+ [ops.scope, "mdHtml"],
224
+ ]);
225
+ assertParse("callExpression", "package:@weborigami/dropbox/auth(creds)", [
226
+ [
227
+ ops.traverse,
310
228
  [
311
- [markers.global, "js:"],
312
- [ops.literal, "Date"],
229
+ [ops.builtin, "package:"],
230
+ [ops.literal, "@weborigami/"],
313
231
  ],
314
- [ops.literal, "2025-01-01"],
315
- ]);
316
- });
232
+ [ops.literal, "dropbox/"],
233
+ [ops.literal, "auth"],
234
+ ],
235
+ [ops.scope, "creds"],
236
+ ]);
317
237
  });
318
238
 
319
239
  test("callExpression using property acccess", () => {
320
240
  assertParse("callExpression", "(foo).bar", [
321
- markers.reference,
322
- [ops.literal, "foo/"],
241
+ ops.traverse,
242
+ [ops.scope, "foo"],
323
243
  [ops.literal, "bar"],
324
244
  ]);
325
245
  assertParse("callExpression", "(foo).bar.baz", [
326
- markers.reference,
327
- [ops.literal, "foo/"],
328
- [ops.literal, "bar/"],
246
+ ops.traverse,
247
+ [ops.traverse, [ops.scope, "foo"], [ops.literal, "bar"]],
329
248
  [ops.literal, "baz"],
330
249
  ]);
331
250
  assertParse("callExpression", "foo[bar]", [
332
- markers.reference,
333
- [ops.literal, "foo/"],
334
- [markers.reference, [ops.literal, "bar"]],
251
+ ops.traverse,
252
+ [ops.scope, "foo/"],
253
+ [ops.scope, "bar"],
335
254
  ]);
336
- assertParse(
337
- "callExpression",
338
- "Tree.map",
339
- [markers.reference, [ops.literal, "Tree/"], [ops.literal, "map"]],
340
- "jse"
341
- );
342
255
  });
343
256
 
344
257
  test("commaExpression", () => {
345
258
  assertParse("commaExpression", "1", [ops.literal, 1]);
346
259
  assertParse("commaExpression", "a, b, c", [
347
260
  ops.comma,
348
- [markers.reference, [ops.literal, "a"]],
349
- [markers.reference, [ops.literal, "b"]],
350
- [markers.reference, [ops.literal, "c"]],
261
+ [ops.scope, "a"],
262
+ [ops.scope, "b"],
263
+ [ops.scope, "c"],
351
264
  ]);
352
265
  });
353
266
 
@@ -355,19 +268,19 @@ describe("Origami parser", () => {
355
268
  assertParse("conditionalExpression", "1", [ops.literal, 1]);
356
269
  assertParse("conditionalExpression", "true ? 1 : 0", [
357
270
  ops.conditional,
358
- [markers.reference, [ops.literal, "true"]],
271
+ [ops.scope, "true"],
359
272
  [ops.literal, 1],
360
273
  [ops.literal, 0],
361
274
  ]);
362
275
  assertParse("conditionalExpression", "false ? () => 1 : 0", [
363
276
  ops.conditional,
364
- [markers.reference, [ops.literal, "false"]],
277
+ [ops.scope, "false"],
365
278
  [ops.lambda, [], [ops.lambda, [], [ops.literal, 1]]],
366
279
  [ops.literal, 0],
367
280
  ]);
368
281
  assertParse("conditionalExpression", "false ? =1 : 0", [
369
282
  ops.conditional,
370
- [markers.reference, [ops.literal, "false"]],
283
+ [ops.scope, "false"],
371
284
  [ops.lambda, [], [ops.lambda, [[ops.literal, "_"]], [ops.literal, 1]]],
372
285
  [ops.literal, 0],
373
286
  ]);
@@ -381,12 +294,8 @@ describe("Origami parser", () => {
381
294
  ]);
382
295
  assertParse("equalityExpression", "a === b === c", [
383
296
  ops.strictEqual,
384
- [
385
- ops.strictEqual,
386
- [markers.reference, [ops.literal, "a"]],
387
- [markers.reference, [ops.literal, "b"]],
388
- ],
389
- [markers.reference, [ops.literal, "c"]],
297
+ [ops.strictEqual, [undetermined, "a"], [undetermined, "b"]],
298
+ [undetermined, "c"],
390
299
  ]);
391
300
  assertParse("equalityExpression", "1 !== 1", [
392
301
  ops.notStrictEqual,
@@ -454,8 +363,8 @@ Body`,
454
363
  [
455
364
  ops.getter,
456
365
  [
457
- [markers.reference, [ops.literal, "index.ori"]],
458
- [markers.reference, [ops.literal, "teamData.yaml"]],
366
+ [ops.scope, "index.ori"],
367
+ [ops.scope, "teamData.yaml"],
459
368
  ],
460
369
  ],
461
370
  ],
@@ -464,12 +373,9 @@ Body`,
464
373
  [
465
374
  ops.getter,
466
375
  [
467
- [markers.global, "map"],
468
- [markers.reference, [ops.literal, "images"]],
469
- [
470
- ops.object,
471
- ["value", [markers.reference, [ops.literal, "thumbnail.js"]]],
472
- ],
376
+ [ops.builtin, "map"],
377
+ [ops.scope, "images"],
378
+ [ops.object, ["value", [ops.scope, "thumbnail.js"]]],
473
379
  ],
474
380
  ],
475
381
  ],
@@ -477,54 +383,57 @@ Body`,
477
383
  );
478
384
 
479
385
  // Builtin on its own is the function itself, not a function call
480
- assertParse("expression", "mdHtml:", [markers.global, "mdHtml:"]);
386
+ assertParse("expression", "mdHtml:", [ops.builtin, "mdHtml:"]);
481
387
 
482
- // Consecutive slashes at start of something = comment
483
- assertParse(
484
- "expression",
485
- "x //comment",
486
- [markers.reference, [ops.literal, "x"]],
487
- "jse",
488
- false
489
- );
388
+ // Consecutive slahes in a path are removed
389
+ assertParse("expression", "path//key", [
390
+ ops.traverse,
391
+ [ops.scope, "path/"],
392
+ [ops.literal, "key"],
393
+ ]);
490
394
 
395
+ // Single slash at start of something = absolute file path
396
+ assertParse("expression", "/path", [
397
+ ops.rootDirectory,
398
+ [ops.literal, "path"],
399
+ ]);
400
+
401
+ // Consecutive slashes at start of something = comment
402
+ assertParse("expression", "path //comment", [ops.scope, "path"], false);
491
403
  assertParse("expression", "page.ori(mdHtml:(about.md))", [
492
- [markers.reference, [ops.literal, "page.ori"]],
404
+ [ops.scope, "page.ori"],
493
405
  [
494
- [markers.global, "mdHtml:"],
495
- [markers.reference, [ops.literal, "about.md"]],
406
+ [ops.builtin, "mdHtml:"],
407
+ [ops.scope, "about.md"],
496
408
  ],
497
409
  ]);
498
410
 
499
411
  // Slash on its own is the root folder
500
412
  assertParse("expression", "keys /", [
501
- [markers.global, "keys"],
413
+ [ops.builtin, "keys"],
502
414
  [ops.rootDirectory],
503
415
  ]);
504
416
 
505
417
  assertParse("expression", "'Hello' -> test.orit", [
506
- [markers.reference, [ops.literal, "test.orit"]],
418
+ [ops.scope, "test.orit"],
507
419
  [ops.literal, "Hello"],
508
420
  ]);
509
- assertParse("expression", "obj.json", [
510
- markers.reference,
511
- [ops.literal, "obj.json"],
512
- ]);
421
+ assertParse("expression", "obj.json", [ops.scope, "obj.json"]);
513
422
  assertParse("expression", "(fn a, b, c)", [
514
- [markers.global, "fn"],
515
- [markers.reference, [ops.literal, "a"]],
516
- [markers.reference, [ops.literal, "b"]],
517
- [markers.reference, [ops.literal, "c"]],
423
+ [ops.builtin, "fn"],
424
+ [undetermined, "a"],
425
+ [undetermined, "b"],
426
+ [undetermined, "c"],
518
427
  ]);
519
428
  assertParse("expression", "foo.bar('hello', 'world')", [
520
- [markers.reference, [ops.literal, "foo.bar"]],
429
+ [ops.scope, "foo.bar"],
521
430
  [ops.literal, "hello"],
522
431
  [ops.literal, "world"],
523
432
  ]);
524
- // assertParse("expression", "(key)('a')", [
525
- // [markers.reference, [ops.literal, "key"]],
526
- // [ops.literal, "a"],
527
- // ]);
433
+ assertParse("expression", "(key)('a')", [
434
+ [ops.scope, "key"],
435
+ [ops.literal, "a"],
436
+ ]);
528
437
  assertParse("expression", "1", [ops.literal, 1]);
529
438
  assertParse("expression", "{ a: 1, b: 2 }", [
530
439
  ops.object,
@@ -532,49 +441,41 @@ Body`,
532
441
  ["b", [ops.literal, 2]],
533
442
  ]);
534
443
  assertParse("expression", "serve { index.html: 'hello' }", [
535
- [markers.global, "serve"],
444
+ [ops.builtin, "serve"],
536
445
  [ops.object, ["index.html", [ops.literal, "hello"]]],
537
446
  ]);
538
447
  assertParse("expression", "fn =`x`", [
539
- [markers.global, "fn"],
540
- [
541
- ops.lambda,
542
- [[ops.literal, "_"]],
543
- [ops.templateTree, [ops.literal, ["x"]]],
544
- ],
448
+ [ops.builtin, "fn"],
449
+ [ops.lambda, [[ops.literal, "_"]], [ops.template, [ops.literal, ["x"]]]],
545
450
  ]);
546
451
  assertParse("expression", "copy app.js(formulas), files:snapshot", [
547
- [markers.global, "copy"],
452
+ [ops.builtin, "copy"],
548
453
  [
549
- [markers.reference, [ops.literal, "app.js"]],
550
- [markers.reference, [ops.literal, "formulas"]],
454
+ [ops.scope, "app.js"],
455
+ [ops.scope, "formulas"],
551
456
  ],
552
457
  [
553
- [markers.global, "files:"],
458
+ [ops.builtin, "files:"],
554
459
  [ops.literal, "snapshot"],
555
460
  ],
556
461
  ]);
557
462
  assertParse("expression", "map =`<li>${_}</li>`", [
558
- [markers.global, "map"],
463
+ [ops.builtin, "map"],
559
464
  [
560
465
  ops.lambda,
561
466
  [[ops.literal, "_"]],
562
- [
563
- ops.templateTree,
564
- [ops.literal, ["<li>", "</li>"]],
565
- [markers.reference, [ops.literal, "_"]],
566
- ],
467
+ [ops.template, [ops.literal, ["<li>", "</li>"]], [ops.scope, "_"]],
567
468
  ],
568
469
  ]);
569
470
  assertParse("expression", `https://example.com/about/`, [
570
- [markers.global, "https:"],
471
+ [ops.builtin, "https:"],
571
472
  [ops.literal, "example.com/"],
572
473
  [ops.literal, "about/"],
573
474
  ]);
574
475
  assertParse("expression", "tag`Hello, ${name}!`", [
575
- [markers.global, "tag"],
476
+ [ops.builtin, "tag"],
576
477
  [ops.literal, ["Hello, ", "!"]],
577
- [ops.concat, [markers.reference, [ops.literal, "name"]]],
478
+ [ops.concat, [ops.scope, "name"]],
578
479
  ]);
579
480
  assertParse("expression", "(post, slug) => fn.js(post, slug)", [
580
481
  ops.lambda,
@@ -583,71 +484,52 @@ Body`,
583
484
  [ops.literal, "slug"],
584
485
  ],
585
486
  [
586
- [markers.reference, [ops.literal, "fn.js"]],
587
- [markers.reference, [ops.literal, "post"]],
588
- [markers.reference, [ops.literal, "slug"]],
487
+ [ops.scope, "fn.js"],
488
+ [ops.scope, "post"],
489
+ [ops.scope, "slug"],
589
490
  ],
590
491
  ]);
591
492
  assertParse("expression", "keys ~", [
592
- [markers.global, "keys"],
493
+ [ops.builtin, "keys"],
593
494
  [ops.homeDirectory],
594
495
  ]);
595
496
  assertParse("expression", "keys /Users/alice", [
596
- [markers.global, "keys"],
597
- [[ops.rootDirectory], [ops.literal, "Users/"], [ops.literal, "alice"]],
497
+ [ops.builtin, "keys"],
498
+ [
499
+ ops.traverse,
500
+ [ops.rootDirectory, [ops.literal, "Users/"]],
501
+ [ops.literal, "alice"],
502
+ ],
598
503
  ]);
599
504
 
600
505
  // Verify parser treatment of identifiers containing operators
601
506
  assertParse("expression", "a + b", [
602
507
  ops.addition,
603
- [markers.reference, [ops.literal, "a"]],
604
- [markers.reference, [ops.literal, "b"]],
508
+ [undetermined, "a"],
509
+ [undetermined, "b"],
605
510
  ]);
606
- assertParse("expression", "a+b", [markers.reference, [ops.literal, "a+b"]]);
511
+ assertParse("expression", "a+b", [ops.scope, "a+b"]);
607
512
  assertParse("expression", "a - b", [
608
513
  ops.subtraction,
609
- [markers.reference, [ops.literal, "a"]],
610
- [markers.reference, [ops.literal, "b"]],
514
+ [undetermined, "a"],
515
+ [undetermined, "b"],
611
516
  ]);
612
- assertParse("expression", "a-b", [markers.reference, [ops.literal, "a-b"]]);
613
- assertParse("expression", "a&b", [markers.reference, [ops.literal, "a&b"]]);
517
+ assertParse("expression", "a-b", [ops.scope, "a-b"]);
518
+ assertParse("expression", "a&b", [ops.scope, "a&b"]);
614
519
  assertParse("expression", "a & b", [
615
520
  ops.bitwiseAnd,
616
- [markers.reference, [ops.literal, "a"]],
617
- [markers.reference, [ops.literal, "b"]],
521
+ [undetermined, "a"],
522
+ [undetermined, "b"],
618
523
  ]);
619
524
  });
620
525
 
621
- test("frontMatterExpression", () => {
622
- assertParse(
623
- "frontMatterExpression",
624
- `---
625
- (name) => _template()
626
- ---
627
- `,
628
- [
629
- ops.lambda,
630
- [[ops.literal, "name"]],
631
- [[markers.reference, [ops.literal, "_template"]], undefined],
632
- ],
633
- "jse",
634
- false
635
- );
636
- });
637
-
638
526
  test("group", () => {
639
- assertParse("group", "(hello)", [
640
- markers.reference,
641
- [ops.literal, "hello"],
642
- ]);
643
- assertParse("group", "(((nested)))", [
644
- markers.reference,
645
- [ops.literal, "nested"],
646
- ]);
647
- assertParse("group", "(fn())", [[markers.global, "fn"], undefined]);
527
+ assertParse("group", "(hello)", [ops.scope, "hello"]);
528
+ assertParse("group", "(((nested)))", [ops.scope, "nested"]);
529
+ assertParse("group", "(fn())", [[ops.builtin, "fn"], undefined]);
648
530
  assertParse("group", "(a -> b)", [
649
- [markers.global, "b"],
650
- [markers.reference, [ops.literal, "a"]],
531
+ [ops.builtin, "b"],
532
+ [ops.scope, "a"],
651
533
  ]);
652
534
  });
653
535
 
@@ -662,50 +544,50 @@ Body`,
662
544
  });
663
545
 
664
546
  test("identifier", () => {
665
- assertParse("identifier", "abc", "abc", "shell", false);
666
- assertParse("identifier", "index.html", "index.html", "shell", false);
667
- assertParse("identifier", "foo\\ bar", "foo bar", "shell", false);
668
- assertParse("identifier", "x-y-z", "x-y-z", "shell", false);
547
+ assertParse("identifier", "abc", "abc", false);
548
+ assertParse("identifier", "index.html", "index.html", false);
549
+ assertParse("identifier", "foo\\ bar", "foo bar", false);
550
+ assertParse("identifier", "x-y-z", "x-y-z", false);
669
551
  });
670
552
 
671
553
  test("implicitParenthesesCallExpression", () => {
672
554
  assertParse("implicitParenthesesCallExpression", "fn arg", [
673
- [markers.global, "fn"],
674
- [markers.reference, [ops.literal, "arg"]],
555
+ [ops.builtin, "fn"],
556
+ [undetermined, "arg"],
675
557
  ]);
676
558
  assertParse("implicitParenthesesCallExpression", "page.ori 'a', 'b'", [
677
- [markers.reference, [ops.literal, "page.ori"]],
559
+ [ops.scope, "page.ori"],
678
560
  [ops.literal, "a"],
679
561
  [ops.literal, "b"],
680
562
  ]);
681
563
  assertParse("implicitParenthesesCallExpression", "fn a(b), c", [
682
- [markers.global, "fn"],
564
+ [ops.builtin, "fn"],
683
565
  [
684
- [markers.global, "a"],
685
- [markers.reference, [ops.literal, "b"]],
566
+ [ops.builtin, "a"],
567
+ [ops.scope, "b"],
686
568
  ],
687
- [markers.reference, [ops.literal, "c"]],
569
+ [undetermined, "c"],
688
570
  ]);
689
571
  assertParse("implicitParenthesesCallExpression", "(fn()) 'arg'", [
690
- [[markers.global, "fn"], undefined],
572
+ [[ops.builtin, "fn"], undefined],
691
573
  [ops.literal, "arg"],
692
574
  ]);
693
575
  assertParse("implicitParenthesesCallExpression", "tree/key arg", [
694
- [markers.reference, [ops.literal, "tree/"], [ops.literal, "key"]],
695
- [markers.reference, [ops.literal, "arg"]],
576
+ [ops.traverse, [ops.scope, "tree/"], [ops.literal, "key"]],
577
+ [undetermined, "arg"],
696
578
  ]);
697
579
  assertParse("implicitParenthesesCallExpression", "foo.js bar.ori 'arg'", [
698
- [markers.reference, [ops.literal, "foo.js"]],
580
+ [ops.scope, "foo.js"],
699
581
  [
700
- [markers.reference, [ops.literal, "bar.ori"]],
582
+ [ops.scope, "bar.ori"],
701
583
  [ops.literal, "arg"],
702
584
  ],
703
585
  ]);
704
586
  });
705
587
 
706
588
  test("jsIdentifier", () => {
707
- assertParse("jsIdentifier", "foo", [ops.literal, "foo"], "jse");
708
- assertParse("jsIdentifier", "$Δelta", [ops.literal, "$Δelta"], "jse");
589
+ assertParse("jsIdentifier", "foo", "foo", false);
590
+ assertParse("jsIdentifier", "$Δelta", "$Δelta", false);
709
591
  assertThrows(
710
592
  "jsIdentifier",
711
593
  "1stCharacterIsNumber",
@@ -755,8 +637,8 @@ Body`,
755
637
  test("logicalAndExpression", () => {
756
638
  assertParse("logicalAndExpression", "true && false", [
757
639
  ops.logicalAnd,
758
- [markers.reference, [ops.literal, "true"]],
759
- [ops.lambda, [], [markers.reference, [ops.literal, "false"]]],
640
+ [ops.scope, "true"],
641
+ [ops.lambda, [], [undetermined, "false"]],
760
642
  ]);
761
643
  });
762
644
 
@@ -768,9 +650,9 @@ Body`,
768
650
  ]);
769
651
  assertParse("logicalOrExpression", "false || false || true", [
770
652
  ops.logicalOr,
771
- [markers.reference, [ops.literal, "false"]],
772
- [ops.lambda, [], [markers.reference, [ops.literal, "false"]]],
773
- [ops.lambda, [], [markers.reference, [ops.literal, "true"]]],
653
+ [ops.scope, "false"],
654
+ [ops.lambda, [], [undetermined, "false"]],
655
+ [ops.lambda, [], [undetermined, "true"]],
774
656
  ]);
775
657
  assertParse("logicalOrExpression", "1 || 2 && 0", [
776
658
  ops.logicalOr,
@@ -780,13 +662,7 @@ Body`,
780
662
  });
781
663
 
782
664
  test("multiLineComment", () => {
783
- assertParse(
784
- "multiLineComment",
785
- "/*\nHello, world!\n*/",
786
- null,
787
- "jse",
788
- false
789
- );
665
+ assertParse("multiLineComment", "/*\nHello, world!\n*/", null, false);
790
666
  });
791
667
 
792
668
  test("multiplicativeExpression", () => {
@@ -808,31 +684,20 @@ Body`,
808
684
  });
809
685
 
810
686
  test("namespace", () => {
811
- assertParse("namespace", "js:", [markers.global, "js:"]);
812
- });
813
-
814
- test("newExpression", () => {
815
- assertParse("newExpression", "new Foo()", [
816
- ops.construct,
817
- [markers.reference, [ops.literal, "Foo"]],
818
- ]);
819
- assertParse("newExpression", "new:Foo()", [
820
- ops.construct,
821
- [markers.reference, [ops.literal, "Foo"]],
822
- ]);
687
+ assertParse("namespace", "js:", [ops.builtin, "js:"]);
823
688
  });
824
689
 
825
690
  test("nullishCoalescingExpression", () => {
826
691
  assertParse("nullishCoalescingExpression", "a ?? b", [
827
692
  ops.nullishCoalescing,
828
- [markers.reference, [ops.literal, "a"]],
829
- [ops.lambda, [], [markers.reference, [ops.literal, "b"]]],
693
+ [ops.scope, "a"],
694
+ [ops.lambda, [], [undetermined, "b"]],
830
695
  ]);
831
696
  assertParse("nullishCoalescingExpression", "a ?? b ?? c", [
832
697
  ops.nullishCoalescing,
833
- [markers.reference, [ops.literal, "a"]],
834
- [ops.lambda, [], [markers.reference, [ops.literal, "b"]]],
835
- [ops.lambda, [], [markers.reference, [ops.literal, "c"]]],
698
+ [ops.scope, "a"],
699
+ [ops.lambda, [], [undetermined, "b"]],
700
+ [ops.lambda, [], [undetermined, "c"]],
836
701
  ]);
837
702
  });
838
703
 
@@ -847,7 +712,7 @@ Body`,
847
712
  assertParse("objectLiteral", "{ a: 1, b }", [
848
713
  ops.object,
849
714
  ["a", [ops.literal, 1]],
850
- ["b", [markers.reference, "b"]],
715
+ ["b", [ops.inherited, "b"]],
851
716
  ]);
852
717
  assertParse("objectLiteral", "{ sub: { a: 1 } }", [
853
718
  ops.object,
@@ -864,7 +729,7 @@ Body`,
864
729
  ]);
865
730
  assertParse("objectLiteral", "{ a = b, b = 2 }", [
866
731
  ops.object,
867
- ["a", [ops.getter, [markers.reference, [ops.literal, "b"]]]],
732
+ ["a", [ops.getter, [ops.scope, "b"]]],
868
733
  ["b", [ops.literal, 2]],
869
734
  ]);
870
735
  assertParse(
@@ -875,7 +740,7 @@ Body`,
875
740
  }`,
876
741
  [
877
742
  ops.object,
878
- ["a", [ops.getter, [markers.reference, [ops.literal, "b"]]]],
743
+ ["a", [ops.getter, [ops.scope, "b"]]],
879
744
  ["b", [ops.literal, 2]],
880
745
  ]
881
746
  );
@@ -891,7 +756,7 @@ Body`,
891
756
  ops.object,
892
757
  [
893
758
  "a",
894
- [ops.object, ["b", [ops.getter, [[markers.global, "fn"], undefined]]]],
759
+ [ops.object, ["b", [ops.getter, [[ops.builtin, "fn"], undefined]]]],
895
760
  ],
896
761
  ]);
897
762
  assertParse("objectLiteral", "{ x = fn.js('a') }", [
@@ -901,28 +766,16 @@ Body`,
901
766
  [
902
767
  ops.getter,
903
768
  [
904
- [markers.reference, [ops.literal, "fn.js"]],
769
+ [ops.scope, "fn.js"],
905
770
  [ops.literal, "a"],
906
771
  ],
907
772
  ],
908
773
  ],
909
774
  ]);
910
- assertParse("objectLiteral", "{ a: 1, ...more, c: a }", [
911
- [
912
- ops.object,
913
- ["a", [ops.literal, 1]],
914
- ["c", [markers.reference, [ops.literal, "a"]]],
915
- [
916
- "_result",
917
- [
918
- ops.merge,
919
- [ops.object, ["a", [ops.getter, [[ops.context, 1], "a"]]]],
920
- [markers.reference, [ops.literal, "more"]],
921
- [ops.object, ["c", [ops.getter, [[ops.context, 1], "c"]]]],
922
- ],
923
- ],
924
- ],
925
- "_result",
775
+ assertParse("objectLiteral", "{ a: 1, ...b }", [
776
+ ops.merge,
777
+ [ops.object, ["a", [ops.literal, 1]]],
778
+ [ops.scope, "b"],
926
779
  ]);
927
780
  assertParse("objectLiteral", "{ a: 1, ...{ b: 2 } }", [
928
781
  ops.object,
@@ -933,63 +786,22 @@ Body`,
933
786
  ops.object,
934
787
  ["(a)", [ops.literal, 1]],
935
788
  ]);
936
- assertParse(
937
- "objectLiteral",
938
- "{ <path/to/file.txt> }",
939
- [
940
- ops.object,
941
- [
942
- "file.txt",
943
- [
944
- [ops.scope],
945
- [ops.literal, "path/"],
946
- [ops.literal, "to/"],
947
- [ops.literal, "file.txt"],
948
- ],
949
- ],
950
- ],
951
- "jse"
952
- );
953
789
  });
954
790
 
955
791
  test("objectEntry", () => {
956
- assertParse("objectEntry", "foo", ["foo", [markers.reference, "foo"]]);
957
- assertParse("objectEntry", "x: y", [
958
- "x",
959
- [markers.reference, [ops.literal, "y"]],
960
- ]);
961
- assertParse("objectEntry", "a: a", [
962
- "a",
963
- [markers.reference, [ops.literal, "a"]],
964
- ]);
965
- assertParse(
966
- "objectEntry",
967
- "<path/to/file.txt>",
968
- [
969
- "file.txt",
970
- [
971
- [ops.scope],
972
- [ops.literal, "path/"],
973
- [ops.literal, "to/"],
974
- [ops.literal, "file.txt"],
975
- ],
976
- ],
977
- "jse"
978
- );
792
+ assertParse("objectEntry", "foo", ["foo", [ops.inherited, "foo"]]);
793
+ assertParse("objectEntry", "x: y", ["x", [ops.scope, "y"]]);
794
+ assertParse("objectEntry", "a: a", ["a", [ops.inherited, "a"]]);
979
795
  assertParse("objectEntry", "a: (a) => a", [
980
796
  "a",
981
- [
982
- ops.lambda,
983
- [[ops.literal, "a"]],
984
- [markers.reference, [ops.literal, "a"]],
985
- ],
797
+ [ops.lambda, [[ops.literal, "a"]], [ops.scope, "a"]],
986
798
  ]);
987
799
  assertParse("objectEntry", "posts/: map(posts, post.ori)", [
988
800
  "posts/",
989
801
  [
990
- [markers.global, "map"],
991
- [markers.reference, [ops.literal, "posts"]],
992
- [markers.reference, [ops.literal, "post.ori"]],
802
+ [ops.builtin, "map"],
803
+ [ops.inherited, "posts"],
804
+ [ops.scope, "post.ori"],
993
805
  ],
994
806
  ]);
995
807
  });
@@ -997,14 +809,14 @@ Body`,
997
809
  test("objectGetter", () => {
998
810
  assertParse("objectGetter", "data = obj.json", [
999
811
  "data",
1000
- [ops.getter, [markers.reference, [ops.literal, "obj.json"]]],
812
+ [ops.getter, [ops.scope, "obj.json"]],
1001
813
  ]);
1002
814
  assertParse("objectGetter", "foo = page.ori 'bar'", [
1003
815
  "foo",
1004
816
  [
1005
817
  ops.getter,
1006
818
  [
1007
- [markers.reference, [ops.literal, "page.ori"]],
819
+ [ops.scope, "page.ori"],
1008
820
  [ops.literal, "bar"],
1009
821
  ],
1010
822
  ],
@@ -1020,31 +832,24 @@ Body`,
1020
832
  assertParse("objectProperty", "x: fn('a')", [
1021
833
  "x",
1022
834
  [
1023
- [markers.global, "fn"],
835
+ [ops.builtin, "fn"],
1024
836
  [ops.literal, "a"],
1025
837
  ],
1026
838
  ]);
1027
839
  });
1028
840
 
1029
841
  test("objectPublicKey", () => {
1030
- assertParse("objectPublicKey", "a", "a", "jse", false);
1031
- assertParse("objectPublicKey", "markdown/", "markdown/", "jse", false);
1032
- assertParse("objectPublicKey", "foo\\ bar", "foo bar", "jse", false);
1033
- });
1034
-
1035
- test("optionalChaining", () => {
1036
- assertParse("optionalChaining", "?.key", [
1037
- ops.optionalTraverse,
1038
- [ops.literal, "key"],
1039
- ]);
842
+ assertParse("objectPublicKey", "a", "a", false);
843
+ assertParse("objectPublicKey", "markdown/", "markdown/", false);
844
+ assertParse("objectPublicKey", "foo\\ bar", "foo bar", false);
1040
845
  });
1041
846
 
1042
847
  test("parenthesesArguments", () => {
1043
848
  assertParse("parenthesesArguments", "()", [undefined]);
1044
849
  assertParse("parenthesesArguments", "(a, b, c)", [
1045
- [markers.reference, [ops.literal, "a"]],
1046
- [markers.reference, [ops.literal, "b"]],
1047
- [markers.reference, [ops.literal, "c"]],
850
+ [ops.scope, "a"],
851
+ [ops.scope, "b"],
852
+ [ops.scope, "c"],
1048
853
  ]);
1049
854
  });
1050
855
 
@@ -1066,61 +871,46 @@ Body`,
1066
871
  });
1067
872
 
1068
873
  test("pathArguments", () => {
1069
- assertParse("pathArguments", "/", [markers.traverse]);
874
+ assertParse("pathArguments", "/", [ops.traverse]);
1070
875
  assertParse("pathArguments", "/tree", [
1071
- markers.traverse,
876
+ ops.traverse,
1072
877
  [ops.literal, "tree"],
1073
878
  ]);
1074
879
  assertParse("pathArguments", "/tree/", [
1075
- markers.traverse,
880
+ ops.traverse,
1076
881
  [ops.literal, "tree/"],
1077
882
  ]);
1078
883
  });
1079
884
 
1080
885
  test("pipelineExpression", () => {
1081
- assertParse("pipelineExpression", "foo", [
1082
- markers.reference,
1083
- [ops.literal, "foo"],
1084
- ]);
886
+ assertParse("pipelineExpression", "foo", [ops.scope, "foo"]);
1085
887
  assertParse("pipelineExpression", "a -> b", [
1086
- [markers.global, "b"],
1087
- [markers.reference, [ops.literal, "a"]],
888
+ [ops.builtin, "b"],
889
+ [ops.scope, "a"],
1088
890
  ]);
1089
891
  assertParse("pipelineExpression", "input → one.js → two.js", [
1090
- [markers.reference, [ops.literal, "two.js"]],
892
+ [ops.scope, "two.js"],
1091
893
  [
1092
- [markers.reference, [ops.literal, "one.js"]],
1093
- [markers.reference, [ops.literal, "input"]],
894
+ [ops.scope, "one.js"],
895
+ [ops.scope, "input"],
1094
896
  ],
1095
897
  ]);
1096
898
  assertParse("pipelineExpression", "fn a -> b", [
1097
- [markers.global, "b"],
899
+ [ops.builtin, "b"],
1098
900
  [
1099
- [markers.global, "fn"],
1100
- [markers.reference, [ops.literal, "a"]],
901
+ [ops.builtin, "fn"],
902
+ [undetermined, "a"],
1101
903
  ],
1102
904
  ]);
1103
905
  });
1104
906
 
1105
907
  test("primary", () => {
1106
- assertParse("primary", "foo.js", [
1107
- markers.reference,
1108
- [ops.literal, "foo.js"],
1109
- ]);
908
+ assertParse("primary", "foo.js", [ops.scope, "foo.js"]);
1110
909
  assertParse("primary", "[1, 2]", [
1111
910
  ops.array,
1112
911
  [ops.literal, 1],
1113
912
  [ops.literal, 2],
1114
913
  ]);
1115
- // Only in JSE
1116
- assertParse(
1117
- "primary",
1118
- "<index.html>",
1119
- [[ops.scope], [ops.literal, "index.html"]],
1120
- "jse",
1121
- false
1122
- );
1123
- assertThrows("primary", "<index.html>", `but "<" found`, 0, "shell");
1124
914
  });
1125
915
 
1126
916
  test("program", () => {
@@ -1130,39 +920,37 @@ Body`,
1130
920
  'Hello'
1131
921
  `,
1132
922
  [ops.literal, "Hello"],
1133
- "jse",
1134
923
  false
1135
924
  );
1136
925
  });
1137
926
 
1138
927
  test("protocolExpression", () => {
1139
- assertParse("protocolExpression", "foo://bar/baz", [
1140
- [markers.global, "foo:"],
1141
- [ops.literal, "bar/"],
1142
- [ops.literal, "baz"],
928
+ assertParse("protocolExpression", "foo://bar", [
929
+ [ops.builtin, "foo:"],
930
+ [ops.literal, "bar"],
1143
931
  ]);
1144
932
  assertParse("protocolExpression", "http://example.com", [
1145
- [markers.global, "http:"],
933
+ [ops.builtin, "http:"],
1146
934
  [ops.literal, "example.com"],
1147
935
  ]);
1148
936
  assertParse("protocolExpression", "https://example.com/about/", [
1149
- [markers.global, "https:"],
937
+ [ops.builtin, "https:"],
1150
938
  [ops.literal, "example.com/"],
1151
939
  [ops.literal, "about/"],
1152
940
  ]);
1153
941
  assertParse("protocolExpression", "https://example.com/about/index.html", [
1154
- [markers.global, "https:"],
942
+ [ops.builtin, "https:"],
1155
943
  [ops.literal, "example.com/"],
1156
944
  [ops.literal, "about/"],
1157
945
  [ops.literal, "index.html"],
1158
946
  ]);
1159
947
  assertParse("protocolExpression", "http://localhost:5000/foo", [
1160
- [markers.global, "http:"],
948
+ [ops.builtin, "http:"],
1161
949
  [ops.literal, "localhost:5000/"],
1162
950
  [ops.literal, "foo"],
1163
951
  ]);
1164
952
  assertParse("protocolExpression", "files:///foo/bar.txt", [
1165
- [markers.global, "files:"],
953
+ [ops.builtin, "files:"],
1166
954
  [ops.literal, "/"],
1167
955
  [ops.literal, "foo/"],
1168
956
  [ops.literal, "bar.txt"],
@@ -1171,15 +959,11 @@ Body`,
1171
959
 
1172
960
  test("qualifiedReference", () => {
1173
961
  assertParse("qualifiedReference", "js:Date", [
1174
- [markers.global, "js:"],
962
+ [ops.builtin, "js:"],
1175
963
  [ops.literal, "Date"],
1176
964
  ]);
1177
965
  });
1178
966
 
1179
- test("regexLiteral", () => {
1180
- assertParse("regexLiteral", "/abc+/g", [ops.literal, /abc+/g]);
1181
- });
1182
-
1183
967
  test("relationalExpression", () => {
1184
968
  assertParse("relationalExpression", "1 < 2", [
1185
969
  ops.lessThan,
@@ -1203,15 +987,15 @@ Body`,
1203
987
  ]);
1204
988
  });
1205
989
 
990
+ test("rootDirectory", () => {
991
+ assertParse("rootDirectory", "/", [ops.rootDirectory]);
992
+ });
993
+
1206
994
  test("scopeReference", () => {
1207
- assertParse("scopeReference", "keys", [
1208
- markers.reference,
1209
- [ops.literal, "keys"],
1210
- ]);
1211
- assertParse("scopeReference", "greet.js", [
1212
- markers.reference,
1213
- [ops.literal, "greet.js"],
1214
- ]);
995
+ assertParse("scopeReference", "keys", [undetermined, "keys"]);
996
+ assertParse("scopeReference", "greet.js", [ops.scope, "greet.js"]);
997
+ // scopeReference checks whether a slash follows; hard to test in isolation
998
+ // assertParse("scopeReference", "markdown/", [ops.scope, "markdown"]);
1215
999
  });
1216
1000
 
1217
1001
  test("shiftExpression", () => {
@@ -1236,40 +1020,30 @@ Body`,
1236
1020
  assertParse("shorthandFunction", "=message", [
1237
1021
  ops.lambda,
1238
1022
  [[ops.literal, "_"]],
1239
- [markers.reference, [ops.literal, "message"]],
1023
+ [undetermined, "message"],
1240
1024
  ]);
1241
1025
  assertParse("shorthandFunction", "=`Hello, ${name}.`", [
1242
1026
  ops.lambda,
1243
1027
  [[ops.literal, "_"]],
1244
- [
1245
- ops.templateTree,
1246
- [ops.literal, ["Hello, ", "."]],
1247
- [markers.reference, [ops.literal, "name"]],
1248
- ],
1028
+ [ops.template, [ops.literal, ["Hello, ", "."]], [ops.scope, "name"]],
1249
1029
  ]);
1250
1030
  assertParse("shorthandFunction", "=indent`hello`", [
1251
1031
  ops.lambda,
1252
1032
  [[ops.literal, "_"]],
1253
1033
  [
1254
- [markers.global, "indent"],
1034
+ [ops.builtin, "indent"],
1255
1035
  [ops.literal, ["hello"]],
1256
1036
  ],
1257
1037
  ]);
1258
1038
  });
1259
1039
 
1260
1040
  test("singleLineComment", () => {
1261
- assertParse("singleLineComment", "// Hello, world!", null, "jse", false);
1041
+ assertParse("singleLineComment", "// Hello, world!", null, false);
1262
1042
  });
1263
1043
 
1264
1044
  test("spreadElement", () => {
1265
- assertParse("spreadElement", "...a", [
1266
- ops.spread,
1267
- [markers.reference, [ops.literal, "a"]],
1268
- ]);
1269
- assertParse("spreadElement", "…a", [
1270
- ops.spread,
1271
- [markers.reference, [ops.literal, "a"]],
1272
- ]);
1045
+ assertParse("spreadElement", "...a", [ops.spread, [ops.scope, "a"]]);
1046
+ assertParse("spreadElement", "…a", [ops.spread, [ops.scope, "a"]]);
1273
1047
  });
1274
1048
 
1275
1049
  test("stringLiteral", () => {
@@ -1288,13 +1062,21 @@ Body`,
1288
1062
 
1289
1063
  test("templateBody", () => {
1290
1064
  assertParse("templateBody", "hello${foo}world", [
1291
- ops.templateIndent,
1292
- [ops.literal, ["hello", "world"]],
1293
- [markers.reference, [ops.literal, "foo"]],
1065
+ ops.lambda,
1066
+ [[ops.literal, "_"]],
1067
+ [
1068
+ ops.templateIndent,
1069
+ [ops.literal, ["hello", "world"]],
1070
+ [ops.scope, "foo"],
1071
+ ],
1294
1072
  ]);
1295
1073
  assertParse("templateBody", "Documents can contain ` backticks", [
1296
- ops.templateIndent,
1297
- [ops.literal, ["Documents can contain ` backticks"]],
1074
+ ops.lambda,
1075
+ [[ops.literal, "_"]],
1076
+ [
1077
+ ops.templateIndent,
1078
+ [ops.literal, ["Documents can contain ` backticks"]],
1079
+ ],
1298
1080
  ]);
1299
1081
  });
1300
1082
 
@@ -1314,9 +1096,13 @@ title: Title goes here
1314
1096
  ---
1315
1097
  Body text`,
1316
1098
  [
1317
- ops.object,
1318
- ["title", [ops.literal, "Title goes here"]],
1319
- ["@text", [ops.templateIndent, [ops.literal, ["Body text"]]]],
1099
+ ops.document,
1100
+ [ops.literal, { title: "Title goes here" }],
1101
+ [
1102
+ ops.lambda,
1103
+ [[ops.literal, "_"]],
1104
+ [ops.templateIndent, [ops.literal, ["Body text"]]],
1105
+ ],
1320
1106
  ]
1321
1107
  );
1322
1108
  });
@@ -1338,9 +1124,16 @@ Body text`,
1338
1124
  [
1339
1125
  "@text",
1340
1126
  [
1341
- ops.templateIndent,
1342
- [ops.literal, ["<h1>", "</h1>\n"]],
1343
- [markers.reference, [ops.literal, "title"]],
1127
+ [
1128
+ ops.lambda,
1129
+ [[ops.literal, "_"]],
1130
+ [
1131
+ ops.templateIndent,
1132
+ [ops.literal, ["<h1>", "</h1>\n"]],
1133
+ [ops.scope, "title"],
1134
+ ],
1135
+ ],
1136
+ undefined,
1344
1137
  ],
1345
1138
  ],
1346
1139
  ]
@@ -1349,52 +1142,36 @@ Body text`,
1349
1142
 
1350
1143
  test("templateLiteral", () => {
1351
1144
  assertParse("templateLiteral", "`Hello, world.`", [
1352
- ops.templateTree,
1145
+ ops.template,
1353
1146
  [ops.literal, ["Hello, world."]],
1354
1147
  ]);
1355
- assertParse(
1356
- "templateLiteral",
1357
- "`Hello, world.`",
1358
- [ops.templateStandard, [ops.literal, ["Hello, world."]]],
1359
- "jse"
1360
- );
1361
1148
  assertParse("templateLiteral", "`foo ${x} bar`", [
1362
- ops.templateTree,
1149
+ ops.template,
1363
1150
  [ops.literal, ["foo ", " bar"]],
1364
- [markers.reference, [ops.literal, "x"]],
1151
+ [ops.scope, "x"],
1365
1152
  ]);
1366
1153
  assertParse("templateLiteral", "`${`nested`}`", [
1367
- ops.templateTree,
1154
+ ops.template,
1368
1155
  [ops.literal, ["", ""]],
1369
- [ops.templateTree, [ops.literal, ["nested"]]],
1156
+ [ops.template, [ops.literal, ["nested"]]],
1370
1157
  ]);
1371
1158
  assertParse("templateLiteral", "`${ map:(people, =`${name}`) }`", [
1372
- ops.templateTree,
1159
+ ops.template,
1373
1160
  [ops.literal, ["", ""]],
1374
1161
  [
1375
- [markers.global, "map:"],
1376
- [markers.reference, [ops.literal, "people"]],
1162
+ [ops.builtin, "map:"],
1163
+ [ops.scope, "people"],
1377
1164
  [
1378
1165
  ops.lambda,
1379
1166
  [[ops.literal, "_"]],
1380
- [
1381
- ops.templateTree,
1382
- [ops.literal, ["", ""]],
1383
- [markers.reference, [ops.literal, "name"]],
1384
- ],
1167
+ [ops.template, [ops.literal, ["", ""]], [ops.scope, "name"]],
1385
1168
  ],
1386
1169
  ],
1387
1170
  ]);
1388
1171
  });
1389
1172
 
1390
1173
  test("templateSubtitution", () => {
1391
- assertParse(
1392
- "templateSubstitution",
1393
- "${foo}",
1394
- [markers.reference, [ops.literal, "foo"]],
1395
- "shell",
1396
- false
1397
- );
1174
+ assertParse("templateSubstitution", "${foo}", [ops.scope, "foo"], false);
1398
1175
  });
1399
1176
 
1400
1177
  test("whitespace block", () => {
@@ -1405,7 +1182,6 @@ Body text`,
1405
1182
  // Second comment
1406
1183
  `,
1407
1184
  null,
1408
- "jse",
1409
1185
  false
1410
1186
  );
1411
1187
  });
@@ -1413,7 +1189,7 @@ Body text`,
1413
1189
  test("unaryExpression", () => {
1414
1190
  assertParse("unaryExpression", "!true", [
1415
1191
  ops.logicalNot,
1416
- [markers.reference, [ops.literal, "true"]],
1192
+ [undetermined, "true"],
1417
1193
  ]);
1418
1194
  assertParse("unaryExpression", "+1", [ops.unaryPlus, [ops.literal, 1]]);
1419
1195
  assertParse("unaryExpression", "-2", [ops.unaryMinus, [ops.literal, 2]]);
@@ -1421,16 +1197,9 @@ Body text`,
1421
1197
  });
1422
1198
  });
1423
1199
 
1424
- function assertParse(
1425
- startRule,
1426
- source,
1427
- expected,
1428
- mode = "shell",
1429
- checkLocation = true
1430
- ) {
1200
+ function assertParse(startRule, source, expected, checkLocation = true) {
1431
1201
  const code = parse(source, {
1432
1202
  grammarSource: { text: source },
1433
- mode,
1434
1203
  startRule,
1435
1204
  });
1436
1205
 
@@ -1458,11 +1227,10 @@ function assertCodeLocations(code) {
1458
1227
  }
1459
1228
  }
1460
1229
 
1461
- function assertThrows(startRule, source, message, position, mode = "shell") {
1230
+ function assertThrows(startRule, source, message, position) {
1462
1231
  try {
1463
1232
  parse(source, {
1464
1233
  grammarSource: { text: source },
1465
- mode,
1466
1234
  startRule,
1467
1235
  });
1468
1236
  } catch (/** @type {any} */ error) {