@weborigami/language 0.0.35

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.
Files changed (69) hide show
  1. package/index.ts +30 -0
  2. package/main.js +21 -0
  3. package/package.json +24 -0
  4. package/src/compiler/code.d.ts +3 -0
  5. package/src/compiler/compile.js +55 -0
  6. package/src/compiler/origami.pegjs +277 -0
  7. package/src/compiler/parse.js +2292 -0
  8. package/src/compiler/parserHelpers.js +26 -0
  9. package/src/runtime/EventTargetMixin.d.ts +9 -0
  10. package/src/runtime/EventTargetMixin.js +117 -0
  11. package/src/runtime/ExpressionTree.js +20 -0
  12. package/src/runtime/FileLoadersTransform.d.ts +5 -0
  13. package/src/runtime/FileLoadersTransform.js +43 -0
  14. package/src/runtime/ImportModulesMixin.d.ts +5 -0
  15. package/src/runtime/ImportModulesMixin.js +48 -0
  16. package/src/runtime/InheritScopeMixin.js +34 -0
  17. package/src/runtime/InheritScopeMixin.ts +9 -0
  18. package/src/runtime/InvokeFunctionsTransform.d.ts +5 -0
  19. package/src/runtime/InvokeFunctionsTransform.js +27 -0
  20. package/src/runtime/OrigamiFiles.d.ts +11 -0
  21. package/src/runtime/OrigamiFiles.js +9 -0
  22. package/src/runtime/OrigamiTransform.d.ts +11 -0
  23. package/src/runtime/OrigamiTransform.js +11 -0
  24. package/src/runtime/OrigamiTree.js +4 -0
  25. package/src/runtime/ReadMe.md +1 -0
  26. package/src/runtime/Scope.js +89 -0
  27. package/src/runtime/TreeEvent.js +6 -0
  28. package/src/runtime/WatchFilesMixin.d.ts +5 -0
  29. package/src/runtime/WatchFilesMixin.js +58 -0
  30. package/src/runtime/concatTreeValues.js +46 -0
  31. package/src/runtime/evaluate.js +90 -0
  32. package/src/runtime/expressionFunction.js +33 -0
  33. package/src/runtime/extname.js +20 -0
  34. package/src/runtime/format.js +126 -0
  35. package/src/runtime/functionResultsMap.js +28 -0
  36. package/src/runtime/internal.js +20 -0
  37. package/src/runtime/ops.js +222 -0
  38. package/test/compiler/compile.test.js +64 -0
  39. package/test/compiler/parse.test.js +389 -0
  40. package/test/runtime/EventTargetMixin.test.js +68 -0
  41. package/test/runtime/ExpressionTree.test.js +27 -0
  42. package/test/runtime/FileLoadersTransform.test.js +41 -0
  43. package/test/runtime/InheritScopeMixin.test.js +29 -0
  44. package/test/runtime/OrigamiFiles.test.js +37 -0
  45. package/test/runtime/Scope.test.js +37 -0
  46. package/test/runtime/concatTreeValues.js +20 -0
  47. package/test/runtime/evaluate.test.js +55 -0
  48. package/test/runtime/fixtures/foo.js +1 -0
  49. package/test/runtime/fixtures/makeTest/a +1 -0
  50. package/test/runtime/fixtures/makeTest/b = a +0 -0
  51. package/test/runtime/fixtures/metagraphs/foo.txt +1 -0
  52. package/test/runtime/fixtures/metagraphs/greeting = this('world').js +3 -0
  53. package/test/runtime/fixtures/metagraphs/obj = this.json +5 -0
  54. package/test/runtime/fixtures/metagraphs/sample.txt = this().js +3 -0
  55. package/test/runtime/fixtures/metagraphs/string = this.json +1 -0
  56. package/test/runtime/fixtures/metagraphs/value = fn() +0 -0
  57. package/test/runtime/fixtures/programs/context.yaml +4 -0
  58. package/test/runtime/fixtures/programs/files.yaml +2 -0
  59. package/test/runtime/fixtures/programs/obj.yaml +3 -0
  60. package/test/runtime/fixtures/programs/simple.yaml +2 -0
  61. package/test/runtime/fixtures/subgraph = this.js +5 -0
  62. package/test/runtime/fixtures/templates/greet.orit +4 -0
  63. package/test/runtime/fixtures/templates/index.orit +15 -0
  64. package/test/runtime/fixtures/templates/names.yaml +3 -0
  65. package/test/runtime/fixtures/templates/plain.txt +1 -0
  66. package/test/runtime/fixtures/virtualKeys/.keys.json +1 -0
  67. package/test/runtime/format.test.js +66 -0
  68. package/test/runtime/functionResultsMap.test.js +27 -0
  69. package/test/runtime/ops.test.js +111 -0
