@weborigami/language 0.3.3-jse.3 → 0.3.4-jse.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/src/compiler/compile.js +5 -3
- package/src/compiler/isOrigamiFrontMatter.js +4 -3
- package/src/compiler/optimize.js +225 -112
- package/src/compiler/origami.pegjs +214 -201
- package/src/compiler/parse.js +1403 -1473
- package/src/compiler/parserHelpers.js +65 -157
- package/src/runtime/expressionObject.js +13 -7
- package/src/runtime/handlers.js +1 -1
- package/src/runtime/jsGlobals.js +7 -0
- package/src/runtime/ops.js +4 -18
- package/test/compiler/compile.test.js +14 -9
- package/test/compiler/optimize.test.js +234 -64
- package/test/compiler/parse.test.js +618 -476
- package/test/runtime/expressionObject.test.js +2 -2
- package/test/runtime/handlers.test.js +2 -2
- package/src/runtime/templateStandard.js +0 -13
- package/test/runtime/templateText.test.js +0 -18
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { trailingSlash } from "@weborigami/async-tree";
|
|
2
1
|
import * as YAMLModule from "yaml";
|
|
2
|
+
import trailingSlash from "../../../origami/src/origami/slash.js";
|
|
3
3
|
import codeFragment from "../runtime/codeFragment.js";
|
|
4
4
|
import * as ops from "../runtime/ops.js";
|
|
5
5
|
|
|
@@ -18,12 +18,12 @@ const YAML = YAMLModule.default ?? YAMLModule.YAML;
|
|
|
18
18
|
// Markers in compiled output, will get optimized away
|
|
19
19
|
export const markers = {
|
|
20
20
|
global: Symbol("global"), // Global reference
|
|
21
|
-
|
|
21
|
+
external: Symbol("external"), // External reference
|
|
22
|
+
property: Symbol("property"), // Property access
|
|
22
23
|
reference: Symbol("reference"), // Reference to local, scope, or global
|
|
24
|
+
traverse: Symbol("traverse"), // Path traversal
|
|
23
25
|
};
|
|
24
26
|
|
|
25
|
-
const builtinRegex = /^[A-Za-z][A-Za-z0-9]*$/;
|
|
26
|
-
|
|
27
27
|
/**
|
|
28
28
|
* If a parse result is an object that will be evaluated at runtime, attach the
|
|
29
29
|
* location of the source code that produced it for debugging and error messages.
|
|
@@ -54,69 +54,21 @@ export function applyMacro(code, name, macro) {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
// We're looking for a function call with the given name.
|
|
57
|
-
// For `foo`, the call would be: [[
|
|
57
|
+
// For `foo`, the call would be: [[markers.traverse, [markers.reference, "foo"]], undefined]
|
|
58
58
|
if (
|
|
59
|
-
code[0] &&
|
|
60
|
-
code[0][0] === markers.
|
|
61
|
-
code[0][1]
|
|
62
|
-
code[0][1][
|
|
63
|
-
code[0][1][1] === name &&
|
|
64
|
-
code[1] === undefined
|
|
59
|
+
code[0] instanceof Array &&
|
|
60
|
+
code[0][0] === markers.traverse &&
|
|
61
|
+
code[0][1][0] === markers.reference &&
|
|
62
|
+
code[0][1][1] === name
|
|
65
63
|
) {
|
|
66
|
-
|
|
64
|
+
// Replace the call with the macro
|
|
65
|
+
return annotate(macro, code.location);
|
|
67
66
|
}
|
|
68
67
|
|
|
69
68
|
const applied = code.map((child) => applyMacro(child, name, macro));
|
|
70
69
|
return annotate(applied, code.location);
|
|
71
70
|
}
|
|
72
71
|
|
|
73
|
-
/**
|
|
74
|
-
* The indicated code is being used to define a property named by the given key.
|
|
75
|
-
* Rewrite any [[ops.scope], key] calls to be [ops.inherited, key] to avoid
|
|
76
|
-
* infinite recursion.
|
|
77
|
-
*
|
|
78
|
-
* @param {AnnotatedCode} code
|
|
79
|
-
* @param {string} key
|
|
80
|
-
*/
|
|
81
|
-
function avoidRecursivePropertyCalls(code, key) {
|
|
82
|
-
if (!(code instanceof Array)) {
|
|
83
|
-
return code;
|
|
84
|
-
}
|
|
85
|
-
/** @type {Code} */
|
|
86
|
-
let modified;
|
|
87
|
-
if (
|
|
88
|
-
code[0] instanceof Array &&
|
|
89
|
-
code[0][0] === ops.scope &&
|
|
90
|
-
trailingSlash.remove(code[1][1]) === trailingSlash.remove(key)
|
|
91
|
-
) {
|
|
92
|
-
// Rewrite to avoid recursion
|
|
93
|
-
modified = [ops.inherited, code[1][1]];
|
|
94
|
-
} else if (
|
|
95
|
-
code[0] === ops.lambda &&
|
|
96
|
-
code[1].some((param) => param[1] === key)
|
|
97
|
-
) {
|
|
98
|
-
// Lambda that defines the key; don't rewrite
|
|
99
|
-
return code;
|
|
100
|
-
} else {
|
|
101
|
-
// Process any nested code
|
|
102
|
-
modified = code.map((value) => avoidRecursivePropertyCalls(value, key));
|
|
103
|
-
}
|
|
104
|
-
return annotate(modified, code.location);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Downgrade a potential global reference to a reference.
|
|
109
|
-
*
|
|
110
|
-
* @param {AnnotatedCode} code
|
|
111
|
-
*/
|
|
112
|
-
export function downgradeReference(code) {
|
|
113
|
-
if (code && code.length === 2 && code[0] === markers.reference) {
|
|
114
|
-
return annotate([markers.reference, code[1]], code.location);
|
|
115
|
-
} else {
|
|
116
|
-
return code;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
72
|
/**
|
|
121
73
|
* Create an array
|
|
122
74
|
*
|
|
@@ -209,7 +161,7 @@ export function makeBinaryOperation(left, [operatorToken, right]) {
|
|
|
209
161
|
* @param {AnnotatedCode} target
|
|
210
162
|
* @param {any[]} args
|
|
211
163
|
*/
|
|
212
|
-
export function makeCall(target, args,
|
|
164
|
+
export function makeCall(target, args, location) {
|
|
213
165
|
if (!(target instanceof Array)) {
|
|
214
166
|
const error = new SyntaxError(`Can't call this like a function: ${target}`);
|
|
215
167
|
/** @type {any} */ (error).location = /** @type {any} */ (target).location;
|
|
@@ -219,69 +171,21 @@ export function makeCall(target, args, mode) {
|
|
|
219
171
|
let fnCall;
|
|
220
172
|
const op = args[0];
|
|
221
173
|
if (op === markers.traverse || op === ops.optionalTraverse) {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
// reference or global where all the args are literals.
|
|
231
|
-
const extend =
|
|
232
|
-
(tree[0] === markers.reference ||
|
|
233
|
-
(tree[0] instanceof Array && tree[0][0] === markers.global)) &&
|
|
234
|
-
!tree
|
|
235
|
-
.slice(1)
|
|
236
|
-
.some((arg) => !(arg instanceof Array && arg[0] === ops.literal));
|
|
237
|
-
if (extend) {
|
|
238
|
-
fnCall = tree;
|
|
239
|
-
// If last key doesn't end with slash, add one
|
|
240
|
-
const last = tree.at(-1);
|
|
241
|
-
if (last instanceof Array && last[0] === ops.literal) {
|
|
242
|
-
last[1] = trailingSlash.add(last[1]);
|
|
243
|
-
}
|
|
244
|
-
} else {
|
|
245
|
-
fnCall = [tree];
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
if (args.length > 1) {
|
|
249
|
-
// Regular traverse
|
|
250
|
-
const keys = args.slice(1);
|
|
251
|
-
fnCall.push(...keys);
|
|
252
|
-
} else if (tree[0] !== ops.rootDirectory) {
|
|
253
|
-
// Traverse without arguments equates to unpack
|
|
254
|
-
fnCall = [ops.unpack, tree];
|
|
255
|
-
} else {
|
|
256
|
-
fnCall = tree;
|
|
257
|
-
}
|
|
258
|
-
} else if (op === ops.templateStandard || op === ops.templateTree) {
|
|
174
|
+
// Traverse
|
|
175
|
+
const keys = args.slice(1);
|
|
176
|
+
fnCall = [target, ...keys];
|
|
177
|
+
} else if (op === markers.property) {
|
|
178
|
+
// Property access
|
|
179
|
+
const property = args[1];
|
|
180
|
+
fnCall = [target, property];
|
|
181
|
+
} else if (op === ops.templateTree) {
|
|
259
182
|
// Tagged template
|
|
260
183
|
const strings = args[1];
|
|
261
184
|
const values = args.slice(2);
|
|
262
|
-
fnCall = makeTaggedTemplateCall(
|
|
263
|
-
upgradeReference(target, mode),
|
|
264
|
-
strings,
|
|
265
|
-
...values
|
|
266
|
-
);
|
|
185
|
+
fnCall = makeTaggedTemplateCall(target, strings, ...values);
|
|
267
186
|
} else {
|
|
268
187
|
// Function call with explicit or implicit parentheses
|
|
269
|
-
fnCall = [
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// Create a location spanning the newly-constructed function call.
|
|
273
|
-
const location = { ...target.location };
|
|
274
|
-
if (args instanceof Array) {
|
|
275
|
-
let end;
|
|
276
|
-
if ("location" in args) {
|
|
277
|
-
end = /** @type {any} */ (args).location.end;
|
|
278
|
-
} else if ("location" in args.at(-1)) {
|
|
279
|
-
end = args.at(-1).location.end;
|
|
280
|
-
}
|
|
281
|
-
if (end === undefined) {
|
|
282
|
-
throw "Internal parser error: no location for function call argument";
|
|
283
|
-
}
|
|
284
|
-
location.end = end;
|
|
188
|
+
fnCall = [target, ...args];
|
|
285
189
|
}
|
|
286
190
|
|
|
287
191
|
return annotate(fnCall, location);
|
|
@@ -306,7 +210,7 @@ export function makeDeferredArguments(args) {
|
|
|
306
210
|
});
|
|
307
211
|
}
|
|
308
212
|
|
|
309
|
-
export function makeDocument(
|
|
213
|
+
export function makeDocument(front, body, location) {
|
|
310
214
|
// In order for template expressions to see the front matter properties,
|
|
311
215
|
// we translate the top-level front properties to object entries.
|
|
312
216
|
const entries = Object.entries(front).map(([key, value]) =>
|
|
@@ -314,22 +218,14 @@ export function makeDocument(mode, front, body, location) {
|
|
|
314
218
|
);
|
|
315
219
|
|
|
316
220
|
// Add an entry for the body
|
|
317
|
-
|
|
318
|
-
entries.push(annotate([
|
|
221
|
+
// TODO: Deprecate @text
|
|
222
|
+
entries.push(annotate(["(@text)", body], location));
|
|
223
|
+
entries.push(annotate(["_body", body], location));
|
|
319
224
|
|
|
320
225
|
// Return the code for the document object
|
|
321
226
|
return annotate([ops.object, ...entries], location);
|
|
322
227
|
}
|
|
323
228
|
|
|
324
|
-
export function makeJsPropertyAccess(expression, property) {
|
|
325
|
-
const location = {
|
|
326
|
-
source: expression.location.source,
|
|
327
|
-
start: expression.location.start,
|
|
328
|
-
end: property.location.end,
|
|
329
|
-
};
|
|
330
|
-
return annotate([expression, property], location);
|
|
331
|
-
}
|
|
332
|
-
|
|
333
229
|
/**
|
|
334
230
|
* From the given spreads within an object spread, return the merge.
|
|
335
231
|
*
|
|
@@ -462,28 +358,47 @@ export function makeObject(entries, location) {
|
|
|
462
358
|
return annotate(code, location);
|
|
463
359
|
}
|
|
464
360
|
|
|
361
|
+
/**
|
|
362
|
+
* Handle a path with one or more segments separated by slashes.
|
|
363
|
+
*
|
|
364
|
+
* @param {AnnotatedCode} keys
|
|
365
|
+
*/
|
|
366
|
+
export function makePath(keys) {
|
|
367
|
+
// Remove empty segments
|
|
368
|
+
const args = keys.filter(
|
|
369
|
+
(key, index) => index === 0 || (key[1] !== "" && key[1] !== "/")
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
// Upgrade head to a reference
|
|
373
|
+
const [head, ...tail] = args;
|
|
374
|
+
const headKey = head[1];
|
|
375
|
+
const reference = annotate([markers.reference, headKey], head.location);
|
|
376
|
+
|
|
377
|
+
let code = [markers.traverse, reference, ...tail];
|
|
378
|
+
code.location = spanLocations(code);
|
|
379
|
+
|
|
380
|
+
// Last key has trailing slash implies unpack operation
|
|
381
|
+
if (trailingSlash.has(args.at(-1)[1])) {
|
|
382
|
+
code = annotate([ops.unpack, code], code.location);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return code;
|
|
386
|
+
}
|
|
387
|
+
|
|
465
388
|
/**
|
|
466
389
|
* Make a pipline: similar to a function call, but the order is reversed.
|
|
467
390
|
*
|
|
468
391
|
* @param {AnnotatedCode} arg
|
|
469
392
|
* @param {AnnotatedCode} fn
|
|
470
|
-
* @param {string} mode
|
|
471
393
|
*/
|
|
472
|
-
export function makePipeline(arg, fn,
|
|
473
|
-
const
|
|
474
|
-
const result = makeCall(upgraded, [arg], mode);
|
|
394
|
+
export function makePipeline(arg, fn, location) {
|
|
395
|
+
const result = makeCall(fn, [arg], location);
|
|
475
396
|
const source = fn.location.source;
|
|
476
397
|
let start = arg.location.start;
|
|
477
398
|
let end = fn.location.end;
|
|
478
399
|
return annotate(result, { start, source, end });
|
|
479
400
|
}
|
|
480
401
|
|
|
481
|
-
// Define a property on an object.
|
|
482
|
-
export function makeProperty(key, value) {
|
|
483
|
-
const modified = avoidRecursivePropertyCalls(value, key);
|
|
484
|
-
return [key, modified];
|
|
485
|
-
}
|
|
486
|
-
|
|
487
402
|
/**
|
|
488
403
|
* Make a tagged template call
|
|
489
404
|
*
|
|
@@ -594,21 +509,14 @@ export function makeYamlObject(text, location) {
|
|
|
594
509
|
return parsed;
|
|
595
510
|
}
|
|
596
511
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
builtinRegex.exec(code[1][1])
|
|
608
|
-
) {
|
|
609
|
-
const result = [markers.global, code[1][1]];
|
|
610
|
-
return annotate(result, code.location);
|
|
611
|
-
} else {
|
|
612
|
-
return code;
|
|
613
|
-
}
|
|
512
|
+
// Create a locations that spans those in the array. This assumes the locations
|
|
513
|
+
// are in order and non-overlapping.
|
|
514
|
+
function spanLocations(code) {
|
|
515
|
+
const first = code.find((item) => item.location).location;
|
|
516
|
+
const last = code[code.findLastIndex((item) => item.location)].location;
|
|
517
|
+
return {
|
|
518
|
+
source: first.source,
|
|
519
|
+
start: first.start,
|
|
520
|
+
end: last.end,
|
|
521
|
+
};
|
|
614
522
|
}
|
|
@@ -136,17 +136,23 @@ export default async function expressionObject(entries, parent) {
|
|
|
136
136
|
return object;
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
function entryKey(object, eagerProperties
|
|
140
|
-
|
|
139
|
+
export function entryKey(entry, object = null, eagerProperties = []) {
|
|
140
|
+
let [key, value] = entry;
|
|
141
141
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
//
|
|
142
|
+
if (key[0] === "(" && key[key.length - 1] === ")") {
|
|
143
|
+
// Non-enumerable property, remove parentheses. This doesn't come up in the
|
|
144
|
+
// constructor, but can happen in situations encountered by the compiler's
|
|
145
|
+
// optimizer.
|
|
146
|
+
key = key.slice(1, -1);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (trailingSlash.has(key)) {
|
|
150
|
+
// Explicit trailing slash, return as is
|
|
145
151
|
return key;
|
|
146
152
|
}
|
|
147
153
|
|
|
148
154
|
// If eager property value is treelike, add slash to the key
|
|
149
|
-
if (eagerProperties.includes(key) && Tree.isTreelike(object[key])) {
|
|
155
|
+
if (eagerProperties.includes(key) && Tree.isTreelike(object?.[key])) {
|
|
150
156
|
return trailingSlash.add(key);
|
|
151
157
|
}
|
|
152
158
|
|
|
@@ -163,5 +169,5 @@ function entryKey(object, eagerProperties, entry) {
|
|
|
163
169
|
function keys(object, eagerProperties, propertyIsEnumerable, entries) {
|
|
164
170
|
return entries
|
|
165
171
|
.filter(([key]) => propertyIsEnumerable[key])
|
|
166
|
-
.map((entry) => entryKey(object, eagerProperties
|
|
172
|
+
.map((entry) => entryKey(entry, object, eagerProperties));
|
|
167
173
|
}
|
package/src/runtime/handlers.js
CHANGED
|
@@ -38,7 +38,7 @@ export async function handleExtension(parent, value, key, handlers) {
|
|
|
38
38
|
: extension.extname(key);
|
|
39
39
|
if (extname) {
|
|
40
40
|
const handlerName = `${extname.slice(1)}.handler`;
|
|
41
|
-
let handler = await handlers
|
|
41
|
+
let handler = await handlers[handlerName];
|
|
42
42
|
if (handler) {
|
|
43
43
|
if (isUnpackable(handler)) {
|
|
44
44
|
// The extension handler itself needs to be unpacked
|
package/src/runtime/jsGlobals.js
CHANGED
|
@@ -7,6 +7,10 @@ import path from "node:path";
|
|
|
7
7
|
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects.
|
|
8
8
|
* That page lists some things like `TypedArrays` which are not globals so are
|
|
9
9
|
* omitted here.
|
|
10
|
+
*
|
|
11
|
+
* Also includes
|
|
12
|
+
* Fetch API
|
|
13
|
+
* URL API
|
|
10
14
|
*/
|
|
11
15
|
export default {
|
|
12
16
|
AggregateError,
|
|
@@ -25,6 +29,7 @@ export default {
|
|
|
25
29
|
Float32Array,
|
|
26
30
|
Float64Array,
|
|
27
31
|
Function,
|
|
32
|
+
Headers,
|
|
28
33
|
Infinity,
|
|
29
34
|
Int16Array,
|
|
30
35
|
Int32Array,
|
|
@@ -44,6 +49,8 @@ export default {
|
|
|
44
49
|
ReferenceError,
|
|
45
50
|
Reflect,
|
|
46
51
|
RegExp,
|
|
52
|
+
Request,
|
|
53
|
+
Response,
|
|
47
54
|
Set,
|
|
48
55
|
SharedArrayBuffer,
|
|
49
56
|
String,
|
package/src/runtime/ops.js
CHANGED
|
@@ -21,7 +21,6 @@ import mergeTrees from "./mergeTrees.js";
|
|
|
21
21
|
import OrigamiFiles from "./OrigamiFiles.js";
|
|
22
22
|
import { codeSymbol } from "./symbols.js";
|
|
23
23
|
import templateFunctionIndent from "./templateIndent.js";
|
|
24
|
-
import templateFunctionStandard from "./templateStandard.js";
|
|
25
24
|
|
|
26
25
|
function addOpLabel(op, label) {
|
|
27
26
|
Object.defineProperty(op, "toString", {
|
|
@@ -181,11 +180,6 @@ export async function flat(...args) {
|
|
|
181
180
|
}
|
|
182
181
|
addOpLabel(flat, "«ops.flat»");
|
|
183
182
|
|
|
184
|
-
/**
|
|
185
|
-
* This op is only used during parsing for an explicit to a global.
|
|
186
|
-
*/
|
|
187
|
-
export const global = new String("«global");
|
|
188
|
-
|
|
189
183
|
/**
|
|
190
184
|
* This op is only used during parsing. It signals to ops.object that the
|
|
191
185
|
* "arguments" of the expression should be used to define a property getter.
|
|
@@ -207,11 +201,11 @@ addOpLabel(greaterThanOrEqual, "«ops.greaterThanOrEqual»");
|
|
|
207
201
|
*
|
|
208
202
|
* @this {AsyncTree|null}
|
|
209
203
|
*/
|
|
210
|
-
export async function homeDirectory() {
|
|
204
|
+
export async function homeDirectory(...keys) {
|
|
211
205
|
const tree = new OrigamiFiles(os.homedir());
|
|
212
206
|
// Use the same handlers as the current tree
|
|
213
207
|
tree.handlers = getHandlers(this);
|
|
214
|
-
return tree;
|
|
208
|
+
return keys.length > 0 ? Tree.traverse(tree, ...keys) : tree;
|
|
215
209
|
}
|
|
216
210
|
addOpLabel(homeDirectory, "«ops.homeDirectory»");
|
|
217
211
|
|
|
@@ -427,11 +421,11 @@ addOpLabel(remainder, "«ops.remainder»");
|
|
|
427
421
|
*
|
|
428
422
|
* @this {AsyncTree|null}
|
|
429
423
|
*/
|
|
430
|
-
export async function rootDirectory() {
|
|
424
|
+
export async function rootDirectory(...keys) {
|
|
431
425
|
const tree = new OrigamiFiles("/");
|
|
432
426
|
// Use the same handlers as the current tree
|
|
433
427
|
tree.handlers = getHandlers(this);
|
|
434
|
-
return tree;
|
|
428
|
+
return keys.length > 0 ? Tree.traverse(tree, ...keys) : tree;
|
|
435
429
|
}
|
|
436
430
|
addOpLabel(rootDirectory, "«ops.rootDirectory»");
|
|
437
431
|
|
|
@@ -496,14 +490,6 @@ export async function templateIndent(strings, ...values) {
|
|
|
496
490
|
}
|
|
497
491
|
addOpLabel(templateIndent, "«ops.templateIndent»");
|
|
498
492
|
|
|
499
|
-
/**
|
|
500
|
-
* Apply the default tagged template function.
|
|
501
|
-
*/
|
|
502
|
-
export function templateStandard(strings, ...values) {
|
|
503
|
-
return templateFunctionStandard(strings, ...values);
|
|
504
|
-
}
|
|
505
|
-
addOpLabel(templateStandard, "«ops.templateStandard»");
|
|
506
|
-
|
|
507
493
|
/**
|
|
508
494
|
* Apply the tree tagged template function.
|
|
509
495
|
*/
|
|
@@ -4,10 +4,10 @@ import { describe, test } from "node:test";
|
|
|
4
4
|
import * as compile from "../../src/compiler/compile.js";
|
|
5
5
|
import { assertCodeEqual } from "./codeHelpers.js";
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const sharedGlobals = {
|
|
8
8
|
greet: (name) => `Hello, ${name}!`,
|
|
9
9
|
name: "Alice",
|
|
10
|
-
}
|
|
10
|
+
};
|
|
11
11
|
|
|
12
12
|
describe("compile", () => {
|
|
13
13
|
test("array", async () => {
|
|
@@ -24,7 +24,11 @@ describe("compile", () => {
|
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
test("angle bracket path", async () => {
|
|
27
|
-
await assertCompile("<
|
|
27
|
+
await assertCompile("<data>", "Bob", {
|
|
28
|
+
target: {
|
|
29
|
+
data: "Bob",
|
|
30
|
+
},
|
|
31
|
+
});
|
|
28
32
|
});
|
|
29
33
|
|
|
30
34
|
test("object literal", async () => {
|
|
@@ -75,7 +79,9 @@ describe("compile", () => {
|
|
|
75
79
|
});
|
|
76
80
|
|
|
77
81
|
test("async object", async () => {
|
|
78
|
-
const fn = compile.expression("{ a: { b = name }}", {
|
|
82
|
+
const fn = compile.expression("{ a: { b = name }}", {
|
|
83
|
+
globals: sharedGlobals,
|
|
84
|
+
});
|
|
79
85
|
const object = await fn.call(null);
|
|
80
86
|
assert.deepEqual(await object.a.b, "Alice");
|
|
81
87
|
});
|
|
@@ -99,7 +105,7 @@ describe("compile", () => {
|
|
|
99
105
|
|
|
100
106
|
test("tagged template string array is identical across calls", async () => {
|
|
101
107
|
let saved;
|
|
102
|
-
const globals =
|
|
108
|
+
const globals = {
|
|
103
109
|
tag: (strings, ...values) => {
|
|
104
110
|
assertCodeEqual(strings, ["Hello, ", "!"]);
|
|
105
111
|
if (saved) {
|
|
@@ -109,7 +115,7 @@ describe("compile", () => {
|
|
|
109
115
|
}
|
|
110
116
|
return strings[0] + values[0] + strings[1];
|
|
111
117
|
},
|
|
112
|
-
}
|
|
118
|
+
};
|
|
113
119
|
const program = compile.expression("=tag`Hello, ${_}!`", { globals });
|
|
114
120
|
const lambda = await program.call(null);
|
|
115
121
|
const alice = await lambda("Alice");
|
|
@@ -121,9 +127,8 @@ describe("compile", () => {
|
|
|
121
127
|
|
|
122
128
|
async function assertCompile(text, expected, options = {}) {
|
|
123
129
|
const mode = options.mode ?? "shell";
|
|
124
|
-
const fn = compile.expression(text, { globals:
|
|
125
|
-
|
|
126
|
-
const target = options.target ?? mode === "shell" ? shared : null;
|
|
130
|
+
const fn = compile.expression(text, { globals: sharedGlobals, mode });
|
|
131
|
+
const target = options.target ?? null;
|
|
127
132
|
let result = await fn.call(target);
|
|
128
133
|
if (Tree.isTreelike(result)) {
|
|
129
134
|
result = await Tree.plain(result);
|