@metta-ts/hyperon 1.0.0

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/dist/index.js ADDED
@@ -0,0 +1,1049 @@
1
+ // src/atoms.ts
2
+ import * as core2 from "@metta-ts/core";
3
+
4
+ // src/bindings.ts
5
+ import * as core from "@metta-ts/core";
6
+ var Bindings = class _Bindings {
7
+ /** The underlying immutable core relations. Replaced (not mutated) by the mutating methods. */
8
+ constructor(rels = core.emptyBindings) {
9
+ this.rels = rels;
10
+ }
11
+ rels;
12
+ /** The core relations backing this frame. */
13
+ raw() {
14
+ return this.rels;
15
+ }
16
+ /** The atom bound to a variable, if any. */
17
+ resolve(variable2) {
18
+ const a = core.lookupVal(this.rels, variable2.name());
19
+ return a === void 0 ? void 0 : Atom.fromCAtom(a);
20
+ }
21
+ /** Bind a variable to an atom; returns `true` (the binding is recorded). */
22
+ addVarBinding(variable2, atom) {
23
+ this.rels = core.addValRaw(this.rels, variable2.name(), atom.catom);
24
+ return true;
25
+ }
26
+ /** Python alias of {@link addVarBinding}. */
27
+ add_var_binding(variable2, atom) {
28
+ return this.addVarBinding(variable2, atom);
29
+ }
30
+ /** Assert that two variables are equal (`$a = $b`); returns `true`. */
31
+ addVarEquality(a, b) {
32
+ this.rels = core.addEqRaw(this.rels, a.name(), b.name());
33
+ return true;
34
+ }
35
+ /** Python alias of {@link addVarEquality}. */
36
+ add_var_equality(a, b) {
37
+ return this.addVarEquality(a, b);
38
+ }
39
+ /** True when this frame has no associations. */
40
+ isEmpty() {
41
+ return this.rels.length === 0;
42
+ }
43
+ /** Python alias of {@link isEmpty}. */
44
+ is_empty() {
45
+ return this.isEmpty();
46
+ }
47
+ /** The variable-atom pairs in this frame. */
48
+ pairs() {
49
+ const out = [];
50
+ for (const r of this.rels) if (r.tag === "val") out.push([V(r.x), Atom.fromCAtom(r.a)]);
51
+ return out;
52
+ }
53
+ /** Python alias of {@link pairs}. */
54
+ iterator() {
55
+ return this.pairs();
56
+ }
57
+ /** Keep only the associations for the given variables. */
58
+ narrowVars(vars) {
59
+ const keep = new Set(vars.map((v) => v.name()));
60
+ const kept = this.rels.filter((r) => r.tag === "val" && keep.has(r.x));
61
+ return new _Bindings(kept);
62
+ }
63
+ /** Python alias of {@link narrowVars}. */
64
+ narrow_vars(vars) {
65
+ return this.narrowVars(vars);
66
+ }
67
+ /** Merge with another frame, yielding the consistent combinations as a {@link BindingsSet}. */
68
+ merge(other) {
69
+ return new BindingsSet(core.merge(this.rels, other.rels).map((b) => new _Bindings(b)));
70
+ }
71
+ /** A copy of this frame (the core relations are immutable, so the copy is independent). */
72
+ clone() {
73
+ return new _Bindings(this.rels);
74
+ }
75
+ equals(other) {
76
+ const a = this.pairs();
77
+ const b = other.pairs();
78
+ if (a.length !== b.length) return false;
79
+ return a.every(([v, at]) => {
80
+ const m = b.find(([v2]) => v2.name() === v.name());
81
+ return m !== void 0 && core.atomEq(at.catom, m[1].catom);
82
+ });
83
+ }
84
+ toString() {
85
+ return `{ ${this.pairs().map(([v, a]) => `${v.toString()}: ${a.toString()}`).join(", ")} }`;
86
+ }
87
+ };
88
+ var BindingsSet = class _BindingsSet {
89
+ constructor(frames = [new Bindings()]) {
90
+ this.frames = frames;
91
+ }
92
+ frames;
93
+ /** A set with no frames (no valid match). */
94
+ static empty() {
95
+ return new _BindingsSet([]);
96
+ }
97
+ /** A set with a single empty frame (a match binding nothing). */
98
+ static single() {
99
+ return new _BindingsSet([new Bindings()]);
100
+ }
101
+ /** True when there are no frames. */
102
+ isEmpty() {
103
+ return this.frames.length === 0;
104
+ }
105
+ /** Python alias of {@link isEmpty}. */
106
+ is_empty() {
107
+ return this.isEmpty();
108
+ }
109
+ /** True when there is exactly one frame and it binds nothing. */
110
+ isSingle() {
111
+ return this.frames.length === 1 && this.frames[0].isEmpty();
112
+ }
113
+ /** Python alias of {@link isSingle}. */
114
+ is_single() {
115
+ return this.isSingle();
116
+ }
117
+ /** The frame at an index. */
118
+ get(index) {
119
+ return this.frames[index];
120
+ }
121
+ /** Iterate the frames. */
122
+ iterator() {
123
+ return this.frames;
124
+ }
125
+ /** Add a frame to the set. */
126
+ push(bindings) {
127
+ this.frames.push(bindings);
128
+ }
129
+ /** Bind a variable to an atom in every frame; returns `true`. */
130
+ addVarBinding(variable2, value) {
131
+ for (const f of this.frames) f.addVarBinding(variable2, value);
132
+ return true;
133
+ }
134
+ /** Python alias of {@link addVarBinding}. */
135
+ add_var_binding(variable2, value) {
136
+ return this.addVarBinding(variable2, value);
137
+ }
138
+ /** Assert that two variables are equal in every frame; returns `true`. */
139
+ addVarEquality(a, b) {
140
+ for (const f of this.frames) f.addVarEquality(a, b);
141
+ return true;
142
+ }
143
+ /** Python alias of {@link addVarEquality}. */
144
+ add_var_equality(a, b) {
145
+ return this.addVarEquality(a, b);
146
+ }
147
+ /** Merge another set (or frame) into this one (cartesian merge of frames). */
148
+ mergeInto(input) {
149
+ const others = input instanceof _BindingsSet ? input.frames : [input];
150
+ const merged = [];
151
+ for (const a of this.frames) for (const b of others) merged.push(...a.merge(b).frames);
152
+ this.frames.length = 0;
153
+ this.frames.push(...merged);
154
+ }
155
+ /** Python alias of {@link mergeInto}. */
156
+ merge_into(input) {
157
+ this.mergeInto(input);
158
+ }
159
+ /** A copy of this set. */
160
+ clone() {
161
+ return new _BindingsSet(this.frames.map((f) => f.clone()));
162
+ }
163
+ toString() {
164
+ return `[ ${this.frames.map((f) => f.toString()).join(", ")} ]`;
165
+ }
166
+ };
167
+
168
+ // src/atoms.ts
169
+ var Atom = class _Atom {
170
+ constructor(catom) {
171
+ this.catom = catom;
172
+ }
173
+ catom;
174
+ /** Wrap a core atom in the matching subclass. */
175
+ static fromCAtom(c) {
176
+ switch (c.kind) {
177
+ case "sym":
178
+ return new SymbolAtom(c);
179
+ case "var":
180
+ return new VariableAtom2(c);
181
+ case "expr":
182
+ return new ExpressionAtom(c);
183
+ case "gnd":
184
+ return new GroundedAtom(c);
185
+ }
186
+ }
187
+ /** The metatype (kind) of this atom. */
188
+ metatype() {
189
+ return core2.metaType(this.catom);
190
+ }
191
+ /** Python alias of {@link metatype}. */
192
+ get_metatype() {
193
+ return this.metatype();
194
+ }
195
+ /** Structural equality with another atom. */
196
+ equals(other) {
197
+ return core2.atomEq(this.catom, other.catom);
198
+ }
199
+ /** MeTTa source rendering. */
200
+ toString() {
201
+ return core2.format(this.catom);
202
+ }
203
+ /** This atom and all descendants, depth-first (Hyperon `iterate`). */
204
+ iterate() {
205
+ const out = [];
206
+ const go = (c) => {
207
+ out.push(_Atom.fromCAtom(c));
208
+ if (c.kind === "expr") for (const it of c.items) go(it);
209
+ };
210
+ go(this.catom);
211
+ return out;
212
+ }
213
+ /** Match this atom against another, returning every resulting binding frame. */
214
+ matchAtom(other) {
215
+ return new BindingsSet(core2.matchAtoms(this.catom, other.catom).map((b) => new Bindings(b)));
216
+ }
217
+ /** Python alias of {@link matchAtom}. */
218
+ match_atom(other) {
219
+ return this.matchAtom(other);
220
+ }
221
+ };
222
+ function assertKind(catom, kind, cls) {
223
+ if (catom.kind !== kind) throw new Error(`${cls} expects a '${kind}' atom, got '${catom.kind}'`);
224
+ }
225
+ var SymbolAtom = class extends Atom {
226
+ /** Wrap a core symbol atom. */
227
+ constructor(catom) {
228
+ assertKind(catom, "sym", "SymbolAtom");
229
+ super(catom);
230
+ }
231
+ /** The symbol's name. */
232
+ name() {
233
+ return this.catom.name;
234
+ }
235
+ /** Python alias of {@link name}. */
236
+ get_name() {
237
+ return this.name();
238
+ }
239
+ };
240
+ var VariableAtom2 = class _VariableAtom extends Atom {
241
+ /** Wrap a core variable atom. */
242
+ constructor(catom) {
243
+ assertKind(catom, "var", "VariableAtom");
244
+ super(catom);
245
+ }
246
+ /** The variable's name (without the leading `$`). */
247
+ name() {
248
+ return this.catom.name;
249
+ }
250
+ /** Python alias of {@link name}. */
251
+ get_name() {
252
+ return this.name();
253
+ }
254
+ /** Construct a variable from a name (Hyperon `parse_name`). */
255
+ static parseName(name) {
256
+ return new _VariableAtom(core2.variable(name));
257
+ }
258
+ };
259
+ var ExpressionAtom = class extends Atom {
260
+ /** Wrap a core expression atom. */
261
+ constructor(catom) {
262
+ assertKind(catom, "expr", "ExpressionAtom");
263
+ super(catom);
264
+ }
265
+ /** The child atoms, in order. */
266
+ children() {
267
+ return this.catom.items.map(Atom.fromCAtom);
268
+ }
269
+ /** Python alias of {@link children}. */
270
+ get_children() {
271
+ return this.children();
272
+ }
273
+ };
274
+ var GroundedObject = class {
275
+ constructor(content, id) {
276
+ this.content = content;
277
+ this.id = id;
278
+ }
279
+ content;
280
+ id;
281
+ toString() {
282
+ return this.id ?? String(this.content);
283
+ }
284
+ /** A copy of this object (content is shared). */
285
+ copy() {
286
+ return this;
287
+ }
288
+ };
289
+ var ValueObject = class extends GroundedObject {
290
+ /** The wrapped value. */
291
+ get value() {
292
+ return this.content;
293
+ }
294
+ equals(other) {
295
+ return this.content === other.content;
296
+ }
297
+ };
298
+ var MatchableObject = class extends ValueObject {
299
+ /** Override to define matching behavior. Throws by default. */
300
+ match_(atom) {
301
+ throw new Error(`MatchableObject.match_ must be overridden (got ${atom.toString()})`);
302
+ }
303
+ };
304
+ var OperationObject = class extends GroundedObject {
305
+ constructor(opName, op, unwrap = true) {
306
+ super(op, opName);
307
+ this.opName = opName;
308
+ this.op = op;
309
+ this.unwrap = unwrap;
310
+ }
311
+ opName;
312
+ op;
313
+ unwrap;
314
+ /** Run the operation over the argument atoms. */
315
+ execute(...args) {
316
+ return this.op(...args);
317
+ }
318
+ };
319
+ var extCounter = 0;
320
+ var EXT_OBJECTS = /* @__PURE__ */ new Map();
321
+ function clearGroundedObjects() {
322
+ EXT_OBJECTS.clear();
323
+ }
324
+ var GroundedAtom = class extends Atom {
325
+ obj;
326
+ /** Wrap a core grounded atom, optionally remembering the JS object behind it. */
327
+ constructor(catom, obj) {
328
+ assertKind(catom, "gnd", "GroundedAtom");
329
+ super(catom);
330
+ this.obj = obj;
331
+ }
332
+ /** The wrapped object: the original `GroundedObject` if known, otherwise a `ValueObject` over the
333
+ * grounded value. The result is cached so repeated calls return the same instance. */
334
+ object() {
335
+ if (this.obj !== void 0) return this.obj;
336
+ const g = this.catom.value;
337
+ if (g.g === "ext") {
338
+ const o = EXT_OBJECTS.get(g.id);
339
+ if (o !== void 0) {
340
+ this.obj = o;
341
+ return o;
342
+ }
343
+ this.obj = new ValueObject(g.id);
344
+ return this.obj;
345
+ }
346
+ this.obj = new ValueObject(groundToJs(g));
347
+ return this.obj;
348
+ }
349
+ /** Python alias of {@link object}. */
350
+ get_object() {
351
+ return this.object();
352
+ }
353
+ /** The plain JS value behind this grounded atom, typed as `T`. */
354
+ jsValue() {
355
+ return this.object().content;
356
+ }
357
+ /** The grounded type atom. */
358
+ groundedType() {
359
+ return Atom.fromCAtom(this.catom.typ);
360
+ }
361
+ /** Python alias of {@link groundedType}. */
362
+ get_grounded_type() {
363
+ return this.groundedType();
364
+ }
365
+ };
366
+ function groundToJs(g) {
367
+ switch (g.g) {
368
+ case "int":
369
+ case "float":
370
+ return g.n;
371
+ case "str":
372
+ return g.s;
373
+ case "bool":
374
+ return g.b;
375
+ case "unit":
376
+ return void 0;
377
+ case "error":
378
+ return g.msg;
379
+ case "ext":
380
+ return EXT_OBJECTS.get(g.id);
381
+ }
382
+ }
383
+ var S = (name) => new SymbolAtom(core2.sym(name));
384
+ var V = (name) => new VariableAtom2(core2.variable(name));
385
+ var E = (...children) => new ExpressionAtom(core2.expr(children.map((c) => c.catom)));
386
+ function G(obj, type) {
387
+ const id = `gnd-${extCounter++}`;
388
+ EXT_OBJECTS.set(id, obj);
389
+ const kind = obj instanceof OperationObject ? "operation" : "value";
390
+ const typ = type?.catom ?? core2.sym("%Undefined%");
391
+ const exec = obj instanceof OperationObject ? (args) => obj.execute(...args.map(Atom.fromCAtom)).map((a) => a.catom) : void 0;
392
+ const match = obj instanceof MatchableObject ? (other) => obj.match_(Atom.fromCAtom(other)) : void 0;
393
+ return new GroundedAtom(core2.gnd({ g: "ext", kind, id }, typ, exec, match), obj);
394
+ }
395
+ function ValueAtom(value, typeName) {
396
+ if (typeName === void 0) {
397
+ if (typeof value === "number")
398
+ return new GroundedAtom(Number.isInteger(value) ? core2.gint(value) : core2.gfloat(value));
399
+ if (typeof value === "string") return new GroundedAtom(core2.gstr(value));
400
+ if (typeof value === "boolean") return new GroundedAtom(core2.gbool(value));
401
+ return G(new ValueObject(value));
402
+ }
403
+ return G(new ValueObject(value), Atom.fromCAtom(core2.sym(typeName)));
404
+ }
405
+ function OperationAtom(name, op, unwrap = true) {
406
+ return G(new OperationObject(name, op, unwrap));
407
+ }
408
+ var AtomType = {
409
+ /** `%Undefined%`. */
410
+ UNDEFINED: S("%Undefined%"),
411
+ /** `Type`. */
412
+ TYPE: S("Type"),
413
+ /** `Atom`. */
414
+ ATOM: S("Atom"),
415
+ /** `Symbol`. */
416
+ SYMBOL: S("Symbol"),
417
+ /** `Variable`. */
418
+ VARIABLE: S("Variable"),
419
+ /** `Expression`. */
420
+ EXPRESSION: S("Expression"),
421
+ /** `Grounded`. */
422
+ GROUNDED: S("Grounded"),
423
+ /** `Number`. */
424
+ NUMBER: S("Number"),
425
+ /** `Bool`. */
426
+ BOOL: S("Bool"),
427
+ /** `String`. */
428
+ STRING: S("String")
429
+ };
430
+ function friendlyTypeName(atom) {
431
+ const c = atom.catom;
432
+ if (c.kind === "gnd") {
433
+ switch (c.value.g) {
434
+ case "int":
435
+ return "Number (integer)";
436
+ case "float":
437
+ return "Number (float)";
438
+ case "str":
439
+ return "String";
440
+ case "bool":
441
+ return "Bool";
442
+ case "unit":
443
+ return "Unit";
444
+ case "error":
445
+ return "Error";
446
+ case "ext":
447
+ return "Grounded";
448
+ }
449
+ }
450
+ return atom.metatype();
451
+ }
452
+ function atomIsError(atom) {
453
+ return core2.isErrorAtom(atom.catom);
454
+ }
455
+ function atomsAreEquivalent(first, second) {
456
+ return core2.alphaEq(first.catom, second.catom);
457
+ }
458
+
459
+ // src/base.ts
460
+ import * as core3 from "@metta-ts/core";
461
+ var DEFAULT_FUEL = 1e5;
462
+ var SpaceRef = class {
463
+ constructor(space) {
464
+ this.space = space;
465
+ }
466
+ space;
467
+ /** Add an atom to the space. */
468
+ addAtom(atom) {
469
+ this.space.add(atom.catom);
470
+ }
471
+ /** Python alias of {@link addAtom}. */
472
+ add_atom(atom) {
473
+ this.addAtom(atom);
474
+ }
475
+ /** Remove an atom from the space; returns whether one was removed. */
476
+ removeAtom(atom) {
477
+ return this.space.remove(atom.catom);
478
+ }
479
+ /** Python alias of {@link removeAtom}. */
480
+ remove_atom(atom) {
481
+ return this.removeAtom(atom);
482
+ }
483
+ /** Every atom in the space. */
484
+ getAtoms() {
485
+ return this.space.atoms().map(Atom.fromCAtom);
486
+ }
487
+ /** Python alias of {@link getAtoms}. */
488
+ get_atoms() {
489
+ return this.getAtoms();
490
+ }
491
+ /** The number of atoms in the space. */
492
+ atomCount() {
493
+ return this.space.atoms().length;
494
+ }
495
+ /** Python alias of {@link atomCount}. */
496
+ atom_count() {
497
+ return this.atomCount();
498
+ }
499
+ /** Match a pattern against the space, returning the binding frames. */
500
+ query(pattern) {
501
+ return new BindingsSet(this.space.query(pattern.catom).map((b) => new Bindings(b)));
502
+ }
503
+ /** Match `pattern`, then instantiate `template` under each resulting binding. */
504
+ subst(pattern, template) {
505
+ return this.space.query(pattern.catom).map((b) => Atom.fromCAtom(core3.instantiate(b, template.catom)));
506
+ }
507
+ };
508
+ var GroundingSpace = class extends SpaceRef {
509
+ constructor() {
510
+ super(new core3.InMemorySpace());
511
+ }
512
+ };
513
+ var RunnerSpace = class {
514
+ constructor(onAdd, onRemove, list) {
515
+ this.onAdd = onAdd;
516
+ this.onRemove = onRemove;
517
+ this.list = list;
518
+ }
519
+ onAdd;
520
+ onRemove;
521
+ list;
522
+ add(atom) {
523
+ this.onAdd(atom);
524
+ }
525
+ remove(atom) {
526
+ return this.onRemove(atom);
527
+ }
528
+ query(pattern, freshen) {
529
+ const out = [];
530
+ for (const a of this.list()) {
531
+ const target2 = freshen ? freshen(a) : a;
532
+ for (const b of core3.matchAtoms(pattern, target2)) out.push(b);
533
+ }
534
+ return out;
535
+ }
536
+ atoms() {
537
+ return this.list();
538
+ }
539
+ };
540
+ var Tokenizer2 = class {
541
+ constructor(ctok = new core3.Tokenizer()) {
542
+ this.ctok = ctok;
543
+ }
544
+ ctok;
545
+ /** Register a token: text matching `regex` becomes the atom built by `constr`. */
546
+ registerToken(regex, constr) {
547
+ this.ctok.register(regex, (s) => constr(s).catom);
548
+ }
549
+ /** Python alias of {@link registerToken}. */
550
+ register_token(regex, constr) {
551
+ this.registerToken(regex, constr);
552
+ }
553
+ };
554
+ var SExprParser = class {
555
+ constructor(text) {
556
+ this.text = text;
557
+ }
558
+ text;
559
+ /** Parse the first atom (Hyperon `parse`). */
560
+ parse(tokenizer) {
561
+ const a = core3.parse(this.text, tokenizer.ctok);
562
+ return a === void 0 ? void 0 : Atom.fromCAtom(a);
563
+ }
564
+ /** Parse every top-level atom. */
565
+ parseAll(tokenizer) {
566
+ return core3.parseAll(this.text, tokenizer.ctok).map((t) => Atom.fromCAtom(t.atom));
567
+ }
568
+ };
569
+ var MeTTa = class {
570
+ gt;
571
+ tok;
572
+ env;
573
+ st;
574
+ // The single authoritative knowledge base (atoms added after the prelude). Both `run` and
575
+ // `space()` mutate this, and the interpreter's `env` is kept in lock-step with it.
576
+ kb = [];
577
+ _space;
578
+ constructor() {
579
+ this.gt = core3.stdTable();
580
+ this.tok = new Tokenizer2(standardTokenizerC());
581
+ this.env = core3.buildEnv([...core3.preludeAtoms(), ...core3.stdlibAtoms()], this.gt);
582
+ this.env.imports = core3.withBuiltinModules();
583
+ this.st = core3.initSt();
584
+ this._space = new SpaceRef(
585
+ new RunnerSpace(
586
+ (a) => this.addToKb(a),
587
+ (a) => this.removeFromKb(a),
588
+ () => this.kb
589
+ )
590
+ );
591
+ }
592
+ // Add an atom to the KB and the interpreter env together.
593
+ addToKb(atom) {
594
+ this.kb.push(atom);
595
+ core3.addAtomToEnv(this.env, atom);
596
+ }
597
+ // Remove an atom from the KB; rebuild env from the prelude + remaining KB so retraction is real
598
+ // (the env's rule/type indexes are derived, not incrementally removable).
599
+ removeFromKb(atom) {
600
+ const i = this.kb.findIndex((a) => core3.atomEq(a, atom));
601
+ if (i < 0) return false;
602
+ this.kb.splice(i, 1);
603
+ this.env = core3.buildEnv([...core3.preludeAtoms(), ...core3.stdlibAtoms(), ...this.kb], this.gt);
604
+ this.env.imports = core3.withBuiltinModules();
605
+ return true;
606
+ }
607
+ /** Run MeTTa source. Non-bang atoms extend the knowledge base; each `!`-query yields its results.
608
+ * Returns one atom list per `!`-query, in order. */
609
+ run(program, fuel = DEFAULT_FUEL) {
610
+ const parsed = core3.parseAll(program, this.tok.ctok);
611
+ const out = [];
612
+ for (const { atom, bang } of parsed) {
613
+ if (!bang) {
614
+ this.addToKb(atom);
615
+ continue;
616
+ }
617
+ const [pairs, st2] = core3.mettaEval(this.env, fuel, this.st, [], atom);
618
+ this.st = st2;
619
+ out.push(pairs.map((p) => Atom.fromCAtom(p[0])));
620
+ }
621
+ return out;
622
+ }
623
+ /** Run MeTTa source asynchronously, awaiting any async grounded operations (registered with
624
+ * {@link registerAsyncOperation}). Identical to {@link run} for a program with no async ops. */
625
+ async runAsync(program, fuel = DEFAULT_FUEL) {
626
+ const parsed = core3.parseAll(program, this.tok.ctok);
627
+ const out = [];
628
+ for (const { atom, bang } of parsed) {
629
+ if (!bang) {
630
+ this.addToKb(atom);
631
+ continue;
632
+ }
633
+ const [pairs, st2] = await core3.mettaEvalAsync(this.env, fuel, this.st, [], atom);
634
+ this.st = st2;
635
+ out.push(pairs.map((p) => Atom.fromCAtom(p[0])));
636
+ }
637
+ return out;
638
+ }
639
+ /** Register an async grounded operation callable from MeTTa source by `name` (resolved by the async
640
+ * runner). The function receives argument atoms and resolves to result atoms. A rejection becomes a
641
+ * MeTTa `(Error ...)` atom. Use it for I/O: fetch, a DAS query, a timer. */
642
+ registerAsyncOperation(name, op) {
643
+ this.env.agt.set(name, async (args) => {
644
+ try {
645
+ const results = await op(args.map(Atom.fromCAtom));
646
+ return { tag: "ok", results: results.map((a) => a.catom) };
647
+ } catch (e) {
648
+ return { tag: "runtimeError", msg: e instanceof Error ? e.message : String(e) };
649
+ }
650
+ });
651
+ }
652
+ /** Parse every top-level atom of a program. */
653
+ parseAll(program) {
654
+ return core3.parseAll(program, this.tok.ctok).map((t) => Atom.fromCAtom(t.atom));
655
+ }
656
+ /** Parse the first atom of a program. */
657
+ parseSingle(program) {
658
+ const a = core3.parse(program, this.tok.ctok);
659
+ return a === void 0 ? void 0 : Atom.fromCAtom(a);
660
+ }
661
+ /** Evaluate a single atom against the runner's knowledge base (Hyperon `evaluate_atom`); returns its
662
+ * results. Unlike `run`, it takes an atom rather than source text. */
663
+ evaluateAtom(atom, fuel = DEFAULT_FUEL) {
664
+ const [pairs, st2] = core3.mettaEval(this.env, fuel, this.st, [], atom.catom);
665
+ this.st = st2;
666
+ return pairs.map((p) => Atom.fromCAtom(p[0]));
667
+ }
668
+ /** Python alias of {@link evaluateAtom}. */
669
+ evaluate_atom(atom, fuel = DEFAULT_FUEL) {
670
+ return this.evaluateAtom(atom, fuel);
671
+ }
672
+ /** Evaluate a single atom, awaiting any async grounded operations reached during evaluation (those
673
+ * registered with {@link registerAsyncOperation}). Identical to {@link evaluateAtom} when no async op
674
+ * is reached. */
675
+ async evaluateAtomAsync(atom, fuel = DEFAULT_FUEL) {
676
+ const [pairs, st2] = await core3.mettaEvalAsync(this.env, fuel, this.st, [], atom.catom);
677
+ this.st = st2;
678
+ return pairs.map((p) => Atom.fromCAtom(p[0]));
679
+ }
680
+ /** The runner's top-level space. Atoms added through it reach the evaluator's knowledge base
681
+ * (same as a non-bang atom in `run`), and querying it sees what the evaluator sees. Removing an
682
+ * atom retracts it from evaluation too. */
683
+ space() {
684
+ return this._space;
685
+ }
686
+ /** The runner's tokenizer. */
687
+ tokenizer() {
688
+ return this.tok;
689
+ }
690
+ /** Register a custom token (text matching `regex` becomes `constr`'s atom). */
691
+ registerToken(regex, constr) {
692
+ this.tok.registerToken(regex, constr);
693
+ }
694
+ /** Register a symbol as a token that produces a fixed atom. */
695
+ registerAtom(name, atom) {
696
+ this.tok.registerToken(new RegExp(`^${escapeRegExp(name)}$`), () => atom);
697
+ }
698
+ /** Register a grounded operation callable from MeTTa source by `name`. The function receives argument
699
+ * atoms and returns result atoms. A thrown error becomes a MeTTa `(Error ...)` atom instead of
700
+ * crashing the run. Throw {@link IncorrectArgumentError} to leave the expression unevaluated so other
701
+ * rewrite rules can try (MeTTa's multiple dispatch on a type mismatch). */
702
+ registerOperation(name, op) {
703
+ this.gt.set(name, (args) => {
704
+ try {
705
+ return { tag: "ok", results: op(args.map(Atom.fromCAtom)).map((a) => a.catom) };
706
+ } catch (e) {
707
+ if (e instanceof IncorrectArgumentError)
708
+ return { tag: "incorrectArgument", msg: e.message };
709
+ return { tag: "runtimeError", msg: e instanceof Error ? e.message : String(e) };
710
+ }
711
+ });
712
+ }
713
+ /** Every type the runner infers for an atom (Hyperon `get_atom_types`). */
714
+ getAtomTypes(atom) {
715
+ return core3.getTypes(this.env, atom.catom).map(Atom.fromCAtom);
716
+ }
717
+ /** Python alias of {@link getAtomTypes}. */
718
+ get_atom_types(atom) {
719
+ return this.getAtomTypes(atom);
720
+ }
721
+ };
722
+ var IncorrectArgumentError = class extends Error {
723
+ };
724
+ function standardTokenizer2() {
725
+ return new Tokenizer2(standardTokenizerC());
726
+ }
727
+ function standardTokenizerC() {
728
+ return core3.standardTokenizer();
729
+ }
730
+ function escapeRegExp(s) {
731
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
732
+ }
733
+
734
+ // src/modules/json.ts
735
+ var SpaceValue = class extends ValueObject {
736
+ constructor(space) {
737
+ super(space);
738
+ this.space = space;
739
+ }
740
+ space;
741
+ toString() {
742
+ return "(dict-space)";
743
+ }
744
+ };
745
+ function spaceOf(atom) {
746
+ if (!(atom instanceof GroundedAtom)) return void 0;
747
+ const obj = atom.object();
748
+ return obj instanceof SpaceValue ? obj.space : void 0;
749
+ }
750
+ function makeDictSpace(pairs) {
751
+ const space = new GroundingSpace();
752
+ for (const p of pairs) space.addAtom(p);
753
+ return G(new SpaceValue(space));
754
+ }
755
+ function jsonToAtom(value) {
756
+ if (value === null) return S("null");
757
+ if (Array.isArray(value)) return E(...value.map(jsonToAtom));
758
+ switch (typeof value) {
759
+ case "string":
760
+ return ValueAtom(value);
761
+ case "number":
762
+ return ValueAtom(value);
763
+ case "boolean":
764
+ return ValueAtom(value);
765
+ case "object":
766
+ return makeDictSpace(
767
+ Object.entries(value).map(
768
+ ([k, v]) => E(ValueAtom(k), jsonToAtom(v))
769
+ )
770
+ );
771
+ default:
772
+ return S("null");
773
+ }
774
+ }
775
+ function atomToJson(atom) {
776
+ const space = spaceOf(atom);
777
+ if (space !== void 0) {
778
+ const obj = {};
779
+ for (const a of space.getAtoms()) {
780
+ if (a instanceof ExpressionAtom) {
781
+ const [k, v] = a.children();
782
+ if (k !== void 0 && v !== void 0) obj[String(jsonScalar(k))] = atomToJson(v);
783
+ }
784
+ }
785
+ return obj;
786
+ }
787
+ if (atom instanceof ExpressionAtom) return atom.children().map(atomToJson);
788
+ if (atom instanceof GroundedAtom) {
789
+ const content = atom.object().content;
790
+ const t = typeof content;
791
+ if (content !== null && t !== "string" && t !== "number" && t !== "boolean")
792
+ throw new Error(
793
+ `json-encode: grounded value of type '${t}' is not JSON-encodable: ${atom.toString()}`
794
+ );
795
+ return content;
796
+ }
797
+ if (atom instanceof SymbolAtom) return atom.name() === "null" ? null : atom.name();
798
+ return atom.toString();
799
+ }
800
+ function jsonScalar(atom) {
801
+ if (atom instanceof GroundedAtom) return atom.object().content;
802
+ return atom.toString();
803
+ }
804
+ function registerJsonModule(m) {
805
+ let freshVar = 0;
806
+ m.registerOperation("dict-space", (args) => {
807
+ const expr2 = args[0];
808
+ const pairs = expr2 instanceof ExpressionAtom ? expr2.children() : [];
809
+ return [makeDictSpace(pairs)];
810
+ });
811
+ m.registerOperation("get-keys", (args) => {
812
+ const space = args[0] === void 0 ? void 0 : spaceOf(args[0]);
813
+ if (space === void 0)
814
+ throw new Error(
815
+ `get-keys: expected a dict-space, got ${args[0]?.toString() ?? "no argument"}`
816
+ );
817
+ const keys = [];
818
+ for (const a of space.getAtoms())
819
+ if (a instanceof ExpressionAtom) {
820
+ const k = a.children()[0];
821
+ if (k !== void 0) keys.push(k);
822
+ }
823
+ return keys;
824
+ });
825
+ m.registerOperation("get-value", (args) => {
826
+ const space = args[0] === void 0 ? void 0 : spaceOf(args[0]);
827
+ const key = args[1];
828
+ if (space === void 0)
829
+ throw new Error(
830
+ `get-value: expected a dict-space, got ${args[0]?.toString() ?? "no argument"}`
831
+ );
832
+ if (key === void 0) throw new Error("get-value: missing key argument");
833
+ const v = V(`__get_value_${freshVar++}`);
834
+ return space.subst(E(key, v), v);
835
+ });
836
+ m.registerOperation("json-decode", (args) => {
837
+ const a = args[0];
838
+ if (!(a instanceof GroundedAtom))
839
+ throw new Error(
840
+ `json-decode: expected a grounded string, got ${a?.toString() ?? "no argument"}`
841
+ );
842
+ const text = a.object().content;
843
+ if (typeof text !== "string")
844
+ throw new Error(`json-decode: expected a string value, got ${typeof text}`);
845
+ return [jsonToAtom(JSON.parse(text))];
846
+ });
847
+ m.registerOperation("json-encode", (args) => {
848
+ const a = args[0];
849
+ if (a === void 0) throw new Error("json-encode: missing argument");
850
+ return [ValueAtom(JSON.stringify(atomToJson(a)))];
851
+ });
852
+ }
853
+
854
+ // src/modules/catalog.ts
855
+ var ALL = "all";
856
+ var ModuleCatalog = class {
857
+ catalogs = /* @__PURE__ */ new Map();
858
+ /** One line per `catalog-list!` call, so tests and UIs can observe the side effect. */
859
+ listing = [];
860
+ /** The names of catalogs updated by `catalog-update!`. */
861
+ updated = [];
862
+ /** Add or replace a named catalog's module list. */
863
+ register(name, modules) {
864
+ this.catalogs.set(name, [...modules]);
865
+ }
866
+ /** The module names in a catalog. */
867
+ modules(name) {
868
+ return this.catalogs.get(name) ?? [];
869
+ }
870
+ /** The catalog names, in insertion order. */
871
+ names() {
872
+ return [...this.catalogs.keys()];
873
+ }
874
+ targets(target2) {
875
+ return target2 === ALL ? this.names() : [target2];
876
+ }
877
+ /** Clear one catalog (or `all`). */
878
+ clear(target2) {
879
+ for (const name of this.targets(target2)) this.catalogs.set(name, []);
880
+ }
881
+ /** Record the contents of one catalog (or `all`) into {@link listing}. */
882
+ list(target2) {
883
+ for (const name of this.targets(target2))
884
+ this.listing.push(`${name}: ${this.modules(name).join(", ")}`);
885
+ }
886
+ /** Mark one catalog (or `all`) updated. */
887
+ update(target2) {
888
+ for (const name of this.targets(target2)) this.updated.push(name);
889
+ }
890
+ };
891
+ var UNIT = E();
892
+ function target(args) {
893
+ const a = args[0];
894
+ return a !== void 0 && a.metatype() === "Symbol" ? a.toString() : ALL;
895
+ }
896
+ function registerCatalogModule(m, catalog) {
897
+ m.registerOperation("catalog-clear!", (args) => {
898
+ catalog.clear(target(args));
899
+ return [UNIT];
900
+ });
901
+ m.registerOperation("catalog-list!", (args) => {
902
+ catalog.list(target(args));
903
+ return [UNIT];
904
+ });
905
+ m.registerOperation("catalog-update!", (args) => {
906
+ catalog.update(target(args));
907
+ return [UNIT];
908
+ });
909
+ }
910
+
911
+ // src/modules/js.ts
912
+ var JsValue = class extends ValueObject {
913
+ };
914
+ function atomToJs(atom) {
915
+ if (atom instanceof GroundedAtom) return atom.object().content;
916
+ if (atom instanceof SymbolAtom) return atom.name();
917
+ if (atom instanceof VariableAtom2) return atom.name();
918
+ if (atom instanceof ExpressionAtom) return atom.children().map(atomToJs);
919
+ return atom.toString();
920
+ }
921
+ function jsToAtom(value) {
922
+ switch (typeof value) {
923
+ case "number":
924
+ case "string":
925
+ case "boolean":
926
+ return ValueAtom(value);
927
+ case "function":
928
+ return OperationAtom(value.name || "js-fn", (...args) => [
929
+ jsToAtom(value(...args.map(atomToJs)))
930
+ ]);
931
+ case "undefined":
932
+ return S("()");
933
+ default:
934
+ return value === null ? S("null") : G(new JsValue(value));
935
+ }
936
+ }
937
+ var BLOCKED_SEGMENTS = /* @__PURE__ */ new Set([
938
+ "eval",
939
+ "Function",
940
+ "constructor",
941
+ "prototype",
942
+ "__proto__",
943
+ "process",
944
+ "require",
945
+ "Reflect",
946
+ "globalThis",
947
+ "global",
948
+ "module",
949
+ "import",
950
+ "child_process"
951
+ ]);
952
+ function assertSafePath(path, op) {
953
+ for (const seg of path.split("."))
954
+ if (BLOCKED_SEGMENTS.has(seg)) throw new Error(`${op}: access to '${seg}' is blocked`);
955
+ }
956
+ function resolvePath(root, path) {
957
+ let owner = void 0;
958
+ let value = root;
959
+ for (const key of path.split(".")) {
960
+ if (value == null) return { value: void 0, owner };
961
+ owner = value;
962
+ value = value[key];
963
+ }
964
+ return { value, owner };
965
+ }
966
+ function asString(atom) {
967
+ if (atom === void 0) return void 0;
968
+ const v = atomToJs(atom);
969
+ return typeof v === "string" ? v : void 0;
970
+ }
971
+ function registerJsInterop(m) {
972
+ m.registerOperation("js-atom", (args) => {
973
+ const path = asString(args[0]);
974
+ if (path === void 0) throw new Error('js-atom: expected a String path (e.g. "Math.abs")');
975
+ assertSafePath(path, "js-atom");
976
+ const { value } = resolvePath(globalThis, path);
977
+ if (value === void 0) throw new Error(`js-atom: '${path}' did not resolve on globalThis`);
978
+ return [jsToAtom(value)];
979
+ });
980
+ m.registerOperation("js-dot", (args) => {
981
+ const objAtom = args[0];
982
+ const name = asString(args[1]);
983
+ if (objAtom === void 0 || name === void 0)
984
+ throw new Error('js-dot: expected (js-dot <object> "property")');
985
+ assertSafePath(name, "js-dot");
986
+ const obj = atomToJs(objAtom);
987
+ const { value, owner } = resolvePath(obj, name);
988
+ if (typeof value === "function")
989
+ return [jsToAtom(value.bind(owner))];
990
+ return [jsToAtom(value)];
991
+ });
992
+ m.registerOperation("js-list", (args) => {
993
+ const e = args[0];
994
+ const items = e instanceof ExpressionAtom ? e.children() : [];
995
+ return [G(new JsValue(items.map(atomToJs)))];
996
+ });
997
+ m.registerOperation("js-dict", (args) => {
998
+ const e = args[0];
999
+ const out = {};
1000
+ if (e instanceof ExpressionAtom) {
1001
+ for (const pair of e.children())
1002
+ if (pair instanceof ExpressionAtom) {
1003
+ const [k, v] = pair.children();
1004
+ if (k !== void 0 && v !== void 0) out[String(atomToJs(k))] = atomToJs(v);
1005
+ }
1006
+ }
1007
+ return [G(new JsValue(out))];
1008
+ });
1009
+ }
1010
+ export {
1011
+ Atom,
1012
+ AtomType,
1013
+ Bindings,
1014
+ BindingsSet,
1015
+ E,
1016
+ ExpressionAtom,
1017
+ G,
1018
+ GroundedAtom,
1019
+ GroundedObject,
1020
+ GroundingSpace,
1021
+ IncorrectArgumentError,
1022
+ JsValue,
1023
+ MatchableObject,
1024
+ MeTTa,
1025
+ ModuleCatalog,
1026
+ OperationAtom,
1027
+ OperationObject,
1028
+ S,
1029
+ SExprParser,
1030
+ SpaceRef,
1031
+ SpaceValue,
1032
+ SymbolAtom,
1033
+ Tokenizer2 as Tokenizer,
1034
+ V,
1035
+ ValueAtom,
1036
+ ValueObject,
1037
+ VariableAtom2 as VariableAtom,
1038
+ atomIsError,
1039
+ atomToJs,
1040
+ atomsAreEquivalent,
1041
+ clearGroundedObjects,
1042
+ friendlyTypeName,
1043
+ groundToJs,
1044
+ jsToAtom,
1045
+ registerCatalogModule,
1046
+ registerJsInterop,
1047
+ registerJsonModule,
1048
+ standardTokenizer2 as standardTokenizer
1049
+ };