@weborigami/language 0.3.3-jse.2 → 0.3.3-jse.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 { undetermined } from "../../src/compiler/parserHelpers.js";
4
+ import { markers } 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,44 +19,61 @@ describe("Origami parser", () => {
19
19
  ]);
20
20
  });
21
21
 
22
- test("angleBracketLiteral", () => {
23
- assertParse(
24
- "angleBracketLiteral",
25
- "<index.html>",
26
- [ops.scope, "index.html"],
27
- "jse",
28
- false
29
- );
30
- assertParse(
31
- "angleBracketLiteral",
32
- "<foo/bar/baz>",
33
- [
34
- ops.traverse,
35
- [ops.scope, "foo/"],
36
- [ops.literal, "bar/"],
37
- [ops.literal, "baz"],
38
- ],
39
- "jse",
40
- false
41
- );
42
- assertParse("angleBracketLiteral", "<files:src/assets>", [
43
- ops.traverse,
44
- [
45
- [ops.builtin, "files:"],
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:"],
46
63
  [ops.literal, "src/"],
47
- ],
48
- [ops.literal, "assets"],
49
- ]);
50
- assertParse(
51
- "angleBracketLiteral",
52
- "<https://example.com/>",
53
- [
54
- [ops.builtin, "https:"],
55
- [ops.literal, "example.com/"],
56
- ],
57
- "jse",
58
- false
59
- );
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
+ });
60
77
  });
61
78
 
62
79
  test("arrayLiteral", () => {
@@ -81,7 +98,7 @@ describe("Origami parser", () => {
81
98
  [ops.literal, 4],
82
99
  ]);
83
100
  assertParse("arrayLiteral", "[ 1, ...[2, 3]]", [
84
- ops.merge,
101
+ ops.flat,
85
102
  [ops.array, [ops.literal, 1]],
86
103
  [ops.array, [ops.literal, 2], [ops.literal, 3]],
87
104
  ]);
@@ -100,12 +117,12 @@ describe("Origami parser", () => {
100
117
  assertParse("arrowFunction", "() => foo", [
101
118
  ops.lambda,
102
119
  [],
103
- [ops.scope, "foo"],
120
+ [markers.reference, [ops.literal, "foo"]],
104
121
  ]);
105
122
  assertParse("arrowFunction", "x => y", [
106
123
  ops.lambda,
107
124
  [[ops.literal, "x"]],
108
- [ops.scope, "y"],
125
+ [markers.reference, [ops.literal, "y"]],
109
126
  ]);
110
127
  assertParse("arrowFunction", "(a, b, c) ⇒ fn(a, b, c)", [
111
128
  ops.lambda,
@@ -115,10 +132,10 @@ describe("Origami parser", () => {
115
132
  [ops.literal, "c"],
116
133
  ],
117
134
  [
118
- [ops.builtin, "fn"],
119
- [ops.scope, "a"],
120
- [ops.scope, "b"],
121
- [ops.scope, "c"],
135
+ [markers.global, "fn"],
136
+ [markers.reference, [ops.literal, "a"]],
137
+ [markers.reference, [ops.literal, "b"]],
138
+ [markers.reference, [ops.literal, "c"]],
122
139
  ],
123
140
  ]);
124
141
  assertParse("arrowFunction", "a => b => fn(a, b)", [
@@ -128,9 +145,9 @@ describe("Origami parser", () => {
128
145
  ops.lambda,
129
146
  [[ops.literal, "b"]],
130
147
  [
131
- [ops.builtin, "fn"],
132
- [ops.scope, "a"],
133
- [ops.scope, "b"],
148
+ [markers.global, "fn"],
149
+ [markers.reference, [ops.literal, "a"]],
150
+ [markers.reference, [ops.literal, "b"]],
134
151
  ],
135
152
  ],
136
153
  ]);
@@ -160,147 +177,177 @@ describe("Origami parser", () => {
160
177
  ]);
161
178
  });
162
179
 
163
- test("callExpression", () => {
164
- assertParse("callExpression", "fn()", [[ops.builtin, "fn"], undefined]);
165
- assertParse("callExpression", "foo.js(arg)", [
166
- [ops.scope, "foo.js"],
167
- [ops.scope, "arg"],
168
- ]);
169
- assertParse("callExpression", "fn(a, b)", [
170
- [ops.builtin, "fn"],
171
- [ops.scope, "a"],
172
- [ops.scope, "b"],
173
- ]);
174
- assertParse("callExpression", "foo.js( a , b )", [
175
- [ops.scope, "foo.js"],
176
- [ops.scope, "a"],
177
- [ops.scope, "b"],
178
- ]);
179
- assertParse("callExpression", "fn()(arg)", [
180
- [[ops.builtin, "fn"], undefined],
181
- [ops.scope, "arg"],
182
- ]);
183
- assertParse("callExpression", "tree/", [ops.unpack, [ops.scope, "tree/"]]);
184
- assertParse("callExpression", "tree/foo/bar", [
185
- ops.traverse,
186
- [ops.scope, "tree/"],
187
- [ops.literal, "foo/"],
188
- [ops.literal, "bar"],
189
- ]);
190
- assertParse("callExpression", "tree/foo/bar/", [
191
- ops.traverse,
192
- [ops.scope, "tree/"],
193
- [ops.literal, "foo/"],
194
- [ops.literal, "bar/"],
195
- ]);
196
- assertParse("callExpression", "/foo/bar", [
197
- ops.traverse,
198
- [ops.rootDirectory, [ops.literal, "foo/"]],
199
- [ops.literal, "bar"],
200
- ]);
201
- assertParse("callExpression", "foo.js()/key", [
202
- ops.traverse,
203
- [[ops.scope, "foo.js"], undefined],
204
- [ops.literal, "key"],
205
- ]);
206
- assertParse("callExpression", "tree/key()", [
207
- [ops.traverse, [ops.scope, "tree/"], [ops.literal, "key"]],
208
- undefined,
209
- ]);
210
- assertParse("callExpression", "(tree)/", [ops.unpack, [ops.scope, "tree"]]);
211
- assertParse("callExpression", "fn()/key()", [
212
- [ops.traverse, [[ops.builtin, "fn"], undefined], [ops.literal, "key"]],
213
- undefined,
214
- ]);
215
- assertParse("callExpression", "(foo.js())('arg')", [
216
- [[ops.scope, "foo.js"], undefined],
217
- [ops.literal, "arg"],
218
- ]);
219
- assertParse("callExpression", "fn('a')('b')", [
220
- [
221
- [ops.builtin, "fn"],
222
- [ops.literal, "a"],
223
- ],
224
- [ops.literal, "b"],
225
- ]);
226
- assertParse("callExpression", "(foo.js())(a, b)", [
227
- [[ops.scope, "foo.js"], undefined],
228
- [ops.scope, "a"],
229
- [ops.scope, "b"],
230
- ]);
231
- assertParse("callExpression", "{ a: 1, b: 2}/b", [
232
- ops.traverse,
233
- [ops.object, ["a", [ops.literal, 1]], ["b", [ops.literal, 2]]],
234
- [ops.literal, "b"],
235
- ]);
236
- assertParse("callExpression", "indent`hello`", [
237
- [ops.builtin, "indent"],
238
- [ops.literal, ["hello"]],
239
- ]);
240
- assertParse("callExpression", "fn.js`Hello, world.`", [
241
- [ops.scope, "fn.js"],
242
- [ops.literal, ["Hello, world."]],
243
- ]);
244
- assertParse("callExpression", "files:src/assets", [
245
- ops.traverse,
246
- [
247
- [ops.builtin, "files:"],
248
- [ops.literal, "src/"],
249
- ],
250
- [ops.literal, "assets"],
251
- ]);
252
- assertParse("callExpression", "new:(js:Date, '2025-01-01')", [
253
- [ops.builtin, "new:"],
254
- [
255
- [ops.builtin, "js:"],
256
- [ops.literal, "Date"],
257
- ],
258
- [ops.literal, "2025-01-01"],
259
- ]);
260
- assertParse("callExpression", "map(markdown, mdHtml)", [
261
- [ops.builtin, "map"],
262
- [ops.scope, "markdown"],
263
- [ops.scope, "mdHtml"],
264
- ]);
265
- assertParse("callExpression", "package:@weborigami/dropbox/auth(creds)", [
266
- [
267
- ops.traverse,
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)", [
268
283
  [
269
- [ops.builtin, "package:"],
284
+ [markers.global, "package:"],
270
285
  [ops.literal, "@weborigami/"],
286
+ [ops.literal, "dropbox/"],
287
+ [ops.literal, "auth"],
271
288
  ],
272
- [ops.literal, "dropbox/"],
273
- [ops.literal, "auth"],
274
- ],
275
- [ops.scope, "creds"],
276
- ]);
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:"],
305
+ [ops.literal, "src/"],
306
+ [ops.literal, "assets"],
307
+ ]);
308
+ assertParse("callExpression", "new:(js:Date, '2025-01-01')", [
309
+ [markers.global, "new:"],
310
+ [
311
+ [markers.global, "js:"],
312
+ [ops.literal, "Date"],
313
+ ],
314
+ [ops.literal, "2025-01-01"],
315
+ ]);
316
+ });
277
317
  });
