shapedef 1.0.19 → 1.0.21

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 (56) hide show
  1. package/dist/cjs/index.d.ts +1 -0
  2. package/dist/cjs/index.d.ts.map +1 -1
  3. package/dist/cjs/index.js +1 -0
  4. package/dist/cjs/index.js.map +1 -1
  5. package/dist/cjs/shape.d.ts +8 -0
  6. package/dist/cjs/shape.d.ts.map +1 -1
  7. package/dist/cjs/shape.js +8 -0
  8. package/dist/cjs/shape.js.map +1 -1
  9. package/dist/cjs/turing/evaluate.d.ts +23 -0
  10. package/dist/cjs/turing/evaluate.d.ts.map +1 -0
  11. package/dist/cjs/turing/evaluate.js +195 -0
  12. package/dist/cjs/turing/evaluate.js.map +1 -0
  13. package/dist/cjs/turing/index.d.ts +4 -0
  14. package/dist/cjs/turing/index.d.ts.map +1 -0
  15. package/dist/cjs/turing/index.js +20 -0
  16. package/dist/cjs/turing/index.js.map +1 -0
  17. package/dist/cjs/turing/op.d.ts +5 -0
  18. package/dist/cjs/turing/op.d.ts.map +1 -0
  19. package/dist/cjs/turing/op.js +3 -0
  20. package/dist/cjs/turing/op.js.map +1 -0
  21. package/dist/cjs/turing/turing.d.ts +59 -0
  22. package/dist/cjs/turing/turing.d.ts.map +1 -0
  23. package/dist/cjs/turing/turing.js +75 -0
  24. package/dist/cjs/turing/turing.js.map +1 -0
  25. package/dist/esm/index.d.ts +1 -0
  26. package/dist/esm/index.d.ts.map +1 -1
  27. package/dist/esm/index.js +1 -0
  28. package/dist/esm/index.js.map +1 -1
  29. package/dist/esm/shape.d.ts +8 -0
  30. package/dist/esm/shape.d.ts.map +1 -1
  31. package/dist/esm/shape.js +8 -0
  32. package/dist/esm/shape.js.map +1 -1
  33. package/dist/esm/turing/evaluate.d.ts +23 -0
  34. package/dist/esm/turing/evaluate.d.ts.map +1 -0
  35. package/dist/esm/turing/evaluate.js +195 -0
  36. package/dist/esm/turing/evaluate.js.map +1 -0
  37. package/dist/esm/turing/index.d.ts +4 -0
  38. package/dist/esm/turing/index.d.ts.map +1 -0
  39. package/dist/esm/turing/index.js +20 -0
  40. package/dist/esm/turing/index.js.map +1 -0
  41. package/dist/esm/turing/op.d.ts +5 -0
  42. package/dist/esm/turing/op.d.ts.map +1 -0
  43. package/dist/esm/turing/op.js +3 -0
  44. package/dist/esm/turing/op.js.map +1 -0
  45. package/dist/esm/turing/turing.d.ts +59 -0
  46. package/dist/esm/turing/turing.d.ts.map +1 -0
  47. package/dist/esm/turing/turing.js +75 -0
  48. package/dist/esm/turing/turing.js.map +1 -0
  49. package/package.json +1 -1
  50. package/src/index.ts +1 -0
  51. package/src/shape.ts +16 -0
  52. package/src/turing/evaluate.test.ts +407 -0
  53. package/src/turing/evaluate.ts +245 -0
  54. package/src/turing/index.ts +3 -0
  55. package/src/turing/op.ts +14 -0
  56. package/src/turing/turing.ts +160 -0
