@weborigami/language 0.0.42 → 0.0.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.ts +4 -9
- package/main.js +1 -0
- package/package.json +3 -3
- package/src/compiler/compile.js +7 -7
- package/src/compiler/origami.pegjs +15 -4
- package/src/compiler/parse.js +573 -331
- package/src/runtime/FileLoadersTransform.js +18 -11
- package/src/runtime/evaluate.js +7 -3
- package/src/runtime/symbols.js +1 -0
- package/test/compiler/compile.test.js +1 -1
- package/test/compiler/parse.test.js +50 -3
- package/test/runtime/FileLoadersTransform.test.js +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Tree, isStringLike } from "@weborigami/async-tree";
|
|
2
2
|
import Scope from "./Scope.js";
|
|
3
3
|
import extname from "./extname.js";
|
|
4
|
+
import * as symbols from "./symbols.js";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
@@ -14,25 +15,31 @@ export default function FileLoadersTransform(Base) {
|
|
|
14
15
|
async get(key) {
|
|
15
16
|
let value = await super.get(key);
|
|
16
17
|
|
|
17
|
-
// If the
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
// string-like object defined by a user function.
|
|
21
|
-
if (isStringLike(value) && isStringLike(key)) {
|
|
18
|
+
// If the key is string-like and has an extension, look for a loader that
|
|
19
|
+
// handles that extension.
|
|
20
|
+
if (value && isStringLike(key)) {
|
|
22
21
|
const extension = extname(String(key)).toLowerCase().slice(1);
|
|
23
22
|
if (extension) {
|
|
24
23
|
/** @type {any} */
|
|
25
24
|
const scope = Scope.getScope(this);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (unpackFn) {
|
|
25
|
+
const loader = await Tree.traverse(scope, "@loaders", extension);
|
|
26
|
+
if (loader) {
|
|
29
27
|
const input = value;
|
|
30
28
|
// If the input is a plain string, convert it to a String so we can
|
|
31
29
|
// attach data to it.
|
|
32
|
-
|
|
30
|
+
if (typeof input === "string") {
|
|
31
|
+
value = new String(input);
|
|
32
|
+
}
|
|
33
33
|
const parent = this;
|
|
34
|
-
value.parent = parent;
|
|
35
|
-
|
|
34
|
+
value[symbols.parent] = parent;
|
|
35
|
+
|
|
36
|
+
// Wrap the loader with a function that will only be called once per
|
|
37
|
+
// value.
|
|
38
|
+
let loaded;
|
|
39
|
+
value.unpack = async () => {
|
|
40
|
+
loaded ??= await loader(input, { key, parent });
|
|
41
|
+
return loaded;
|
|
42
|
+
};
|
|
36
43
|
}
|
|
37
44
|
}
|
|
38
45
|
}
|
package/src/runtime/evaluate.js
CHANGED
|
@@ -81,9 +81,13 @@ export default async function evaluate(code) {
|
|
|
81
81
|
Object.isExtensible(result) &&
|
|
82
82
|
!isPlainObject(result)
|
|
83
83
|
) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
try {
|
|
85
|
+
result[codeSymbol] = code;
|
|
86
|
+
if (/** @type {any} */ (code).location) {
|
|
87
|
+
result[sourceSymbol] = codeFragment(code);
|
|
88
|
+
}
|
|
89
|
+
} catch (/** @type {any} */ error) {
|
|
90
|
+
// Ignore errors.
|
|
87
91
|
}
|
|
88
92
|
}
|
|
89
93
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const parent = Symbol("parent");
|
|
@@ -49,7 +49,7 @@ describe("compile", () => {
|
|
|
49
49
|
});
|
|
50
50
|
|
|
51
51
|
test("templateLiteral", async () => {
|
|
52
|
-
await assertCompile("`Hello, {
|
|
52
|
+
await assertCompile("`Hello, ${name}!`", "Hello, Alice!");
|
|
53
53
|
await assertCompile(
|
|
54
54
|
"`escape characters with \\`backslash\\``",
|
|
55
55
|
"escape characters with `backslash`"
|
|
@@ -70,7 +70,7 @@ describe("Origami parser", () => {
|
|
|
70
70
|
],
|
|
71
71
|
[[ops.scope, "files"], "snapshot"],
|
|
72
72
|
]);
|
|
73
|
-
assertParse("expr", "@map =`<li
|
|
73
|
+
assertParse("expr", "@map =`<li>${_}</li>`", [
|
|
74
74
|
[ops.scope, "@map"],
|
|
75
75
|
[ops.lambda, null, [ops.concat, "<li>", [ops.scope, "_"], "</li>"]],
|
|
76
76
|
]);
|
|
@@ -109,6 +109,18 @@ describe("Origami parser", () => {
|
|
|
109
109
|
],
|
|
110
110
|
]
|
|
111
111
|
);
|
|
112
|
+
|
|
113
|
+
// Consecutive slahes inside a path = empty string key
|
|
114
|
+
assertParse("expression", "path//key", [
|
|
115
|
+
ops.traverse,
|
|
116
|
+
[ops.scope, "path"],
|
|
117
|
+
"",
|
|
118
|
+
"key",
|
|
119
|
+
]);
|
|
120
|
+
// Single slash at start of something = absolute file path
|
|
121
|
+
assertParse("expression", "/path", [[ops.filesRoot], "path"]);
|
|
122
|
+
// Consecutive slashes at start of something = comment
|
|
123
|
+
assertParse("expression", "path //comment", [ops.scope, "path"]);
|
|
112
124
|
});
|
|
113
125
|
|
|
114
126
|
test("functionComposition", () => {
|
|
@@ -237,7 +249,7 @@ describe("Origami parser", () => {
|
|
|
237
249
|
null,
|
|
238
250
|
[ops.scope, "message"],
|
|
239
251
|
]);
|
|
240
|
-
assertParse("lambda", "=`Hello, {
|
|
252
|
+
assertParse("lambda", "=`Hello, ${name}.`", [
|
|
241
253
|
ops.lambda,
|
|
242
254
|
null,
|
|
243
255
|
[ops.concat, "Hello, ", [ops.scope, "name"], "."],
|
|
@@ -257,6 +269,10 @@ describe("Origami parser", () => {
|
|
|
257
269
|
assertParse("list", "'a' , 'b' , 'c'", ["a", "b", "c"]);
|
|
258
270
|
});
|
|
259
271
|
|
|
272
|
+
test("multiLineComment", () => {
|
|
273
|
+
assertParse("multiLineComment", "/*\nHello, world!\n*/", null);
|
|
274
|
+
});
|
|
275
|
+
|
|
260
276
|
test("number", () => {
|
|
261
277
|
assertParse("number", "123", 123);
|
|
262
278
|
assertParse("number", "-456", -456);
|
|
@@ -369,6 +385,14 @@ describe("Origami parser", () => {
|
|
|
369
385
|
]);
|
|
370
386
|
});
|
|
371
387
|
|
|
388
|
+
test("singleLineComment", () => {
|
|
389
|
+
assertParse("singleLineComment", "# Hello, world!", null);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
test("singleLineComment (JS)", () => {
|
|
393
|
+
assertParse("singleLineComment", "// Hello, world!", null);
|
|
394
|
+
});
|
|
395
|
+
|
|
372
396
|
test("scopeReference", () => {
|
|
373
397
|
assertParse("scopeReference", "x", [ops.scope, "x"]);
|
|
374
398
|
});
|
|
@@ -383,7 +407,7 @@ describe("Origami parser", () => {
|
|
|
383
407
|
});
|
|
384
408
|
|
|
385
409
|
test("templateDocument", () => {
|
|
386
|
-
assertParse("templateDocument", "hello{
|
|
410
|
+
assertParse("templateDocument", "hello${foo}world", [
|
|
387
411
|
ops.lambda,
|
|
388
412
|
null,
|
|
389
413
|
[ops.concat, "hello", [ops.scope, "foo"], "world"],
|
|
@@ -414,10 +438,33 @@ describe("Origami parser", () => {
|
|
|
414
438
|
]);
|
|
415
439
|
});
|
|
416
440
|
|
|
441
|
+
test("templateLiteral (JS)", () => {
|
|
442
|
+
assertParse("templateLiteral", "`Hello, world.`", "Hello, world.");
|
|
443
|
+
assertParse("templateLiteral", "`foo ${x} bar`", [
|
|
444
|
+
ops.concat,
|
|
445
|
+
"foo ",
|
|
446
|
+
[ops.scope, "x"],
|
|
447
|
+
" bar",
|
|
448
|
+
]);
|
|
449
|
+
assertParse("templateLiteral", "`${`nested`}`", "nested");
|
|
450
|
+
assertParse("templateLiteral", "`${map(people, =`${name}`)}`", [
|
|
451
|
+
ops.concat,
|
|
452
|
+
[
|
|
453
|
+
[ops.scope, "map"],
|
|
454
|
+
[ops.scope, "people"],
|
|
455
|
+
[ops.lambda, null, [ops.concat, [ops.scope, "name"]]],
|
|
456
|
+
],
|
|
457
|
+
]);
|
|
458
|
+
});
|
|
459
|
+
|
|
417
460
|
test("templateSubstitution", () => {
|
|
418
461
|
assertParse("templateSubstitution", "{{foo}}", [ops.scope, "foo"]);
|
|
419
462
|
});
|
|
420
463
|
|
|
464
|
+
test("templateSubtitution (JS)", () => {
|
|
465
|
+
assertParse("templateSubstitution", "${foo}", [ops.scope, "foo"]);
|
|
466
|
+
});
|
|
467
|
+
|
|
421
468
|
test("tree", () => {
|
|
422
469
|
assertParse("tree", "{}", [ops.tree]);
|
|
423
470
|
assertParse("tree", "{ a = 1, b }", [
|