@metta-ts/core 1.0.2 → 1.0.4

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.d.ts CHANGED
@@ -209,15 +209,26 @@ declare class FlatAtomSpace {
209
209
  private readonly dead;
210
210
  readonly liveCount: number;
211
211
  readonly nonGroundCount: number;
212
+ private arr;
212
213
  private constructor();
213
214
  static empty(): FlatAtomSpace;
214
215
  static fromAtoms(atoms: readonly Atom[]): FlatAtomSpace | undefined;
215
216
  get size(): number;
216
- appendAll(atoms: readonly Atom[]): FlatAtomSpace;
217
+ /** Append a batch as new visible facts. Returns undefined when some atom is not flat-storable (a
218
+ * grounded executor/matcher); the caller keeps such a batch on the plain log instead. */
219
+ appendAll(atoms: readonly Atom[]): FlatAtomSpace | undefined;
217
220
  removeOne(atom: Atom): FlatAtomSpace;
218
221
  exactCount(atom: Atom): number;
219
222
  candidatesFor(patternHead: string | undefined): Iterable<Atom>;
220
223
  toArray(): Atom[];
224
+ /** Columnar mirror of the `&self` direct tally in eval.ts: over the visible facts the head filter
225
+ * admits (head symbol `patternHead` or no symbol head, same filter as `candidatesFor`), count the
226
+ * ones an all-distinct-variable pattern `(k $v..)` of `arity` items unifies with, without decoding
227
+ * any fact. `iterated` is the admitted total (it advances the candidate counter). */
228
+ countHeadArity(patternHead: string, arity: number): {
229
+ count: number;
230
+ iterated: number;
231
+ };
221
232
  roundTrip(atom: Atom): Atom;
222
233
  private decodeFact;
223
234
  private visibleFactIds;
@@ -297,6 +308,20 @@ interface SymbolicHolder {
297
308
  }
298
309
  interface CompiledImpureOps {
299
310
  readonly addAtom: (env: MinEnv, st: St, space: Atom, atom: Atom) => St | undefined;
311
+ /** Solutions of a `(match space pattern template)` under the current world: the instantiated
312
+ * template plus that solution's bindings, in the interpreter's own candidate order, and the
313
+ * fresh-variable counter advance the interpreted match would have cost. Undefined = not a space. */
314
+ readonly matchSolutions?: (env: MinEnv, st: St, space: Atom, pattern: Atom, template: Atom) => {
315
+ readonly pairs: ReadonlyArray<readonly [Atom, Bindings]>;
316
+ readonly counterDelta: number;
317
+ } | undefined;
318
+ /** The add-if-absent idiom on a ground atom: exact-membership probe, then append when absent.
319
+ * Undefined when the fast probe is unsound for this space (non-ground facts, static facts of the
320
+ * same head, state handles), sending the caller back to the interpreter. */
321
+ readonly addIfAbsent?: (env: MinEnv, st: St, space: Atom, atom: Atom) => {
322
+ readonly added: boolean;
323
+ readonly state: St;
324
+ } | undefined;
300
325
  }
301
326
  type ImpEval = {
302
327
  readonly value: Atom;
@@ -308,7 +333,13 @@ interface ImperativeHolder {
308
333
  clauseCount: number;
309
334
  run: (partAtoms: readonly Atom[], st: St, ops: CompiledImpureOps, discard?: boolean) => ImpEval;
310
335
  }
311
- type CompiledHolder = FunctionalHolder | RewriteHolder | SymbolicHolder | ImperativeHolder;
336
+ interface NondetHolder {
337
+ kind: "nondet";
338
+ arity: number;
339
+ clauseCount: number;
340
+ run: (env: MinEnv, partAtoms: readonly Atom[], st: St, ops: CompiledImpureOps) => CompiledRunResult | undefined;
341
+ }
342
+ type CompiledHolder = FunctionalHolder | RewriteHolder | SymbolicHolder | ImperativeHolder | NondetHolder;
312
343
  type CompiledFns = Map<string, CompiledHolder>;
313
344
  /** Compile every compilable pure single-clause function in `env` to a memoised native closure.
314
345
  * Phase 1 infers return types (fixpoint, optimistic over recursion). Phase 2 compiles bodies with
package/dist/index.js CHANGED
@@ -1048,6 +1048,7 @@ function canCompactAtom(a) {
1048
1048
  return a.items.every(canCompactAtom);
1049
1049
  }
1050
1050
  }
1051
+ var NOT_COMPACT = new Error("flat-atomspace: atom has an executor or custom matcher");
1051
1052
  var FlatAtomSpaceTable = class {
1052
1053
  termKind = new Int32Chunks();
1053
1054
  termStart = new Int32Chunks();
@@ -1057,7 +1058,17 @@ var FlatAtomSpaceTable = class {
1057
1058
  termData = new Int32Chunks();
1058
1059
  factRoot = new Int32Chunks();
1059
1060
  factHeadSym = new Int32Chunks();
1060
- hashBuckets = /* @__PURE__ */ new Map();
1061
+ // Open-addressing intern table (linear probing over a power-of-two Int32Array; a slot holds
1062
+ // termId + 1, 0 means empty; append-only, so no tombstones). Replaces a Map<number, TermId[]>
1063
+ // whose per-hash bucket arrays were the dominant insert allocation on bulk loads.
1064
+ slots = new Int32Array(1 << 12);
1065
+ slotCount = 0;
1066
+ // The empty slot where the last failed lookup probe for `missHash` ended. intern* runs
1067
+ // lookup-then-push, so pushTerm claims this slot instead of re-probing the chain. Slots only ever
1068
+ // fill (append-only), so a chain never shortens and the recorded slot stays on its hash's chain;
1069
+ // pushTerm still re-checks the hash matches and the slot is still empty before trusting it.
1070
+ missSlot = -1;
1071
+ missHash = 0;
1061
1072
  symByName = /* @__PURE__ */ new Map();
1062
1073
  groundByKey = /* @__PURE__ */ new Map();
1063
1074
  varByName = /* @__PURE__ */ new Map();
@@ -1065,6 +1076,15 @@ var FlatAtomSpaceTable = class {
1065
1076
  symbols = [];
1066
1077
  grounds = [];
1067
1078
  vars = [];
1079
+ // One canonical Atom per term, filled on first decode. Terms are interned and append-only, so a
1080
+ // decoded atom is valid forever; without this, candidate enumeration rebuilt a fresh tree per fact
1081
+ // per match, which on deep terms (peano's S^n numerals) made matching O(n^3) instead of O(n^2).
1082
+ // Children resolve through the cache too, so the cached forest is maximally shared (hash-consed).
1083
+ decoded = [];
1084
+ // Reverse intern: an Atom object to its TermId. Weak, so the cache never pins an atom. Match
1085
+ // bindings hold decoded (canonical) subterms, so an atom derived from them re-interns in O(new
1086
+ // nodes) instead of O(depth), and a ground membership lookup is O(1) after its first walk.
1087
+ termIdOf = /* @__PURE__ */ new WeakMap();
1068
1088
  get factCount() {
1069
1089
  return this.factRoot.length;
1070
1090
  }
@@ -1081,6 +1101,14 @@ var FlatAtomSpaceTable = class {
1081
1101
  return this.termFacts.get(term) ?? [];
1082
1102
  }
1083
1103
  insertAtom(atom) {
1104
+ if (atom.kind !== "expr") return this.insertAtomUncached(atom);
1105
+ const known = this.termIdOf.get(atom);
1106
+ if (known !== void 0) return known;
1107
+ const term = this.insertAtomUncached(atom);
1108
+ this.termIdOf.set(atom, term);
1109
+ return term;
1110
+ }
1111
+ insertAtomUncached(atom) {
1084
1112
  switch (atom.kind) {
1085
1113
  case "sym":
1086
1114
  return this.internLeaf(
@@ -1090,6 +1118,7 @@ var FlatAtomSpaceTable = class {
1090
1118
  true
1091
1119
  );
1092
1120
  case "gnd": {
1121
+ if (atom.exec !== void 0 || atom.match !== void 0) throw NOT_COMPACT;
1093
1122
  const key = groundKey(atom.value);
1094
1123
  return this.internLeaf(
1095
1124
  TERM_GND,
@@ -1112,6 +1141,13 @@ var FlatAtomSpaceTable = class {
1112
1141
  }
1113
1142
  }
1114
1143
  lookupAtom(atom) {
1144
+ const known = this.termIdOf.get(atom);
1145
+ if (known !== void 0) return known;
1146
+ const term = this.lookupAtomUncached(atom);
1147
+ if (term !== void 0) this.termIdOf.set(atom, term);
1148
+ return term;
1149
+ }
1150
+ lookupAtomUncached(atom) {
1115
1151
  switch (atom.kind) {
1116
1152
  case "sym": {
1117
1153
  const leaf = this.symByName.get(atom.name);
@@ -1139,6 +1175,14 @@ var FlatAtomSpaceTable = class {
1139
1175
  }
1140
1176
  }
1141
1177
  decodeTerm(term) {
1178
+ const hit = this.decoded[term];
1179
+ if (hit !== void 0) return hit;
1180
+ const atom = this.decodeTermUncached(term);
1181
+ this.decoded[term] = atom;
1182
+ this.termIdOf.set(atom, term);
1183
+ return atom;
1184
+ }
1185
+ decodeTermUncached(term) {
1142
1186
  const kind = this.termKind.get(term);
1143
1187
  const start = this.termStart.get(term);
1144
1188
  switch (kind) {
@@ -1203,11 +1247,22 @@ var FlatAtomSpaceTable = class {
1203
1247
  return this.pushTerm(kind, leaf, 1, hash, ground);
1204
1248
  }
1205
1249
  lookupLeaf(kind, leaf, hash) {
1206
- const bucket = this.hashBuckets.get(hash);
1207
- if (bucket === void 0) return void 0;
1208
- for (const term of bucket)
1209
- if (this.termKind.get(term) === kind && this.termStart.get(term) === leaf) return term;
1210
- return void 0;
1250
+ hash |= 0;
1251
+ const slots = this.slots;
1252
+ const mask = slots.length - 1;
1253
+ let i = hash & mask;
1254
+ for (; ; ) {
1255
+ const s = slots[i];
1256
+ if (s === 0) {
1257
+ this.missSlot = i;
1258
+ this.missHash = hash;
1259
+ return void 0;
1260
+ }
1261
+ const term = s - 1;
1262
+ if (this.termHash.get(term) === hash && this.termKind.get(term) === kind && this.termStart.get(term) === leaf)
1263
+ return term;
1264
+ i = i + 1 & mask;
1265
+ }
1211
1266
  }
1212
1267
  internExpr(children) {
1213
1268
  const existing = this.lookupExpr(children);
@@ -1222,33 +1277,69 @@ var FlatAtomSpaceTable = class {
1222
1277
  }
1223
1278
  return this.pushTerm(TERM_EXPR, start, children.length, hash, ground);
1224
1279
  }
1280
+ // The probe skeleton repeats lookupLeaf's on purpose: sharing it would take a per-call equality
1281
+ // closure, and avoiding that allocation on the intern path is why the open table exists.
1225
1282
  lookupExpr(children) {
1226
1283
  let hash = mixHash(1163415634, children.length);
1227
1284
  for (const child of children) hash = mixHash(hash, child);
1228
- const bucket = this.hashBuckets.get(hash);
1229
- if (bucket === void 0) return void 0;
1230
- termLoop: for (const term of bucket) {
1231
- if (this.termKind.get(term) !== TERM_EXPR || this.termLen.get(term) !== children.length)
1232
- continue;
1233
- const start = this.termStart.get(term);
1234
- for (let i = 0; i < children.length; i++)
1235
- if (this.termData.get(start + i) !== children[i]) continue termLoop;
1236
- return term;
1285
+ hash |= 0;
1286
+ const slots = this.slots;
1287
+ const mask = slots.length - 1;
1288
+ let i = hash & mask;
1289
+ probe: for (; ; ) {
1290
+ const s = slots[i];
1291
+ if (s === 0) {
1292
+ this.missSlot = i;
1293
+ this.missHash = hash;
1294
+ return void 0;
1295
+ }
1296
+ const term = s - 1;
1297
+ if (this.termHash.get(term) === hash && this.termKind.get(term) === TERM_EXPR && this.termLen.get(term) === children.length) {
1298
+ const start = this.termStart.get(term);
1299
+ for (let j = 0; j < children.length; j++)
1300
+ if (this.termData.get(start + j) !== children[j]) {
1301
+ i = i + 1 & mask;
1302
+ continue probe;
1303
+ }
1304
+ return term;
1305
+ }
1306
+ i = i + 1 & mask;
1237
1307
  }
1238
- return void 0;
1239
1308
  }
1240
1309
  pushTerm(kind, start, len, hash, ground) {
1310
+ hash |= 0;
1241
1311
  const term = this.termKind.length;
1242
1312
  this.termKind.push(kind);
1243
1313
  this.termStart.push(start);
1244
1314
  this.termLen.push(len);
1245
1315
  this.termHash.push(hash);
1246
1316
  this.termGround.push(ground ? 1 : 0);
1247
- const bucket = this.hashBuckets.get(hash);
1248
- if (bucket === void 0) this.hashBuckets.set(hash, [term]);
1249
- else bucket.push(term);
1317
+ if ((this.slotCount + 1) * 4 > this.slots.length * 3) {
1318
+ this.growSlots();
1319
+ this.missSlot = -1;
1320
+ }
1321
+ let i = this.missSlot;
1322
+ this.missSlot = -1;
1323
+ if (i < 0 || this.missHash !== hash || this.slots[i] !== 0) {
1324
+ const mask = this.slots.length - 1;
1325
+ i = hash & mask;
1326
+ while (this.slots[i] !== 0) i = i + 1 & mask;
1327
+ }
1328
+ this.slots[i] = term + 1;
1329
+ this.slotCount += 1;
1250
1330
  return term;
1251
1331
  }
1332
+ growSlots() {
1333
+ const next = new Int32Array(this.slots.length * 2);
1334
+ const mask = next.length - 1;
1335
+ for (const s of this.slots) {
1336
+ if (s === 0) continue;
1337
+ let i = this.termHash.get(s - 1) & mask;
1338
+ while (next[i] !== 0) i = i + 1 & mask;
1339
+ next[i] = s;
1340
+ }
1341
+ this.slots = next;
1342
+ }
1252
1343
  };
1253
1344
  var FlatAtomSpace = class _FlatAtomSpace {
1254
1345
  constructor(table, ranges, dead, liveCount, nonGroundCount) {
@@ -1263,23 +1354,31 @@ var FlatAtomSpace = class _FlatAtomSpace {
1263
1354
  dead;
1264
1355
  liveCount;
1265
1356
  nonGroundCount;
1357
+ // toArray memo for this version (see toArray).
1358
+ arr;
1266
1359
  static empty() {
1267
1360
  return new _FlatAtomSpace(new FlatAtomSpaceTable(), [], /* @__PURE__ */ new Set(), 0, 0);
1268
1361
  }
1269
1362
  static fromAtoms(atoms) {
1270
- if (!atoms.every(canCompactAtom)) return void 0;
1271
1363
  return _FlatAtomSpace.empty().appendAll(atoms);
1272
1364
  }
1273
1365
  get size() {
1274
1366
  return this.liveCount;
1275
1367
  }
1368
+ /** Append a batch as new visible facts. Returns undefined when some atom is not flat-storable (a
1369
+ * grounded executor/matcher); the caller keeps such a batch on the plain log instead. */
1276
1370
  appendAll(atoms) {
1277
1371
  if (atoms.length === 0) return this;
1278
1372
  const start = this.table.factCount;
1279
1373
  let nonGround = this.nonGroundCount;
1280
- for (const atom of atoms) {
1281
- const fact = this.table.insertFact(atom);
1282
- if (!this.table.isTermGround(this.table.factRoot.get(fact))) nonGround += 1;
1374
+ try {
1375
+ for (const atom of atoms) {
1376
+ const fact = this.table.insertFact(atom);
1377
+ if (!this.table.isTermGround(this.table.factRoot.get(fact))) nonGround += 1;
1378
+ }
1379
+ } catch (e) {
1380
+ if (e === NOT_COMPACT) return void 0;
1381
+ throw e;
1283
1382
  }
1284
1383
  const end = this.table.factCount;
1285
1384
  return new _FlatAtomSpace(
@@ -1293,8 +1392,8 @@ var FlatAtomSpace = class _FlatAtomSpace {
1293
1392
  removeOne(atom) {
1294
1393
  const term = this.table.lookupAtom(atom);
1295
1394
  if (term === void 0) return this;
1296
- for (const fact of this.visibleFactIds()) {
1297
- if (this.table.factRoot.get(fact) !== term) continue;
1395
+ for (const fact of this.table.factsForTerm(term)) {
1396
+ if (!this.factVisible(fact) || this.dead.has(fact)) continue;
1298
1397
  const dead = new Set(this.dead);
1299
1398
  dead.add(fact);
1300
1399
  const nonGround = this.table.isTermGround(term) ? this.nonGroundCount : this.nonGroundCount - 1;
@@ -1328,9 +1427,38 @@ var FlatAtomSpace = class _FlatAtomSpace {
1328
1427
  }
1329
1428
  }
1330
1429
  toArray() {
1331
- const out = [];
1332
- for (const fact of this.visibleFactIds()) out.push(this.decodeFact(fact));
1333
- return out;
1430
+ if (this.arr === void 0) {
1431
+ const out = [];
1432
+ for (const fact of this.visibleFactIds()) out.push(this.decodeFact(fact));
1433
+ this.arr = out;
1434
+ }
1435
+ return this.arr;
1436
+ }
1437
+ /** Columnar mirror of the `&self` direct tally in eval.ts: over the visible facts the head filter
1438
+ * admits (head symbol `patternHead` or no symbol head, same filter as `candidatesFor`), count the
1439
+ * ones an all-distinct-variable pattern `(k $v..)` of `arity` items unifies with, without decoding
1440
+ * any fact. `iterated` is the admitted total (it advances the candidate counter). */
1441
+ countHeadArity(patternHead, arity) {
1442
+ const t = this.table;
1443
+ const headId = t.lookupHeadSym(patternHead);
1444
+ let count = 0;
1445
+ let iterated = 0;
1446
+ for (const fact of this.visibleFactIds()) {
1447
+ const fh = t.factHeadSym.get(fact);
1448
+ if (fh !== ABSENT && fh !== headId) continue;
1449
+ iterated += 1;
1450
+ const root = t.factRoot.get(fact);
1451
+ const kind = t.termKind.get(root);
1452
+ if (kind === TERM_VAR) {
1453
+ count += 1;
1454
+ continue;
1455
+ }
1456
+ if (kind !== TERM_EXPR || t.termLen.get(root) !== arity) continue;
1457
+ if (fh !== ABSENT) count += 1;
1458
+ else if (arity > 0 && t.termKind.get(t.termData.get(t.termStart.get(root))) === TERM_VAR)
1459
+ count += 1;
1460
+ }
1461
+ return { count, iterated };
1334
1462
  }
1335
1463
  roundTrip(atom) {
1336
1464
  return this.table.decodeTerm(this.table.insertAtom(atom));
@@ -2734,8 +2862,187 @@ function compileSymbolic(env, functor) {
2734
2862
  };
2735
2863
  return { kind: "symbolic", arity, clauseCount, run };
2736
2864
  }
2865
+ var NONDET_CALL_CAP = 4e6;
2866
+ function nondetIsData(env, a) {
2867
+ if (a.kind !== "expr" || a.items.length === 0) return true;
2868
+ const h = a.items[0];
2869
+ if (h.kind === "expr" && h.items.length > 0) return false;
2870
+ if (h.kind === "sym" && (env.ruleIndex.has(h.name) || env.gt.has(h.name) || IMPURE_OPS.has(h.name)))
2871
+ return false;
2872
+ return a.items.every((x) => nondetIsData(env, x));
2873
+ }
2874
+ function nondetCall(env, functor, val) {
2875
+ if (val.kind !== "expr" || val.items.length === 0 || val.items[0].kind !== "sym")
2876
+ return void 0;
2877
+ const op = val.items[0].name;
2878
+ if (op === "match" && val.items.length === 4) {
2879
+ if (!nondetIsData(env, val.items[2]) || !nondetIsData(env, val.items[3])) return void 0;
2880
+ return {
2881
+ tag: "match",
2882
+ space: val.items[1],
2883
+ pattern: val.items[2],
2884
+ template: val.items[3]
2885
+ };
2886
+ }
2887
+ if (op === functor) {
2888
+ const args = val.items.slice(1);
2889
+ if (!args.every((x) => nondetIsData(env, x))) return void 0;
2890
+ return { tag: "self", args };
2891
+ }
2892
+ return void 0;
2893
+ }
2894
+ function nondetUnwrap(env, functor, rhs) {
2895
+ const goals = [];
2896
+ let cur = rhs;
2897
+ for (; ; ) {
2898
+ if (cur.kind !== "expr" || cur.items.length === 0 || cur.items[0].kind !== "sym") break;
2899
+ const op = cur.items[0].name;
2900
+ if (op === "let*" && cur.items.length === 3 && cur.items[1].kind === "expr") {
2901
+ for (const pv of cur.items[1].items) {
2902
+ if (pv.kind !== "expr" || pv.items.length !== 2) return void 0;
2903
+ if (!nondetIsData(env, pv.items[0])) return void 0;
2904
+ const call = nondetCall(env, functor, pv.items[1]);
2905
+ if (call === void 0) return void 0;
2906
+ goals.push({ pat: pv.items[0], call });
2907
+ }
2908
+ cur = cur.items[2];
2909
+ continue;
2910
+ }
2911
+ if (op === "let" && cur.items.length === 4) {
2912
+ if (!nondetIsData(env, cur.items[1])) return void 0;
2913
+ const call = nondetCall(env, functor, cur.items[2]);
2914
+ if (call === void 0) return void 0;
2915
+ goals.push({ pat: cur.items[1], call });
2916
+ cur = cur.items[3];
2917
+ continue;
2918
+ }
2919
+ break;
2920
+ }
2921
+ const tailCall = nondetCall(env, functor, cur);
2922
+ if (tailCall !== void 0) return { goals, tail: tailCall };
2923
+ if (!nondetIsData(env, cur)) return void 0;
2924
+ return { goals, tail: { tag: "tpl", atom: cur } };
2925
+ }
2926
+ function compileNondet(env, functor) {
2927
+ if (env.varRulesVar.length !== 0) return void 0;
2928
+ const eqs = env.ruleIndex.get(functor);
2929
+ if (eqs === void 0 || eqs.length === 0) return void 0;
2930
+ const clauses = [];
2931
+ let arity;
2932
+ let hasCalls = false;
2933
+ for (const [lhs, rhs] of eqs) {
2934
+ if (lhs.kind !== "expr" || lhs.items.length === 0) return void 0;
2935
+ const h = lhs.items[0];
2936
+ if (h.kind !== "sym" || h.name !== functor) return void 0;
2937
+ const a = lhs.items.length - 1;
2938
+ if (arity === void 0) arity = a;
2939
+ else if (a !== arity) return void 0;
2940
+ if (!lhs.items.slice(1).every((x) => nondetIsData(env, x))) return void 0;
2941
+ const un = nondetUnwrap(env, functor, rhs);
2942
+ if (un === void 0) return void 0;
2943
+ if (un.goals.length > 0 || un.tail.tag !== "tpl") hasCalls = true;
2944
+ clauses.push({ lhs, goals: un.goals, tail: un.tail });
2945
+ }
2946
+ if (arity === void 0 || !hasCalls) return void 0;
2947
+ const clauseCount = clauses.length;
2948
+ const run = (envR, partAtoms, st, ops) => {
2949
+ const matchSolutions = ops.matchSolutions;
2950
+ if (matchSolutions === void 0) return void 0;
2951
+ const ctr = { c: st.counter };
2952
+ let dispatches = 0;
2953
+ const world = st.world;
2954
+ const walk = (b, a) => {
2955
+ let v = a;
2956
+ let hops = 0;
2957
+ while (v.kind === "var") {
2958
+ const next = lookupVal(b, v.name);
2959
+ if (next === void 0) return v;
2960
+ v = next;
2961
+ if (++hops > 1e4) throw BAIL;
2962
+ }
2963
+ return v;
2964
+ };
2965
+ const walkStar = (b, a) => {
2966
+ const v = walk(b, a);
2967
+ if (v.ground || v.kind !== "expr") return v;
2968
+ const its = v.items;
2969
+ let items = null;
2970
+ for (let i = 0; i < its.length; i++) {
2971
+ const it = its[i];
2972
+ const r = walkStar(b, it);
2973
+ if (items !== null) items.push(r);
2974
+ else if (r !== it) {
2975
+ items = its.slice(0, i);
2976
+ items.push(r);
2977
+ }
2978
+ }
2979
+ return items === null ? v : expr(items);
2980
+ };
2981
+ const resolve = (b, a, suffix) => walkStar(b, instantiate(b, a, suffix));
2982
+ const runMatch = (b, suffix, call) => {
2983
+ const m = matchSolutions(
2984
+ envR,
2985
+ { counter: ctr.c, world },
2986
+ resolve(b, call.space, suffix),
2987
+ resolve(b, call.pattern, suffix),
2988
+ resolve(b, call.template, suffix)
2989
+ );
2990
+ if (m === void 0) throw BAIL;
2991
+ ctr.c += m.counterDelta;
2992
+ return m.pairs;
2993
+ };
2994
+ const solve = (clause, gi, b, suffix, out) => {
2995
+ if (gi === clause.goals.length) {
2996
+ if (clause.tail.tag === "tpl") {
2997
+ out.push([resolve(b, clause.tail.atom, suffix), b]);
2998
+ return;
2999
+ }
3000
+ const pairs2 = clause.tail.tag === "match" ? runMatch(b, suffix, clause.tail) : runCall(clause.tail.args.map((x) => resolve(b, x, suffix)));
3001
+ for (const [atom, vb] of pairs2)
3002
+ for (const mm of merge(b, vb)) if (!hasLoop(mm)) out.push([atom, mm]);
3003
+ return;
3004
+ }
3005
+ const goal = clause.goals[gi];
3006
+ const pat = resolve(b, goal.pat, suffix);
3007
+ const pairs = goal.call.tag === "match" ? runMatch(b, suffix, goal.call) : runCall(goal.call.args.map((x) => resolve(b, x, suffix)));
3008
+ for (const [atom, vb] of pairs)
3009
+ for (const withVal of merge(b, vb)) {
3010
+ if (hasLoop(withVal)) continue;
3011
+ for (const pm of matchAtoms(pat, atom))
3012
+ for (const mm of merge(withVal, pm))
3013
+ if (!hasLoop(mm)) solve(clause, gi + 1, mm, suffix, out);
3014
+ }
3015
+ };
3016
+ const runCall = (args) => {
3017
+ if (++dispatches > NONDET_CALL_CAP) throw BAIL;
3018
+ const app = expr([sym(functor), ...args]);
3019
+ const out = [];
3020
+ for (const clause of clauses) {
3021
+ const suffix = "#" + ctr.c;
3022
+ ctr.c += 1;
3023
+ for (const b0 of matchAtomsScoped(clause.lhs, app, suffix))
3024
+ if (!hasLoop(b0)) solve(clause, 0, b0, suffix, out);
3025
+ }
3026
+ return out;
3027
+ };
3028
+ try {
3029
+ const top = runCall(partAtoms);
3030
+ const results = top.map(([atom, bnd]) => ({ atom, bnd }));
3031
+ return { results, counterDelta: ctr.c - st.counter };
3032
+ } catch (e) {
3033
+ if (e === BAIL || e instanceof RangeError) return void 0;
3034
+ throw e;
3035
+ }
3036
+ };
3037
+ return { kind: "nondet", arity, clauseCount, run };
3038
+ }
2737
3039
  var IMP_GROUNDED = /* @__PURE__ */ new Set(["==", "<", ">", "<=", ">=", "+", "-", "*", "%"]);
2738
- var DATA_DENY = /* @__PURE__ */ new Set([...KNOWN_OPS, "let*", "add-atom"]);
3040
+ var DATA_DENY_CACHE;
3041
+ function dataDeny() {
3042
+ DATA_DENY_CACHE ??= /* @__PURE__ */ new Set([...KNOWN_OPS, ...IMPURE_OPS, "let*", "add-atom"]);
3043
+ return DATA_DENY_CACHE;
3044
+ }
3045
+ var EMPTY_VALUE = sym("Empty");
2739
3046
  var addCounter = (st, n) => n === 0 ? st : { counter: st.counter + n, world: st.world };
2740
3047
  var impBail = () => BAIL;
2741
3048
  function impMeta(parts) {
@@ -2750,8 +3057,37 @@ function impMeta(parts) {
2750
3057
  function impConst(atom) {
2751
3058
  return { node: (_slots, st) => ({ value: atom, st }), directEffect: false, callees: /* @__PURE__ */ new Set() };
2752
3059
  }
3060
+ function impAssembleExpr(parts) {
3061
+ return {
3062
+ node: (slots, st, ops) => {
3063
+ const out = [];
3064
+ let cur = st;
3065
+ for (const part of parts) {
3066
+ const r = part.node(slots, cur, ops);
3067
+ if (r === BAIL) return BAIL;
3068
+ out.push(r.value);
3069
+ cur = r.st;
3070
+ }
3071
+ return { value: expr(out), st: cur };
3072
+ },
3073
+ ...impMeta(parts)
3074
+ };
3075
+ }
3076
+ function impEvalArgs(parts, slots, st, ops) {
3077
+ const vals = [];
3078
+ let cur = st;
3079
+ let empty = false;
3080
+ for (const part of parts) {
3081
+ const r = part.node(slots, cur, ops);
3082
+ if (r === BAIL) return BAIL;
3083
+ if (r.value === EMPTY_VALUE) empty = true;
3084
+ vals.push(r.value);
3085
+ cur = r.st;
3086
+ }
3087
+ return { vals, st: cur, empty };
3088
+ }
2753
3089
  function isDataSymbol(env, name) {
2754
- return !DATA_DENY.has(name) && !env.ruleIndex.has(name) && !env.gt.has(name);
3090
+ return !dataDeny().has(name) && !env.ruleIndex.has(name) && !env.gt.has(name);
2755
3091
  }
2756
3092
  function compileImpStaticAtom(env, a, scope) {
2757
3093
  if (a.kind === "var") {
@@ -2771,21 +3107,7 @@ function compileImpStaticAtom(env, a, scope) {
2771
3107
  if (head.kind === "sym" && !isDataSymbol(env, head.name)) return void 0;
2772
3108
  const items = a.items.map((it) => compileImpStaticAtom(env, it, scope));
2773
3109
  if (items.some((it) => it === void 0)) return void 0;
2774
- const parts = items;
2775
- return {
2776
- node: (slots, st, ops) => {
2777
- const out = [];
2778
- let cur = st;
2779
- for (const part of parts) {
2780
- const r = part.node(slots, cur, ops);
2781
- if (r === BAIL) return BAIL;
2782
- out.push(r.value);
2783
- cur = r.st;
2784
- }
2785
- return { value: expr(out), st: cur };
2786
- },
2787
- ...impMeta(parts)
2788
- };
3110
+ return impAssembleExpr(items);
2789
3111
  }
2790
3112
  function compileImpGrounded(env, op, args, scope, holders) {
2791
3113
  if (env.ruleIndex.has(op)) return void 0;
@@ -2794,22 +3116,73 @@ function compileImpGrounded(env, op, args, scope, holders) {
2794
3116
  const compiled = parts;
2795
3117
  return {
2796
3118
  node: (slots, st, ops) => {
2797
- const vals = [];
2798
- let cur = st;
2799
- for (const part of compiled) {
2800
- const r = part.node(slots, cur, ops);
2801
- if (r === BAIL) return BAIL;
2802
- vals.push(r.value);
2803
- cur = r.st;
2804
- }
2805
- const gr = callGrounded(env.gt, op, vals);
2806
- return gr.tag === "ok" && gr.results.length === 1 ? { value: gr.results[0], st: cur } : BAIL;
3119
+ const r = impEvalArgs(compiled, slots, st, ops);
3120
+ if (r === BAIL) return BAIL;
3121
+ if (r.empty) return { value: EMPTY_VALUE, st: r.st };
3122
+ const gr = callGrounded(env.gt, op, r.vals);
3123
+ return gr.tag === "ok" && gr.results.length === 1 ? { value: gr.results[0], st: r.st } : BAIL;
2807
3124
  },
2808
3125
  ...impMeta(compiled)
2809
3126
  };
2810
3127
  }
3128
+ function impMatchInsideOnce(a) {
3129
+ if (a.kind !== "expr" || a.items.length !== 2) return void 0;
3130
+ const h = a.items[0];
3131
+ if (h.kind !== "sym" || h.name !== "once") return void 0;
3132
+ const inner = a.items[1];
3133
+ if (inner.kind !== "expr" || inner.items.length !== 4) return void 0;
3134
+ const ih = inner.items[0];
3135
+ return ih.kind === "sym" && ih.name === "match" ? inner : void 0;
3136
+ }
3137
+ function impEmptyCollapseMatch(a) {
3138
+ if (a.kind !== "expr" || a.items.length !== 3) return void 0;
3139
+ const h = a.items[0];
3140
+ if (h.kind !== "sym" || h.name !== "==") return void 0;
3141
+ const fromCollapse = (x) => {
3142
+ if (x.kind !== "expr" || x.items.length !== 2) return void 0;
3143
+ const ch = x.items[0];
3144
+ return ch.kind === "sym" && ch.name === "collapse" ? impMatchInsideOnce(x.items[1]) : void 0;
3145
+ };
3146
+ if (atomEq(a.items[1], emptyExpr)) return fromCollapse(a.items[2]);
3147
+ if (atomEq(a.items[2], emptyExpr)) return fromCollapse(a.items[1]);
3148
+ return void 0;
3149
+ }
3150
+ function compileImpAddIfAbsent(env, args, scope) {
3151
+ const match = impEmptyCollapseMatch(args[0]);
3152
+ if (match === void 0) return void 0;
3153
+ const add = args[1];
3154
+ const otherwise = args[2];
3155
+ if (add.kind !== "expr" || add.items.length !== 3) return void 0;
3156
+ const addHead = add.items[0];
3157
+ if (addHead.kind !== "sym" || addHead.name !== "add-atom") return void 0;
3158
+ if (otherwise.kind !== "expr" || otherwise.items.length !== 1) return void 0;
3159
+ const oh = otherwise.items[0];
3160
+ if (oh.kind !== "sym" || oh.name !== "empty") return void 0;
3161
+ if (!atomEq(match.items[1], add.items[1]) || !atomEq(match.items[2], match.items[3]) || !atomEq(match.items[2], add.items[2]))
3162
+ return void 0;
3163
+ const space = compileImpStaticAtom(env, add.items[1], scope);
3164
+ const atom = compileImpStaticAtom(env, add.items[2], scope);
3165
+ if (space === void 0 || atom === void 0) return void 0;
3166
+ return {
3167
+ node: (slots, st, ops) => {
3168
+ const addIfAbsent = ops.addIfAbsent;
3169
+ if (addIfAbsent === void 0) return BAIL;
3170
+ const s = space.node(slots, st, ops);
3171
+ if (s === BAIL) return BAIL;
3172
+ const a = atom.node(slots, s.st, ops);
3173
+ if (a === BAIL) return BAIL;
3174
+ const r = addIfAbsent(env, a.st, s.value, a.value);
3175
+ if (r === void 0) return BAIL;
3176
+ return { value: r.added ? emptyExpr : EMPTY_VALUE, st: r.state };
3177
+ },
3178
+ directEffect: true,
3179
+ callees: /* @__PURE__ */ new Set()
3180
+ };
3181
+ }
2811
3182
  function compileImpIf(env, args, scope, holders) {
2812
3183
  if (args.length !== 3 || (env.ruleIndex.get("if")?.length ?? 0) !== 2) return void 0;
3184
+ const addIfAbsent = compileImpAddIfAbsent(env, args, scope);
3185
+ if (addIfAbsent !== void 0) return addIfAbsent;
2813
3186
  const cond = compileImpAtom(env, args[0], scope, holders);
2814
3187
  const then_ = compileImpAtom(env, args[1], scope, holders);
2815
3188
  const els = compileImpAtom(env, args[2], scope, holders);
@@ -2818,6 +3191,7 @@ function compileImpIf(env, args, scope, holders) {
2818
3191
  node: (slots, st, ops, discard) => {
2819
3192
  const c = cond.node(slots, st, ops);
2820
3193
  if (c === BAIL) return BAIL;
3194
+ if (c.value === EMPTY_VALUE) return { value: EMPTY_VALUE, st: c.st };
2821
3195
  const stIf = addCounter(c.st, 2);
2822
3196
  if (c.value.kind !== "gnd" || c.value.value.g !== "bool") return BAIL;
2823
3197
  return (c.value.value.b ? then_ : els).node(slots, stIf, ops, discard);
@@ -2841,6 +3215,7 @@ function compileImpLet(env, args, scope, holders) {
2841
3215
  node: (slots, st, ops, discard) => {
2842
3216
  const v = value.node(slots, st, ops);
2843
3217
  if (v === BAIL) return BAIL;
3218
+ if (v.value === EMPTY_VALUE) return { value: EMPTY_VALUE, st: v.st };
2844
3219
  const local = slots.slice();
2845
3220
  local[slot] = v.value;
2846
3221
  return body.node(local, addCounter(v.st, 1), ops, discard);
@@ -2875,6 +3250,7 @@ function compileImpLetStar(env, args, scope, holders) {
2875
3250
  for (const binding of bindings) {
2876
3251
  const v = binding.value.node(local, cur, ops);
2877
3252
  if (v === BAIL) return BAIL;
3253
+ if (v.value === EMPTY_VALUE) return { value: EMPTY_VALUE, st: v.st };
2878
3254
  local[binding.slot] = v.value;
2879
3255
  cur = addCounter(addCounter(v.st, 1), 1);
2880
3256
  }
@@ -2904,6 +3280,72 @@ function compileImpAddAtom(env, args, scope) {
2904
3280
  callees: /* @__PURE__ */ new Set()
2905
3281
  };
2906
3282
  }
3283
+ function compileImpPatternAtom(a, scope) {
3284
+ if (a.kind === "var") {
3285
+ const slot = scope.vars.get(a.name);
3286
+ if (slot === void 0) return impConst(a);
3287
+ return {
3288
+ node: (slots, st) => ({ value: slots[slot], st }),
3289
+ directEffect: false,
3290
+ callees: /* @__PURE__ */ new Set()
3291
+ };
3292
+ }
3293
+ if (a.kind !== "expr" || a.items.length === 0) return impConst(a);
3294
+ return impAssembleExpr(a.items.map((it) => compileImpPatternAtom(it, scope)));
3295
+ }
3296
+ function compileImpCaseMatch(env, args, scope, holders) {
3297
+ if (args.length !== 2 || (env.ruleIndex.get("case")?.length ?? 0) !== 1) return void 0;
3298
+ const scrut = args[0];
3299
+ if (scrut.kind !== "expr" || scrut.items.length !== 4) return void 0;
3300
+ const sh = scrut.items[0];
3301
+ if (sh.kind !== "sym" || sh.name !== "match") return void 0;
3302
+ const pairs = args[1];
3303
+ if (pairs.kind !== "expr" || pairs.items.length !== 1) return void 0;
3304
+ const branch = pairs.items[0];
3305
+ if (branch.kind !== "expr" || branch.items.length !== 2 || branch.items[0].kind !== "var")
3306
+ return void 0;
3307
+ const space = compileImpStaticAtom(env, scrut.items[1], scope);
3308
+ if (space === void 0) return void 0;
3309
+ const pattern = compileImpPatternAtom(scrut.items[2], scope);
3310
+ const template = compileImpPatternAtom(scrut.items[3], scope);
3311
+ const slot = scope.len;
3312
+ const branchScope = {
3313
+ vars: new Map(scope.vars).set(branch.items[0].name, slot),
3314
+ len: slot + 1
3315
+ };
3316
+ const body = compileImpAtom(env, branch.items[1], branchScope, holders);
3317
+ if (body === void 0) return void 0;
3318
+ return {
3319
+ node: (slots, st, ops, discard) => {
3320
+ const matchSolutions = ops.matchSolutions;
3321
+ if (matchSolutions === void 0) return BAIL;
3322
+ const s = space.node(slots, st, ops);
3323
+ if (s === BAIL) return BAIL;
3324
+ const p = pattern.node(slots, s.st, ops);
3325
+ if (p === BAIL) return BAIL;
3326
+ const t = template.node(slots, p.st, ops);
3327
+ if (t === BAIL) return BAIL;
3328
+ const m = matchSolutions(env, t.st, s.value, p.value, t.value);
3329
+ if (m === void 0) return BAIL;
3330
+ let cur = addCounter(t.st, m.counterDelta);
3331
+ const local = slots.slice();
3332
+ let survived;
3333
+ for (const [value] of m.pairs) {
3334
+ local[slot] = value;
3335
+ const r = body.node(local, cur, ops, discard);
3336
+ if (r === BAIL) return BAIL;
3337
+ cur = r.st;
3338
+ if (r.value !== EMPTY_VALUE) {
3339
+ if (survived !== void 0) return BAIL;
3340
+ survived = r.value;
3341
+ }
3342
+ }
3343
+ return { value: survived ?? EMPTY_VALUE, st: cur };
3344
+ },
3345
+ directEffect: true,
3346
+ callees: body.callees
3347
+ };
3348
+ }
2907
3349
  function compileImpCall(env, op, args, scope, holders) {
2908
3350
  const h = holders.get(op);
2909
3351
  if (h === void 0 || args.length !== h.arity) return void 0;
@@ -2913,15 +3355,10 @@ function compileImpCall(env, op, args, scope, holders) {
2913
3355
  const meta = impMeta(parts);
2914
3356
  return {
2915
3357
  node: (slots, st, ops, discard) => {
2916
- const vals = [];
2917
- let cur = st;
2918
- for (const part of parts) {
2919
- const r = part.node(slots, cur, ops);
2920
- if (r === BAIL) return BAIL;
2921
- vals.push(r.value);
2922
- cur = r.st;
2923
- }
2924
- return h.run(vals, cur, ops, discard);
3358
+ const r = impEvalArgs(parts, slots, st, ops);
3359
+ if (r === BAIL) return BAIL;
3360
+ if (r.empty) return { value: EMPTY_VALUE, st: r.st };
3361
+ return h.run(r.vals, r.st, ops, discard);
2925
3362
  },
2926
3363
  directEffect: meta.directEffect,
2927
3364
  callees: /* @__PURE__ */ new Set([...meta.callees, op])
@@ -2943,12 +3380,15 @@ function compileImpTuple(env, items, scope, holders) {
2943
3380
  return { value: emptyExpr, st: cur };
2944
3381
  }
2945
3382
  const out = [];
3383
+ let empty = false;
2946
3384
  for (const part of compiled) {
2947
3385
  const r = part.node(slots, cur, ops);
2948
3386
  if (r === BAIL) return BAIL;
3387
+ if (r.value === EMPTY_VALUE) empty = true;
2949
3388
  out.push(r.value);
2950
3389
  cur = r.st;
2951
3390
  }
3391
+ if (empty) return { value: EMPTY_VALUE, st: cur };
2952
3392
  return { value: expr(out), st: cur };
2953
3393
  },
2954
3394
  ...impMeta(compiled)
@@ -2967,6 +3407,7 @@ function compileImpAtom(env, a, scope, holders) {
2967
3407
  if (op === "let") return compileImpLet(env, args, scope, holders);
2968
3408
  if (op === "let*") return compileImpLetStar(env, args, scope, holders);
2969
3409
  if (op === "add-atom") return compileImpAddAtom(env, args, scope);
3410
+ if (op === "case") return compileImpCaseMatch(env, args, scope, holders);
2970
3411
  if (IMP_GROUNDED.has(op)) return compileImpGrounded(env, op, args, scope, holders);
2971
3412
  const call = compileImpCall(env, op, args, scope, holders);
2972
3413
  if (call !== void 0) return call;
@@ -3112,6 +3553,11 @@ function compileEnv(env) {
3112
3553
  if (symbolic !== void 0) compiled.set(f, symbolic);
3113
3554
  }
3114
3555
  compileImperative(env, compiled);
3556
+ for (const f of env.ruleIndex.keys()) {
3557
+ if (compiled.has(f)) continue;
3558
+ const nondet = compileNondet(env, f);
3559
+ if (nondet !== void 0) compiled.set(f, nondet);
3560
+ }
3115
3561
  return compiled;
3116
3562
  }
3117
3563
  }
@@ -3121,10 +3567,16 @@ function runCompiled(env, op, partAtoms, st, ops, discard) {
3121
3567
  if (h === void 0 || partAtoms.length !== h.arity) return void 0;
3122
3568
  if (h.kind === "rewrite") return h.run(partAtoms);
3123
3569
  if (h.kind === "symbolic") return h.run(partAtoms, st.counter);
3570
+ if (h.kind === "nondet") {
3571
+ if (ops === void 0) return void 0;
3572
+ return h.run(env, partAtoms, st, ops);
3573
+ }
3124
3574
  if (h.kind === "imperative") {
3125
3575
  if (ops === void 0) return void 0;
3126
3576
  const r = h.run(partAtoms, st, ops, discard);
3127
- return r === BAIL ? void 0 : { results: [{ atom: r.value, bnd: emptyBindings }], counterDelta: 0, state: r.st };
3577
+ if (r === BAIL) return void 0;
3578
+ if (r.value === EMPTY_VALUE) return { results: [], counterDelta: 0, state: r.st };
3579
+ return { results: [{ atom: r.value, bnd: emptyBindings }], counterDelta: 0, state: r.st };
3128
3580
  }
3129
3581
  const vals = [];
3130
3582
  for (const a of partAtoms) {
@@ -4093,7 +4545,7 @@ function* matchCandidates(env, w, pInst) {
4093
4545
  }
4094
4546
  cands.push(...env.varHeadedFacts);
4095
4547
  if (pInst.ground && logNonGround(w.selfExtra) === 0 && (w.flatSelfExtra?.nonGroundCount ?? 0) === 0 && w.store.size === 0) {
4096
- const c = idxCount(logGroundIdx(w.selfExtra), pInst);
4548
+ const c = w.selfExtra === null ? 0 : idxCount(logGroundIdx(w.selfExtra), pInst);
4097
4549
  for (const atom of cands) yield atom;
4098
4550
  const flatCount = w.flatSelfExtra?.exactCount(pInst) ?? 0;
4099
4551
  for (let i = 0; i < c + flatCount; i++) yield pInst;
@@ -4730,8 +5182,9 @@ function appendSpace(env, w0, name, atoms) {
4730
5182
  if (env.useFlatAtomspace === true) {
4731
5183
  if (flatSelfExtra !== void 0 || logSize(selfExtra) === 0) {
4732
5184
  const base = flatSelfExtra ?? FlatAtomSpace.empty();
4733
- if (atoms.every(canCompactAtom)) {
4734
- flatSelfExtra = base.appendAll(atoms);
5185
+ const appended = base.appendAll(atoms);
5186
+ if (appended !== void 0) {
5187
+ flatSelfExtra = appended;
4735
5188
  } else {
4736
5189
  selfExtra = logFromArray([...base.toArray(), ...logToArray(selfExtra), ...atoms]);
4737
5190
  flatSelfExtra = void 0;
@@ -4788,7 +5241,52 @@ function compiledAddAtom(env, st, space, added) {
4788
5241
  if (name === void 0) return void 0;
4789
5242
  return { counter: st.counter, world: appendSpace(env, st.world, name, [added]) };
4790
5243
  }
4791
- var COMPILED_IMPURE_OPS = { addAtom: compiledAddAtom };
5244
+ function compiledMatchSolutions(env, st, space, pattern, template) {
5245
+ const { getCandidates, patterns } = matchSetup(env, st, space, pattern, emptyBindings);
5246
+ if (patterns.length !== 1) return void 0;
5247
+ const pat = patterns[0];
5248
+ const { endState } = matchSingleEndState(env, getCandidates, pat, template, st, emptyBindings);
5249
+ const pairs = [];
5250
+ for (const m of matchSingleSolutions(env, getCandidates, pat, st, emptyBindings))
5251
+ pairs.push([inst(env, m, template), m]);
5252
+ return { pairs, counterDelta: endState.counter - st.counter };
5253
+ }
5254
+ function compiledAddIfAbsent(env, st, space, atom) {
5255
+ if (!atom.ground || opOf(atom) === "=") return void 0;
5256
+ const w = st.world;
5257
+ if (w.store.size !== 0) return void 0;
5258
+ const name = spaceName(w, space);
5259
+ if (name === void 0) return void 0;
5260
+ if (name === "&self") {
5261
+ const k = headKey(atom);
5262
+ if (k === void 0) return void 0;
5263
+ if (env.varHeadedFacts.length !== 0 || (env.factIndex.get(k)?.length ?? 0) !== 0)
5264
+ return void 0;
5265
+ if (logNonGround(w.selfExtra) !== 0 || (w.flatSelfExtra?.nonGroundCount ?? 0) !== 0)
5266
+ return void 0;
5267
+ const size2 = logSize(w.selfExtra) + (w.flatSelfExtra?.size ?? 0);
5268
+ const checked2 = { counter: st.counter + size2, world: w };
5269
+ const present = idxCount(logGroundIdx(w.selfExtra), atom) + (w.flatSelfExtra?.exactCount(atom) ?? 0);
5270
+ if (present !== 0) return { added: false, state: checked2 };
5271
+ return {
5272
+ added: true,
5273
+ state: { counter: checked2.counter, world: appendSpace(env, w, "&self", [atom]) }
5274
+ };
5275
+ }
5276
+ const log = w.spaces.get(name) ?? emptyLog;
5277
+ if (logNonGround(log) !== 0) return void 0;
5278
+ const checked = { counter: st.counter + logSize(log), world: w };
5279
+ if (idxCount(logGroundIdx(log), atom) !== 0) return { added: false, state: checked };
5280
+ return {
5281
+ added: true,
5282
+ state: { counter: checked.counter, world: appendSpace(env, w, name, [atom]) }
5283
+ };
5284
+ }
5285
+ var COMPILED_IMPURE_OPS = {
5286
+ addAtom: compiledAddAtom,
5287
+ matchSolutions: compiledMatchSolutions,
5288
+ addIfAbsent: compiledAddIfAbsent
5289
+ };
4792
5290
  function* getTypeOpG(env, fuel, st, prev, xi, b) {
4793
5291
  const emit = function* (st0) {
4794
5292
  let acc = [];
@@ -5403,26 +5901,37 @@ var conjCountEnabled = () => process.env.METTA_CONJ_COUNT !== "0";
5403
5901
  function splitVoidBuild(buildExpr, env) {
5404
5902
  if (buildExpr.kind !== "expr") return void 0;
5405
5903
  const voidable = (rhs) => {
5406
- if (rhs.kind !== "expr" || rhs.items.length === 0 || rhs.items[0].kind !== "sym") return void 0;
5904
+ if (rhs.kind !== "expr" || rhs.items.length === 0 || rhs.items[0].kind !== "sym")
5905
+ return void 0;
5407
5906
  const op = rhs.items[0].name;
5408
5907
  const args = rhs.items.slice(1);
5409
- if (env.compiled?.get(op)?.kind !== "imperative" || args.some((a) => !a.ground)) return void 0;
5908
+ if (env.compiled?.get(op)?.kind !== "imperative" || args.some((a) => !a.ground))
5909
+ return void 0;
5410
5910
  return { op, args };
5411
5911
  };
5412
5912
  const head = opOf(buildExpr);
5413
5913
  if (head === "let" && buildExpr.items.length === 4 && atomEq(buildExpr.items[3], DONE_UNIT)) {
5414
5914
  const v = voidable(buildExpr.items[2]);
5415
5915
  if (v === void 0) return void 0;
5416
- return { prefix: expr([buildExpr.items[0], buildExpr.items[1], DONE_UNIT, DONE_UNIT]), op: v.op, args: v.args };
5916
+ return {
5917
+ prefix: expr([buildExpr.items[0], buildExpr.items[1], DONE_UNIT, DONE_UNIT]),
5918
+ op: v.op,
5919
+ args: v.args
5920
+ };
5417
5921
  }
5418
5922
  if (head === "let*" && buildExpr.items.length === 3 && buildExpr.items[1].kind === "expr" && atomEq(buildExpr.items[2], DONE_UNIT)) {
5419
5923
  const pairs = buildExpr.items[1].items;
5420
5924
  const lastPair = pairs[pairs.length - 1];
5421
- if (lastPair === void 0 || lastPair.kind !== "expr" || lastPair.items.length !== 2) return void 0;
5925
+ if (lastPair === void 0 || lastPair.kind !== "expr" || lastPair.items.length !== 2)
5926
+ return void 0;
5422
5927
  const v = voidable(lastPair.items[1]);
5423
5928
  if (v === void 0) return void 0;
5424
5929
  const newPairs = [...pairs.slice(0, -1), expr([lastPair.items[0], DONE_UNIT])];
5425
- return { prefix: expr([buildExpr.items[0], expr(newPairs), DONE_UNIT]), op: v.op, args: v.args };
5930
+ return {
5931
+ prefix: expr([buildExpr.items[0], expr(newPairs), DONE_UNIT]),
5932
+ op: v.op,
5933
+ args: v.args
5934
+ };
5426
5935
  }
5427
5936
  return void 0;
5428
5937
  }
@@ -5510,7 +6019,8 @@ function tryCountAggregate(env, st, bnd, match) {
5510
6019
  const { getCandidates, patterns } = matchSetup(env, st, match.items[1], match.items[2], bnd);
5511
6020
  if (patterns.length !== 1) return void 0;
5512
6021
  const pat = inst(env, bnd, patterns[0]);
5513
- if (pat.kind !== "expr" || pat.items.length === 0 || pat.items[0].kind !== "sym") return void 0;
6022
+ if (pat.kind !== "expr" || pat.items.length === 0 || pat.items[0].kind !== "sym")
6023
+ return void 0;
5514
6024
  const seen = /* @__PURE__ */ new Set();
5515
6025
  for (let i = 1; i < pat.items.length; i++) {
5516
6026
  const a = pat.items[i];
@@ -5523,7 +6033,7 @@ function tryCountAggregate(env, st, bnd, match) {
5523
6033
  const unifies = (a) => a.kind === "var" || a.kind === "expr" && a.items.length === arity && (headKey(a) === k || a.items[0].kind === "var");
5524
6034
  const w = st.world;
5525
6035
  const sn = spaceName(w, inst(env, bnd, match.items[1]));
5526
- if ((sn === void 0 || sn === "&self") && w.store.size === 0 && w.flatSelfExtra === void 0 && env.varHeadedFacts.length === 0 && (env.factIndex.get(k)?.length ?? 0) === 0) {
6036
+ if ((sn === void 0 || sn === "&self") && w.store.size === 0 && env.varHeadedFacts.length === 0 && (env.factIndex.get(k)?.length ?? 0) === 0) {
5527
6037
  let count2 = 0;
5528
6038
  let iterated2 = 0;
5529
6039
  for (let p = w.selfExtra; p !== null; p = p.prev) {
@@ -5533,6 +6043,11 @@ function tryCountAggregate(env, st, bnd, match) {
5533
6043
  if (unifies(p.atom)) count2 += 1;
5534
6044
  }
5535
6045
  }
6046
+ if (w.flatSelfExtra !== void 0) {
6047
+ const flat = w.flatSelfExtra.countHeadArity(k, arity);
6048
+ count2 += flat.count;
6049
+ iterated2 += flat.iterated;
6050
+ }
5536
6051
  return { count: count2, iterated: iterated2 };
5537
6052
  }
5538
6053
  const source = getCandidates(pat);
@@ -5810,7 +6325,7 @@ function* mettaEvalG(env, fuel, st, bnd, a) {
5810
6325
  out.push([errFound, partB]);
5811
6326
  continue;
5812
6327
  }
5813
- const wApp = makeExpr(env, [sym(op), ...partAtoms]);
6328
+ const wApp = partAtoms.every((p, i) => p === args[i]) ? lw : makeExpr(env, [sym(op), ...partAtoms]);
5814
6329
  if (env.curry && partAtoms.length >= 1) {
5815
6330
  const ar = functionArity(env, cur2.world, op);
5816
6331
  if (ar !== void 0 && partAtoms.length < ar) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metta-ts/core",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",