@@ -0,0 +1,55 @@
1
+ import { ObjectTree } from "@weborigami/async-tree";
2
+ import assert from "node:assert";
3
+ import { describe, test } from "node:test";
4
+ import * as ops from "../../src/runtime/ops.js";
5
+
6
+ import evaluate from "../../src/runtime/evaluate.js";
7
+
8
+ describe("evaluate", () => {
9
+ test("can retrieve values from scope", async () => {
10
+ const code = [ops.scope, "message"];
11
+ const scope = {
12
+ message: "Hello",
13
+ };
14
+ const result = await evaluate.call(scope, code);
15
+ assert.equal(result, "Hello");
16
+ });
17
+
18
+ test("can invoke functions in scope", async () => {
19
+ // Match the array representation of code generated by the parser.
20
+ const code = [
21
+ [ops.scope, "greet"],
22
+ [ops.scope, "name"],
23
+ ];
24
+
25
+ const scope = new ObjectTree({
26
+ async greet(name) {
27
+ return `Hello ${name}`;
28
+ },
29
+ name: "world",
30
+ });
31
+
32
+ const result = await evaluate.call(scope, code);
33
+ assert.equal(result, "Hello world");
34
+ });
35
+
36
+ test("passes context to invoked functions", async () => {
37
+ const code = [ops.scope, "fn"];
38
+ const scope = {
39
+ async fn() {
40
+ assert.equal(this, scope);
41
+ },
42
+ };
43
+ await evaluate.call(scope, code);
44
+ });
45
+
46
+ test("if object in function position isn't a function, can unpack it", async () => {
47
+ const fn = (...args) => args.join(",");
48
+ const packed = {
49
+ unpack: async () => fn,
50
+ };
51
+ const code = [packed, "a", "b", "c"];
52
+ const result = await evaluate.call(null, code);
53
+ assert.equal(result, "a,b,c");
54
+ });
55
+ });
@@ -0,0 +1 @@
1
+ export default () => "bar";
@@ -0,0 +1 @@
1
+ Hello, world.
File without changes
@@ -0,0 +1 @@
1
+ Foo
@@ -0,0 +1,3 @@
1
+ export default function (name) {
2
+ return `Hello, ${name}.`;
3
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "a": "Hello, a.",
3
+ "b": "Hello, b.",
4
+ "c": "Hello, c."
5
+ }
@@ -0,0 +1,3 @@
1
+ export default function () {
2
+ return "Hello, world.";
3
+ }
@@ -0,0 +1 @@
1
+ "Hello, world."
File without changes
@@ -0,0 +1,4 @@
1
+ title: Reference the special context key
2
+
3
+ # This will pull in the value of "obj.yaml" from the result tree.
4
+ obj = context 'obj.yaml':
@@ -0,0 +1,2 @@
1
+ title: Files demonstration
2
+ files = keys context():
@@ -0,0 +1,3 @@
1
+ a: Hello, a.
2
+ b: Hello, b.
3
+ c: Hello, c.
@@ -0,0 +1,2 @@
1
+ a = b: ""
2
+ b: Hello, world.
@@ -0,0 +1,5 @@
1
+ import { ObjectTree } from "@weborigami/async-tree";
2
+ export default new ObjectTree({
3
+ a: "Hello, a.",
4
+ b: "Hello, b.",
5
+ });
@@ -0,0 +1,4 @@
1
+ Greetings:
2
+
3
+ {{ @map(=`Hello, {{ _ }}.
4
+ `)(names.yaml) }}
@@ -0,0 +1,15 @@
1
+ ---
2
+ title: Greetings
3
+ message: !ori title
4
+ ---
5
+ <!DOCTYPE html>
6
+ <html lang="en">
7
+ <head>
8
+ <meta charset="utf-8" />
9
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
10
+ <title>{{ title }}</title>
11
+ </head>
12
+ <body>
13
+ {{ message }}
14
+ </body>
15
+ </html>
@@ -0,0 +1,3 @@
1
+ - Alice
2
+ - Bob
3
+ - Carol
@@ -0,0 +1 @@
1
+ Hello, world.
@@ -0,0 +1 @@
1
+ ["a", "b", "c"]
@@ -0,0 +1,66 @@
1
+ import assert from "node:assert";
2
+ import { describe, test } from "node:test";
3
+ import format from "../../src/runtime/format.js";
4
+ import * as ops from "../../src/runtime/ops.js";
5
+
6
+ describe("Origami language code formatter", () => {
7
+ test("assignment", () => {
8
+ const code = [ops.assign, "foo", [ops.scope, "bar"]];
9
+ assert.equal(format(code), "foo = bar");
10
+ });
11
+
12
+ test("scope reference", () => {
13
+ const code = [ops.scope, "foo"];
14
+ assert.equal(format(code), "foo");
15
+ });
16
+
17
+ test("implicit function call", () => {
18
+ const code = [ops.scope, "foo"];
19
+ assert.equal(format(code, true), "foo()");
20
+ });
21
+
22
+ test("function call", () => {
23
+ const code = [[ops.scope, "foo"], undefined];
24
+ assert.equal(format(code, true), "foo()");
25
+ });
26
+
27
+ test("tree traversal with string args", () => {
28
+ const code = [[ops.scope, "a"], "b", "c"];
29
+ assert.equal(format(code), "a/b/c");
30
+ });
31
+
32
+ test("tree traversal with numeric and string args", () => {
33
+ const code = [ops.scope, "fn", "x", 1, 2];
34
+ assert.equal(format(code), "fn('x', 1, 2)");
35
+ });
36
+
37
+ test("tree traversal with function arg and string arg", () => {
38
+ const code = [ops.scope, "fn", [ops.scope, "foo"], "bar"];
39
+ assert.equal(format(code), "fn(foo, 'bar')");
40
+ });
41
+
42
+ test("function composition", () => {
43
+ const code = [[[ops.scope, "fn"], "a"], "b"];
44
+ assert.equal(format(code), "(fn/a)/b");
45
+ });
46
+
47
+ test("lambda", () => {
48
+ const code = [ops.lambda, [ops.scope, "message"]];
49
+ assert.equal(format(code), "=message");
50
+ });
51
+
52
+ test("object", () => {
53
+ const code = [ops.object, ["a", "Hello"], ["b", "Goodbye"]];
54
+ assert.equal(format(code), "{ a: 'Hello', b: 'Goodbye' }");
55
+ });
56
+
57
+ test("template", () => {
58
+ const code = [ops.concat, "Hello, ", [ops.scope, "name"], "."];
59
+ assert.equal(format(code), "`Hello, {{name}}.`");
60
+ });
61
+
62
+ test("tree", () => {
63
+ const code = [ops.tree, ["x", [[ops.scope, "fn"], undefined]]];
64
+ assert.equal(format(code), "{ x = fn() }");
65
+ });
66
+ });
@@ -0,0 +1,27 @@
1
+ import { Tree } from "@weborigami/async-tree";
2
+ import assert from "node:assert";
3
+ import { describe, test } from "node:test";
4
+ import Scope from "../../src/runtime/Scope.js";
5
+ import functionResultsMap from "../../src/runtime/functionResultsMap.js";
6
+
7
+ describe("functionResultsMap", () => {
8
+ test("get() invokes functions using scope, returns other values as is", async () => {
9
+ const scope = {
10
+ message: "Hello",
11
+ };
12
+ const tree = Scope.treeWithScope(
13
+ {
14
+ fn: /** @this {import("@weborigami/types").AsyncTree} */ function () {
15
+ return this.get("message");
16
+ },
17
+ string: "string",
18
+ },
19
+ scope
20
+ );
21
+ const fixture = functionResultsMap(tree);
22
+ assert.deepEqual(await Tree.plain(fixture), {
23
+ fn: "Hello",
24
+ string: "string",
25
+ });
26
+ });
27
+ });
@@ -0,0 +1,111 @@
1
+ import { ObjectTree, Tree } from "@weborigami/async-tree";
2
+ import assert from "node:assert";
3
+ import { describe, test } from "node:test";
4
+ import Scope from "../../src/runtime/Scope.js";
5
+
6
+ import {
7
+ OrigamiTree,
8
+ evaluate,
9
+ expressionFunction,
10
+ ops,
11
+ } from "../../src/runtime/internal.js";
12
+
13
+ describe("ops", () => {
14
+ test("can resolve substitutions in a template literal", async () => {
15
+ const scope = new ObjectTree({
16
+ name: "world",
17
+ });
18
+
19
+ const code = [ops.concat, "Hello, ", [ops.scope, "name"], "."];
20
+
21
+ const result = await evaluate.call(scope, code);
22
+ assert.equal(result, "Hello, world.");
23
+ });
24
+
25
+ test("can invoke a lambda", async () => {
26
+ const scope = new ObjectTree({
27
+ message: "Hello",
28
+ });
29
+
30
+ const code = [ops.lambda, [ops.scope, "message"]];
31
+
32
+ const fn = await evaluate.call(scope, code);
33
+ const result = await fn.call(scope);
34
+ assert.equal(result, "Hello");
35
+ });
36
+
37
+ test("lambda adds input to scope as `_`", async () => {
38
+ const code = [ops.lambda, [ops.scope, "_"]];
39
+ const fn = await evaluate.call(null, code);
40
+ const result = await fn("Hello");
41
+ assert.equal(result, "Hello");
42
+ });
43
+
44
+ test("a lambda can reference itself with @recurse", async () => {
45
+ const code = [ops.lambda, [ops.scope, "@recurse"]];
46
+ const fn = await evaluate.call(null, code);
47
+ const result = await fn();
48
+ assert.equal(result, fn);
49
+ });
50
+
51
+ test("can instantiate an object", async () => {
52
+ const scope = new ObjectTree({
53
+ upper: (s) => s.toUpperCase(),
54
+ });
55
+
56
+ const code = [
57
+ ops.object,
58
+ ["hello", [[ops.scope, "upper"], "hello"]],
59
+ ["world", [[ops.scope, "upper"], "world"]],
60
+ ];
61
+
62
+ const result = await evaluate.call(scope, code);
63
+ assert.equal(result.hello, "HELLO");
64
+ assert.equal(result.world, "WORLD");
65
+ });
66
+
67
+ test("can instantiate an array", async () => {
68
+ const scope = new ObjectTree({
69
+ upper: (s) => s.toUpperCase(),
70
+ });
71
+ const code = [ops.array, "Hello", 1, [[ops.scope, "upper"], "world"]];
72
+ const result = await evaluate.call(scope, code);
73
+ assert.deepEqual(result, ["Hello", 1, "WORLD"]);
74
+ });
75
+
76
+ test("can instantiate an Origami tree", async () => {
77
+ const code = [
78
+ ops.tree,
79
+ ["name", "world"],
80
+ [
81
+ "message",
82
+ expressionFunction.createExpressionFunction([
83
+ ops.concat,
84
+ "Hello, ",
85
+ [ops.scope, "name"],
86
+ "!",
87
+ ]),
88
+ ],
89
+ ];
90
+ const result = await evaluate.call({}, code);
91
+ assert(result instanceof OrigamiTree);
92
+ assert.deepEqual(await Tree.plain(result), {
93
+ name: "world",
94
+ message: "Hello, world!",
95
+ });
96
+ });
97
+
98
+ test("can search inherited scope", async () => {
99
+ const a = new ObjectTree({
100
+ a: 1, // This is the inherited value we want
101
+ });
102
+ /** @type {any} */
103
+ const b = new ObjectTree({
104
+ a: 2, // Should be ignored
105
+ });
106
+ b.scope = new Scope(b, a);
107
+ const code = [ops.inherited, "a"];
108
+ const result = await evaluate.call(b.scope, code);
109
+ assert.equal(result, 1);
110
+ });
111
+ });