@weborigami/language 0.4.2 → 0.5.1
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/main.js +0 -1
- package/package.json +7 -7
- package/src/compiler/compile.js +2 -4
- package/src/compiler/optimize.js +7 -2
- package/src/compiler/origami.pegjs +14 -10
- package/src/compiler/parse.js +1190 -1019
- package/src/compiler/parserHelpers.js +10 -8
- package/src/runtime/errors.js +4 -0
- package/src/runtime/handlers.js +1 -0
- package/src/runtime/jsGlobals.js +3 -0
- package/src/runtime/ops.js +54 -10
- package/test/compiler/compile.test.js +4 -5
- package/test/compiler/optimize.test.js +4 -3
- package/test/compiler/parse.test.js +145 -147
- package/test/runtime/ops.test.js +8 -1
- package/src/runtime/templateIndent.js +0 -120
- package/test/runtime/taggedTemplateIndent.test.js +0 -44
|
@@ -177,8 +177,8 @@ export function makeCall(target, args, location) {
|
|
|
177
177
|
} else if (op === markers.property) {
|
|
178
178
|
// Property access
|
|
179
179
|
const property = args[1];
|
|
180
|
-
fnCall = [target, property];
|
|
181
|
-
} else if (op === ops.
|
|
180
|
+
fnCall = [ops.property, target, property];
|
|
181
|
+
} else if (op === ops.templateText) {
|
|
182
182
|
// Tagged template
|
|
183
183
|
const strings = args[1];
|
|
184
184
|
const values = args.slice(2);
|
|
@@ -218,8 +218,6 @@ export function makeDocument(front, body, location) {
|
|
|
218
218
|
);
|
|
219
219
|
|
|
220
220
|
// Add an entry for the body
|
|
221
|
-
// TODO: Deprecate @text
|
|
222
|
-
entries.push(annotate(["(@text)", body], location));
|
|
223
221
|
entries.push(annotate(["_body", body], location));
|
|
224
222
|
|
|
225
223
|
// Return the code for the document object
|
|
@@ -233,7 +231,7 @@ export function makeDocument(front, body, location) {
|
|
|
233
231
|
*
|
|
234
232
|
* {
|
|
235
233
|
* x = { a: 1 }
|
|
236
|
-
*
|
|
234
|
+
* ...x
|
|
237
235
|
* y = x
|
|
238
236
|
* }
|
|
239
237
|
*
|
|
@@ -244,7 +242,7 @@ export function makeDocument(front, body, location) {
|
|
|
244
242
|
* y = x
|
|
245
243
|
* _result: {
|
|
246
244
|
* x
|
|
247
|
-
*
|
|
245
|
+
* ...x
|
|
248
246
|
* y
|
|
249
247
|
* }
|
|
250
248
|
* }.result
|
|
@@ -348,8 +346,12 @@ export function makeObject(entries, location) {
|
|
|
348
346
|
// Merge multiple spreads
|
|
349
347
|
code = makeMerge(spreads, location);
|
|
350
348
|
} else if (spreads.length === 1) {
|
|
351
|
-
//
|
|
352
|
-
|
|
349
|
+
// Spreading a single reference equates to an unpack
|
|
350
|
+
if (spreads[0][0] === markers.traverse) {
|
|
351
|
+
code = annotate([ops.unpack, spreads[0]], location);
|
|
352
|
+
} else {
|
|
353
|
+
code = spreads[0];
|
|
354
|
+
}
|
|
353
355
|
} else {
|
|
354
356
|
// Empty object
|
|
355
357
|
code = [ops.object];
|
package/src/runtime/errors.js
CHANGED
|
@@ -23,6 +23,10 @@ const origamiSourceSignals = [
|
|
|
23
23
|
const displayedWarnings = new Set();
|
|
24
24
|
|
|
25
25
|
export function attachWarning(value, message) {
|
|
26
|
+
if (value == null) {
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
|
|
26
30
|
if (typeof value === "object" && value?.[symbols.warningSymbol]) {
|
|
27
31
|
// Already has a warning, don't overwrite it
|
|
28
32
|
return value;
|
package/src/runtime/handlers.js
CHANGED
|
@@ -31,6 +31,7 @@ export async function handleExtension(parent, value, key, handlers) {
|
|
|
31
31
|
|
|
32
32
|
// Special cases: `.ori.<ext>` extensions are Origami documents,
|
|
33
33
|
// `.jse.<ext>` are JSE documents.
|
|
34
|
+
// TODO: Remove .jse.<ext>
|
|
34
35
|
const extname = key.match(/\.ori\.\S+$/)
|
|
35
36
|
? ".oridocument"
|
|
36
37
|
: key.match(/\.jse\.\S+$/)
|
package/src/runtime/jsGlobals.js
CHANGED
|
@@ -163,6 +163,9 @@ Object.defineProperty(globals, "globalThis", {
|
|
|
163
163
|
export default globals;
|
|
164
164
|
|
|
165
165
|
async function fetchWrapper(resource, options) {
|
|
166
|
+
console.warn(
|
|
167
|
+
"Warning: A plain `fetch` reference will eventually call the standard JavaScript fetch() function. For Origami's fetch behavior, update your code to call Origami.fetch()."
|
|
168
|
+
);
|
|
166
169
|
const response = await fetch(resource, options);
|
|
167
170
|
return response.ok ? await response.arrayBuffer() : undefined;
|
|
168
171
|
}
|
package/src/runtime/ops.js
CHANGED
|
@@ -6,12 +6,13 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
|
-
ObjectTree,
|
|
10
|
-
Tree,
|
|
11
9
|
deepText,
|
|
10
|
+
indent,
|
|
12
11
|
isUnpackable,
|
|
12
|
+
ObjectTree,
|
|
13
13
|
scope as scopeFn,
|
|
14
|
-
text
|
|
14
|
+
text,
|
|
15
|
+
Tree,
|
|
15
16
|
} from "@weborigami/async-tree";
|
|
16
17
|
import os from "node:os";
|
|
17
18
|
import expressionObject from "./expressionObject.js";
|
|
@@ -20,7 +21,6 @@ import { evaluate } from "./internal.js";
|
|
|
20
21
|
import mergeTrees from "./mergeTrees.js";
|
|
21
22
|
import OrigamiFiles from "./OrigamiFiles.js";
|
|
22
23
|
import { codeSymbol } from "./symbols.js";
|
|
23
|
-
import templateFunctionIndent from "./templateIndent.js";
|
|
24
24
|
|
|
25
25
|
function addOpLabel(op, label) {
|
|
26
26
|
Object.defineProperty(op, "toString", {
|
|
@@ -273,7 +273,7 @@ export function lambda(parameters, code) {
|
|
|
273
273
|
invoke.code = code;
|
|
274
274
|
return invoke;
|
|
275
275
|
}
|
|
276
|
-
addOpLabel(lambda, "«ops.lambda");
|
|
276
|
+
addOpLabel(lambda, "«ops.lambda»");
|
|
277
277
|
lambda.unevaluatedArgs = true;
|
|
278
278
|
|
|
279
279
|
export function lessThan(a, b) {
|
|
@@ -293,7 +293,12 @@ addOpLabel(lessThanOrEqual, "«ops.lessThanOrEqual»");
|
|
|
293
293
|
* literals.
|
|
294
294
|
*/
|
|
295
295
|
export async function literal(value) {
|
|
296
|
-
|
|
296
|
+
if (value instanceof Array) {
|
|
297
|
+
// Strip code properties like `source`
|
|
298
|
+
return [...value];
|
|
299
|
+
} else {
|
|
300
|
+
return value;
|
|
301
|
+
}
|
|
297
302
|
}
|
|
298
303
|
addOpLabel(literal, "«ops.literal»");
|
|
299
304
|
literal.unevaluatedArgs = true;
|
|
@@ -416,6 +421,45 @@ export function optionalTraverse(treelike, key) {
|
|
|
416
421
|
}
|
|
417
422
|
addOpLabel(optionalTraverse, "«ops.optionalTraverse");
|
|
418
423
|
|
|
424
|
+
/**
|
|
425
|
+
* Return the indicated property
|
|
426
|
+
*
|
|
427
|
+
* @param {any} obj
|
|
428
|
+
* @param {string} key
|
|
429
|
+
*/
|
|
430
|
+
export async function property(obj, key) {
|
|
431
|
+
if (obj == null) {
|
|
432
|
+
throw new ReferenceError();
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (isUnpackable(obj)) {
|
|
436
|
+
obj = await obj.unpack();
|
|
437
|
+
} else if (typeof obj === "string") {
|
|
438
|
+
obj = new String(obj);
|
|
439
|
+
} else if (typeof obj === "number") {
|
|
440
|
+
obj = new Number(obj);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if (key in obj) {
|
|
444
|
+
// Object defines the property, get it
|
|
445
|
+
let value = obj[key];
|
|
446
|
+
// Is value an instance method? Copied from ObjectTree.
|
|
447
|
+
const isInstanceMethod =
|
|
448
|
+
!(obj instanceof Function) &&
|
|
449
|
+
value instanceof Function &&
|
|
450
|
+
!Object.hasOwn(obj, key);
|
|
451
|
+
if (isInstanceMethod) {
|
|
452
|
+
// Bind it to the object
|
|
453
|
+
value = value.bind(obj);
|
|
454
|
+
}
|
|
455
|
+
return value;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Handle as tree traversal
|
|
459
|
+
return Tree.traverseOrThrow(obj, key);
|
|
460
|
+
}
|
|
461
|
+
addOpLabel(property, "«ops.property»");
|
|
462
|
+
|
|
419
463
|
export function remainder(a, b) {
|
|
420
464
|
return a % b;
|
|
421
465
|
}
|
|
@@ -491,17 +535,17 @@ addOpLabel(subtraction, "«ops.subtraction»");
|
|
|
491
535
|
* Apply the tree indent tagged template function.
|
|
492
536
|
*/
|
|
493
537
|
export async function templateIndent(strings, ...values) {
|
|
494
|
-
return
|
|
538
|
+
return indent(strings, ...values);
|
|
495
539
|
}
|
|
496
540
|
addOpLabel(templateIndent, "«ops.templateIndent»");
|
|
497
541
|
|
|
498
542
|
/**
|
|
499
543
|
* Apply the tree tagged template function.
|
|
500
544
|
*/
|
|
501
|
-
export async function
|
|
502
|
-
return
|
|
545
|
+
export async function templateText(strings, ...values) {
|
|
546
|
+
return text(strings, ...values);
|
|
503
547
|
}
|
|
504
|
-
addOpLabel(
|
|
548
|
+
addOpLabel(templateText, "«ops.templateText»");
|
|
505
549
|
|
|
506
550
|
export function unaryMinus(a) {
|
|
507
551
|
return -a;
|
|
@@ -19,8 +19,7 @@ describe("compile", () => {
|
|
|
19
19
|
test("functionComposition", async () => {
|
|
20
20
|
await assertCompile("greet()", "Hello, undefined!");
|
|
21
21
|
await assertCompile("greet(name)", "Hello, Alice!");
|
|
22
|
-
await assertCompile("greet
|
|
23
|
-
await assertCompile("greet 'world'", "Hello, world!");
|
|
22
|
+
await assertCompile("greet 'world'", "Hello, world!", { mode: "shell" });
|
|
24
23
|
});
|
|
25
24
|
|
|
26
25
|
test("angle bracket path", async () => {
|
|
@@ -40,7 +39,7 @@ describe("compile", () => {
|
|
|
40
39
|
{
|
|
41
40
|
message: "Hello, Alice!",
|
|
42
41
|
},
|
|
43
|
-
{ mode: "
|
|
42
|
+
{ mode: "shell" }
|
|
44
43
|
);
|
|
45
44
|
});
|
|
46
45
|
|
|
@@ -109,7 +108,7 @@ describe("compile", () => {
|
|
|
109
108
|
tag: (strings, ...values) => {
|
|
110
109
|
assertCodeEqual(strings, ["Hello, ", "!"]);
|
|
111
110
|
if (saved) {
|
|
112
|
-
assert.
|
|
111
|
+
assert.deepEqual(strings, saved);
|
|
113
112
|
} else {
|
|
114
113
|
saved = strings;
|
|
115
114
|
}
|
|
@@ -126,7 +125,7 @@ describe("compile", () => {
|
|
|
126
125
|
});
|
|
127
126
|
|
|
128
127
|
async function assertCompile(text, expected, options = {}) {
|
|
129
|
-
const mode = options.mode ?? "
|
|
128
|
+
const mode = options.mode ?? "program";
|
|
130
129
|
const fn = compile.expression(text, { globals: sharedGlobals, mode });
|
|
131
130
|
const target = options.target ?? null;
|
|
132
131
|
let result = await fn.call(target);
|
|
@@ -89,7 +89,7 @@ describe("optimize", () => {
|
|
|
89
89
|
],
|
|
90
90
|
];
|
|
91
91
|
assertCompile(expression, expected);
|
|
92
|
-
assertCompile(expression, expected, "
|
|
92
|
+
assertCompile(expression, expected, "shell");
|
|
93
93
|
});
|
|
94
94
|
|
|
95
95
|
describe("resolve reference", () => {
|
|
@@ -171,7 +171,7 @@ describe("optimize", () => {
|
|
|
171
171
|
[markers.reference, "Math.PI"],
|
|
172
172
|
]);
|
|
173
173
|
const globals = { Math: { PI: null } }; // value doesn't matter
|
|
174
|
-
const expected = [globals.Math, "PI"];
|
|
174
|
+
const expected = [ops.property, globals.Math, "PI"];
|
|
175
175
|
assertCodeEqual(optimize(code, { globals }), expected);
|
|
176
176
|
});
|
|
177
177
|
|
|
@@ -195,7 +195,8 @@ describe("optimize", () => {
|
|
|
195
195
|
const locals = [["post"]];
|
|
196
196
|
const actual = optimize(code, { globals, locals });
|
|
197
197
|
const expected = [
|
|
198
|
-
|
|
198
|
+
ops.property,
|
|
199
|
+
[ops.property, [[ops.context], [ops.literal, "post"]], "author"],
|
|
199
200
|
"name",
|
|
200
201
|
];
|
|
201
202
|
assertCodeEqual(actual, expected);
|