@weborigami/language 0.1.0 → 0.2.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/index.ts +1 -0
- package/package.json +6 -4
- package/src/compiler/compile.js +33 -15
- package/src/compiler/origami.pegjs +309 -195
- package/src/compiler/parse.js +2753 -1658
- package/src/compiler/parserHelpers.js +187 -45
- package/src/runtime/ops.js +246 -54
- package/test/cases/ReadMe.md +1 -0
- package/test/cases/conditionalExpression.yaml +101 -0
- package/test/cases/logicalAndExpression.yaml +146 -0
- package/test/cases/logicalOrExpression.yaml +145 -0
- package/test/cases/nullishCoalescingExpression.yaml +105 -0
- package/test/compiler/compile.test.js +3 -3
- package/test/compiler/parse.test.js +588 -359
- package/test/generated/conditionalExpression.test.js +58 -0
- package/test/generated/logicalAndExpression.test.js +80 -0
- package/test/generated/logicalOrExpression.test.js +78 -0
- package/test/generated/nullishCoalescingExpression.test.js +64 -0
- package/test/generator/generateTests.js +80 -0
- package/test/generator/oriEval.js +15 -0
- package/test/runtime/ops.test.js +242 -20
package/index.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weborigami/language",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Web Origami expression language compiler and runtime",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./main.js",
|
|
@@ -8,15 +8,17 @@
|
|
|
8
8
|
"devDependencies": {
|
|
9
9
|
"@types/node": "22.7.4",
|
|
10
10
|
"peggy": "4.0.3",
|
|
11
|
-
"typescript": "5.6.2"
|
|
11
|
+
"typescript": "5.6.2",
|
|
12
|
+
"yaml": "2.5.1"
|
|
12
13
|
},
|
|
13
14
|
"dependencies": {
|
|
14
|
-
"@weborigami/async-tree": "0.1
|
|
15
|
-
"@weborigami/types": "0.1
|
|
15
|
+
"@weborigami/async-tree": "0.2.1",
|
|
16
|
+
"@weborigami/types": "0.2.1",
|
|
16
17
|
"watcher": "2.3.1"
|
|
17
18
|
},
|
|
18
19
|
"scripts": {
|
|
19
20
|
"build": "peggy --allowed-start-rules=\"*\" --format es src/compiler/origami.pegjs --output src/compiler/parse.js",
|
|
21
|
+
"buildTests": "node ./test/generator/generateTests.js",
|
|
20
22
|
"prepublishOnly": "npm run build",
|
|
21
23
|
"test": "node --test --test-reporter=spec",
|
|
22
24
|
"typecheck": "tsc"
|
package/src/compiler/compile.js
CHANGED
|
@@ -2,11 +2,11 @@ import { trailingSlash } from "@weborigami/async-tree";
|
|
|
2
2
|
import { createExpressionFunction } from "../runtime/expressionFunction.js";
|
|
3
3
|
import { ops } from "../runtime/internal.js";
|
|
4
4
|
import { parse } from "./parse.js";
|
|
5
|
-
import { annotate } from "./parserHelpers.js";
|
|
5
|
+
import { annotate, undetermined } from "./parserHelpers.js";
|
|
6
6
|
|
|
7
7
|
function compile(source, options) {
|
|
8
8
|
const { startRule } = options;
|
|
9
|
-
const
|
|
9
|
+
const enableCaching = options.scopeCaching ?? true;
|
|
10
10
|
if (typeof source === "string") {
|
|
11
11
|
source = { text: source };
|
|
12
12
|
}
|
|
@@ -15,9 +15,7 @@ function compile(source, options) {
|
|
|
15
15
|
startRule,
|
|
16
16
|
});
|
|
17
17
|
const cache = {};
|
|
18
|
-
const modified =
|
|
19
|
-
? cacheExternalScopeReferences(code, cache)
|
|
20
|
-
: code;
|
|
18
|
+
const modified = transformScopeReferences(code, cache, enableCaching);
|
|
21
19
|
const fn = createExpressionFunction(modified);
|
|
22
20
|
return fn;
|
|
23
21
|
}
|
|
@@ -29,24 +27,39 @@ export function expression(source, options = {}) {
|
|
|
29
27
|
});
|
|
30
28
|
}
|
|
31
29
|
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
|
|
30
|
+
// Transform any remaining undetermined references to scope references. At the
|
|
31
|
+
// same time, transform those or explicit ops.scope calls to ops.external calls
|
|
32
|
+
// unless they refer to local variables (variables defined by object literals or
|
|
33
|
+
// lambda parameters).
|
|
34
|
+
export function transformScopeReferences(
|
|
35
|
+
code,
|
|
36
|
+
cache,
|
|
37
|
+
enableCaching,
|
|
38
|
+
locals = {}
|
|
39
|
+
) {
|
|
36
40
|
const [fn, ...args] = code;
|
|
37
41
|
|
|
38
42
|
let additionalLocalNames;
|
|
39
43
|
switch (fn) {
|
|
44
|
+
case undetermined:
|
|
40
45
|
case ops.scope:
|
|
41
46
|
const key = args[0];
|
|
42
47
|
const normalizedKey = trailingSlash.remove(key);
|
|
43
|
-
if (locals[normalizedKey]) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
+
if (enableCaching && !locals[normalizedKey]) {
|
|
49
|
+
// Upgrade to cached external reference
|
|
50
|
+
const modified = [ops.external, key, cache];
|
|
51
|
+
// @ts-ignore
|
|
52
|
+
annotate(modified, code.location);
|
|
53
|
+
return modified;
|
|
54
|
+
} else if (fn === undetermined) {
|
|
55
|
+
// Transform undetermined reference to regular scope call
|
|
56
|
+
const modified = [ops.scope, key];
|
|
57
|
+
// @ts-ignore
|
|
48
58
|
annotate(modified, code.location);
|
|
49
59
|
return modified;
|
|
60
|
+
} else {
|
|
61
|
+
// Internal ops.scope call; leave as is
|
|
62
|
+
return code;
|
|
50
63
|
}
|
|
51
64
|
|
|
52
65
|
case ops.lambda:
|
|
@@ -74,7 +87,12 @@ export function cacheExternalScopeReferences(code, cache, locals = {}) {
|
|
|
74
87
|
// be preferable to only descend into instructions. This would require
|
|
75
88
|
// surrounding ops.lambda parameters with ops.literal, and ops.object
|
|
76
89
|
// entries with ops.array.
|
|
77
|
-
return
|
|
90
|
+
return transformScopeReferences(
|
|
91
|
+
child,
|
|
92
|
+
cache,
|
|
93
|
+
enableCaching,
|
|
94
|
+
updatedLocals
|
|
95
|
+
);
|
|
78
96
|
} else {
|
|
79
97
|
return child;
|
|
80
98
|
}
|