278
318
 
279
319
  test("callExpression using property acccess", () => {
280
320
  assertParse("callExpression", "(foo).bar", [
281
- ops.traverse,
282
- [ops.scope, "foo"],
321
+ markers.reference,
322
+ [ops.literal, "foo/"],
283
323
  [ops.literal, "bar"],
284
324
  ]);
285
325
  assertParse("callExpression", "(foo).bar.baz", [
286
- ops.traverse,
287
- [ops.traverse, [ops.scope, "foo"], [ops.literal, "bar"]],
326
+ markers.reference,
327
+ [ops.literal, "foo/"],
328
+ [ops.literal, "bar/"],
288
329
  [ops.literal, "baz"],
289
330
  ]);
290
331
  assertParse("callExpression", "foo[bar]", [
291
- ops.traverse,
292
- [ops.scope, "foo/"],
293
- [ops.scope, "bar"],
332
+ markers.reference,
333
+ [ops.literal, "foo/"],
334
+ [markers.reference, [ops.literal, "bar"]],
294
335
  ]);
336
+ assertParse(
337
+ "callExpression",
338
+ "Tree.map",
339
+ [markers.reference, [ops.literal, "Tree/"], [ops.literal, "map"]],
340
+ "jse"
341
+ );
295
342
  });
296
343
 
297
344
  test("commaExpression", () => {
298
345
  assertParse("commaExpression", "1", [ops.literal, 1]);
299
346
  assertParse("commaExpression", "a, b, c", [
300
347
  ops.comma,
301
- [ops.scope, "a"],
302
- [ops.scope, "b"],
303
- [ops.scope, "c"],
348
+ [markers.reference, [ops.literal, "a"]],
349
+ [markers.reference, [ops.literal, "b"]],
350
+ [markers.reference, [ops.literal, "c"]],
304
351
  ]);
305
352
  });
306
353
 
