@weborigami/language 0.5.4 → 0.5.6
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 +16 -6
- package/main.js +9 -4
- package/package.json +4 -3
- package/src/compiler/compile.js +10 -4
- package/src/compiler/optimize.js +115 -97
- package/src/compiler/origami.pegjs +1 -4
- package/src/compiler/parse.js +568 -588
- package/src/compiler/parserHelpers.js +3 -3
- package/src/handlers/css_handler.js +7 -0
- package/src/handlers/csv_handler.js +129 -0
- package/src/handlers/handlers.js +33 -0
- package/src/handlers/htm_handler.js +2 -0
- package/src/handlers/html_handler.js +7 -0
- package/src/handlers/jpeg_handler.js +62 -0
- package/src/handlers/jpg_handler.js +2 -0
- package/src/handlers/js_handler.js +51 -0
- package/src/handlers/json_handler.js +26 -0
- package/src/handlers/md_handler.js +7 -0
- package/src/handlers/mjs_handler.js +2 -0
- package/src/handlers/ori_handler.js +47 -0
- package/src/handlers/oridocument_handler.js +77 -0
- package/src/handlers/parseFrontMatter.js +16 -0
- package/src/handlers/ts_handler.js +1 -0
- package/src/handlers/txt_handler.js +108 -0
- package/src/handlers/wasm_handler.js +15 -0
- package/src/handlers/xhtml_handler.js +2 -0
- package/src/handlers/yaml_handler.js +33 -0
- package/src/handlers/yml_handler.js +2 -0
- package/src/project/builtins.js +5 -0
- package/src/project/coreGlobals.js +17 -0
- package/src/{runtime → project}/jsGlobals.js +3 -1
- package/src/project/projectConfig.js +36 -0
- package/src/project/projectGlobals.js +19 -0
- package/src/project/projectRoot.js +59 -0
- package/src/protocols/constructHref.js +20 -0
- package/src/protocols/constructSiteTree.js +26 -0
- package/src/protocols/explore.js +14 -0
- package/src/protocols/fetchAndHandleExtension.js +25 -0
- package/src/protocols/files.js +26 -0
- package/src/protocols/http.js +15 -0
- package/src/protocols/https.js +15 -0
- package/src/protocols/httpstree.js +14 -0
- package/src/protocols/httptree.js +14 -0
- package/src/protocols/node.js +13 -0
- package/src/protocols/package.js +67 -0
- package/src/protocols/protocolGlobals.js +12 -0
- package/src/protocols/protocols.js +8 -0
- package/src/runtime/EventTargetMixin.js +1 -1
- package/src/runtime/HandleExtensionsTransform.js +3 -12
- package/src/runtime/ImportModulesMixin.js +4 -10
- package/src/runtime/InvokeFunctionsTransform.js +1 -1
- package/src/runtime/errors.js +2 -2
- package/src/runtime/evaluate.js +15 -8
- package/src/runtime/expressionFunction.js +5 -7
- package/src/runtime/expressionObject.js +10 -20
- package/src/runtime/functionResultsMap.js +5 -12
- package/src/runtime/{handlers.js → handleExtension.js} +14 -12
- package/src/runtime/mergeTrees.js +2 -10
- package/src/runtime/ops.js +91 -106
- package/test/compiler/compile.test.js +20 -19
- package/test/compiler/optimize.test.js +60 -25
- package/test/compiler/parse.test.js +10 -10
- package/test/generator/oriEval.js +4 -5
- package/test/handlers/csv.handler.test.js +36 -0
- package/test/handlers/fixtures/add.wasm +0 -0
- package/test/handlers/fixtures/exif.jpeg +0 -0
- package/test/handlers/fixtures/frontMatter.md +5 -0
- package/test/handlers/fixtures/list.js +4 -0
- package/test/handlers/fixtures/multiple.js +4 -0
- package/test/handlers/fixtures/obj.js +3 -0
- package/test/handlers/fixtures/site.ori +5 -0
- package/test/handlers/fixtures/string.js +1 -0
- package/test/handlers/fixtures/tag.yaml +5 -0
- package/test/handlers/fixtures/test.ori +9 -0
- package/test/handlers/jpeg.handler.test.js +18 -0
- package/test/handlers/js.handler.test.js +46 -0
- package/test/handlers/json.handler.test.js +14 -0
- package/test/handlers/ori.handler.test.js +87 -0
- package/test/handlers/oridocument.handler.test.js +68 -0
- package/test/handlers/txt.handler.test.js +41 -0
- package/test/handlers/wasm.handler.test.js +20 -0
- package/test/handlers/yaml.handler.test.js +17 -0
- package/test/project/fixtures/withConfig/config.ori +4 -0
- package/test/project/fixtures/withConfig/subfolder/greet.js +1 -0
- package/test/project/fixtures/withPackageJson/package.json +0 -0
- package/test/project/jsGlobals.test.js +21 -0
- package/test/project/projectConfig.test.js +28 -0
- package/test/project/projectRoot.test.js +40 -0
- package/test/protocols/package.test.js +11 -0
- package/test/runtime/evaluate.test.js +26 -42
- package/test/runtime/expressionObject.test.js +16 -20
- package/test/runtime/functionResultsMap.test.js +5 -9
- package/test/runtime/{handlers.test.js → handleExtension.test.js} +4 -20
- package/test/runtime/jsGlobals.test.js +4 -6
- package/test/runtime/mergeTrees.test.js +2 -4
- package/test/runtime/ops.test.js +70 -72
- package/src/runtime/getHandlers.js +0 -10
|
@@ -6,8 +6,7 @@ import {
|
|
|
6
6
|
trailingSlash,
|
|
7
7
|
Tree,
|
|
8
8
|
} from "@weborigami/async-tree";
|
|
9
|
-
import
|
|
10
|
-
import { handleExtension } from "./handlers.js";
|
|
9
|
+
import handleExtension from "./handleExtension.js";
|
|
11
10
|
import { evaluate, ops } from "./internal.js";
|
|
12
11
|
|
|
13
12
|
/**
|
|
@@ -25,11 +24,12 @@ import { evaluate, ops } from "./internal.js";
|
|
|
25
24
|
* property getter on the object.
|
|
26
25
|
*
|
|
27
26
|
* @param {*} entries
|
|
28
|
-
* @param {import("
|
|
27
|
+
* @param {import("../../index.ts").RuntimeState} [state]
|
|
29
28
|
*/
|
|
30
|
-
export default async function expressionObject(entries,
|
|
29
|
+
export default async function expressionObject(entries, state = {}) {
|
|
31
30
|
// Create the object and set its parent
|
|
32
31
|
const object = {};
|
|
32
|
+
const parent = state?.object ?? null;
|
|
33
33
|
if (parent !== null && !Tree.isAsyncTree(parent)) {
|
|
34
34
|
throw new TypeError(`Parent must be an AsyncTree or null`);
|
|
35
35
|
}
|
|
@@ -86,22 +86,12 @@ export default async function expressionObject(entries, parent) {
|
|
|
86
86
|
code = value;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const handlers = getHandlers(tree);
|
|
96
|
-
return handleExtension(tree, result, key, handlers);
|
|
97
|
-
};
|
|
98
|
-
} else {
|
|
99
|
-
// No extension, so getter just invokes code.
|
|
100
|
-
get = async () => {
|
|
101
|
-
tree ??= new ObjectTree(object);
|
|
102
|
-
return evaluate.call(tree, code);
|
|
103
|
-
};
|
|
104
|
-
}
|
|
89
|
+
const get = async () => {
|
|
90
|
+
tree ??= new ObjectTree(object);
|
|
91
|
+
const newState = Object.assign({}, state, { object: tree });
|
|
92
|
+
const result = await evaluate(code, newState);
|
|
93
|
+
return extname ? handleExtension(result, key, tree) : result;
|
|
94
|
+
};
|
|
105
95
|
|
|
106
96
|
Object.defineProperty(object, key, {
|
|
107
97
|
configurable: true,
|
|
@@ -1,23 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Tree } from "@weborigami/async-tree";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* When using `get` to retrieve a value from a tree, if the value is a
|
|
5
5
|
* function, invoke it and return the result.
|
|
6
6
|
*/
|
|
7
|
-
export default function functionResultsMap(treelike) {
|
|
8
|
-
return map(treelike, {
|
|
7
|
+
export default async function functionResultsMap(treelike) {
|
|
8
|
+
return Tree.map(treelike, {
|
|
9
9
|
description: "functionResultsMap",
|
|
10
10
|
|
|
11
11
|
value: async (sourceValue, sourceKey, tree) => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
resultValue = await sourceValue.call(tree);
|
|
15
|
-
if (Tree.isAsyncTree(resultValue) && !resultValue.parent) {
|
|
16
|
-
resultValue.parent = tree;
|
|
17
|
-
}
|
|
18
|
-
} else {
|
|
19
|
-
resultValue = sourceValue;
|
|
20
|
-
}
|
|
12
|
+
const resultValue =
|
|
13
|
+
typeof sourceValue === "function" ? await sourceValue() : sourceValue;
|
|
21
14
|
return resultValue;
|
|
22
15
|
},
|
|
23
16
|
});
|
|
@@ -2,28 +2,27 @@ import {
|
|
|
2
2
|
box,
|
|
3
3
|
extension,
|
|
4
4
|
isPacked,
|
|
5
|
-
|
|
5
|
+
isStringlike,
|
|
6
6
|
isUnpackable,
|
|
7
7
|
setParent,
|
|
8
8
|
trailingSlash,
|
|
9
9
|
} from "@weborigami/async-tree";
|
|
10
|
+
import globals from "../project/projectGlobals.js";
|
|
11
|
+
|
|
12
|
+
let projectGlobals;
|
|
10
13
|
|
|
11
14
|
/**
|
|
12
15
|
* If the given value is packed (e.g., buffer) and the key is a string-like path
|
|
13
16
|
* that ends in an extension, search for a handler for that extension and, if
|
|
14
17
|
* found, attach it to the value.
|
|
15
18
|
*
|
|
16
|
-
* @param {import("@weborigami/types").AsyncTree} parent
|
|
17
19
|
* @param {any} value
|
|
18
20
|
* @param {any} key
|
|
21
|
+
* @param {import("@weborigami/types").AsyncTree} [parent]
|
|
19
22
|
*/
|
|
20
|
-
export async function handleExtension(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
isPacked(value) &&
|
|
24
|
-
isStringLike(key) &&
|
|
25
|
-
value.unpack === undefined
|
|
26
|
-
) {
|
|
23
|
+
export default async function handleExtension(value, key, parent) {
|
|
24
|
+
projectGlobals ??= await globals();
|
|
25
|
+
if (isPacked(value) && isStringlike(key) && value.unpack === undefined) {
|
|
27
26
|
const hasSlash = trailingSlash.has(key);
|
|
28
27
|
if (hasSlash) {
|
|
29
28
|
key = trailingSlash.remove(key);
|
|
@@ -38,8 +37,8 @@ export async function handleExtension(parent, value, key, handlers) {
|
|
|
38
37
|
? ".jsedocument"
|
|
39
38
|
: extension.extname(key);
|
|
40
39
|
if (extname) {
|
|
41
|
-
const handlerName = `${extname.slice(1)}
|
|
42
|
-
let handler = await
|
|
40
|
+
const handlerName = `${extname.slice(1)}_handler`;
|
|
41
|
+
let handler = await projectGlobals[handlerName];
|
|
43
42
|
if (handler) {
|
|
44
43
|
if (isUnpackable(handler)) {
|
|
45
44
|
// The extension handler itself needs to be unpacked
|
|
@@ -57,7 +56,10 @@ export async function handleExtension(parent, value, key, handlers) {
|
|
|
57
56
|
if (handler.mediaType) {
|
|
58
57
|
value.mediaType = handler.mediaType;
|
|
59
58
|
}
|
|
60
|
-
|
|
59
|
+
|
|
60
|
+
if (parent) {
|
|
61
|
+
setParent(value, parent);
|
|
62
|
+
}
|
|
61
63
|
|
|
62
64
|
const unpack = handler.unpack;
|
|
63
65
|
if (unpack) {
|
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isPlainObject,
|
|
3
|
-
isUnpackable,
|
|
4
|
-
merge,
|
|
5
|
-
setParent,
|
|
6
|
-
Tree,
|
|
7
|
-
} from "@weborigami/async-tree";
|
|
1
|
+
import { isPlainObject, isUnpackable, Tree } from "@weborigami/async-tree";
|
|
8
2
|
|
|
9
3
|
/**
|
|
10
4
|
* Create a tree that's the result of merging the given trees.
|
|
@@ -12,7 +6,6 @@ import {
|
|
|
12
6
|
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
13
7
|
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
|
|
14
8
|
*
|
|
15
|
-
* @this {AsyncTree|null}
|
|
16
9
|
* @param {(Treelike|null)[]} trees
|
|
17
10
|
*/
|
|
18
11
|
export default async function mergeTrees(...trees) {
|
|
@@ -54,7 +47,6 @@ export default async function mergeTrees(...trees) {
|
|
|
54
47
|
}
|
|
55
48
|
|
|
56
49
|
// Merge the trees.
|
|
57
|
-
const result = merge(...unpacked);
|
|
58
|
-
setParent(result, this);
|
|
50
|
+
const result = Tree.merge(...unpacked);
|
|
59
51
|
return result;
|
|
60
52
|
}
|
package/src/runtime/ops.js
CHANGED
|
@@ -3,20 +3,12 @@
|
|
|
3
3
|
* @typedef {import("@weborigami/async-tree").PlainObject} PlainObject
|
|
4
4
|
* @typedef {import("@weborigami/async-tree").Treelike} Treelike
|
|
5
5
|
* @typedef {import("@weborigami/types").AsyncTree} AsyncTree
|
|
6
|
+
* @typedef {import("../../index.ts").RuntimeState} RuntimeState
|
|
6
7
|
*/
|
|
7
8
|
|
|
8
|
-
import {
|
|
9
|
-
deepText,
|
|
10
|
-
indent,
|
|
11
|
-
isUnpackable,
|
|
12
|
-
ObjectTree,
|
|
13
|
-
scope as scopeFn,
|
|
14
|
-
text,
|
|
15
|
-
Tree,
|
|
16
|
-
} from "@weborigami/async-tree";
|
|
9
|
+
import { isUnpackable, symbols, Tree } from "@weborigami/async-tree";
|
|
17
10
|
import os from "node:os";
|
|
18
11
|
import expressionObject from "./expressionObject.js";
|
|
19
|
-
import getHandlers from "./getHandlers.js";
|
|
20
12
|
import { evaluate } from "./internal.js";
|
|
21
13
|
import mergeTrees from "./mergeTrees.js";
|
|
22
14
|
import OrigamiFiles from "./OrigamiFiles.js";
|
|
@@ -37,7 +29,6 @@ addOpLabel(addition, "«ops.addition»");
|
|
|
37
29
|
/**
|
|
38
30
|
* Construct an array.
|
|
39
31
|
*
|
|
40
|
-
* @this {AsyncTree|null}
|
|
41
32
|
* @param {any[]} items
|
|
42
33
|
*/
|
|
43
34
|
export async function array(...items) {
|
|
@@ -68,7 +59,6 @@ addOpLabel(bitwiseXor, "«ops.bitwiseXor»");
|
|
|
68
59
|
/**
|
|
69
60
|
* Cache the value of the code for an external reference
|
|
70
61
|
*
|
|
71
|
-
* @this {AsyncTree|null}
|
|
72
62
|
* @param {any} cache
|
|
73
63
|
* @param {string} path
|
|
74
64
|
* @param {AnnotatedCode} code
|
|
@@ -80,7 +70,7 @@ export async function cache(cache, path, code) {
|
|
|
80
70
|
}
|
|
81
71
|
|
|
82
72
|
// Don't await: might get another request for this before promise resolves
|
|
83
|
-
const promise = await evaluate
|
|
73
|
+
const promise = await evaluate(code);
|
|
84
74
|
|
|
85
75
|
// Save promise so another request will get the same promise
|
|
86
76
|
cache[path] = promise;
|
|
@@ -99,13 +89,12 @@ cache.unevaluatedArgs = true;
|
|
|
99
89
|
/**
|
|
100
90
|
* JavaScript comma operator, returns the last argument.
|
|
101
91
|
*
|
|
102
|
-
* @this {AsyncTree|null}
|
|
103
92
|
* @param {...AnnotatedCode} args
|
|
104
93
|
*/
|
|
105
94
|
export async function comma(...args) {
|
|
106
95
|
let result;
|
|
107
96
|
for (const arg of args) {
|
|
108
|
-
result = await evaluate
|
|
97
|
+
result = await evaluate(arg);
|
|
109
98
|
}
|
|
110
99
|
return result;
|
|
111
100
|
}
|
|
@@ -115,11 +104,10 @@ comma.unevaluatedArgs = true;
|
|
|
115
104
|
/**
|
|
116
105
|
* Concatenate the given arguments.
|
|
117
106
|
*
|
|
118
|
-
* @this {AsyncTree|null}
|
|
119
107
|
* @param {any[]} args
|
|
120
108
|
*/
|
|
121
109
|
export async function concat(...args) {
|
|
122
|
-
return deepText
|
|
110
|
+
return Tree.deepText(args);
|
|
123
111
|
}
|
|
124
112
|
addOpLabel(concat, "«ops.concat»");
|
|
125
113
|
|
|
@@ -135,23 +123,6 @@ export async function construct(constructor, ...args) {
|
|
|
135
123
|
return Reflect.construct(constructor, args);
|
|
136
124
|
}
|
|
137
125
|
|
|
138
|
-
/**
|
|
139
|
-
* Return the nth parent of the current tree
|
|
140
|
-
*
|
|
141
|
-
* @this {AsyncTree|null|undefined}
|
|
142
|
-
*/
|
|
143
|
-
export function context(n = 0) {
|
|
144
|
-
let tree = this;
|
|
145
|
-
for (let i = 0; i < n; i++) {
|
|
146
|
-
if (!tree) {
|
|
147
|
-
throw new Error("Internal error: couldn't find tree ancestor.");
|
|
148
|
-
}
|
|
149
|
-
tree = tree.parent;
|
|
150
|
-
}
|
|
151
|
-
return tree;
|
|
152
|
-
}
|
|
153
|
-
addOpLabel(context, "«ops.context»");
|
|
154
|
-
|
|
155
126
|
export function division(a, b) {
|
|
156
127
|
return a / b;
|
|
157
128
|
}
|
|
@@ -203,65 +174,74 @@ addOpLabel(greaterThanOrEqual, "«ops.greaterThanOrEqual»");
|
|
|
203
174
|
|
|
204
175
|
/**
|
|
205
176
|
* Files tree for the user's home directory.
|
|
206
|
-
*
|
|
207
|
-
* @this {AsyncTree|null}
|
|
208
177
|
*/
|
|
209
178
|
export async function homeDirectory(...keys) {
|
|
210
179
|
const tree = new OrigamiFiles(os.homedir());
|
|
211
|
-
// Use the same handlers as the current tree
|
|
212
|
-
/** @type {any} */ (tree).handlers = getHandlers(this);
|
|
213
180
|
return keys.length > 0 ? Tree.traverse(tree, ...keys) : tree;
|
|
214
181
|
}
|
|
215
182
|
addOpLabel(homeDirectory, "«ops.homeDirectory»");
|
|
216
183
|
|
|
184
|
+
/**
|
|
185
|
+
* Given the tree currently be using as the context for the runtime, walk up the
|
|
186
|
+
* parent chain `depth` levels and return that tree.
|
|
187
|
+
*
|
|
188
|
+
* @param {number} depth
|
|
189
|
+
* @param {RuntimeState} state
|
|
190
|
+
*/
|
|
191
|
+
export async function inherited(depth, state) {
|
|
192
|
+
let current = state.object;
|
|
193
|
+
for (let i = 0; i < depth; i++) {
|
|
194
|
+
if (!current) {
|
|
195
|
+
throw new ReferenceError(
|
|
196
|
+
`Origami internal error: Can't find context object`
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
current = current.parent ?? current[symbols.parent];
|
|
200
|
+
}
|
|
201
|
+
return current;
|
|
202
|
+
}
|
|
203
|
+
addOpLabel(inherited, "«ops.inherited»");
|
|
204
|
+
inherited.needsState = true;
|
|
205
|
+
|
|
217
206
|
/**
|
|
218
207
|
* Return a function that will invoke the given code.
|
|
219
208
|
*
|
|
220
|
-
* @this {AsyncTree|null}
|
|
221
209
|
* @param {string[]} parameters
|
|
222
210
|
* @param {AnnotatedCode} code
|
|
223
211
|
*/
|
|
224
|
-
export function lambda(parameters, code) {
|
|
225
|
-
const
|
|
212
|
+
export function lambda(parameters, code, state = {}) {
|
|
213
|
+
const stack = state.stack ?? [];
|
|
226
214
|
|
|
227
|
-
/** @this {Treelike|null} */
|
|
228
215
|
async function invoke(...args) {
|
|
229
|
-
let
|
|
216
|
+
let newState;
|
|
230
217
|
if (parameters.length === 0) {
|
|
231
218
|
// No parameters
|
|
232
|
-
|
|
219
|
+
newState = state;
|
|
233
220
|
} else {
|
|
234
|
-
//
|
|
235
|
-
const
|
|
221
|
+
// Create a stack frame for the parameters
|
|
222
|
+
const frame = {};
|
|
236
223
|
for (const parameter of parameters) {
|
|
237
224
|
const parameterName = parameter[1];
|
|
238
|
-
|
|
225
|
+
frame[parameterName] = args.shift();
|
|
239
226
|
}
|
|
240
|
-
|
|
227
|
+
// Record which code this stack frame is associated with
|
|
228
|
+
Object.defineProperty(frame, codeSymbol, {
|
|
241
229
|
value: code,
|
|
242
230
|
enumerable: false,
|
|
243
231
|
});
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
let result = await evaluate.call(target, code);
|
|
250
|
-
|
|
251
|
-
// Bind a function result to the ambients so that it has access to the
|
|
252
|
-
// parameter values -- i.e., like a closure.
|
|
253
|
-
if (result instanceof Function) {
|
|
254
|
-
const resultCode = result.code;
|
|
255
|
-
result = result.bind(target);
|
|
256
|
-
if (code) {
|
|
257
|
-
// Copy over Origami code
|
|
258
|
-
result.code = resultCode;
|
|
259
|
-
}
|
|
232
|
+
const newStack = stack.slice();
|
|
233
|
+
newStack.push(frame);
|
|
234
|
+
newState = Object.assign({}, state, { stack: newStack });
|
|
260
235
|
}
|
|
261
236
|
|
|
237
|
+
const result = await evaluate(code, newState);
|
|
262
238
|
return result;
|
|
263
239
|
}
|
|
264
240
|
|
|
241
|
+
// Retain a reference to the original code for debugging, and so that functions
|
|
242
|
+
// can be compared by their code (e.g., for caching purposes).
|
|
243
|
+
invoke.code = code;
|
|
244
|
+
|
|
265
245
|
// We set the `length` property on the function so that Tree.traverseOrThrow()
|
|
266
246
|
// will correctly identify how many parameters it wants. This is unorthodox
|
|
267
247
|
// but doesn't appear to affect other behavior.
|
|
@@ -270,11 +250,11 @@ export function lambda(parameters, code) {
|
|
|
270
250
|
value: fnLength,
|
|
271
251
|
});
|
|
272
252
|
|
|
273
|
-
invoke.code = code;
|
|
274
253
|
return invoke;
|
|
275
254
|
}
|
|
276
255
|
addOpLabel(lambda, "«ops.lambda»");
|
|
277
256
|
lambda.unevaluatedArgs = true;
|
|
257
|
+
lambda.needsState = true;
|
|
278
258
|
|
|
279
259
|
export function lessThan(a, b) {
|
|
280
260
|
return a < b;
|
|
@@ -356,11 +336,10 @@ addOpLabel(logicalOr, "«ops.logicalOr»");
|
|
|
356
336
|
/**
|
|
357
337
|
* Merge the given trees. If they are all plain objects, return a plain object.
|
|
358
338
|
*
|
|
359
|
-
* @this {AsyncTree|null}
|
|
360
339
|
* @param {any[]} trees
|
|
361
340
|
*/
|
|
362
341
|
export async function merge(...trees) {
|
|
363
|
-
return mergeTrees
|
|
342
|
+
return mergeTrees(...trees);
|
|
364
343
|
}
|
|
365
344
|
addOpLabel(merge, "«ops.merge»");
|
|
366
345
|
|
|
@@ -404,59 +383,71 @@ addOpLabel(nullishCoalescing, "«ops.nullishCoalescing»");
|
|
|
404
383
|
* parameter's, and the values will be the results of evaluating the
|
|
405
384
|
* corresponding code values in `obj`.
|
|
406
385
|
*
|
|
407
|
-
* @this {AsyncTree|null}
|
|
408
386
|
* @param {any[]} entries
|
|
409
387
|
*/
|
|
410
388
|
export async function object(...entries) {
|
|
411
|
-
|
|
389
|
+
const state = entries.pop();
|
|
390
|
+
return expressionObject(entries, state);
|
|
412
391
|
}
|
|
413
392
|
addOpLabel(object, "«ops.object»");
|
|
414
393
|
object.unevaluatedArgs = true;
|
|
394
|
+
object.needsState = true;
|
|
415
395
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
396
|
+
/**
|
|
397
|
+
* Return the stack frame that's `depth` levels up the stack.
|
|
398
|
+
*
|
|
399
|
+
* @param {number} depth
|
|
400
|
+
* @param {RuntimeState} state
|
|
401
|
+
*/
|
|
402
|
+
export async function params(depth, state = {}) {
|
|
403
|
+
const stack = state.stack ?? [];
|
|
404
|
+
return stack[stack.length - 1 - depth];
|
|
421
405
|
}
|
|
422
|
-
addOpLabel(
|
|
406
|
+
addOpLabel(params, "«ops.params»");
|
|
407
|
+
params.needsState = true;
|
|
408
|
+
|
|
409
|
+
// export function optionalTraverse(treelike, key) {
|
|
410
|
+
// if (!treelike) {
|
|
411
|
+
// return undefined;
|
|
412
|
+
// }
|
|
413
|
+
// return Tree.traverseOrThrow(treelike, key);
|
|
414
|
+
// }
|
|
415
|
+
// addOpLabel(optionalTraverse, "«ops.optionalTraverse");
|
|
423
416
|
|
|
424
417
|
/**
|
|
425
418
|
* Return the indicated property
|
|
426
419
|
*
|
|
427
|
-
* @param {any}
|
|
420
|
+
* @param {any} object
|
|
428
421
|
* @param {string} key
|
|
429
422
|
*/
|
|
430
|
-
export async function property(
|
|
431
|
-
if (
|
|
423
|
+
export async function property(object, key) {
|
|
424
|
+
if (object == null) {
|
|
432
425
|
throw new ReferenceError();
|
|
433
426
|
}
|
|
434
427
|
|
|
435
|
-
if (isUnpackable(
|
|
436
|
-
|
|
437
|
-
} else if (typeof
|
|
438
|
-
|
|
439
|
-
} else if (typeof
|
|
440
|
-
|
|
428
|
+
if (isUnpackable(object)) {
|
|
429
|
+
object = await object.unpack();
|
|
430
|
+
} else if (typeof object === "string") {
|
|
431
|
+
object = new String(object);
|
|
432
|
+
} else if (typeof object === "number") {
|
|
433
|
+
object = new Number(object);
|
|
441
434
|
}
|
|
442
435
|
|
|
443
|
-
if (key in
|
|
436
|
+
if (key in object) {
|
|
444
437
|
// Object defines the property, get it
|
|
445
|
-
let value =
|
|
438
|
+
let value = object[key];
|
|
446
439
|
// Is value an instance method? Copied from ObjectTree.
|
|
447
440
|
const isInstanceMethod =
|
|
448
|
-
|
|
449
|
-
value instanceof Function &&
|
|
450
|
-
!Object.hasOwn(obj, key);
|
|
441
|
+
value instanceof Function && !Object.hasOwn(object, key);
|
|
451
442
|
if (isInstanceMethod) {
|
|
452
443
|
// Bind it to the object
|
|
453
|
-
value = value.bind(
|
|
444
|
+
value = value.bind(object);
|
|
454
445
|
}
|
|
455
446
|
return value;
|
|
456
447
|
}
|
|
457
448
|
|
|
458
449
|
// Handle as tree traversal
|
|
459
|
-
return Tree.traverseOrThrow(
|
|
450
|
+
return Tree.traverseOrThrow(object, key);
|
|
460
451
|
}
|
|
461
452
|
addOpLabel(property, "«ops.property»");
|
|
462
453
|
|
|
@@ -467,13 +458,9 @@ addOpLabel(remainder, "«ops.remainder»");
|
|
|
467
458
|
|
|
468
459
|
/**
|
|
469
460
|
* Files tree for the filesystem root.
|
|
470
|
-
*
|
|
471
|
-
* @this {AsyncTree|null}
|
|
472
461
|
*/
|
|
473
462
|
export async function rootDirectory(...keys) {
|
|
474
463
|
const tree = new OrigamiFiles("/");
|
|
475
|
-
// Use the same handlers as the current tree
|
|
476
|
-
/** @type {any} */ (tree).handlers = getHandlers(this);
|
|
477
464
|
return keys.length > 0 ? Tree.traverse(tree, ...keys) : tree;
|
|
478
465
|
}
|
|
479
466
|
addOpLabel(rootDirectory, "«ops.rootDirectory»");
|
|
@@ -481,17 +468,15 @@ addOpLabel(rootDirectory, "«ops.rootDirectory»");
|
|
|
481
468
|
/**
|
|
482
469
|
* Return the scope of the current tree
|
|
483
470
|
*
|
|
484
|
-
* @
|
|
485
|
-
* @param {AsyncTree|null} [context]
|
|
471
|
+
* @param {AsyncTree} parent
|
|
486
472
|
*/
|
|
487
|
-
export async function scope(
|
|
488
|
-
if (
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
return null;
|
|
473
|
+
export async function scope(parent) {
|
|
474
|
+
if (!parent) {
|
|
475
|
+
throw new ReferenceError(
|
|
476
|
+
"Tried to find a value in scope, but no container was provided as the parent."
|
|
477
|
+
);
|
|
493
478
|
}
|
|
494
|
-
return
|
|
479
|
+
return Tree.scope(parent);
|
|
495
480
|
}
|
|
496
481
|
addOpLabel(scope, "«ops.scope»");
|
|
497
482
|
|
|
@@ -535,7 +520,7 @@ addOpLabel(subtraction, "«ops.subtraction»");
|
|
|
535
520
|
* Apply the tree indent tagged template function.
|
|
536
521
|
*/
|
|
537
522
|
export async function templateIndent(strings, ...values) {
|
|
538
|
-
return indent(strings, ...values);
|
|
523
|
+
return Tree.indent(strings, ...values);
|
|
539
524
|
}
|
|
540
525
|
addOpLabel(templateIndent, "«ops.templateIndent»");
|
|
541
526
|
|
|
@@ -543,7 +528,7 @@ addOpLabel(templateIndent, "«ops.templateIndent»");
|
|
|
543
528
|
* Apply the tree tagged template function.
|
|
544
529
|
*/
|
|
545
530
|
export async function templateText(strings, ...values) {
|
|
546
|
-
return text(strings, ...values);
|
|
531
|
+
return Tree.text(strings, ...values);
|
|
547
532
|
}
|
|
548
533
|
addOpLabel(templateText, "«ops.templateText»");
|
|
549
534
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Tree } from "@weborigami/async-tree";
|
|
2
2
|
import assert from "node:assert";
|
|
3
3
|
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 globals = {
|
|
8
8
|
greet: (name) => `Hello, ${name}!`,
|
|
9
9
|
name: "Alice",
|
|
10
10
|
};
|
|
@@ -43,21 +43,24 @@ describe("compile", () => {
|
|
|
43
43
|
);
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
-
test
|
|
46
|
+
test("merge", async () => {
|
|
47
47
|
{
|
|
48
|
-
const
|
|
48
|
+
const globals = {
|
|
49
49
|
more: {
|
|
50
50
|
b: 2,
|
|
51
51
|
},
|
|
52
|
-
}
|
|
53
|
-
const fn = compile.expression(
|
|
52
|
+
};
|
|
53
|
+
const fn = compile.expression(
|
|
54
|
+
`
|
|
54
55
|
{
|
|
55
56
|
a: 1
|
|
56
57
|
...more
|
|
57
58
|
c: a
|
|
58
59
|
}
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
`,
|
|
61
|
+
{ globals }
|
|
62
|
+
);
|
|
63
|
+
const result = await fn();
|
|
61
64
|
assert.deepEqual(await Tree.plain(result), {
|
|
62
65
|
a: 1,
|
|
63
66
|
b: 2,
|
|
@@ -78,10 +81,8 @@ describe("compile", () => {
|
|
|
78
81
|
});
|
|
79
82
|
|
|
80
83
|
test("async object", async () => {
|
|
81
|
-
const fn = compile.expression("{ a: { b = name }}", {
|
|
82
|
-
|
|
83
|
-
});
|
|
84
|
-
const object = await fn.call(null);
|
|
84
|
+
const fn = compile.expression("{ a: { b = name }}", { globals });
|
|
85
|
+
const object = await fn();
|
|
85
86
|
assert.deepEqual(await object.a.b, "Alice");
|
|
86
87
|
});
|
|
87
88
|
|
|
@@ -89,8 +90,8 @@ describe("compile", () => {
|
|
|
89
90
|
const defineTemplateFn = compile.templateDocument(
|
|
90
91
|
"Documents can contain ` backticks"
|
|
91
92
|
);
|
|
92
|
-
const templateFn = await defineTemplateFn
|
|
93
|
-
const value = await templateFn
|
|
93
|
+
const templateFn = await defineTemplateFn();
|
|
94
|
+
const value = await templateFn();
|
|
94
95
|
assert.deepEqual(value, "Documents can contain ` backticks");
|
|
95
96
|
});
|
|
96
97
|
|
|
@@ -115,8 +116,8 @@ describe("compile", () => {
|
|
|
115
116
|
return strings[0] + values[0] + strings[1];
|
|
116
117
|
},
|
|
117
118
|
};
|
|
118
|
-
const program = compile.expression("
|
|
119
|
-
const lambda = await program
|
|
119
|
+
const program = compile.expression("(_) => tag`Hello, ${_}!`", { globals });
|
|
120
|
+
const lambda = await program();
|
|
120
121
|
const alice = await lambda("Alice");
|
|
121
122
|
assert.equal(alice, "Hello, Alice!");
|
|
122
123
|
const bob = await lambda("Bob");
|
|
@@ -126,9 +127,9 @@ describe("compile", () => {
|
|
|
126
127
|
|
|
127
128
|
async function assertCompile(text, expected, options = {}) {
|
|
128
129
|
const mode = options.mode ?? "program";
|
|
129
|
-
const
|
|
130
|
-
const
|
|
131
|
-
let result = await fn
|
|
130
|
+
const parent = options.target ?? null;
|
|
131
|
+
const fn = compile.expression(text, { globals, mode, parent });
|
|
132
|
+
let result = await fn();
|
|
132
133
|
if (Tree.isTreelike(result)) {
|
|
133
134
|
result = await Tree.plain(result);
|
|
134
135
|
}
|