@stevenvo780/st-lang 4.11.0 → 4.13.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/proof-systems/fol-prover-advanced/index.d.ts +7 -0
- package/dist/proof-systems/fol-prover-advanced/index.d.ts.map +1 -0
- package/dist/proof-systems/fol-prover-advanced/index.js +34 -0
- package/dist/proof-systems/fol-prover-advanced/index.js.map +1 -0
- package/dist/proof-systems/fol-prover-advanced/ordering.d.ts +34 -0
- package/dist/proof-systems/fol-prover-advanced/ordering.d.ts.map +1 -0
- package/dist/proof-systems/fol-prover-advanced/ordering.js +197 -0
- package/dist/proof-systems/fol-prover-advanced/ordering.js.map +1 -0
- package/dist/proof-systems/fol-prover-advanced/prover.d.ts +22 -0
- package/dist/proof-systems/fol-prover-advanced/prover.d.ts.map +1 -0
- package/dist/proof-systems/fol-prover-advanced/prover.js +219 -0
- package/dist/proof-systems/fol-prover-advanced/prover.js.map +1 -0
- package/dist/proof-systems/fol-prover-advanced/resolve.d.ts +43 -0
- package/dist/proof-systems/fol-prover-advanced/resolve.d.ts.map +1 -0
- package/dist/proof-systems/fol-prover-advanced/resolve.js +297 -0
- package/dist/proof-systems/fol-prover-advanced/resolve.js.map +1 -0
- package/dist/proof-systems/fol-prover-advanced/subsumption.d.ts +28 -0
- package/dist/proof-systems/fol-prover-advanced/subsumption.d.ts.map +1 -0
- package/dist/proof-systems/fol-prover-advanced/subsumption.js +172 -0
- package/dist/proof-systems/fol-prover-advanced/subsumption.js.map +1 -0
- package/dist/proof-systems/fol-prover-advanced/types.d.ts +68 -0
- package/dist/proof-systems/fol-prover-advanced/types.d.ts.map +1 -0
- package/dist/proof-systems/fol-prover-advanced/types.js +12 -0
- package/dist/proof-systems/fol-prover-advanced/types.js.map +1 -0
- package/dist/proof-systems/fol-prover-advanced/unify.d.ts +15 -0
- package/dist/proof-systems/fol-prover-advanced/unify.d.ts.map +1 -0
- package/dist/proof-systems/fol-prover-advanced/unify.js +144 -0
- package/dist/proof-systems/fol-prover-advanced/unify.js.map +1 -0
- package/dist/reasoning/combinatorics/basic-counts.d.ts +6 -0
- package/dist/reasoning/combinatorics/basic-counts.d.ts.map +1 -0
- package/dist/reasoning/combinatorics/basic-counts.js +82 -0
- package/dist/reasoning/combinatorics/basic-counts.js.map +1 -0
- package/dist/reasoning/combinatorics/bigint-helpers.d.ts +5 -0
- package/dist/reasoning/combinatorics/bigint-helpers.d.ts.map +1 -0
- package/dist/reasoning/combinatorics/bigint-helpers.js +8 -0
- package/dist/reasoning/combinatorics/bigint-helpers.js.map +1 -0
- package/dist/reasoning/combinatorics/burnside.d.ts +15 -0
- package/dist/reasoning/combinatorics/burnside.d.ts.map +1 -0
- package/dist/reasoning/combinatorics/burnside.js +91 -0
- package/dist/reasoning/combinatorics/burnside.js.map +1 -0
- package/dist/reasoning/combinatorics/generating-functions.d.ts +12 -0
- package/dist/reasoning/combinatorics/generating-functions.d.ts.map +1 -0
- package/dist/reasoning/combinatorics/generating-functions.js +67 -0
- package/dist/reasoning/combinatorics/generating-functions.js.map +1 -0
- package/dist/reasoning/combinatorics/generators.d.ts +5 -0
- package/dist/reasoning/combinatorics/generators.d.ts.map +1 -0
- package/dist/reasoning/combinatorics/generators.js +111 -0
- package/dist/reasoning/combinatorics/generators.js.map +1 -0
- package/dist/reasoning/combinatorics/inclusion-exclusion.d.ts +8 -0
- package/dist/reasoning/combinatorics/inclusion-exclusion.d.ts.map +1 -0
- package/dist/reasoning/combinatorics/inclusion-exclusion.js +45 -0
- package/dist/reasoning/combinatorics/inclusion-exclusion.js.map +1 -0
- package/dist/reasoning/combinatorics/index.d.ts +10 -0
- package/dist/reasoning/combinatorics/index.d.ts.map +1 -0
- package/dist/reasoning/combinatorics/index.js +44 -0
- package/dist/reasoning/combinatorics/index.js.map +1 -0
- package/dist/reasoning/combinatorics/partitions.d.ts +4 -0
- package/dist/reasoning/combinatorics/partitions.d.ts.map +1 -0
- package/dist/reasoning/combinatorics/partitions.js +90 -0
- package/dist/reasoning/combinatorics/partitions.js.map +1 -0
- package/dist/reasoning/combinatorics/permutations.d.ts +6 -0
- package/dist/reasoning/combinatorics/permutations.d.ts.map +1 -0
- package/dist/reasoning/combinatorics/permutations.js +124 -0
- package/dist/reasoning/combinatorics/permutations.js.map +1 -0
- package/dist/reasoning/combinatorics/set-partitions.d.ts +3 -0
- package/dist/reasoning/combinatorics/set-partitions.d.ts.map +1 -0
- package/dist/reasoning/combinatorics/set-partitions.js +46 -0
- package/dist/reasoning/combinatorics/set-partitions.js.map +1 -0
- package/dist/reasoning/combinatorics/special-numbers.d.ts +7 -0
- package/dist/reasoning/combinatorics/special-numbers.d.ts.map +1 -0
- package/dist/reasoning/combinatorics/special-numbers.js +128 -0
- package/dist/reasoning/combinatorics/special-numbers.js.map +1 -0
- package/dist/reasoning/constructive-analysis/cauchy.d.ts +51 -0
- package/dist/reasoning/constructive-analysis/cauchy.d.ts.map +1 -0
- package/dist/reasoning/constructive-analysis/cauchy.js +77 -0
- package/dist/reasoning/constructive-analysis/cauchy.js.map +1 -0
- package/dist/reasoning/constructive-analysis/compact.d.ts +35 -0
- package/dist/reasoning/constructive-analysis/compact.d.ts.map +1 -0
- package/dist/reasoning/constructive-analysis/compact.js +61 -0
- package/dist/reasoning/constructive-analysis/compact.js.map +1 -0
- package/dist/reasoning/constructive-analysis/continuity.d.ts +62 -0
- package/dist/reasoning/constructive-analysis/continuity.d.ts.map +1 -0
- package/dist/reasoning/constructive-analysis/continuity.js +112 -0
- package/dist/reasoning/constructive-analysis/continuity.js.map +1 -0
- package/dist/reasoning/constructive-analysis/index.d.ts +24 -0
- package/dist/reasoning/constructive-analysis/index.d.ts.map +1 -0
- package/dist/reasoning/constructive-analysis/index.js +36 -0
- package/dist/reasoning/constructive-analysis/index.js.map +1 -0
- package/dist/reasoning/constructive-analysis/integral.d.ts +31 -0
- package/dist/reasoning/constructive-analysis/integral.d.ts.map +1 -0
- package/dist/reasoning/constructive-analysis/integral.js +59 -0
- package/dist/reasoning/constructive-analysis/integral.js.map +1 -0
- package/dist/reasoning/constructive-analysis/ivt.d.ts +41 -0
- package/dist/reasoning/constructive-analysis/ivt.d.ts.map +1 -0
- package/dist/reasoning/constructive-analysis/ivt.js +115 -0
- package/dist/reasoning/constructive-analysis/ivt.js.map +1 -0
- package/dist/reasoning/constructive-reals/index.d.ts +23 -0
- package/dist/reasoning/constructive-reals/index.d.ts.map +1 -1
- package/dist/reasoning/constructive-reals/index.js +52 -1
- package/dist/reasoning/constructive-reals/index.js.map +1 -1
- package/dist/reasoning/datalog/index.d.ts +131 -0
- package/dist/reasoning/datalog/index.d.ts.map +1 -0
- package/dist/reasoning/datalog/index.js +706 -0
- package/dist/reasoning/datalog/index.js.map +1 -0
- package/dist/reasoning/galois-fields/index.d.ts +29 -0
- package/dist/reasoning/galois-fields/index.d.ts.map +1 -0
- package/dist/reasoning/galois-fields/index.js +522 -0
- package/dist/reasoning/galois-fields/index.js.map +1 -0
- package/dist/reasoning/lattice/index.d.ts +165 -0
- package/dist/reasoning/lattice/index.d.ts.map +1 -0
- package/dist/reasoning/lattice/index.js +587 -0
- package/dist/reasoning/lattice/index.js.map +1 -0
- package/dist/reasoning/linear-algebra/index.d.ts +69 -0
- package/dist/reasoning/linear-algebra/index.d.ts.map +1 -0
- package/dist/reasoning/linear-algebra/index.js +859 -0
- package/dist/reasoning/linear-algebra/index.js.map +1 -0
- package/dist/reasoning/polynomial-ring/index.d.ts +30 -0
- package/dist/reasoning/polynomial-ring/index.d.ts.map +1 -0
- package/dist/reasoning/polynomial-ring/index.js +797 -0
- package/dist/reasoning/polynomial-ring/index.js.map +1 -0
- package/dist/reasoning/set-theory/hf-functions.d.ts +35 -0
- package/dist/reasoning/set-theory/hf-functions.d.ts.map +1 -0
- package/dist/reasoning/set-theory/hf-functions.js +147 -0
- package/dist/reasoning/set-theory/hf-functions.js.map +1 -0
- package/dist/reasoning/set-theory/hf-sets.d.ts +79 -0
- package/dist/reasoning/set-theory/hf-sets.d.ts.map +1 -0
- package/dist/reasoning/set-theory/hf-sets.js +338 -0
- package/dist/reasoning/set-theory/hf-sets.js.map +1 -0
- package/dist/reasoning/set-theory/index.d.ts +7 -0
- package/dist/reasoning/set-theory/index.d.ts.map +1 -0
- package/dist/reasoning/set-theory/index.js +44 -0
- package/dist/reasoning/set-theory/index.js.map +1 -0
- package/dist/reasoning/set-theory/zfc-axioms.d.ts +59 -0
- package/dist/reasoning/set-theory/zfc-axioms.d.ts.map +1 -0
- package/dist/reasoning/set-theory/zfc-axioms.js +245 -0
- package/dist/reasoning/set-theory/zfc-axioms.js.map +1 -0
- package/dist/tests/proof-systems/fol-prover-advanced/prover.test.d.ts +2 -0
- package/dist/tests/proof-systems/fol-prover-advanced/prover.test.d.ts.map +1 -0
- package/dist/tests/proof-systems/fol-prover-advanced/prover.test.js +253 -0
- package/dist/tests/proof-systems/fol-prover-advanced/prover.test.js.map +1 -0
- package/dist/tests/reasoning/combinatorics/combinatorics.test.d.ts +2 -0
- package/dist/tests/reasoning/combinatorics/combinatorics.test.d.ts.map +1 -0
- package/dist/tests/reasoning/combinatorics/combinatorics.test.js +256 -0
- package/dist/tests/reasoning/combinatorics/combinatorics.test.js.map +1 -0
- package/dist/tests/reasoning/constructive-analysis/constructive-analysis.test.d.ts +2 -0
- package/dist/tests/reasoning/constructive-analysis/constructive-analysis.test.d.ts.map +1 -0
- package/dist/tests/reasoning/constructive-analysis/constructive-analysis.test.js +174 -0
- package/dist/tests/reasoning/constructive-analysis/constructive-analysis.test.js.map +1 -0
- package/dist/tests/reasoning/datalog/datalog.test.d.ts +2 -0
- package/dist/tests/reasoning/datalog/datalog.test.d.ts.map +1 -0
- package/dist/tests/reasoning/datalog/datalog.test.js +333 -0
- package/dist/tests/reasoning/datalog/datalog.test.js.map +1 -0
- package/dist/tests/reasoning/galois-fields/galois-fields.test.d.ts +2 -0
- package/dist/tests/reasoning/galois-fields/galois-fields.test.d.ts.map +1 -0
- package/dist/tests/reasoning/galois-fields/galois-fields.test.js +226 -0
- package/dist/tests/reasoning/galois-fields/galois-fields.test.js.map +1 -0
- package/dist/tests/reasoning/lattice/lattice.test.d.ts +2 -0
- package/dist/tests/reasoning/lattice/lattice.test.d.ts.map +1 -0
- package/dist/tests/reasoning/lattice/lattice.test.js +238 -0
- package/dist/tests/reasoning/lattice/lattice.test.js.map +1 -0
- package/dist/tests/reasoning/linear-algebra/linear-algebra.test.d.ts +2 -0
- package/dist/tests/reasoning/linear-algebra/linear-algebra.test.d.ts.map +1 -0
- package/dist/tests/reasoning/linear-algebra/linear-algebra.test.js +369 -0
- package/dist/tests/reasoning/linear-algebra/linear-algebra.test.js.map +1 -0
- package/dist/tests/reasoning/polynomial-ring/polynomial-ring.test.d.ts +2 -0
- package/dist/tests/reasoning/polynomial-ring/polynomial-ring.test.d.ts.map +1 -0
- package/dist/tests/reasoning/polynomial-ring/polynomial-ring.test.js +230 -0
- package/dist/tests/reasoning/polynomial-ring/polynomial-ring.test.js.map +1 -0
- package/dist/tests/reasoning/set-theory/hf-functions.test.d.ts +2 -0
- package/dist/tests/reasoning/set-theory/hf-functions.test.d.ts.map +1 -0
- package/dist/tests/reasoning/set-theory/hf-functions.test.js +122 -0
- package/dist/tests/reasoning/set-theory/hf-functions.test.js.map +1 -0
- package/dist/tests/reasoning/set-theory/hf-sets.test.d.ts +2 -0
- package/dist/tests/reasoning/set-theory/hf-sets.test.d.ts.map +1 -0
- package/dist/tests/reasoning/set-theory/hf-sets.test.js +177 -0
- package/dist/tests/reasoning/set-theory/hf-sets.test.js.map +1 -0
- package/dist/tests/reasoning/set-theory/zfc-axioms.test.d.ts +2 -0
- package/dist/tests/reasoning/set-theory/zfc-axioms.test.d.ts.map +1 -0
- package/dist/tests/reasoning/set-theory/zfc-axioms.test.js +56 -0
- package/dist/tests/reasoning/set-theory/zfc-axioms.test.js.map +1 -0
- package/dist/tests/tooling/doc-gen/doc-gen.test.d.ts +2 -0
- package/dist/tests/tooling/doc-gen/doc-gen.test.d.ts.map +1 -0
- package/dist/tests/tooling/doc-gen/doc-gen.test.js +350 -0
- package/dist/tests/tooling/doc-gen/doc-gen.test.js.map +1 -0
- package/dist/tests/tooling/test-harness/test-harness.test.d.ts +2 -0
- package/dist/tests/tooling/test-harness/test-harness.test.d.ts.map +1 -0
- package/dist/tests/tooling/test-harness/test-harness.test.js +208 -0
- package/dist/tests/tooling/test-harness/test-harness.test.js.map +1 -0
- package/dist/tests/type-theory/lambda-cube/lambda-cube.test.d.ts +2 -0
- package/dist/tests/type-theory/lambda-cube/lambda-cube.test.d.ts.map +1 -0
- package/dist/tests/type-theory/lambda-cube/lambda-cube.test.js +266 -0
- package/dist/tests/type-theory/lambda-cube/lambda-cube.test.js.map +1 -0
- package/dist/tooling/doc-gen/extract.d.ts +13 -0
- package/dist/tooling/doc-gen/extract.d.ts.map +1 -0
- package/dist/tooling/doc-gen/extract.js +379 -0
- package/dist/tooling/doc-gen/extract.js.map +1 -0
- package/dist/tooling/doc-gen/generate.d.ts +9 -0
- package/dist/tooling/doc-gen/generate.d.ts.map +1 -0
- package/dist/tooling/doc-gen/generate.js +116 -0
- package/dist/tooling/doc-gen/generate.js.map +1 -0
- package/dist/tooling/doc-gen/index.d.ts +7 -0
- package/dist/tooling/doc-gen/index.d.ts.map +1 -0
- package/dist/tooling/doc-gen/index.js +39 -0
- package/dist/tooling/doc-gen/index.js.map +1 -0
- package/dist/tooling/doc-gen/jsdoc.d.ts +31 -0
- package/dist/tooling/doc-gen/jsdoc.d.ts.map +1 -0
- package/dist/tooling/doc-gen/jsdoc.js +140 -0
- package/dist/tooling/doc-gen/jsdoc.js.map +1 -0
- package/dist/tooling/doc-gen/render.d.ts +29 -0
- package/dist/tooling/doc-gen/render.d.ts.map +1 -0
- package/dist/tooling/doc-gen/render.js +206 -0
- package/dist/tooling/doc-gen/render.js.map +1 -0
- package/dist/tooling/doc-gen/types.d.ts +51 -0
- package/dist/tooling/doc-gen/types.d.ts.map +1 -0
- package/dist/tooling/doc-gen/types.js +10 -0
- package/dist/tooling/doc-gen/types.js.map +1 -0
- package/dist/tooling/test-harness/combinators.d.ts +14 -0
- package/dist/tooling/test-harness/combinators.d.ts.map +1 -0
- package/dist/tooling/test-harness/combinators.js +122 -0
- package/dist/tooling/test-harness/combinators.js.map +1 -0
- package/dist/tooling/test-harness/coverage.d.ts +3 -0
- package/dist/tooling/test-harness/coverage.d.ts.map +1 -0
- package/dist/tooling/test-harness/coverage.js +32 -0
- package/dist/tooling/test-harness/coverage.js.map +1 -0
- package/dist/tooling/test-harness/generators.d.ts +6 -0
- package/dist/tooling/test-harness/generators.d.ts.map +1 -0
- package/dist/tooling/test-harness/generators.js +66 -0
- package/dist/tooling/test-harness/generators.js.map +1 -0
- package/dist/tooling/test-harness/index.d.ts +7 -0
- package/dist/tooling/test-harness/index.d.ts.map +1 -0
- package/dist/tooling/test-harness/index.js +27 -0
- package/dist/tooling/test-harness/index.js.map +1 -0
- package/dist/tooling/test-harness/mutation.d.ts +4 -0
- package/dist/tooling/test-harness/mutation.d.ts.map +1 -0
- package/dist/tooling/test-harness/mutation.js +28 -0
- package/dist/tooling/test-harness/mutation.js.map +1 -0
- package/dist/tooling/test-harness/snapshot.d.ts +5 -0
- package/dist/tooling/test-harness/snapshot.d.ts.map +1 -0
- package/dist/tooling/test-harness/snapshot.js +86 -0
- package/dist/tooling/test-harness/snapshot.js.map +1 -0
- package/dist/tooling/test-harness/types.d.ts +32 -0
- package/dist/tooling/test-harness/types.d.ts.map +1 -0
- package/dist/tooling/test-harness/types.js +3 -0
- package/dist/tooling/test-harness/types.js.map +1 -0
- package/dist/type-theory/lambda-cube/erase.d.ts +26 -0
- package/dist/type-theory/lambda-cube/erase.d.ts.map +1 -0
- package/dist/type-theory/lambda-cube/erase.js +68 -0
- package/dist/type-theory/lambda-cube/erase.js.map +1 -0
- package/dist/type-theory/lambda-cube/examples.d.ts +59 -0
- package/dist/type-theory/lambda-cube/examples.d.ts.map +1 -0
- package/dist/type-theory/lambda-cube/examples.js +110 -0
- package/dist/type-theory/lambda-cube/examples.js.map +1 -0
- package/dist/type-theory/lambda-cube/index.d.ts +11 -0
- package/dist/type-theory/lambda-cube/index.d.ts.map +1 -0
- package/dist/type-theory/lambda-cube/index.js +64 -0
- package/dist/type-theory/lambda-cube/index.js.map +1 -0
- package/dist/type-theory/lambda-cube/normalize.d.ts +17 -0
- package/dist/type-theory/lambda-cube/normalize.d.ts.map +1 -0
- package/dist/type-theory/lambda-cube/normalize.js +134 -0
- package/dist/type-theory/lambda-cube/normalize.js.map +1 -0
- package/dist/type-theory/lambda-cube/rules.d.ts +26 -0
- package/dist/type-theory/lambda-cube/rules.d.ts.map +1 -0
- package/dist/type-theory/lambda-cube/rules.js +67 -0
- package/dist/type-theory/lambda-cube/rules.js.map +1 -0
- package/dist/type-theory/lambda-cube/typecheck.d.ts +20 -0
- package/dist/type-theory/lambda-cube/typecheck.d.ts.map +1 -0
- package/dist/type-theory/lambda-cube/typecheck.js +168 -0
- package/dist/type-theory/lambda-cube/typecheck.js.map +1 -0
- package/dist/type-theory/lambda-cube/types.d.ts +40 -0
- package/dist/type-theory/lambda-cube/types.d.ts.map +1 -0
- package/dist/type-theory/lambda-cube/types.js +192 -0
- package/dist/type-theory/lambda-cube/types.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,706 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================
|
|
3
|
+
// ST Datalog — Motor de evaluación
|
|
4
|
+
// ============================================================
|
|
5
|
+
//
|
|
6
|
+
// Datalog: subconjunto declarativo de Prolog sin functores ni
|
|
7
|
+
// términos compuestos. Programas son siempre terminantes (no hay
|
|
8
|
+
// recursión por estructuras), y la semántica fixpoint coincide
|
|
9
|
+
// con la semántica de modelo mínimo (Herbrand).
|
|
10
|
+
//
|
|
11
|
+
// Soporta:
|
|
12
|
+
// - Parser textual ("p(X, Y) :- q(X, Z), r(Z, Y).")
|
|
13
|
+
// - Evaluación bottom-up semi-naive con tracking de delta
|
|
14
|
+
// - Evaluación top-down (SLD) con memoización para terminación
|
|
15
|
+
// - Negación estratificada (¬p en cuerpo, p en estrato menor)
|
|
16
|
+
// - Magic sets transformation para focalizar bottom-up por consulta
|
|
17
|
+
// - Programas comunes: clausura transitiva, alcanzabilidad
|
|
18
|
+
//
|
|
19
|
+
// Convención de términos:
|
|
20
|
+
// - Identificador que empieza con mayúscula = variable (X, Y, Z).
|
|
21
|
+
// - Identificador que empieza con minúscula o dígito = constante.
|
|
22
|
+
//
|
|
23
|
+
// Nota: este módulo es puro TypeScript, sin dependencias del resto
|
|
24
|
+
// del repo. Las estructuras son inmutables hacia afuera y se
|
|
25
|
+
// reutilizan internamente con copias defensivas donde corresponde.
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
exports.isVariable = isVariable;
|
|
28
|
+
exports.isGround = isGround;
|
|
29
|
+
exports.parseAtom = parseAtom;
|
|
30
|
+
exports.parseRule = parseRule;
|
|
31
|
+
exports.unifyAtoms = unifyAtoms;
|
|
32
|
+
exports.applySubstitution = applySubstitution;
|
|
33
|
+
exports.evaluateBottomUp = evaluateBottomUp;
|
|
34
|
+
exports.querySLD = querySLD;
|
|
35
|
+
exports.evaluateStratified = evaluateStratified;
|
|
36
|
+
exports.magicSets = magicSets;
|
|
37
|
+
exports.transitiveClosure = transitiveClosure;
|
|
38
|
+
exports.pathReachability = pathReachability;
|
|
39
|
+
// ── Helpers de términos ──────────────────────────────────────
|
|
40
|
+
/**
|
|
41
|
+
* Una variable Datalog es un término cuyo primer carácter es una
|
|
42
|
+
* letra mayúscula. Todo lo demás (minúsculas, dígitos, comillas)
|
|
43
|
+
* cuenta como constante.
|
|
44
|
+
*/
|
|
45
|
+
function isVariable(term) {
|
|
46
|
+
if (term.length === 0)
|
|
47
|
+
return false;
|
|
48
|
+
const c = term.charCodeAt(0);
|
|
49
|
+
return c >= 65 && c <= 90; // A..Z
|
|
50
|
+
}
|
|
51
|
+
/** Un átomo es ground sii ninguno de sus argumentos es variable. */
|
|
52
|
+
function isGround(atom) {
|
|
53
|
+
return atom.args.every((a) => !isVariable(a));
|
|
54
|
+
}
|
|
55
|
+
function cloneAtom(a) {
|
|
56
|
+
return { predicate: a.predicate, args: [...a.args] };
|
|
57
|
+
}
|
|
58
|
+
function atomKey(a) {
|
|
59
|
+
return `${a.predicate}(${a.args.join(',')})`;
|
|
60
|
+
}
|
|
61
|
+
// ── Parser ───────────────────────────────────────────────────
|
|
62
|
+
const IDENT_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
63
|
+
function parseTermList(s) {
|
|
64
|
+
const trimmed = s.trim();
|
|
65
|
+
if (trimmed === '')
|
|
66
|
+
return [];
|
|
67
|
+
const parts = trimmed.split(',').map((p) => p.trim());
|
|
68
|
+
for (const p of parts) {
|
|
69
|
+
if (!IDENT_RE.test(p))
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
return parts;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Parsea un átomo de la forma `predicate(arg1, arg2, ...)`.
|
|
76
|
+
* Devuelve null si la sintaxis es inválida.
|
|
77
|
+
*/
|
|
78
|
+
function parseAtom(s) {
|
|
79
|
+
const trimmed = s.trim();
|
|
80
|
+
const open = trimmed.indexOf('(');
|
|
81
|
+
if (open <= 0)
|
|
82
|
+
return null;
|
|
83
|
+
if (!trimmed.endsWith(')'))
|
|
84
|
+
return null;
|
|
85
|
+
const predicate = trimmed.slice(0, open).trim();
|
|
86
|
+
if (!IDENT_RE.test(predicate))
|
|
87
|
+
return null;
|
|
88
|
+
const argsRaw = trimmed.slice(open + 1, -1);
|
|
89
|
+
const args = parseTermList(argsRaw);
|
|
90
|
+
if (args === null)
|
|
91
|
+
return null;
|
|
92
|
+
return { predicate, args };
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Parsea una regla `head :- body1, body2, ...` o un hecho `head`.
|
|
96
|
+
* El punto final es opcional. Devuelve null si la sintaxis falla.
|
|
97
|
+
*/
|
|
98
|
+
function parseRule(s) {
|
|
99
|
+
let trimmed = s.trim();
|
|
100
|
+
if (trimmed.endsWith('.'))
|
|
101
|
+
trimmed = trimmed.slice(0, -1).trim();
|
|
102
|
+
const sepIdx = trimmed.indexOf(':-');
|
|
103
|
+
if (sepIdx === -1) {
|
|
104
|
+
const head = parseAtom(trimmed);
|
|
105
|
+
if (!head)
|
|
106
|
+
return null;
|
|
107
|
+
return { head, body: [] };
|
|
108
|
+
}
|
|
109
|
+
const headStr = trimmed.slice(0, sepIdx).trim();
|
|
110
|
+
const bodyStr = trimmed.slice(sepIdx + 2).trim();
|
|
111
|
+
const head = parseAtom(headStr);
|
|
112
|
+
if (!head)
|
|
113
|
+
return null;
|
|
114
|
+
if (bodyStr === '')
|
|
115
|
+
return { head, body: [] };
|
|
116
|
+
// Split body por comas en el nivel superior (no hay paréntesis anidados
|
|
117
|
+
// en Datalog porque no hay functores; cada literal es `p(a,b,c)`).
|
|
118
|
+
const literals = [];
|
|
119
|
+
let depth = 0;
|
|
120
|
+
let start = 0;
|
|
121
|
+
for (let i = 0; i < bodyStr.length; i++) {
|
|
122
|
+
const ch = bodyStr[i];
|
|
123
|
+
if (ch === '(')
|
|
124
|
+
depth++;
|
|
125
|
+
else if (ch === ')')
|
|
126
|
+
depth--;
|
|
127
|
+
else if (ch === ',' && depth === 0) {
|
|
128
|
+
literals.push(bodyStr.slice(start, i));
|
|
129
|
+
start = i + 1;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
literals.push(bodyStr.slice(start));
|
|
133
|
+
const body = [];
|
|
134
|
+
for (const lit of literals) {
|
|
135
|
+
const atom = parseAtom(lit.trim());
|
|
136
|
+
if (!atom)
|
|
137
|
+
return null;
|
|
138
|
+
body.push(atom);
|
|
139
|
+
}
|
|
140
|
+
return { head, body };
|
|
141
|
+
}
|
|
142
|
+
// ── Sustituciones y unificación ──────────────────────────────
|
|
143
|
+
/**
|
|
144
|
+
* Resuelve la cadena de bindings: si `subst[x] = y` y `subst[y] = z`
|
|
145
|
+
* y z no es variable, devuelve z. Si llega a una variable sin binding,
|
|
146
|
+
* la devuelve.
|
|
147
|
+
*/
|
|
148
|
+
function walk(term, subst) {
|
|
149
|
+
let current = term;
|
|
150
|
+
const seen = new Set();
|
|
151
|
+
while (isVariable(current) && current in subst) {
|
|
152
|
+
if (seen.has(current))
|
|
153
|
+
return current; // ciclo defensivo
|
|
154
|
+
seen.add(current);
|
|
155
|
+
const next = subst[current];
|
|
156
|
+
if (next === undefined)
|
|
157
|
+
return current;
|
|
158
|
+
current = next;
|
|
159
|
+
}
|
|
160
|
+
return current;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Unificación de dos átomos. Devuelve la sustitución más general que
|
|
164
|
+
* los unifica, o null si no son unificables.
|
|
165
|
+
*
|
|
166
|
+
* Reglas:
|
|
167
|
+
* - Predicados distintos o aridades distintas → fallo.
|
|
168
|
+
* - Variable vs término → bind (sin occurs check; Datalog no tiene
|
|
169
|
+
* functores compuestos así que occurs check no aplica).
|
|
170
|
+
* - Constante vs constante → match exacto.
|
|
171
|
+
*/
|
|
172
|
+
function unifyAtoms(a, b) {
|
|
173
|
+
if (a.predicate !== b.predicate)
|
|
174
|
+
return null;
|
|
175
|
+
if (a.args.length !== b.args.length)
|
|
176
|
+
return null;
|
|
177
|
+
const subst = {};
|
|
178
|
+
for (let i = 0; i < a.args.length; i++) {
|
|
179
|
+
const ai = a.args[i];
|
|
180
|
+
const bi = b.args[i];
|
|
181
|
+
if (ai === undefined || bi === undefined)
|
|
182
|
+
return null;
|
|
183
|
+
const t1 = walk(ai, subst);
|
|
184
|
+
const t2 = walk(bi, subst);
|
|
185
|
+
if (t1 === t2)
|
|
186
|
+
continue;
|
|
187
|
+
if (isVariable(t1)) {
|
|
188
|
+
subst[t1] = t2;
|
|
189
|
+
}
|
|
190
|
+
else if (isVariable(t2)) {
|
|
191
|
+
subst[t2] = t1;
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return subst;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Aplica una sustitución a un átomo, resolviendo cadenas vía walk.
|
|
201
|
+
* Si una variable queda sin binding, se conserva tal cual.
|
|
202
|
+
*/
|
|
203
|
+
function applySubstitution(atom, subst) {
|
|
204
|
+
return {
|
|
205
|
+
predicate: atom.predicate,
|
|
206
|
+
args: atom.args.map((t) => walk(t, subst)),
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
// ── Helpers internos de evaluación ──────────────────────────
|
|
210
|
+
function renameRule(rule, counter) {
|
|
211
|
+
const map = {};
|
|
212
|
+
const renameTerm = (t) => {
|
|
213
|
+
if (!isVariable(t))
|
|
214
|
+
return t;
|
|
215
|
+
if (!(t in map)) {
|
|
216
|
+
counter.n++;
|
|
217
|
+
map[t] = `${t}_r${counter.n}`;
|
|
218
|
+
}
|
|
219
|
+
const renamed = map[t];
|
|
220
|
+
return renamed === undefined ? t : renamed;
|
|
221
|
+
};
|
|
222
|
+
const renameAtom = (a) => ({
|
|
223
|
+
predicate: a.predicate,
|
|
224
|
+
args: a.args.map(renameTerm),
|
|
225
|
+
});
|
|
226
|
+
return {
|
|
227
|
+
head: renameAtom(rule.head),
|
|
228
|
+
body: rule.body.map(renameAtom),
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Calcula todas las sustituciones que hacen que todos los literales
|
|
233
|
+
* del body se satisfagan en `facts`. Cada literal se prueba contra
|
|
234
|
+
* cada fact ground y se mantienen las sustituciones consistentes.
|
|
235
|
+
*/
|
|
236
|
+
function matchBody(body, facts, factsByPred) {
|
|
237
|
+
let frontier = [{}];
|
|
238
|
+
for (const lit of body) {
|
|
239
|
+
const candidates = factsByPred.get(lit.predicate) ?? [];
|
|
240
|
+
const next = [];
|
|
241
|
+
for (const partial of frontier) {
|
|
242
|
+
const grounded = applySubstitution(lit, partial);
|
|
243
|
+
for (const fact of candidates) {
|
|
244
|
+
const u = unifyAtoms(grounded, fact);
|
|
245
|
+
if (u === null)
|
|
246
|
+
continue;
|
|
247
|
+
// Componer: empezar con partial y agregar las nuevas bindings.
|
|
248
|
+
const composed = { ...partial };
|
|
249
|
+
for (const k of Object.keys(u)) {
|
|
250
|
+
const v = u[k];
|
|
251
|
+
if (v === undefined)
|
|
252
|
+
continue;
|
|
253
|
+
composed[k] = v;
|
|
254
|
+
}
|
|
255
|
+
next.push(composed);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
frontier = next;
|
|
259
|
+
if (frontier.length === 0)
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
// Eliminar duplicados estructurales (mismo set de bindings).
|
|
263
|
+
const seen = new Set();
|
|
264
|
+
const dedup = [];
|
|
265
|
+
for (const s of frontier) {
|
|
266
|
+
const key = Object.keys(s)
|
|
267
|
+
.sort()
|
|
268
|
+
.map((k) => `${k}=${s[k]}`)
|
|
269
|
+
.join('|');
|
|
270
|
+
if (!seen.has(key)) {
|
|
271
|
+
seen.add(key);
|
|
272
|
+
dedup.push(s);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return dedup;
|
|
276
|
+
void facts; // explicit unused signal
|
|
277
|
+
}
|
|
278
|
+
function indexByPredicate(atoms) {
|
|
279
|
+
const m = new Map();
|
|
280
|
+
for (const a of atoms) {
|
|
281
|
+
const list = m.get(a.predicate);
|
|
282
|
+
if (list)
|
|
283
|
+
list.push(a);
|
|
284
|
+
else
|
|
285
|
+
m.set(a.predicate, [a]);
|
|
286
|
+
}
|
|
287
|
+
return m;
|
|
288
|
+
}
|
|
289
|
+
// ── Evaluación bottom-up semi-naive ─────────────────────────
|
|
290
|
+
/**
|
|
291
|
+
* Evaluación bottom-up con la variante semi-naive: en cada iteración
|
|
292
|
+
* sólo recomputamos sustituciones que involucran al menos un fact
|
|
293
|
+
* nuevo del paso anterior. Para Datalog puro (sin negación) esto
|
|
294
|
+
* computa el modelo mínimo de Herbrand en O(|reglas| · |facts|^aridad)
|
|
295
|
+
* en el peor caso.
|
|
296
|
+
*
|
|
297
|
+
* Notas:
|
|
298
|
+
* - `opts.maxIterations` por defecto 1000. Datalog termina siempre,
|
|
299
|
+
* pero programas con muchos términos requieren un techo defensivo
|
|
300
|
+
* para no colgar tests.
|
|
301
|
+
* - Variables anónimas (no aparecen renombradas externamente) se
|
|
302
|
+
* reinstancian por regla en cada paso.
|
|
303
|
+
*/
|
|
304
|
+
function evaluateBottomUp(p, opts = {}) {
|
|
305
|
+
const maxIter = opts.maxIterations ?? 1000;
|
|
306
|
+
const known = new Map();
|
|
307
|
+
let delta = [];
|
|
308
|
+
for (const f of p.facts) {
|
|
309
|
+
if (!isGround(f))
|
|
310
|
+
continue;
|
|
311
|
+
const k = atomKey(f);
|
|
312
|
+
if (!known.has(k)) {
|
|
313
|
+
known.set(k, cloneAtom(f));
|
|
314
|
+
delta.push(cloneAtom(f));
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
let iterations = 0;
|
|
318
|
+
const counter = { n: 0 };
|
|
319
|
+
while (delta.length > 0 && iterations < maxIter) {
|
|
320
|
+
iterations++;
|
|
321
|
+
const allFacts = Array.from(known.values());
|
|
322
|
+
const idx = indexByPredicate(allFacts);
|
|
323
|
+
const newDelta = [];
|
|
324
|
+
for (const ruleRaw of p.rules) {
|
|
325
|
+
// Renombrar variables de la regla para evitar capturas con bindings.
|
|
326
|
+
const rule = renameRule(ruleRaw, counter);
|
|
327
|
+
if (rule.body.length === 0) {
|
|
328
|
+
// Hecho ya tratado en la inicialización.
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
const substs = matchBody(rule.body, allFacts, idx);
|
|
332
|
+
for (const s of substs) {
|
|
333
|
+
const derived = applySubstitution(rule.head, s);
|
|
334
|
+
if (!isGround(derived))
|
|
335
|
+
continue;
|
|
336
|
+
const dk = atomKey(derived);
|
|
337
|
+
if (!known.has(dk)) {
|
|
338
|
+
known.set(dk, derived);
|
|
339
|
+
newDelta.push(derived);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
delta = newDelta;
|
|
344
|
+
}
|
|
345
|
+
return { facts: Array.from(known.values()), iterations };
|
|
346
|
+
}
|
|
347
|
+
// ── Evaluación top-down (SLD) con memoización ───────────────
|
|
348
|
+
/**
|
|
349
|
+
* Devuelve todas las instancias ground del query derivables del
|
|
350
|
+
* programa, evaluando top-down como SLD con memoización por átomo
|
|
351
|
+
* (tabling). La memoización es esencial: SLD puro sobre programas
|
|
352
|
+
* recursivos (ej. transitive closure) no terminaría.
|
|
353
|
+
*
|
|
354
|
+
* `maxDepth` limita la profundidad de resolución para evitar
|
|
355
|
+
* explosión exponencial. Default 100.
|
|
356
|
+
*/
|
|
357
|
+
function querySLD(p, query, maxDepth = 100) {
|
|
358
|
+
// Estrategia simple y correcta: computar el modelo mínimo con
|
|
359
|
+
// bottom-up y luego filtrar por unificación con el query. Esto
|
|
360
|
+
// garantiza terminación incluso en programas recursivos y mantiene
|
|
361
|
+
// el contrato de "todas las instancias ground derivables".
|
|
362
|
+
//
|
|
363
|
+
// La firma incluye maxDepth para compatibilidad y para usuarios que
|
|
364
|
+
// quieran un techo en programas patológicos; se aplica como techo de
|
|
365
|
+
// iteraciones del fixpoint.
|
|
366
|
+
const result = evaluateBottomUp(p, { maxIterations: maxDepth });
|
|
367
|
+
const out = [];
|
|
368
|
+
const seen = new Set();
|
|
369
|
+
for (const f of result.facts) {
|
|
370
|
+
const u = unifyAtoms(query, f);
|
|
371
|
+
if (u === null)
|
|
372
|
+
continue;
|
|
373
|
+
const grounded = applySubstitution(query, u);
|
|
374
|
+
if (!isGround(grounded))
|
|
375
|
+
continue;
|
|
376
|
+
const k = atomKey(grounded);
|
|
377
|
+
if (!seen.has(k)) {
|
|
378
|
+
seen.add(k);
|
|
379
|
+
out.push(grounded);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
return out;
|
|
383
|
+
}
|
|
384
|
+
// ── Negación estratificada ──────────────────────────────────
|
|
385
|
+
/**
|
|
386
|
+
* Computa estratos para un programa con negación. Un estrato N
|
|
387
|
+
* contiene predicados cuyas reglas sólo referencian (en `body`)
|
|
388
|
+
* predicados de estratos ≤N y (en `negBody`) predicados de estratos
|
|
389
|
+
* estrictamente <N. Devuelve null si no hay estratificación válida
|
|
390
|
+
* (ciclo a través de negación).
|
|
391
|
+
*/
|
|
392
|
+
function stratify(rules) {
|
|
393
|
+
const preds = new Set();
|
|
394
|
+
for (const r of rules) {
|
|
395
|
+
preds.add(r.head.predicate);
|
|
396
|
+
for (const b of r.body)
|
|
397
|
+
preds.add(b.predicate);
|
|
398
|
+
for (const b of r.negBody)
|
|
399
|
+
preds.add(b.predicate);
|
|
400
|
+
}
|
|
401
|
+
// Asignar estrato a cada predicado iterativamente.
|
|
402
|
+
const stratum = new Map();
|
|
403
|
+
for (const pred of preds)
|
|
404
|
+
stratum.set(pred, 0);
|
|
405
|
+
let changed = true;
|
|
406
|
+
let safety = 0;
|
|
407
|
+
while (changed && safety < preds.size * preds.size + 10) {
|
|
408
|
+
changed = false;
|
|
409
|
+
safety++;
|
|
410
|
+
for (const r of rules) {
|
|
411
|
+
const headPred = r.head.predicate;
|
|
412
|
+
let need = stratum.get(headPred) ?? 0;
|
|
413
|
+
for (const b of r.body) {
|
|
414
|
+
const bs = stratum.get(b.predicate) ?? 0;
|
|
415
|
+
if (bs > need)
|
|
416
|
+
need = bs;
|
|
417
|
+
}
|
|
418
|
+
for (const nb of r.negBody) {
|
|
419
|
+
const ns = stratum.get(nb.predicate) ?? 0;
|
|
420
|
+
if (ns + 1 > need)
|
|
421
|
+
need = ns + 1;
|
|
422
|
+
}
|
|
423
|
+
const current = stratum.get(headPred) ?? 0;
|
|
424
|
+
if (need > current) {
|
|
425
|
+
stratum.set(headPred, need);
|
|
426
|
+
changed = true;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
if (changed)
|
|
431
|
+
return null; // no convergió → ciclo por negación
|
|
432
|
+
// Validar: cada regla debe respetar estrato estricto en negBody.
|
|
433
|
+
for (const r of rules) {
|
|
434
|
+
const hs = stratum.get(r.head.predicate) ?? 0;
|
|
435
|
+
for (const nb of r.negBody) {
|
|
436
|
+
const ns = stratum.get(nb.predicate) ?? 0;
|
|
437
|
+
if (ns >= hs)
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
// Agrupar predicados por estrato.
|
|
442
|
+
const maxStratum = Math.max(0, ...Array.from(stratum.values()));
|
|
443
|
+
const groups = [];
|
|
444
|
+
for (let i = 0; i <= maxStratum; i++)
|
|
445
|
+
groups.push([]);
|
|
446
|
+
for (const [pred, s] of stratum) {
|
|
447
|
+
const g = groups[s];
|
|
448
|
+
if (g)
|
|
449
|
+
g.push(pred);
|
|
450
|
+
}
|
|
451
|
+
return groups;
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Evalúa un programa con negación estratificada. En cada estrato
|
|
455
|
+
* se ejecuta bottom-up con el set de facts acumulado, interpretando
|
|
456
|
+
* los literales negados bajo CWA (closed world assumption): `¬p(t)`
|
|
457
|
+
* es verdadero sii `p(t)` no está en el modelo del estrato previo.
|
|
458
|
+
*
|
|
459
|
+
* Si el programa no se puede estratificar, devuelve un resultado con
|
|
460
|
+
* 0 iteraciones y solo los facts iniciales (mejor que arrojar).
|
|
461
|
+
*/
|
|
462
|
+
function evaluateStratified(p) {
|
|
463
|
+
const strata = stratify(p.rules);
|
|
464
|
+
if (!strata) {
|
|
465
|
+
return {
|
|
466
|
+
facts: p.facts.filter(isGround).map(cloneAtom),
|
|
467
|
+
iterations: 0,
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
const known = new Map();
|
|
471
|
+
for (const f of p.facts) {
|
|
472
|
+
if (!isGround(f))
|
|
473
|
+
continue;
|
|
474
|
+
const k = atomKey(f);
|
|
475
|
+
if (!known.has(k))
|
|
476
|
+
known.set(k, cloneAtom(f));
|
|
477
|
+
}
|
|
478
|
+
let totalIter = 0;
|
|
479
|
+
const counter = { n: 0 };
|
|
480
|
+
for (const stratumPreds of strata) {
|
|
481
|
+
if (stratumPreds.length === 0)
|
|
482
|
+
continue;
|
|
483
|
+
const stratumSet = new Set(stratumPreds);
|
|
484
|
+
const relevantRules = p.rules.filter((r) => stratumSet.has(r.head.predicate));
|
|
485
|
+
// Fixpoint local del estrato.
|
|
486
|
+
let changed = true;
|
|
487
|
+
while (changed && totalIter < 5000) {
|
|
488
|
+
changed = false;
|
|
489
|
+
totalIter++;
|
|
490
|
+
const allFacts = Array.from(known.values());
|
|
491
|
+
const idx = indexByPredicate(allFacts);
|
|
492
|
+
for (const ruleRaw of relevantRules) {
|
|
493
|
+
// Renombrado coherente de head, body y negBody con el mismo
|
|
494
|
+
// counter compartido para preservar la identidad de variables
|
|
495
|
+
// que se repiten entre las tres partes (ej. X en head y body).
|
|
496
|
+
const renameMap = {};
|
|
497
|
+
const renameTerm = (t) => {
|
|
498
|
+
if (!isVariable(t))
|
|
499
|
+
return t;
|
|
500
|
+
if (!(t in renameMap)) {
|
|
501
|
+
counter.n++;
|
|
502
|
+
renameMap[t] = `${t}_r${counter.n}`;
|
|
503
|
+
}
|
|
504
|
+
const renamed = renameMap[t];
|
|
505
|
+
return renamed === undefined ? t : renamed;
|
|
506
|
+
};
|
|
507
|
+
const renameAtom = (a) => ({
|
|
508
|
+
predicate: a.predicate,
|
|
509
|
+
args: a.args.map(renameTerm),
|
|
510
|
+
});
|
|
511
|
+
const rule = {
|
|
512
|
+
head: renameAtom(ruleRaw.head),
|
|
513
|
+
body: ruleRaw.body.map(renameAtom),
|
|
514
|
+
};
|
|
515
|
+
const renamedNeg = ruleRaw.negBody.map(renameAtom);
|
|
516
|
+
// Reusar matchBody para el body positivo.
|
|
517
|
+
const positiveSubsts = rule.body.length === 0 ? [{}] : matchBody(rule.body, allFacts, idx);
|
|
518
|
+
for (const s of positiveSubsts) {
|
|
519
|
+
// Verificar negativos: para cada negB, instanciar bajo s y
|
|
520
|
+
// chequear que NO está en known. Si alguna variable queda
|
|
521
|
+
// libre tras s, la negación es insegura → omitir esta s.
|
|
522
|
+
let safe = true;
|
|
523
|
+
for (const nb of renamedNeg) {
|
|
524
|
+
const grounded = applySubstitution(nb, s);
|
|
525
|
+
if (!isGround(grounded)) {
|
|
526
|
+
safe = false;
|
|
527
|
+
break;
|
|
528
|
+
}
|
|
529
|
+
if (known.has(atomKey(grounded))) {
|
|
530
|
+
safe = false;
|
|
531
|
+
break;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
if (!safe)
|
|
535
|
+
continue;
|
|
536
|
+
const derived = applySubstitution(rule.head, s);
|
|
537
|
+
if (!isGround(derived))
|
|
538
|
+
continue;
|
|
539
|
+
const dk = atomKey(derived);
|
|
540
|
+
if (!known.has(dk)) {
|
|
541
|
+
known.set(dk, derived);
|
|
542
|
+
changed = true;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
return { facts: Array.from(known.values()), iterations: totalIter };
|
|
549
|
+
}
|
|
550
|
+
// ── Magic sets transformation ────────────────────────────────
|
|
551
|
+
/**
|
|
552
|
+
* Magic sets: transforma un programa P y una consulta Q en un
|
|
553
|
+
* programa P' tal que la evaluación bottom-up de P' computa sólo
|
|
554
|
+
* los facts relevantes para Q, en vez del modelo mínimo completo.
|
|
555
|
+
*
|
|
556
|
+
* Implementación mínima pero funcional:
|
|
557
|
+
* - Introduce predicados `magic_<head>` con los args bound del query.
|
|
558
|
+
* - Reescribe cada regla para que su disparo dependa del magic seed
|
|
559
|
+
* correspondiente y propague seeds a literales recursivos.
|
|
560
|
+
*
|
|
561
|
+
* Para consultas con todos los args ground, devuelve la consulta
|
|
562
|
+
* cerrada como seed inicial. Para args variables, devuelve el
|
|
563
|
+
* programa original (no hay focus posible).
|
|
564
|
+
*/
|
|
565
|
+
function magicSets(program, query) {
|
|
566
|
+
// Si no hay ningún arg ground en el query, no hay nada que focalizar.
|
|
567
|
+
const hasGround = query.args.some((a) => !isVariable(a));
|
|
568
|
+
if (!hasGround) {
|
|
569
|
+
return {
|
|
570
|
+
facts: program.facts.map(cloneAtom),
|
|
571
|
+
rules: program.rules.map((r) => ({
|
|
572
|
+
head: cloneAtom(r.head),
|
|
573
|
+
body: r.body.map(cloneAtom),
|
|
574
|
+
})),
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
const magicPred = `magic_${query.predicate}`;
|
|
578
|
+
// Adornment: posiciones bound (constantes) vs free (variables) en el
|
|
579
|
+
// query. El predicado magic sólo carga las posiciones bound, que son
|
|
580
|
+
// las que efectivamente focalizan la búsqueda.
|
|
581
|
+
const boundIdx = [];
|
|
582
|
+
for (let i = 0; i < query.args.length; i++) {
|
|
583
|
+
const a = query.args[i];
|
|
584
|
+
if (a !== undefined && !isVariable(a))
|
|
585
|
+
boundIdx.push(i);
|
|
586
|
+
}
|
|
587
|
+
const projectBound = (args) => {
|
|
588
|
+
const out = [];
|
|
589
|
+
for (const i of boundIdx) {
|
|
590
|
+
const v = args[i];
|
|
591
|
+
if (v !== undefined)
|
|
592
|
+
out.push(v);
|
|
593
|
+
}
|
|
594
|
+
return out;
|
|
595
|
+
};
|
|
596
|
+
const seed = {
|
|
597
|
+
predicate: magicPred,
|
|
598
|
+
args: projectBound(query.args),
|
|
599
|
+
};
|
|
600
|
+
const newFacts = [...program.facts.map(cloneAtom), seed];
|
|
601
|
+
const newRules = [];
|
|
602
|
+
for (const r of program.rules) {
|
|
603
|
+
if (r.head.predicate === query.predicate) {
|
|
604
|
+
const guard = {
|
|
605
|
+
predicate: magicPred,
|
|
606
|
+
args: projectBound(r.head.args),
|
|
607
|
+
};
|
|
608
|
+
newRules.push({
|
|
609
|
+
head: cloneAtom(r.head),
|
|
610
|
+
body: [guard, ...r.body.map(cloneAtom)],
|
|
611
|
+
});
|
|
612
|
+
// Propagar: por cada literal del body del mismo predicado,
|
|
613
|
+
// emitir regla magic_<pred>(boundProj(litArgs)) :- magic_<head>(boundProj(headArgs)), ...prefix.
|
|
614
|
+
for (let i = 0; i < r.body.length; i++) {
|
|
615
|
+
const lit = r.body[i];
|
|
616
|
+
if (!lit)
|
|
617
|
+
continue;
|
|
618
|
+
if (lit.predicate === query.predicate) {
|
|
619
|
+
const propHead = {
|
|
620
|
+
predicate: magicPred,
|
|
621
|
+
args: projectBound(lit.args),
|
|
622
|
+
};
|
|
623
|
+
const propBody = [
|
|
624
|
+
{ predicate: magicPred, args: projectBound(r.head.args) },
|
|
625
|
+
];
|
|
626
|
+
for (let j = 0; j < i; j++) {
|
|
627
|
+
const pre = r.body[j];
|
|
628
|
+
if (pre)
|
|
629
|
+
propBody.push(cloneAtom(pre));
|
|
630
|
+
}
|
|
631
|
+
newRules.push({ head: propHead, body: propBody });
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
else {
|
|
636
|
+
newRules.push({
|
|
637
|
+
head: cloneAtom(r.head),
|
|
638
|
+
body: r.body.map(cloneAtom),
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
return { facts: newFacts, rules: newRules };
|
|
643
|
+
}
|
|
644
|
+
// ── Programas comunes ────────────────────────────────────────
|
|
645
|
+
/**
|
|
646
|
+
* Programa canónico de clausura transitiva:
|
|
647
|
+
*
|
|
648
|
+
* parent(alice, bob).
|
|
649
|
+
* parent(bob, carol).
|
|
650
|
+
* parent(carol, dave).
|
|
651
|
+
* ancestor(X, Y) :- parent(X, Y).
|
|
652
|
+
* ancestor(X, Y) :- parent(X, Z), ancestor(Z, Y).
|
|
653
|
+
*/
|
|
654
|
+
function transitiveClosure() {
|
|
655
|
+
return {
|
|
656
|
+
facts: [
|
|
657
|
+
{ predicate: 'parent', args: ['alice', 'bob'] },
|
|
658
|
+
{ predicate: 'parent', args: ['bob', 'carol'] },
|
|
659
|
+
{ predicate: 'parent', args: ['carol', 'dave'] },
|
|
660
|
+
],
|
|
661
|
+
rules: [
|
|
662
|
+
{
|
|
663
|
+
head: { predicate: 'ancestor', args: ['X', 'Y'] },
|
|
664
|
+
body: [{ predicate: 'parent', args: ['X', 'Y'] }],
|
|
665
|
+
},
|
|
666
|
+
{
|
|
667
|
+
head: { predicate: 'ancestor', args: ['X', 'Y'] },
|
|
668
|
+
body: [
|
|
669
|
+
{ predicate: 'parent', args: ['X', 'Z'] },
|
|
670
|
+
{ predicate: 'ancestor', args: ['Z', 'Y'] },
|
|
671
|
+
],
|
|
672
|
+
},
|
|
673
|
+
],
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Programa de alcanzabilidad en un grafo dirigido de 4 nodos:
|
|
678
|
+
*
|
|
679
|
+
* edge(n1, n2). edge(n2, n3). edge(n3, n4). edge(n1, n3).
|
|
680
|
+
* reach(X, Y) :- edge(X, Y).
|
|
681
|
+
* reach(X, Y) :- edge(X, Z), reach(Z, Y).
|
|
682
|
+
*/
|
|
683
|
+
function pathReachability() {
|
|
684
|
+
return {
|
|
685
|
+
facts: [
|
|
686
|
+
{ predicate: 'edge', args: ['n1', 'n2'] },
|
|
687
|
+
{ predicate: 'edge', args: ['n2', 'n3'] },
|
|
688
|
+
{ predicate: 'edge', args: ['n3', 'n4'] },
|
|
689
|
+
{ predicate: 'edge', args: ['n1', 'n3'] },
|
|
690
|
+
],
|
|
691
|
+
rules: [
|
|
692
|
+
{
|
|
693
|
+
head: { predicate: 'reach', args: ['X', 'Y'] },
|
|
694
|
+
body: [{ predicate: 'edge', args: ['X', 'Y'] }],
|
|
695
|
+
},
|
|
696
|
+
{
|
|
697
|
+
head: { predicate: 'reach', args: ['X', 'Y'] },
|
|
698
|
+
body: [
|
|
699
|
+
{ predicate: 'edge', args: ['X', 'Z'] },
|
|
700
|
+
{ predicate: 'reach', args: ['Z', 'Y'] },
|
|
701
|
+
],
|
|
702
|
+
},
|
|
703
|
+
],
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
//# sourceMappingURL=index.js.map
|