@@ -308,19 +355,19 @@ describe("Origami parser", () => {
308
355
  assertParse("conditionalExpression", "1", [ops.literal, 1]);
309
356
  assertParse("conditionalExpression", "true ? 1 : 0", [
310
357
  ops.conditional,
311
- [ops.scope, "true"],
358
+ [markers.reference, [ops.literal, "true"]],
312
359
  [ops.literal, 1],
313
360
  [ops.literal, 0],
314
361
  ]);
315
362
  assertParse("conditionalExpression", "false ? () => 1 : 0", [
316
363
  ops.conditional,
317
- [ops.scope, "false"],
364
+ [markers.reference, [ops.literal, "false"]],
318
365
  [ops.lambda, [], [ops.lambda, [], [ops.literal, 1]]],
319
366
  [ops.literal, 0],
320
367
  ]);
321
368
  assertParse("conditionalExpression", "false ? =1 : 0", [
322
369
  ops.conditional,
323
- [ops.scope, "false"],
370
+ [markers.reference, [ops.literal, "false"]],
324
371
  [ops.lambda, [], [ops.lambda, [[ops.literal, "_"]], [ops.literal, 1]]],
325
372
  [ops.literal, 0],
326
373
  ]);
@@ -334,8 +381,12 @@ describe("Origami parser", () => {
334
381
  ]);
335
382
  assertParse("equalityExpression", "a === b === c", [
336
383
  ops.strictEqual,
337
- [ops.strictEqual, [undetermined, "a"], [undetermined, "b"]],
338
- [undetermined, "c"],
384
+ [
385
+ ops.strictEqual,
386
+ [markers.reference, [ops.literal, "a"]],
387
+ [markers.reference, [ops.literal, "b"]],
388
+ ],
389
+ [markers.reference, [ops.literal, "c"]],
339
390
  ]);
340
391
  assertParse("equalityExpression", "1 !== 1", [
341
392
  ops.notStrictEqual,
@@ -403,8 +454,8 @@ Body`,
403
454
  [
404
455
  ops.getter,
405
456
  [
406
- [ops.scope, "index.ori"],
407
- [ops.scope, "teamData.yaml"],
457
+ [markers.reference, [ops.literal, "index.ori"]],
458
+ [markers.reference, [ops.literal, "teamData.yaml"]],
408
459
  ],
409
460
  ],
410
461
  ],
@@ -413,9 +464,12 @@ Body`,
413
464
  [
414
465
  ops.getter,
415
466
  [
416
- [ops.builtin, "map"],
417
- [ops.scope, "images"],
418
- [ops.object, ["value", [ops.scope, "thumbnail.js"]]],
467
+ [markers.global, "map"],
468
+ [markers.reference, [ops.literal, "images"]],
469
+ [
470
+ ops.object,
471
+ ["value", [markers.reference, [ops.literal, "thumbnail.js"]]],
472
+ ],
419
473
  ],
420
474
  ],
421
475
  ],
@@ -423,58 +477,54 @@ Body`,
423
477
  );
424
478
 
425
479
  // Builtin on its own is the function itself, not a function call
426
- assertParse("expression", "mdHtml:", [ops.builtin, "mdHtml:"]);
427
-
428
- // Consecutive slahes in a path are removed
429
- assertParse("expression", "path//key", [
430
- ops.traverse,
431
- [ops.scope, "path/"],
432
- [ops.literal, "key"],
433
- ]);
434
-
435
- // Single slash at start of something = absolute file path
436
- assertParse("expression", "/path", [
437
- ops.rootDirectory,
438
- [ops.literal, "path"],
439
- ]);
480
+ assertParse("expression", "mdHtml:", [markers.global, "mdHtml:"]);
440
481
 
441
482
  // Consecutive slashes at start of something = comment
442
- assertParse("expression", "x //comment", [ops.scope, "x"], "jse", false);
483
+ assertParse(
484
+ "expression",
485
+ "x //comment",
486
+ [markers.reference, [ops.literal, "x"]],
487
+ "jse",
488
+ false
489
+ );
443
490
 
444
491
  assertParse("expression", "page.ori(mdHtml:(about.md))", [
445
- [ops.scope, "page.ori"],
492
+ [markers.reference, [ops.literal, "page.ori"]],
446
493
  [
447
- [ops.builtin, "mdHtml:"],
448
- [ops.scope, "about.md"],
494
+ [markers.global, "mdHtml:"],
495
+ [markers.reference, [ops.literal, "about.md"]],
449
496
  ],
450
497
  ]);
451
498
 
452
499
  // Slash on its own is the root folder
453
500
  assertParse("expression", "keys /", [
454
- [ops.builtin, "keys"],
501
+ [markers.global, "keys"],
455
502
  [ops.rootDirectory],
456
503
  ]);
457
504
 
458
505
  assertParse("expression", "'Hello' -> test.orit", [
459
- [ops.scope, "test.orit"],
506
+ [markers.reference, [ops.literal, "test.orit"]],
460
507
  [ops.literal, "Hello"],
461
508
  ]);
462
- assertParse("expression", "obj.json", [ops.scope, "obj.json"]);
509
+ assertParse("expression", "obj.json", [
510
+ markers.reference,
511
+ [ops.literal, "obj.json"],
512
+ ]);
463
513
  assertParse("expression", "(fn a, b, c)", [
464
- [ops.builtin, "fn"],
465
- [undetermined, "a"],
466
- [undetermined, "b"],
467
- [undetermined, "c"],
514
+ [markers.global, "fn"],
515
+ [markers.reference, [ops.literal, "a"]],
516
+ [markers.reference, [ops.literal, "b"]],
517
+ [markers.reference, [ops.literal, "c"]],
468
518
  ]);
469
519
  assertParse("expression", "foo.bar('hello', 'world')", [
470
- [ops.scope, "foo.bar"],
520
+ [markers.reference, [ops.literal, "foo.bar"]],
471
521
  [ops.literal, "hello"],
472
522
  [ops.literal, "world"],
473
523
  ]);
474
- assertParse("expression", "(key)('a')", [
475
- [ops.scope, "key"],
476
- [ops.literal, "a"],
477
- ]);
524
+ // assertParse("expression", "(key)('a')", [
525
+ // [markers.reference, [ops.literal, "key"]],
526
+ // [ops.literal, "a"],
527
+ // ]);
478
528
  assertParse("expression", "1", [ops.literal, 1]);
479
529
  assertParse("expression", "{ a: 1, b: 2 }", [
480
530
  ops.object,
@@ -482,11 +532,11 @@ Body`,
482
532
  ["b", [ops.literal, 2]],
483
533
  ]);
484
534
  assertParse("expression", "serve { index.html: 'hello' }", [
485
- [ops.builtin, "serve"],
535
+ [markers.global, "serve"],
486
536
  [ops.object, ["index.html", [ops.literal, "hello"]]],
487
537
  ]);
488
538
  assertParse("expression", "fn =`x`", [
489
- [ops.builtin, "fn"],
539
+ [markers.global, "fn"],
490
540
  [
491
541
  ops.lambda,
492
542
  [[ops.literal, "_"]],
@@ -494,33 +544,37 @@ Body`,
494
544
  ],
495
545
  ]);
496
546
  assertParse("expression", "copy app.js(formulas), files:snapshot", [
497
- [ops.builtin, "copy"],
547
+ [markers.global, "copy"],
498
548
  [
499
- [ops.scope, "app.js"],
500
- [ops.scope, "formulas"],
549
+ [markers.reference, [ops.literal, "app.js"]],
550
+ [markers.reference, [ops.literal, "formulas"]],
501
551
  ],
502
552
  [
503
- [ops.builtin, "files:"],
553
+ [markers.global, "files:"],
504
554
  [ops.literal, "snapshot"],
505
555
  ],
506
556
  ]);
507
557
  assertParse("expression", "map =`<li>${_}</li>`", [
508
- [ops.builtin, "map"],
558
+ [markers.global, "map"],
509
559
  [
510
560
  ops.lambda,
511
561
  [[ops.literal, "_"]],
512
- [ops.templateTree, [ops.literal, ["<li>", "</li>"]], [ops.scope, "_"]],
562
+ [
563
+ ops.templateTree,
564
+ [ops.literal, ["<li>", "</li>"]],
565
+ [markers.reference, [ops.literal, "_"]],
566
+ ],
513
567
  ],
514
568
  ]);
515
569
  assertParse("expression", `https://example.com/about/`, [
516
- [ops.builtin, "https:"],
570
+ [markers.global, "https:"],
517
571
  [ops.literal, "example.com/"],
518
572
  [ops.literal, "about/"],
519
573
  ]);
520
574
  assertParse("expression", "tag`Hello, ${name}!`", [
521
- [ops.builtin, "tag"],
575
+ [markers.global, "tag"],
522
576
  [ops.literal, ["Hello, ", "!"]],
523
- [ops.concat, [ops.scope, "name"]],
577
+ [ops.concat, [markers.reference, [ops.literal, "name"]]],
524
578
  ]);
525
579
  assertParse("expression", "(post, slug) => fn.js(post, slug)", [
526
580
  ops.lambda,
@@ -529,42 +583,38 @@ Body`,
529
583
  [ops.literal, "slug"],
530
584
  ],
531
585
  [
532
- [ops.scope, "fn.js"],
533
- [ops.scope, "post"],
534
- [ops.scope, "slug"],
586
+ [markers.reference, [ops.literal, "fn.js"]],
587
+ [markers.reference, [ops.literal, "post"]],
588
+ [markers.reference, [ops.literal, "slug"]],
535
589
  ],
536
590
  ]);
537
591
  assertParse("expression", "keys ~", [
538
- [ops.builtin, "keys"],
592
+ [markers.global, "keys"],
539
593
  [ops.homeDirectory],
540
594
  ]);
541
595
  assertParse("expression", "keys /Users/alice", [
542
- [ops.builtin, "keys"],
543
- [
544
- ops.traverse,
545
- [ops.rootDirectory, [ops.literal, "Users/"]],
546
- [ops.literal, "alice"],
547
- ],
596
+ [markers.global, "keys"],
597
+ [[ops.rootDirectory], [ops.literal, "Users/"], [ops.literal, "alice"]],
548
598
  ]);
549
599
 
550
600
  // Verify parser treatment of identifiers containing operators
551
601
  assertParse("expression", "a + b", [
552
602
  ops.addition,
553
- [undetermined, "a"],
554
- [undetermined, "b"],
603
+ [markers.reference, [ops.literal, "a"]],
604
+ [markers.reference, [ops.literal, "b"]],
555
605
  ]);
556
- assertParse("expression", "a+b", [ops.scope, "a+b"]);
606
+ assertParse("expression", "a+b", [markers.reference, [ops.literal, "a+b"]]);
557
607
  assertParse("expression", "a - b", [
558
608
  ops.subtraction,
559
- [undetermined, "a"],
560
- [undetermined, "b"],
609
+ [markers.reference, [ops.literal, "a"]],
610
+ [markers.reference, [ops.literal, "b"]],
561
611
  ]);
562
- assertParse("expression", "a-b", [ops.scope, "a-b"]);
563
- assertParse("expression", "a&b", [ops.scope, "a&b"]);
612
+ assertParse("expression", "a-b", [markers.reference, [ops.literal, "a-b"]]);
613
+ assertParse("expression", "a&b", [markers.reference, [ops.literal, "a&b"]]);
564
614
  assertParse("expression", "a & b", [
565
615
  ops.bitwiseAnd,
566
- [undetermined, "a"],
567
- [undetermined, "b"],
616
+ [markers.reference, [ops.literal, "a"]],
617
+ [markers.reference, [ops.literal, "b"]],
568
618
  ]);
569
619
  });
570
620
 
@@ -578,7 +628,7 @@ Body`,
578
628
  [
579
629
  ops.lambda,
580
630
  [[ops.literal, "name"]],
581
- [[ops.scope, "_template"], undefined],
631
+ [[markers.reference, [ops.literal, "_template"]], undefined],
582
632
  ],
583
633
  "jse",
584
634
  false
@@ -586,12 +636,18 @@ Body`,
586
636
  });
587
637
 
588
638
  test("group", () => {
589
- assertParse("group", "(hello)", [ops.scope, "hello"]);
590
- assertParse("group", "(((nested)))", [ops.scope, "nested"]);
591
- assertParse("group", "(fn())", [[ops.builtin, "fn"], undefined]);
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]);
592
648
  assertParse("group", "(a -> b)", [
593
- [ops.builtin, "b"],
594
- [ops.scope, "a"],
649
+ [markers.global, "b"],
650
+ [markers.reference, [ops.literal, "a"]],
595
651
  ]);
596
652
  });
597
653
 
@@ -614,42 +670,42 @@ Body`,
614
670
 
615
671
  test("implicitParenthesesCallExpression", () => {
616
672
  assertParse("implicitParenthesesCallExpression", "fn arg", [
617
- [ops.builtin, "fn"],
618
- [undetermined, "arg"],
673
+ [markers.global, "fn"],
674
+ [markers.reference, [ops.literal, "arg"]],
619
675
  ]);
620
676
  assertParse("implicitParenthesesCallExpression", "page.ori 'a', 'b'", [
621
- [ops.scope, "page.ori"],
677
+ [markers.reference, [ops.literal, "page.ori"]],
622
678
  [ops.literal, "a"],
623
679
  [ops.literal, "b"],
624
680
  ]);
625
681
  assertParse("implicitParenthesesCallExpression", "fn a(b), c", [
626
- [ops.builtin, "fn"],
682
+ [markers.global, "fn"],
627
683
  [
628
- [ops.builtin, "a"],
629
- [ops.scope, "b"],
684
+ [markers.global, "a"],
685
+ [markers.reference, [ops.literal, "b"]],
630
686
  ],
631
- [undetermined, "c"],
687
+ [markers.reference, [ops.literal, "c"]],
632
688
  ]);
633
689
  assertParse("implicitParenthesesCallExpression", "(fn()) 'arg'", [
634
- [[ops.builtin, "fn"], undefined],
690
+ [[markers.global, "fn"], undefined],
635
691
  [ops.literal, "arg"],
636
692
  ]);
637
693
  assertParse("implicitParenthesesCallExpression", "tree/key arg", [
638
- [ops.traverse, [ops.scope, "tree/"], [ops.literal, "key"]],
639
- [undetermined, "arg"],
694
+ [markers.reference, [ops.literal, "tree/"], [ops.literal, "key"]],
695
+ [markers.reference, [ops.literal, "arg"]],
640
696
  ]);
641
697
  assertParse("implicitParenthesesCallExpression", "foo.js bar.ori 'arg'", [
642
- [ops.scope, "foo.js"],
698
+ [markers.reference, [ops.literal, "foo.js"]],
643
699
  [
644
- [ops.scope, "bar.ori"],
700
+ [markers.reference, [ops.literal, "bar.ori"]],
645
701
  [ops.literal, "arg"],
646
702
  ],
647
703
  ]);
648
704
  });
649
705
 
650
706
  test("jsIdentifier", () => {
651
- assertParse("jsIdentifier", "foo", "foo", "jse", false);
652
- assertParse("jsIdentifier", "$Δelta", "$Δelta", "jse", false);
707
+ assertParse("jsIdentifier", "foo", [ops.literal, "foo"], "jse");
708
+ assertParse("jsIdentifier", "$Δelta", [ops.literal, "$Δelta"], "jse");
653
709
  assertThrows(
654
710
  "jsIdentifier",
655
711
  "1stCharacterIsNumber",
@@ -689,7 +745,6 @@ Body`,
689
745
  [ops.literal, 2],
690
746
  [ops.literal, 3],
691
747
  ]);
692
- assertThrows("list", "1\n2\n3", `but "\\n" found`, 0, "jse");
693
748
  assertParse("list", "'a' , 'b' , 'c'", [
694
749
  [ops.literal, "a"],
695
750
  [ops.literal, "b"],
@@ -700,8 +755,8 @@ Body`,
700
755
  test("logicalAndExpression", () => {
701
756
  assertParse("logicalAndExpression", "true && false", [
702
757
  ops.logicalAnd,
703
- [ops.scope, "true"],
704
- [ops.lambda, [], [undetermined, "false"]],
758
+ [markers.reference, [ops.literal, "true"]],
759
+ [ops.lambda, [], [markers.reference, [ops.literal, "false"]]],
705
760
  ]);
706
761
  });
707
762
 
@@ -713,9 +768,9 @@ Body`,
713
768
  ]);
714
769
  assertParse("logicalOrExpression", "false || false || true", [
715
770
  ops.logicalOr,
716
- [ops.scope, "false"],
717
- [ops.lambda, [], [undetermined, "false"]],
718
- [ops.lambda, [], [undetermined, "true"]],
771
+ [markers.reference, [ops.literal, "false"]],
772
+ [ops.lambda, [], [markers.reference, [ops.literal, "false"]]],
773
+ [ops.lambda, [], [markers.reference, [ops.literal, "true"]]],
719
774
  ]);
720
775
  assertParse("logicalOrExpression", "1 || 2 && 0", [
721
776
  ops.logicalOr,
@@ -753,27 +808,31 @@ Body`,
753
808
  });
754
809
 
755
810
  test("namespace", () => {
756
- assertParse("namespace", "js:", [ops.builtin, "js:"]);
811
+ assertParse("namespace", "js:", [markers.global, "js:"]);
757
812
  });
758
813
 
759
814
  test("newExpression", () => {
760
815
  assertParse("newExpression", "new Foo()", [
761
816
  ops.construct,
762
- [ops.scope, "Foo"],
817
+ [markers.reference, [ops.literal, "Foo"]],
818
+ ]);
819
+ assertParse("newExpression", "new:Foo()", [
820
+ ops.construct,
821
+ [markers.reference, [ops.literal, "Foo"]],
763
822
  ]);
764
823
  });
765
824
 
766
825
  test("nullishCoalescingExpression", () => {
767
826
  assertParse("nullishCoalescingExpression", "a ?? b", [
768
827
  ops.nullishCoalescing,
769
- [ops.scope, "a"],
770
- [ops.lambda, [], [undetermined, "b"]],
828
+ [markers.reference, [ops.literal, "a"]],
829
+ [ops.lambda, [], [markers.reference, [ops.literal, "b"]]],
771
830
  ]);
772
831
  assertParse("nullishCoalescingExpression", "a ?? b ?? c", [
773
832
  ops.nullishCoalescing,
774
- [ops.scope, "a"],
775
- [ops.lambda, [], [undetermined, "b"]],
776
- [ops.lambda, [], [undetermined, "c"]],
833
+ [markers.reference, [ops.literal, "a"]],
834
+ [ops.lambda, [], [markers.reference, [ops.literal, "b"]]],
835
+ [ops.lambda, [], [markers.reference, [ops.literal, "c"]]],
777
836
  ]);
778
837
  });
779
838
 
@@ -788,7 +847,7 @@ Body`,
788
847
  assertParse("objectLiteral", "{ a: 1, b }", [
789
848
  ops.object,
790
849
  ["a", [ops.literal, 1]],
791
- ["b", [ops.inherited, "b"]],
850
+ ["b", [markers.reference, "b"]],
792
851
  ]);
793
852
  assertParse("objectLiteral", "{ sub: { a: 1 } }", [
794
853
  ops.object,
@@ -805,7 +864,7 @@ Body`,
805
864
  ]);
806
865
  assertParse("objectLiteral", "{ a = b, b = 2 }", [
807
866
  ops.object,
808
- ["a", [ops.getter, [ops.scope, "b"]]],
867
+ ["a", [ops.getter, [markers.reference, [ops.literal, "b"]]]],
809
868
  ["b", [ops.literal, 2]],
810
869
  ]);
811
870
  assertParse(
@@ -816,7 +875,7 @@ Body`,
816
875
  }`,
817
876
  [
818
877
  ops.object,
819
- ["a", [ops.getter, [ops.scope, "b"]]],
878
+ ["a", [ops.getter, [markers.reference, [ops.literal, "b"]]]],
820
879
  ["b", [ops.literal, 2]],
821
880
  ]
822
881
  );
@@ -832,7 +891,7 @@ Body`,
832
891
  ops.object,
833
892
  [
834
893
  "a",
835
- [ops.object, ["b", [ops.getter, [[ops.builtin, "fn"], undefined]]]],
894
+ [ops.object, ["b", [ops.getter, [[markers.global, "fn"], undefined]]]],
836
895
  ],
837
896
  ]);
838
897
  assertParse("objectLiteral", "{ x = fn.js('a') }", [
@@ -842,16 +901,28 @@ Body`,
842
901
  [
843
902
  ops.getter,
844
903
  [
845
- [ops.scope, "fn.js"],
904
+ [markers.reference, [ops.literal, "fn.js"]],
846
905
  [ops.literal, "a"],
847
906
  ],
848
907
  ],
849
908
  ],
850
909
  ]);
