@weborigami/language 0.0.40 → 0.0.42

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.
@@ -27,7 +27,7 @@ describe("ops", () => {
27
27
  message: "Hello",
28
28
  });
29
29
 
30
- const code = [ops.lambda, [ops.scope, "message"]];
30
+ const code = [ops.lambda, null, [ops.scope, "message"]];
31
31
 
32
32
  const fn = await evaluate.call(scope, code);
33
33
  const result = await fn.call(scope);
@@ -35,17 +35,30 @@ describe("ops", () => {
35
35
  });
36
36
 
37
37
  test("lambda adds input to scope as `_`", async () => {
38
- const code = [ops.lambda, [ops.scope, "_"]];
38
+ const code = [ops.lambda, null, [ops.scope, "_"]];
39
39
  const fn = await evaluate.call(null, code);
40
40
  const result = await fn("Hello");
41
41
  assert.equal(result, "Hello");
42
42
  });
43
43
 
44
+ test("parameterized lambda adds input args to scope", async () => {
45
+ const code = [
46
+ ops.lambda,
47
+ ["a", "b"],
48
+ [ops.concat, [ops.scope, "b"], [ops.scope, "a"]],
49
+ ];
50
+ const fn = await evaluate.call(null, code);
51
+ const result = await fn("x", "y");
52
+ assert.equal(result, "yx");
53
+ });
54
+
44
55
  test("a lambda can reference itself with @recurse", async () => {
45
- const code = [ops.lambda, [ops.scope, "@recurse"]];
56
+ const code = [ops.lambda, null, [ops.scope, "@recurse"]];
46
57
  const fn = await evaluate.call(null, code);
47
58
  const result = await fn();
48
- assert.equal(result, fn);
59
+ // We're expecting the function to return itself, but testing recursion is
60
+ // messy. We just confirm that the result has the same code as the original.
61
+ assert.equal(result.code, fn.code);
49
62
  });
50
63
 
51
64
  test("can instantiate an object", async () => {
@@ -1,3 +0,0 @@
1
- import type { Treelike } from "@weborigami/async-tree";
2
-
3
- type Code = [Treelike, ...any[]] | any;
@@ -1,126 +0,0 @@
1
- import { ops } from "./internal.js";
2
-
3
- export default function format(code, implicitFunctionCall = false) {
4
- if (code === null) {
5
- return "";
6
- } else if (typeof code === "string") {
7
- return `'${code}'`;
8
- } else if (typeof code === "symbol") {
9
- return `«${code.description}»`;
10
- } else if (!(code instanceof Array)) {
11
- return code;
12
- } else {
13
- switch (code[0]) {
14
- case ops.assign:
15
- return formatAssignment(code);
16
-
17
- case ops.concat:
18
- return formatTemplate(code);
19
-
20
- case ops.lambda:
21
- return formatLambda(code);
22
-
23
- case ops.object:
24
- return formatObject(code);
25
-
26
- case ops.scope:
27
- return formatScopeTraversal(code, implicitFunctionCall);
28
-
29
- case ops.tree:
30
- return formatTree(code);
31
-
32
- default:
33
- return code[0] instanceof Array
34
- ? formatFunctionCall(code)
35
- : "** Unknown Origami code **";
36
- }
37
- }
38
- }
39
-
40
- function formatArgument(arg) {
41
- return typeof arg === "string" ? `'${arg}'` : format(arg);
42
- }
43
-
44
- function formatArguments(args) {
45
- const allStrings = args.every((arg) => typeof arg === "string");
46
- return allStrings
47
- ? // Use tree traversal syntax.
48
- formatSlashPath(args)
49
- : // Use function invocation syntax.
50
- formatArgumentsList(args);
51
- }
52
-
53
- function formatArgumentsList(args) {
54
- const formatted = args.map((arg) => formatArgument(arg));
55
- const list = formatted.join(", ");
56
- return `(${list})`;
57
- }
58
-
59
- function formatAssignment(code) {
60
- const [_, declaration, expression] = code;
61
- return `${declaration} = ${format(expression)}`;
62
- }
63
-
64
- function formatFunctionCall(code) {
65
- const [fn, ...args] = code;
66
- let formattedFn = format(fn);
67
- if (formattedFn.includes("/") || formattedFn.includes("(")) {
68
- formattedFn = `(${formattedFn})`;
69
- }
70
- return `${formattedFn}${formatArguments(args)}`;
71
- }
72
-
73
- function formatObject(code) {
74
- const [_, ...entries] = code;
75
- const formatted = entries.map(([key, value]) => {
76
- return value === null ? key : `${key}: ${format(value)}`;
77
- });
78
- return formatted ? `{ ${formatted.join(", ")} }` : "{}";
79
- }
80
-
81
- function formatName(name) {
82
- return typeof name === "string"
83
- ? name
84
- : name instanceof Array
85
- ? `(${format(name)})`
86
- : format(name);
87
- }
88
-
89
- function formatLambda(code) {
90
- return `=${format(code[1])}`;
91
- }
92
-
93
- function formatScopeTraversal(code, implicitFunctionCall = false) {
94
- const operands = code.slice(1);
95
- const name = formatName(operands[0]);
96
- if (operands.length === 1) {
97
- return implicitFunctionCall ? `${name}()` : name;
98
- }
99
-
100
- const args = formatArguments(operands.slice(1));
101
- return `${name}${args}`;
102
- }
103
-
104
- function formatSlashPath(args) {
105
- return "/" + args.join("/");
106
- }
107
-
108
- function formatTemplate(code) {
109
- const args = code.slice(1);
110
- const formatted = args.map((arg) =>
111
- typeof arg === "string" ? arg : `{{${format(arg)}}}`
112
- );
113
- return `\`${formatted.join("")}\``;
114
- }
115
-
116
- function formatTree(code) {
117
- const [_, ...entries] = code;
118
- const formatted = entries.map(([key, value]) => {
119
- const rhs =
120
- typeof value === "function" && value.code !== undefined
121
- ? value.code
122
- : value;
123
- return `${key} = ${format(rhs)}`;
124
- });
125
- return formatted ? `{ ${formatted.join(", ")} }` : "{}";
126
- }
@@ -1,66 +0,0 @@
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
- });