@@ -0,0 +1,407 @@
1
+ import { describe, expect } from "vitest";
2
+ import { turing } from "./turing";
3
+ import { createEvalContext, EvalContext, turingEvaluate } from "./evaluate";
4
+
5
+ const ctx = () => new EvalContext();
6
+
7
+ describe("turingEvaluate", (it) => {
8
+ describe("literals", (it) => {
9
+ it("returns number as-is", () => {
10
+ expect(turingEvaluate(ctx(), turing.number(42))).toEqual({
11
+ ok: true,
12
+ node: turing.number(42),
13
+ });
14
+ });
15
+
16
+ it("returns str as-is", () => {
17
+ expect(turingEvaluate(ctx(), turing.str("hello"))).toEqual({
18
+ ok: true,
19
+ node: turing.str("hello"),
20
+ });
21
+ });
22
+
23
+ it("returns bool as-is", () => {
24
+ expect(turingEvaluate(ctx(), turing.bool(true))).toEqual({
25
+ ok: true,
26
+ node: turing.bool(true),
27
+ });
28
+ });
29
+
30
+ it("returns nil as-is", () => {
31
+ expect(turingEvaluate(ctx(), turing.nil())).toEqual({
32
+ ok: true,
33
+ node: turing.nil(),
34
+ });
35
+ });
36
+
37
+ it("returns date as-is", () => {
38
+ const d = new Date("2024-01-01");
39
+ expect(turingEvaluate(ctx(), turing.date(d))).toEqual({
40
+ ok: true,
41
+ node: turing.date(d),
42
+ });
43
+ });
44
+ });
45
+
46
+ describe("ref", (it) => {
47
+ it("resolves a defined variable", () => {
48
+ const c = createEvalContext({ x: 10 });
49
+ expect(turingEvaluate(c, turing.ref("x"))).toMatchObject({
50
+ ok: true,
51
+ node: turing.number(10),
52
+ });
53
+ });
54
+
55
+ it("returns error for undefined variable", () => {
56
+ expect(turingEvaluate(ctx(), turing.ref("missing"))).toMatchObject({
57
+ ok: false,
58
+ });
59
+ });
60
+ });
61
+
62
+ describe("binop: arithmetic", (it) => {
63
+ it("adds numbers", () => {
64
+ expect(
65
+ turingEvaluate(
66
+ ctx(),
67
+ turing.binop(turing.number(1), "add", turing.number(2)),
68
+ ),
69
+ ).toEqual({ ok: true, node: turing.number(3) });
70
+ });
71
+
72
+ it("concatenates strings with add", () => {
73
+ expect(
74
+ turingEvaluate(
75
+ ctx(),
76
+ turing.binop(turing.str("foo"), "add", turing.str("bar")),
77
+ ),
78
+ ).toEqual({ ok: true, node: turing.str("foobar") });
79
+ });
80
+
81
+ it("rejects add on mismatched types", () => {
82
+ expect(
83
+ turingEvaluate(
84
+ ctx(),
85
+ turing.binop(turing.number(1), "add", turing.str("x")),
86
+ ),
87
+ ).toMatchObject({ ok: false });
88
+ });
89
+
90
+ it("subtracts numbers", () => {
91
+ expect(
92
+ turingEvaluate(
93
+ ctx(),
94
+ turing.binop(turing.number(10), "sub", turing.number(3)),
95
+ ),
96
+ ).toEqual({ ok: true, node: turing.number(7) });
97
+ });
98
+
99
+ it("removes substring with sub", () => {
100
+ expect(
101
+ turingEvaluate(
102
+ ctx(),
103
+ turing.binop(turing.str("foobar"), "sub", turing.str("bar")),
104
+ ),
105
+ ).toEqual({ ok: true, node: turing.str("foo") });
106
+ });
107
+
108
+ it("multiplies numbers", () => {
109
+ expect(
110
+ turingEvaluate(
111
+ ctx(),
112
+ turing.binop(turing.number(4), "mul", turing.number(3)),
113
+ ),
114
+ ).toEqual({ ok: true, node: turing.number(12) });
115
+ });
116
+
117
+ it("divides numbers", () => {
118
+ expect(
119
+ turingEvaluate(
120
+ ctx(),
121
+ turing.binop(turing.number(10), "div", turing.number(4)),
122
+ ),
123
+ ).toEqual({ ok: true, node: turing.number(2.5) });
124
+ });
125
+
126
+ it("computes modulo", () => {
127
+ expect(
128
+ turingEvaluate(
129
+ ctx(),
130
+ turing.binop(turing.number(10), "mod", turing.number(3)),
131
+ ),
132
+ ).toEqual({ ok: true, node: turing.number(1) });
133
+ });
134
+
135
+ it("rejects mul on non-numbers", () => {
136
+ expect(
137
+ turingEvaluate(
138
+ ctx(),
139
+ turing.binop(turing.str("a"), "mul", turing.str("b")),
140
+ ),
141
+ ).toMatchObject({ ok: false });
142
+ });
143
+ });
144
+
145
+ describe("binop: boolean logic", (it) => {
146
+ it("and: true && true", () => {
147
+ expect(
148
+ turingEvaluate(
149
+ ctx(),
150
+ turing.binop(turing.bool(true), "and", turing.bool(true)),
151
+ ),
152
+ ).toEqual({ ok: true, node: turing.bool(true) });
153
+ });
154
+
155
+ it("and: true && false", () => {
156
+ expect(
157
+ turingEvaluate(
158
+ ctx(),
159
+ turing.binop(turing.bool(true), "and", turing.bool(false)),
160
+ ),
161
+ ).toEqual({ ok: true, node: turing.bool(false) });
162
+ });
163
+
164
+ it("or: false || true", () => {
165
+ expect(
166
+ turingEvaluate(
167
+ ctx(),
168
+ turing.binop(turing.bool(false), "or", turing.bool(true)),
169
+ ),
170
+ ).toEqual({ ok: true, node: turing.bool(true) });
171
+ });
172
+
173
+ it("or: false || false", () => {
174
+ expect(
175
+ turingEvaluate(
176
+ ctx(),
177
+ turing.binop(turing.bool(false), "or", turing.bool(false)),
178
+ ),
179
+ ).toEqual({ ok: true, node: turing.bool(false) });
180
+ });
181
+
182
+ it("rejects and on non-bools", () => {
183
+ expect(
184
+ turingEvaluate(
185
+ ctx(),
186
+ turing.binop(turing.number(1), "and", turing.number(0)),
187
+ ),
188
+ ).toMatchObject({ ok: false });
189
+ });
190
+ });
191
+
192
+ describe("binop: equality", (it) => {
193
+ it("eq: equal numbers", () => {
194
+ expect(
195
+ turingEvaluate(
196
+ ctx(),
197
+ turing.binop(turing.number(1), "eq", turing.number(1)),
198
+ ),
199
+ ).toEqual({ ok: true, node: turing.bool(true) });
200
+ });
201
+
202
+ it("eq: unequal numbers", () => {
203
+ expect(
204
+ turingEvaluate(
205
+ ctx(),
206
+ turing.binop(turing.number(1), "eq", turing.number(2)),
207
+ ),
208
+ ).toEqual({ ok: true, node: turing.bool(false) });
209
+ });
210
+
211
+ it("eq: equal strings", () => {
212
+ expect(
213
+ turingEvaluate(
214
+ ctx(),
215
+ turing.binop(turing.str("a"), "eq", turing.str("a")),
216
+ ),
217
+ ).toEqual({ ok: true, node: turing.bool(true) });
218
+ });
219
+
220
+ it("eq: equal bools", () => {
221
+ expect(
222
+ turingEvaluate(
223
+ ctx(),
224
+ turing.binop(turing.bool(false), "eq", turing.bool(false)),
225
+ ),
226
+ ).toEqual({ ok: true, node: turing.bool(true) });
227
+ });
228
+
229
+ it("eq: equal dates", () => {
230
+ const d = new Date("2024-06-01");
231
+ expect(
232
+ turingEvaluate(
233
+ ctx(),
234
+ turing.binop(turing.date(d), "eq", turing.date(new Date(d))),
235
+ ),
236
+ ).toEqual({ ok: true, node: turing.bool(true) });
237
+ });
238
+
239
+ it("eq: different types returns false", () => {
240
+ expect(
241
+ turingEvaluate(
242
+ ctx(),
243
+ turing.binop(turing.number(1), "eq", turing.str("1")),
244
+ ),
245
+ ).toEqual({ ok: true, node: turing.bool(false) });
246
+ });
247
+
248
+ it("eq: nil == nil", () => {
249
+ expect(
250
+ turingEvaluate(ctx(), turing.binop(turing.nil(), "eq", turing.nil())),
251
+ ).toEqual({ ok: true, node: turing.bool(true) });
252
+ });
253
+
254
+ it("neq: unequal numbers", () => {
255
+ expect(
256
+ turingEvaluate(
257
+ ctx(),
258
+ turing.binop(turing.number(1), "neq", turing.number(2)),
259
+ ),
260
+ ).toEqual({ ok: true, node: turing.bool(true) });
261
+ });
262
+
263
+ it("neq: equal numbers", () => {
264
+ expect(
265
+ turingEvaluate(
266
+ ctx(),
267
+ turing.binop(turing.number(5), "neq", turing.number(5)),
268
+ ),
269
+ ).toEqual({ ok: true, node: turing.bool(false) });
270
+ });
271
+
272
+ it("neq: different types returns true", () => {
273
+ expect(
274
+ turingEvaluate(
275
+ ctx(),
276
+ turing.binop(turing.number(1), "neq", turing.str("1")),
277
+ ),
278
+ ).toEqual({ ok: true, node: turing.bool(true) });
279
+ });
280
+ });
281
+
282
+ describe("binop: comparison", (it) => {
283
+ it("gt: 5 > 3", () => {
284
+ expect(
285
+ turingEvaluate(
286
+ ctx(),
287
+ turing.binop(turing.number(5), "gt", turing.number(3)),
288
+ ),
289
+ ).toEqual({ ok: true, node: turing.bool(true) });
290
+ });
291
+
292
+ it("gt: 3 > 5", () => {
293
+ expect(
294
+ turingEvaluate(
295
+ ctx(),
296
+ turing.binop(turing.number(3), "gt", turing.number(5)),
297
+ ),
298
+ ).toEqual({ ok: true, node: turing.bool(false) });
299
+ });
300
+
301
+ it("gte: 5 >= 5", () => {
302
+ expect(
303
+ turingEvaluate(
304
+ ctx(),
305
+ turing.binop(turing.number(5), "gte", turing.number(5)),
306
+ ),
307
+ ).toEqual({ ok: true, node: turing.bool(true) });
308
+ });
309
+
310
+ it("lt: 2 < 9", () => {
311
+ expect(
312
+ turingEvaluate(
313
+ ctx(),
314
+ turing.binop(turing.number(2), "lt", turing.number(9)),
315
+ ),
316
+ ).toEqual({ ok: true, node: turing.bool(true) });
317
+ });
318
+
319
+ it("lte: 9 <= 9", () => {
320
+ expect(
321
+ turingEvaluate(
322
+ ctx(),
323
+ turing.binop(turing.number(9), "lte", turing.number(9)),
324
+ ),
325
+ ).toEqual({ ok: true, node: turing.bool(true) });
326
+ });
327
+
328
+ it("rejects gt on non-numbers", () => {
329
+ expect(
330
+ turingEvaluate(
331
+ ctx(),
332
+ turing.binop(turing.str("a"), "gt", turing.str("b")),
333
+ ),
334
+ ).toMatchObject({ ok: false });
335
+ });
336
+ });
337
+
338
+ describe("binop: regex-match", (it) => {
339
+ it("matches a pattern", () => {
340
+ expect(
341
+ turingEvaluate(
342
+ ctx(),
343
+ turing.binop(
344
+ turing.str("hello world"),
345
+ "regex-match",
346
+ turing.str("world"),
347
+ ),
348
+ ),
349
+ ).toEqual({ ok: true, node: turing.bool(true) });
350
+ });
351
+
352
+ it("returns false when pattern does not match", () => {
353
+ expect(
354
+ turingEvaluate(
355
+ ctx(),
356
+ turing.binop(
357
+ turing.str("hello"),
358
+ "regex-match",
359
+ turing.str("^world"),
360
+ ),
361
+ ),
362
+ ).toEqual({ ok: true, node: turing.bool(false) });
363
+ });
364
+
365
+ it("rejects regex-match on non-strings", () => {
366
+ expect(
367
+ turingEvaluate(
368
+ ctx(),
369
+ turing.binop(turing.number(42), "regex-match", turing.str("\\d+")),
370
+ ),
371
+ ).toMatchObject({ ok: false });
372
+ });
373
+ });
374
+
375
+ describe("nested expressions", (it) => {
376
+ it("evaluates nested binops", () => {
377
+ // (2 + 3) * 4 = 20
378
+ const expr = turing.binop(
379
+ turing.binop(turing.number(2), "add", turing.number(3)),
380
+ "mul",
381
+ turing.number(4),
382
+ );
383
+ expect(turingEvaluate(ctx(), expr)).toEqual({
384
+ ok: true,
385
+ node: turing.number(20),
386
+ });
387
+ });
388
+
389
+ it("propagates left-side error", () => {
390
+ const expr = turing.binop(
391
+ turing.binop(turing.str("a"), "mul", turing.str("b")),
392
+ "add",
393
+ turing.number(1),
394
+ );
395
+ expect(turingEvaluate(ctx(), expr)).toMatchObject({ ok: false });
396
+ });
397
+
398
+ it("resolves refs within binop", () => {
399
+ const c = createEvalContext({ a: 5, b: 3 });
400
+ const expr = turing.binop(turing.ref("a"), "sub", turing.ref("b"));
401
+ expect(turingEvaluate(c, expr)).toEqual({
402
+ ok: true,
403
+ node: turing.number(2),
404
+ });
405
+ });
406
+ });
407
+ });
@@ -0,0 +1,245 @@
1
+ import { turing, TuringNode } from "./turing";
2
+
3
+ export class EvalFrame {
4
+ symbols: Map<string, TuringNode> = new Map();
5
+
6
+ constructor(symbols?: Record<string, TuringNode> | Map<string, TuringNode>) {
7
+ if (symbols) {
8
+ this.extend(symbols);
9
+ }
10
+ }
11
+
12
+ extend(symbols: Record<string, TuringNode> | Map<string, TuringNode>) {
13
+ if (symbols instanceof Map) {
14
+ for (const [k, v] of Array.from(symbols.entries())) {
15
+ this.symbols.set(k, v);
16
+ }
17
+ } else {
18
+ for (const [k, v] of Object.entries(symbols)) {
19
+ this.symbols.set(k, v);
20
+ }
21
+ }
22
+ }
23
+ }
24
+
25
+ export class EvalContext {
26
+ frames: EvalFrame[] = [new EvalFrame()];
27
+
28
+ get frame(): EvalFrame {
29
+ if (this.frames.length <= 0) throw new Error("Out of frames");
30
+ const frame = this.frames[this.frames.length - 1]!;
31
+ return frame;
32
+ }
33
+
34
+ beginFrame(): EvalFrame {
35
+ const frame = new EvalFrame();
36
+ this.frames.push(frame);
37
+ return frame;
38
+ }
39
+
40
+ endFrame(): EvalFrame {
41
+ const frame = this.frames.pop() || null;
42
+ if (!frame) throw new Error("endFrame(): Out of frames");
43
+ return frame;
44
+ }
45
+
46
+ with<T>(fn: (frame: EvalFrame) => T): T {
47
+ const frame = this.beginFrame();
48
+ const r = fn(frame);
49
+ this.endFrame();
50
+ return r;
51
+ }
52
+ }
53
+
54
+ export const createEvalContext = (
55
+ vars: Record<string, unknown>,
56
+ ): EvalContext => {
57
+ const ctx = new EvalContext();
58
+ ctx.frame.extend(
59
+ Object.assign(
60
+ {},
61
+ ...Object.entries(vars).map(([k, v]) => {
62
+ return { [k]: turing.auto(v) };
63
+ }),
64
+ ),
65
+ );
66
+ return ctx;
67
+ };
68
+
69
+ export type EvalResult =
70
+ | {
71
+ ok: true;
72
+ node: TuringNode;
73
+ }
74
+ | {
75
+ ok: false;
76
+ error: string;
77
+ };
78
+
79
+ const ok = (node: TuringNode): Extract<EvalResult, { ok: true }> => ({
80
+ ok: true,
81
+ node,
82
+ });
83
+
84
+ const err = (error: string): Extract<EvalResult, { ok: false }> => ({
85
+ ok: false,
86
+ error,
87
+ });
88
+
89
+ export const turingEvaluate = (
90
+ ctx: EvalContext,
91
+ node: TuringNode,
92
+ ): EvalResult => {
93
+ switch (node.type) {
94
+ case "number":
95
+ case "date":
96
+ case "bool":
97
+ case "nil":
98
+ case "str":
99
+ return ok(node);
100
+ case "ref": {
101
+ const value = ctx.frame.symbols.get(node.name);
102
+ if (!value) return err(`Undefined variable: ${node.name}`);
103
+ return turingEvaluate(ctx, value);
104
+ }
105
+ case "binop": {
106
+ const leftR = turingEvaluate(ctx, node.left);
107
+ if (!leftR.ok) return leftR;
108
+ const rightR = turingEvaluate(ctx, node.right);
109
+ if (!rightR.ok) return rightR;
110
+
111
+ const left = leftR.node;
112
+ const right = rightR.node;
113
+ const op = node.op;
114
+
115
+ switch (op) {
116
+ case "add": {
117
+ if (left.type === "number" && right.type === "number")
118
+ return ok(turing.number(left.value + right.value));
119
+ if (left.type === "str" && right.type === "str")
120
+ return ok(turing.str(left.value + right.value));
121
+ return err(
122
+ `Invalid operation '${op}' for (${left.type}, ${right.type})`,
123
+ );
124
+ }
125
+ case "sub": {
126
+ if (left.type === "number" && right.type === "number")
127
+ return ok(turing.number(left.value - right.value));
128
+ if (left.type === "str" && right.type === "str")
129
+ return ok(turing.str(left.value.replace(right.value, "")));
130
+ return err(
131
+ `Invalid operation '${op}' for (${left.type}, ${right.type})`,
132
+ );
133
+ }
134
+ case "mul": {
135
+ if (left.type === "number" && right.type === "number")
136
+ return ok(turing.number(left.value * right.value));
137
+ return err(
138
+ `Invalid operation '${op}' for (${left.type}, ${right.type})`,
139
+ );
140
+ }
141
+ case "div": {
142
+ if (left.type === "number" && right.type === "number")
143
+ return ok(turing.number(left.value / right.value));
144
+ return err(
145
+ `Invalid operation '${op}' for (${left.type}, ${right.type})`,
146
+ );
147
+ }
148
+ case "mod": {
149
+ if (left.type === "number" && right.type === "number")
150
+ return ok(turing.number(left.value % right.value));
151
+ return err(
152
+ `Invalid operation '${op}' for (${left.type}, ${right.type})`,
153
+ );
154
+ }
155
+ case "and": {
156
+ if (left.type === "bool" && right.type === "bool")
157
+ return ok(turing.bool(left.value && right.value));
158
+ return err(
159
+ `Invalid operation '${op}' for (${left.type}, ${right.type})`,
160
+ );
161
+ }
162
+ case "or": {
163
+ if (left.type === "bool" && right.type === "bool")
164
+ return ok(turing.bool(left.value || right.value));
165
+ return err(
166
+ `Invalid operation '${op}' for (${left.type}, ${right.type})`,
167
+ );
168
+ }
169
+ case "eq": {
170
+ if (left.type !== right.type) return ok(turing.bool(false));
171
+ if (left.type === "bool" && right.type === "bool")
172
+ return ok(turing.bool(left.value === right.value));
173
+ if (left.type === "number" && right.type === "number")
174
+ return ok(turing.bool(left.value === right.value));
175
+ if (left.type === "str" && right.type === "str")
176
+ return ok(turing.bool(left.value === right.value));
177
+ if (left.type === "date" && right.type === "date")
178
+ return ok(
179
+ turing.bool(left.value.getTime() === right.value.getTime()),
180
+ );
181
+ if (left.type === "nil" && right.type === "nil")
182
+ return ok(turing.bool(left.value === right.value));
183
+ return err(
184
+ `Invalid operation '${op}' for (${left.type}, ${right.type})`,
185
+ );
186
+ }
187
+ case "neq": {
188
+ if (left.type !== right.type) return ok(turing.bool(true));
189
+ if (left.type === "bool" && right.type === "bool")
190
+ return ok(turing.bool(left.value !== right.value));
191
+ if (left.type === "number" && right.type === "number")
192
+ return ok(turing.bool(left.value !== right.value));
193
+ if (left.type === "str" && right.type === "str")
194
+ return ok(turing.bool(left.value !== right.value));
195
+ if (left.type === "date" && right.type === "date")
196
+ return ok(
197
+ turing.bool(left.value.getTime() !== right.value.getTime()),
198
+ );
199
+ if (left.type === "nil" && right.type === "nil")
200
+ return ok(turing.bool(left.value !== right.value));
201
+ return err(
202
+ `Invalid operation '${op}' for (${left.type}, ${right.type})`,
203
+ );
204
+ }
205
+ case "gt": {
206
+ if (left.type === "number" && right.type === "number")
207
+ return ok(turing.bool(left.value > right.value));
208
+ return err(
209
+ `Invalid operation '${op}' for (${left.type}, ${right.type})`,
210
+ );
211
+ }
212
+ case "gte": {
213
+ if (left.type === "number" && right.type === "number")
214
+ return ok(turing.bool(left.value >= right.value));
215
+ return err(
216
+ `Invalid operation '${op}' for (${left.type}, ${right.type})`,
217
+ );
218
+ }
219
+ case "lt": {
220
+ if (left.type === "number" && right.type === "number")
221
+ return ok(turing.bool(left.value < right.value));
222
+ return err(
223
+ `Invalid operation '${op}' for (${left.type}, ${right.type})`,
224
+ );
225
+ }
226
+ case "lte": {
227
+ if (left.type === "number" && right.type === "number")
228
+ return ok(turing.bool(left.value <= right.value));
229
+ return err(
230
+ `Invalid operation '${op}' for (${left.type}, ${right.type})`,
231
+ );
232
+ }
233
+ case "regex-match": {
234
+ if (left.type === "str" && right.type === "str") {
235
+ const reg = new RegExp(right.value);
236
+ return ok(turing.bool(reg.test(left.value)));
237
+ }
238
+ return err(
239
+ `Invalid operation '${op}' for (${left.type}, ${right.type})`,
240
+ );
241
+ }
242
+ }
243
+ }
244
+ }
245
+ };
@@ -0,0 +1,3 @@
1
+ export * from "./op";
2
+ export * from "./turing";
3
+ export * from "./evaluate";
@@ -0,0 +1,14 @@
1
+ export type TuringOpCompare =
2
+ | "eq"
3
+ | "neq"
4
+ | "gt"
5
+ | "gte"
6
+ | "lt"
7
+ | "lte"
8
+ | "regex-match";
9
+
10
+ export type TuringOpBool = "or" | "and";
11
+
12
+ export type TuringOpArith = "add" | "sub" | "mul" | "div" | "mod";
13
+
14
+ export type TuringOp = TuringOpCompare | TuringOpBool | TuringOpArith;