851
- assertParse("objectLiteral", "{ a: 1, ...b }", [
852
- ops.merge,
853
- [ops.object, ["a", [ops.literal, 1]]],
854
- [ops.scope, "b"],
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",
855
926
  ]);
856
927
  assertParse("objectLiteral", "{ a: 1, ...{ b: 2 } }", [
857
928
  ops.object,
@@ -862,22 +933,63 @@ Body`,
862
933
  ops.object,
863
934
  ["(a)", [ops.literal, 1]],
864
935
  ]);
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
+ );
865
953
  });
866
954
 
867
955
  test("objectEntry", () => {
868
- assertParse("objectEntry", "foo", ["foo", [ops.inherited, "foo"]]);
869
- assertParse("objectEntry", "x: y", ["x", [ops.scope, "y"]]);
870
- assertParse("objectEntry", "a: a", ["a", [ops.inherited, "a"]]);
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
+ );
871
979
  assertParse("objectEntry", "a: (a) => a", [
872
980
  "a",
873
- [ops.lambda, [[ops.literal, "a"]], [ops.scope, "a"]],
981
+ [
982
+ ops.lambda,
983
+ [[ops.literal, "a"]],
984
+ [markers.reference, [ops.literal, "a"]],
985
+ ],
874
986
  ]);
875
987
  assertParse("objectEntry", "posts/: map(posts, post.ori)", [
876
988
  "posts/",
877
989
  [
878
- [ops.builtin, "map"],
879
- [ops.inherited, "posts"],
880
- [ops.scope, "post.ori"],
990
+ [markers.global, "map"],
991
+ [markers.reference, [ops.literal, "posts"]],
992
+ [markers.reference, [ops.literal, "post.ori"]],
881
993
  ],
882
994
  ]);
883
995
  });
@@ -885,14 +997,14 @@ Body`,
885
997
  test("objectGetter", () => {
886
998
  assertParse("objectGetter", "data = obj.json", [
887
999
  "data",
888
- [ops.getter, [ops.scope, "obj.json"]],
1000
+ [ops.getter, [markers.reference, [ops.literal, "obj.json"]]],
889
1001
  ]);
890
1002
  assertParse("objectGetter", "foo = page.ori 'bar'", [
891
1003
  "foo",
892
1004
  [
893
1005
  ops.getter,
894
1006
  [
895
- [ops.scope, "page.ori"],
1007
+ [markers.reference, [ops.literal, "page.ori"]],
896
1008
  [ops.literal, "bar"],
897
1009
  ],
898
1010
  ],
@@ -908,7 +1020,7 @@ Body`,
908
1020
  assertParse("objectProperty", "x: fn('a')", [
909
1021
  "x",
910
1022
  [
911
- [ops.builtin, "fn"],
1023
+ [markers.global, "fn"],
912
1024
  [ops.literal, "a"],
913
1025
  ],
914
1026
  ]);
@@ -930,9 +1042,9 @@ Body`,
930
1042
  test("parenthesesArguments", () => {
931
1043
  assertParse("parenthesesArguments", "()", [undefined]);
932
1044
  assertParse("parenthesesArguments", "(a, b, c)", [
933
- [ops.scope, "a"],
934
- [ops.scope, "b"],
935
- [ops.scope, "c"],
1045
+ [markers.reference, [ops.literal, "a"]],
1046
+ [markers.reference, [ops.literal, "b"]],
1047
+ [markers.reference, [ops.literal, "c"]],
936
1048
  ]);
937
1049
  });
938
1050
 
@@ -954,41 +1066,47 @@ Body`,
954
1066
  });
955
1067
 
956
1068
  test("pathArguments", () => {
957
- assertParse("pathArguments", "/", [ops.traverse]);
1069
+ assertParse("pathArguments", "/", [markers.traverse]);
958
1070
  assertParse("pathArguments", "/tree", [
959
- ops.traverse,
1071
+ markers.traverse,
960
1072
  [ops.literal, "tree"],
961
1073
  ]);
962
1074
  assertParse("pathArguments", "/tree/", [
963
- ops.traverse,
1075
+ markers.traverse,
964
1076
  [ops.literal, "tree/"],
965
1077
  ]);
966
1078
  });
967
1079
 
968
1080
  test("pipelineExpression", () => {
969
- assertParse("pipelineExpression", "foo", [ops.scope, "foo"]);
1081
+ assertParse("pipelineExpression", "foo", [
1082
+ markers.reference,
1083
+ [ops.literal, "foo"],
1084
+ ]);
970
1085
  assertParse("pipelineExpression", "a -> b", [
971
- [ops.builtin, "b"],
972
- [ops.scope, "a"],
1086
+ [markers.global, "b"],
1087
+ [markers.reference, [ops.literal, "a"]],
973
1088
  ]);
974
1089
  assertParse("pipelineExpression", "input → one.js → two.js", [
975
- [ops.scope, "two.js"],
1090
+ [markers.reference, [ops.literal, "two.js"]],
976
1091
  [
977
- [ops.scope, "one.js"],
978
- [ops.scope, "input"],
1092
+ [markers.reference, [ops.literal, "one.js"]],
1093
+ [markers.reference, [ops.literal, "input"]],
979
1094
  ],
980
1095
  ]);
981
1096
  assertParse("pipelineExpression", "fn a -> b", [
982
- [ops.builtin, "b"],
1097
+ [markers.global, "b"],
983
1098
  [
984
- [ops.builtin, "fn"],
985
- [undetermined, "a"],
1099
+ [markers.global, "fn"],
1100
+ [markers.reference, [ops.literal, "a"]],
986
1101
  ],
987
1102
  ]);
988
1103
  });
989
1104
 
990
1105
  test("primary", () => {
991
- assertParse("primary", "foo.js", [ops.scope, "foo.js"]);
1106
+ assertParse("primary", "foo.js", [
1107
+ markers.reference,
1108
+ [ops.literal, "foo.js"],
1109
+ ]);
992
1110
  assertParse("primary", "[1, 2]", [
993
1111
  ops.array,
994
1112
  [ops.literal, 1],
@@ -998,7 +1116,7 @@ Body`,
998
1116
  assertParse(
999
1117
  "primary",
1000
1118
  "<index.html>",
1001
- [ops.scope, "index.html"],
1119
+ [[ops.scope], [ops.literal, "index.html"]],
1002
1120
  "jse",
1003
1121
  false
1004
1122
  );
@@ -1018,32 +1136,33 @@ Body`,
1018
1136
  });
1019
1137
 
1020
1138
  test("protocolExpression", () => {
1021
- assertParse("protocolExpression", "foo://bar", [
1022
- [ops.builtin, "foo:"],
1023
- [ops.literal, "bar"],
1139
+ assertParse("protocolExpression", "foo://bar/baz", [
1140
+ [markers.global, "foo:"],
1141
+ [ops.literal, "bar/"],
1142
+ [ops.literal, "baz"],
1024
1143
  ]);
1025
1144
  assertParse("protocolExpression", "http://example.com", [
1026
- [ops.builtin, "http:"],
1145
+ [markers.global, "http:"],
1027
1146
  [ops.literal, "example.com"],
1028
1147
  ]);
1029
1148
  assertParse("protocolExpression", "https://example.com/about/", [
1030
- [ops.builtin, "https:"],
1149
+ [markers.global, "https:"],
1031
1150
  [ops.literal, "example.com/"],
1032
1151
  [ops.literal, "about/"],
1033
1152
  ]);
1034
1153
  assertParse("protocolExpression", "https://example.com/about/index.html", [
1035
- [ops.builtin, "https:"],
1154
+ [markers.global, "https:"],
1036
1155
  [ops.literal, "example.com/"],
1037
1156
  [ops.literal, "about/"],
1038
1157
  [ops.literal, "index.html"],
1039
1158
  ]);
1040
1159
  assertParse("protocolExpression", "http://localhost:5000/foo", [
1041
- [ops.builtin, "http:"],
1160
+ [markers.global, "http:"],
1042
1161
  [ops.literal, "localhost:5000/"],
1043
1162
  [ops.literal, "foo"],
1044
1163
  ]);
1045
1164
  assertParse("protocolExpression", "files:///foo/bar.txt", [
1046
- [ops.builtin, "files:"],
1165
+ [markers.global, "files:"],
1047
1166
  [ops.literal, "/"],
1048
1167
  [ops.literal, "foo/"],
1049
1168
  [ops.literal, "bar.txt"],
@@ -1052,7 +1171,7 @@ Body`,
1052
1171
 
1053
1172
  test("qualifiedReference", () => {
1054
1173
  assertParse("qualifiedReference", "js:Date", [
1055
- [ops.builtin, "js:"],
1174
+ [markers.global, "js:"],
1056
1175
  [ops.literal, "Date"],
1057
1176
  ]);
1058
1177
  });
@@ -1084,15 +1203,15 @@ Body`,
1084
1203
  ]);
1085
1204
  });
1086
1205
 
1087
- test("rootDirectory", () => {
1088
- assertParse("rootDirectory", "/", [ops.rootDirectory]);
1089
- });
1090
-
1091
1206
  test("scopeReference", () => {
1092
- assertParse("scopeReference", "keys", [undetermined, "keys"]);
1093
- assertParse("scopeReference", "greet.js", [ops.scope, "greet.js"]);
1094
- // scopeReference checks whether a slash follows; hard to test in isolation
1095
- // assertParse("scopeReference", "markdown/", [ops.scope, "markdown"]);
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
+ ]);
1096
1215
  });
1097
1216
 
1098
1217
  test("shiftExpression", () => {
@@ -1117,18 +1236,22 @@ Body`,
1117
1236
  assertParse("shorthandFunction", "=message", [
1118
1237
  ops.lambda,
1119
1238
  [[ops.literal, "_"]],
1120
- [undetermined, "message"],
1239
+ [markers.reference, [ops.literal, "message"]],
1121
1240
  ]);
1122
1241
  assertParse("shorthandFunction", "=`Hello, ${name}.`", [
1123
1242
  ops.lambda,
1124
1243
  [[ops.literal, "_"]],
1125
- [ops.templateTree, [ops.literal, ["Hello, ", "."]], [ops.scope, "name"]],
1244
+ [
1245
+ ops.templateTree,
1246
+ [ops.literal, ["Hello, ", "."]],
1247
+ [markers.reference, [ops.literal, "name"]],
1248
+ ],
1126
1249
  ]);
1127
1250
  assertParse("shorthandFunction", "=indent`hello`", [
1128
1251
  ops.lambda,
1129
1252
  [[ops.literal, "_"]],
1130
1253
  [
1131
- [ops.builtin, "indent"],
1254
+ [markers.global, "indent"],
1132
1255
  [ops.literal, ["hello"]],
1133
1256
  ],
1134
1257
  ]);
@@ -1139,8 +1262,14 @@ Body`,
1139
1262
  });
1140
1263
 
1141
1264
  test("spreadElement", () => {
1142
- assertParse("spreadElement", "...a", [ops.spread, [ops.scope, "a"]]);
1143
- assertParse("spreadElement", "…a", [ops.spread, [ops.scope, "a"]]);
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
+ ]);
1144
1273
  });
1145
1274
 
1146
1275
  test("stringLiteral", () => {
@@ -1159,21 +1288,13 @@ Body`,
1159
1288
 
1160
1289
  test("templateBody", () => {
1161
1290
  assertParse("templateBody", "hello${foo}world", [
1162
- ops.lambda,
1163
- [[ops.literal, "_"]],
1164
- [
1165
- ops.templateIndent,
1166
- [ops.literal, ["hello", "world"]],
1167
- [ops.scope, "foo"],
1168
- ],
1291
+ ops.templateIndent,
1292
+ [ops.literal, ["hello", "world"]],
1293
+ [markers.reference, [ops.literal, "foo"]],
1169
1294
  ]);
1170
1295
  assertParse("templateBody", "Documents can contain ` backticks", [
1171
- ops.lambda,
1172
- [[ops.literal, "_"]],
1173
- [
1174
- ops.templateIndent,
1175
- [ops.literal, ["Documents can contain ` backticks"]],
1176
- ],
1296
+ ops.templateIndent,
1297
+ [ops.literal, ["Documents can contain ` backticks"]],
1177
1298
  ]);
1178
1299
  });
1179
1300
 
@@ -1193,13 +1314,9 @@ title: Title goes here
1193
1314
  ---
1194
1315
  Body text`,
1195
1316
  [
1196
- ops.document,
1197
- [ops.literal, { title: "Title goes here" }],
1198
- [
1199
- ops.lambda,
1200
- [[ops.literal, "_"]],
1201
- [ops.templateIndent, [ops.literal, ["Body text"]]],
1202
- ],
1317
+ ops.object,
1318
+ ["title", [ops.literal, "Title goes here"]],
1319
+ ["@text", [ops.templateIndent, [ops.literal, ["Body text"]]]],
1203
1320
  ]
1204
1321
  );
1205
1322
  });
@@ -1221,16 +1338,9 @@ Body text`,
1221
1338
  [
1222
1339
  "@text",
1223
1340
  [
1224
- [
1225
- ops.lambda,
1226
- [[ops.literal, "_"]],
1227
- [
1228
- ops.templateIndent,
1229
- [ops.literal, ["<h1>", "</h1>\n"]],
1230
- [ops.scope, "title"],
1231
- ],
1232
- ],
1233
- undefined,
1341
+ ops.templateIndent,
1342
+ [ops.literal, ["<h1>", "</h1>\n"]],
1343
+ [markers.reference, [ops.literal, "title"]],
1234
1344
  ],
1235
1345
  ],
1236
1346
  ]
@@ -1251,7 +1361,7 @@ Body text`,
1251
1361
  assertParse("templateLiteral", "`foo ${x} bar`", [
1252
1362
  ops.templateTree,
1253
1363
  [ops.literal, ["foo ", " bar"]],
1254
- [ops.scope, "x"],
1364
+ [markers.reference, [ops.literal, "x"]],
1255
1365
  ]);
1256
1366
  assertParse("templateLiteral", "`${`nested`}`", [
1257
1367
  ops.templateTree,
@@ -1262,12 +1372,16 @@ Body text`,
1262
1372
  ops.templateTree,
1263
1373
  [ops.literal, ["", ""]],
1264
1374
  [
1265
- [ops.builtin, "map:"],
1266
- [ops.scope, "people"],
1375
+ [markers.global, "map:"],
1376
+ [markers.reference, [ops.literal, "people"]],
1267
1377
  [
1268
1378
  ops.lambda,
1269
1379
  [[ops.literal, "_"]],
1270
- [ops.templateTree, [ops.literal, ["", ""]], [ops.scope, "name"]],
1380
+ [
1381
+ ops.templateTree,
1382
+ [ops.literal, ["", ""]],
1383
+ [markers.reference, [ops.literal, "name"]],
1384
+ ],
1271
1385
  ],
1272
1386
  ],
1273
1387
  ]);
@@ -1277,7 +1391,7 @@ Body text`,
1277
1391
  assertParse(
1278
1392
  "templateSubstitution",
1279
1393
  "${foo}",
1280
- [ops.scope, "foo"],
1394
+ [markers.reference, [ops.literal, "foo"]],
1281
1395
  "shell",
1282
1396
  false
1283
1397
  );
@@ -1299,7 +1413,7 @@ Body text`,
1299
1413
  test("unaryExpression", () => {
1300
1414
  assertParse("unaryExpression", "!true", [
1301
1415
  ops.logicalNot,
1302
- [undetermined, "true"],
1416
+ [markers.reference, [ops.literal, "true"]],
1303
1417
  ]);
1304
1418
  assertParse("unaryExpression", "+1", [ops.unaryPlus, [ops.literal, 1]]);
1305
1419
  assertParse("unaryExpression", "-2", [ops.unaryMinus, [ops.literal, 2]]);