@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/test/runtime/ops.test.js
CHANGED
|
@@ -5,12 +5,42 @@ import { describe, test } from "node:test";
|
|
|
5
5
|
import { evaluate, ops } from "../../src/runtime/internal.js";
|
|
6
6
|
|
|
7
7
|
describe("ops", () => {
|
|
8
|
+
test("ops.addition adds two numbers", async () => {
|
|
9
|
+
assert.strictEqual(ops.addition(2, 2), 4);
|
|
10
|
+
assert.strictEqual(ops.addition(2, true), 3);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test("ops.addition concatenates two strings", async () => {
|
|
14
|
+
assert.strictEqual(ops.addition("hello ", "everyone"), "hello everyone");
|
|
15
|
+
assert.strictEqual(
|
|
16
|
+
ops.addition("2001", ": A Space Odyssey"),
|
|
17
|
+
"2001: A Space Odyssey"
|
|
18
|
+
);
|
|
19
|
+
});
|
|
20
|
+
|
|
8
21
|
test("ops.array creates an array", async () => {
|
|
9
22
|
const code = createCode([ops.array, 1, 2, 3]);
|
|
10
23
|
const result = await evaluate.call(null, code);
|
|
11
24
|
assert.deepEqual(result, [1, 2, 3]);
|
|
12
25
|
});
|
|
13
26
|
|
|
27
|
+
test("ops.bitwiseAnd", () => {
|
|
28
|
+
assert.strictEqual(ops.bitwiseAnd(5, 3), 1);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("ops.bitwiseNot", () => {
|
|
32
|
+
assert.strictEqual(ops.bitwiseNot(5), -6);
|
|
33
|
+
assert.strictEqual(ops.bitwiseNot(-3), 2);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("ops.bitwiseOr", () => {
|
|
37
|
+
assert.strictEqual(ops.bitwiseOr(5, 3), 7);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("ops.bitwiseXor", () => {
|
|
41
|
+
assert.strictEqual(ops.bitwiseXor(5, 3), 6);
|
|
42
|
+
});
|
|
43
|
+
|
|
14
44
|
test("ops.builtin gets a value from the top of the scope chain", async () => {
|
|
15
45
|
const root = new ObjectTree({
|
|
16
46
|
a: 1,
|
|
@@ -19,32 +49,80 @@ describe("ops", () => {
|
|
|
19
49
|
tree.parent = root;
|
|
20
50
|
const code = createCode([ops.builtin, "a"]);
|
|
21
51
|
const result = await evaluate.call(tree, code);
|
|
22
|
-
assert.
|
|
52
|
+
assert.strictEqual(result, 1);
|
|
23
53
|
});
|
|
24
54
|
|
|
25
|
-
test("ops.
|
|
55
|
+
test("ops.comma returns the last value", async () => {
|
|
56
|
+
const code = createCode([ops.comma, 1, 2, 3]);
|
|
57
|
+
const result = await evaluate.call(null, code);
|
|
58
|
+
assert.strictEqual(result, 3);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("ops.concat concatenates tree value text", async () => {
|
|
62
|
+
const scope = new ObjectTree({
|
|
63
|
+
name: "world",
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const code = createCode([ops.concat, "Hello, ", [ops.scope, "name"], "."]);
|
|
67
|
+
|
|
68
|
+
const result = await evaluate.call(scope, code);
|
|
69
|
+
assert.strictEqual(result, "Hello, world.");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("ops.conditional", async () => {
|
|
73
|
+
assert.strictEqual(await ops.conditional(true, trueFn, falseFn), true);
|
|
74
|
+
assert.strictEqual(await ops.conditional(true, falseFn, trueFn), false);
|
|
75
|
+
assert.strictEqual(await ops.conditional(false, trueFn, falseFn), false);
|
|
76
|
+
assert.strictEqual(await ops.conditional(false, falseFn, trueFn), true);
|
|
77
|
+
|
|
78
|
+
// Short-circuiting
|
|
79
|
+
assert.strictEqual(await ops.conditional(false, errorFn, trueFn), true);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("ops.division divides two numbers", async () => {
|
|
83
|
+
assert.strictEqual(ops.division(12, 2), 6);
|
|
84
|
+
assert.strictEqual(ops.division(3, 2), 1.5);
|
|
85
|
+
assert.strictEqual(ops.division(6, "3"), 2);
|
|
86
|
+
assert.strictEqual(ops.division(2, 0), Infinity);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("ops.equal", () => {
|
|
90
|
+
assert(ops.equal(1, 1));
|
|
91
|
+
assert(!ops.equal(1, 2));
|
|
92
|
+
assert(ops.equal("1", 1));
|
|
93
|
+
assert(ops.equal("1", "1"));
|
|
94
|
+
assert(ops.equal(null, undefined));
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("ops.exponentiation", () => {
|
|
98
|
+
assert.strictEqual(ops.exponentiation(2, 3), 8);
|
|
99
|
+
assert.strictEqual(ops.exponentiation(2, 0), 1);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("ops.external looks up a value in scope and memoizes it", async () => {
|
|
26
103
|
let count = 0;
|
|
27
104
|
const tree = new ObjectTree({
|
|
28
105
|
get count() {
|
|
29
106
|
return ++count;
|
|
30
107
|
},
|
|
31
108
|
});
|
|
32
|
-
const code = createCode([ops.
|
|
109
|
+
const code = createCode([ops.external, "count", {}]);
|
|
33
110
|
const result = await evaluate.call(tree, code);
|
|
34
|
-
assert.
|
|
111
|
+
assert.strictEqual(result, 1);
|
|
35
112
|
const result2 = await evaluate.call(tree, code);
|
|
36
|
-
assert.
|
|
113
|
+
assert.strictEqual(result2, 1);
|
|
37
114
|
});
|
|
38
115
|
|
|
39
|
-
test("ops.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const code = createCode([ops.concat, "Hello, ", [ops.scope, "name"], "."]);
|
|
116
|
+
test("ops.greaterThan", () => {
|
|
117
|
+
assert(ops.greaterThan(5, 3));
|
|
118
|
+
assert(!ops.greaterThan(3, 3));
|
|
119
|
+
assert(ops.greaterThan("ab", "aa"));
|
|
120
|
+
});
|
|
45
121
|
|
|
46
|
-
|
|
47
|
-
assert.
|
|
122
|
+
test("ops.greaterThanOrEqual", () => {
|
|
123
|
+
assert(ops.greaterThanOrEqual(5, 3));
|
|
124
|
+
assert(ops.greaterThanOrEqual(3, 3));
|
|
125
|
+
assert(ops.greaterThanOrEqual("ab", "aa"));
|
|
48
126
|
});
|
|
49
127
|
|
|
50
128
|
test("ops.inherited searches inherited scope", async () => {
|
|
@@ -58,10 +136,17 @@ describe("ops", () => {
|
|
|
58
136
|
child.parent = parent;
|
|
59
137
|
const code = createCode([ops.inherited, "a"]);
|
|
60
138
|
const result = await evaluate.call(child, code);
|
|
61
|
-
assert.
|
|
139
|
+
assert.strictEqual(result, 1);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("ops.lambda defines a function with no inputs", async () => {
|
|
143
|
+
const code = createCode([ops.lambda, [], [ops.literal, "result"]]);
|
|
144
|
+
const fn = await evaluate.call(null, code);
|
|
145
|
+
const result = await fn.call();
|
|
146
|
+
assert.strictEqual(result, "result");
|
|
62
147
|
});
|
|
63
148
|
|
|
64
|
-
test("ops.lambda defines a function", async () => {
|
|
149
|
+
test("ops.lambda defines a function with underscore input", async () => {
|
|
65
150
|
const scope = new ObjectTree({
|
|
66
151
|
message: "Hello",
|
|
67
152
|
});
|
|
@@ -70,7 +155,7 @@ describe("ops", () => {
|
|
|
70
155
|
|
|
71
156
|
const fn = await evaluate.call(scope, code);
|
|
72
157
|
const result = await fn.call(scope);
|
|
73
|
-
assert.
|
|
158
|
+
assert.strictEqual(result, "Hello");
|
|
74
159
|
});
|
|
75
160
|
|
|
76
161
|
test("ops.lambda adds input parameters to scope", async () => {
|
|
@@ -81,7 +166,83 @@ describe("ops", () => {
|
|
|
81
166
|
]);
|
|
82
167
|
const fn = await evaluate.call(null, code);
|
|
83
168
|
const result = await fn("x", "y");
|
|
84
|
-
assert.
|
|
169
|
+
assert.strictEqual(result, "yx");
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test("ops.lessThan", () => {
|
|
173
|
+
assert(!ops.lessThan(5, 3));
|
|
174
|
+
assert(!ops.lessThan(3, 3));
|
|
175
|
+
assert(ops.lessThan("aa", "ab"));
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test("ops.lessThanOrEqual", () => {
|
|
179
|
+
assert(!ops.lessThanOrEqual(5, 3));
|
|
180
|
+
assert(ops.lessThanOrEqual(3, 3));
|
|
181
|
+
assert(ops.lessThanOrEqual("aa", "ab"));
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test("ops.logicalAnd", async () => {
|
|
185
|
+
assert.strictEqual(await ops.logicalAnd(true, trueFn), true);
|
|
186
|
+
assert.strictEqual(await ops.logicalAnd(true, falseFn), false);
|
|
187
|
+
assert.strictEqual(await ops.logicalAnd(false, trueFn), false);
|
|
188
|
+
assert.strictEqual(await ops.logicalAnd(false, falseFn), false);
|
|
189
|
+
|
|
190
|
+
assert.strictEqual(await ops.logicalAnd(true, "hi"), "hi");
|
|
191
|
+
|
|
192
|
+
// Short-circuiting
|
|
193
|
+
assert.strictEqual(await ops.logicalAnd(false, errorFn), false);
|
|
194
|
+
assert.strictEqual(await ops.logicalAnd(0, true), 0);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test("ops.logicalNot", async () => {
|
|
198
|
+
assert.strictEqual(await ops.logicalNot(true), false);
|
|
199
|
+
assert.strictEqual(await ops.logicalNot(false), true);
|
|
200
|
+
assert.strictEqual(await ops.logicalNot(0), true);
|
|
201
|
+
assert.strictEqual(await ops.logicalNot(1), false);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test("ops.logicalOr", async () => {
|
|
205
|
+
assert.strictEqual(await ops.logicalOr(true, trueFn), true);
|
|
206
|
+
assert.strictEqual(await ops.logicalOr(true, falseFn), true);
|
|
207
|
+
assert.strictEqual(await ops.logicalOr(false, trueFn), true);
|
|
208
|
+
assert.strictEqual(await ops.logicalOr(false, falseFn), false);
|
|
209
|
+
|
|
210
|
+
assert.strictEqual(await ops.logicalOr(false, "hi"), "hi");
|
|
211
|
+
|
|
212
|
+
// Short-circuiting
|
|
213
|
+
assert.strictEqual(await ops.logicalOr(true, errorFn), true);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
test("ops.multiplication multiplies two numbers", async () => {
|
|
217
|
+
assert.strictEqual(ops.multiplication(3, 4), 12);
|
|
218
|
+
assert.strictEqual(ops.multiplication(-3, 4), -12);
|
|
219
|
+
assert.strictEqual(ops.multiplication("3", 2), 6);
|
|
220
|
+
assert.strictEqual(ops.multiplication("foo", 2), NaN);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test("ops.notEqual", () => {
|
|
224
|
+
assert(!ops.notEqual(1, 1));
|
|
225
|
+
assert(ops.notEqual(1, 2));
|
|
226
|
+
assert(!ops.notEqual("1", 1));
|
|
227
|
+
assert(!ops.notEqual("1", "1"));
|
|
228
|
+
assert(!ops.notEqual(null, undefined));
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test("ops.notStrictEqual", () => {
|
|
232
|
+
assert(!ops.notStrictEqual(1, 1));
|
|
233
|
+
assert(ops.notStrictEqual(1, 2));
|
|
234
|
+
assert(ops.notStrictEqual("1", 1));
|
|
235
|
+
assert(!ops.notStrictEqual("1", "1"));
|
|
236
|
+
assert(ops.notStrictEqual(null, undefined));
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
test("ops.nullishCoalescing", async () => {
|
|
240
|
+
assert.strictEqual(await ops.nullishCoalescing(1, falseFn), 1);
|
|
241
|
+
assert.strictEqual(await ops.nullishCoalescing(null, trueFn), true);
|
|
242
|
+
assert.strictEqual(await ops.nullishCoalescing(undefined, trueFn), true);
|
|
243
|
+
|
|
244
|
+
// Short-circuiting
|
|
245
|
+
assert.strictEqual(await ops.nullishCoalescing(1, errorFn), 1);
|
|
85
246
|
});
|
|
86
247
|
|
|
87
248
|
test("ops.object instantiates an object", async () => {
|
|
@@ -96,8 +257,8 @@ describe("ops", () => {
|
|
|
96
257
|
]);
|
|
97
258
|
|
|
98
259
|
const result = await evaluate.call(scope, code);
|
|
99
|
-
assert.
|
|
100
|
-
assert.
|
|
260
|
+
assert.strictEqual(result.hello, "HELLO");
|
|
261
|
+
assert.strictEqual(result.world, "WORLD");
|
|
101
262
|
});
|
|
102
263
|
|
|
103
264
|
test("ops.object instantiates an array", async () => {
|
|
@@ -114,11 +275,60 @@ describe("ops", () => {
|
|
|
114
275
|
assert.deepEqual(result, ["Hello", 1, "WORLD"]);
|
|
115
276
|
});
|
|
116
277
|
|
|
278
|
+
test("ops.remainder calculates the remainder of two numbers", async () => {
|
|
279
|
+
assert.strictEqual(ops.remainder(13, 5), 3);
|
|
280
|
+
assert.strictEqual(ops.remainder(-13, 5), -3);
|
|
281
|
+
assert.strictEqual(ops.remainder(4, 2), 0);
|
|
282
|
+
assert.strictEqual(ops.remainder(-4, 2), -0);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
test("ops.shiftLeft", () => {
|
|
286
|
+
assert.strictEqual(ops.shiftLeft(5, 2), 20);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
test("ops.shiftRightSigned", () => {
|
|
290
|
+
assert.strictEqual(ops.shiftRightSigned(20, 2), 5);
|
|
291
|
+
assert.strictEqual(ops.shiftRightSigned(-20, 2), -5);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
test("ops.shiftRightUnsigned", () => {
|
|
295
|
+
assert.strictEqual(ops.shiftRightUnsigned(20, 2), 5);
|
|
296
|
+
assert.strictEqual(ops.shiftRightUnsigned(-5, 2), 1073741822);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
test("ops.strictEqual", () => {
|
|
300
|
+
assert(ops.strictEqual(1, 1));
|
|
301
|
+
assert(!ops.strictEqual(1, 2));
|
|
302
|
+
assert(!ops.strictEqual("1", 1));
|
|
303
|
+
assert(ops.strictEqual("1", "1"));
|
|
304
|
+
assert(!ops.strictEqual(null, undefined));
|
|
305
|
+
assert(ops.strictEqual(null, null));
|
|
306
|
+
assert(ops.strictEqual(undefined, undefined));
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
test("ops.subtraction subtracts two numbers", async () => {
|
|
310
|
+
assert.strictEqual(ops.subtraction(5, 3), 2);
|
|
311
|
+
assert.strictEqual(ops.subtraction(3.5, 5), -1.5);
|
|
312
|
+
assert.strictEqual(ops.subtraction(5, "hello"), NaN);
|
|
313
|
+
assert.strictEqual(ops.subtraction(5, true), 4);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
test("ops.unaryMinus", () => {
|
|
317
|
+
assert.strictEqual(ops.unaryMinus(4), -4);
|
|
318
|
+
assert.strictEqual(ops.unaryMinus(-4), 4);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
test("ops.unaryPlus", () => {
|
|
322
|
+
assert.strictEqual(ops.unaryPlus(1), 1);
|
|
323
|
+
assert.strictEqual(ops.unaryPlus(-1), -1);
|
|
324
|
+
assert.strictEqual(ops.unaryPlus(""), 0);
|
|
325
|
+
});
|
|
326
|
+
|
|
117
327
|
test("ops.unpack unpacks a value", async () => {
|
|
118
328
|
const fixture = new String("packed");
|
|
119
329
|
/** @type {any} */ (fixture).unpack = async () => "unpacked";
|
|
120
330
|
const result = await ops.unpack.call(null, fixture);
|
|
121
|
-
assert.
|
|
331
|
+
assert.strictEqual(result, "unpacked");
|
|
122
332
|
});
|
|
123
333
|
});
|
|
124
334
|
|
|
@@ -134,3 +344,15 @@ function createCode(array) {
|
|
|
134
344
|
};
|
|
135
345
|
return code;
|
|
136
346
|
}
|
|
347
|
+
|
|
348
|
+
function errorFn() {
|
|
349
|
+
throw new Error("This should not be called");
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function falseFn() {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function trueFn() {
|
|
357
|
+
return true;
|
|
358
|
+
}
|