@stevenvo780/st-lang 2.6.0 → 2.7.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/bin/st +0 -0
- package/dist/ast/nodes.d.ts +18 -1
- package/dist/ast/nodes.d.ts.map +1 -1
- package/dist/parser/parser.d.ts +2 -0
- package/dist/parser/parser.d.ts.map +1 -1
- package/dist/parser/parser.js +82 -0
- package/dist/parser/parser.js.map +1 -1
- package/dist/profiles/arithmetic/index.d.ts.map +1 -1
- package/dist/profiles/arithmetic/index.js.map +1 -1
- package/dist/profiles/classical/first-order.d.ts +3 -3
- package/dist/profiles/classical/first-order.d.ts.map +1 -1
- package/dist/profiles/classical/first-order.js +71 -392
- package/dist/profiles/classical/first-order.js.map +1 -1
- package/dist/profiles/classical/propositional.d.ts +6 -1
- package/dist/profiles/classical/propositional.d.ts.map +1 -1
- package/dist/profiles/classical/propositional.js +334 -35
- package/dist/profiles/classical/propositional.js.map +1 -1
- package/dist/profiles/intuitionistic/propositional.d.ts.map +1 -1
- package/dist/profiles/intuitionistic/propositional.js +7 -1
- package/dist/profiles/intuitionistic/propositional.js.map +1 -1
- package/dist/profiles/paraconsistent/belnap.d.ts.map +1 -1
- package/dist/profiles/paraconsistent/belnap.js +151 -3
- package/dist/profiles/paraconsistent/belnap.js.map +1 -1
- package/dist/profiles/shared/tableau-engine.d.ts.map +1 -1
- package/dist/profiles/shared/tableau-engine.js +49 -22
- package/dist/profiles/shared/tableau-engine.js.map +1 -1
- package/dist/runtime/format.d.ts.map +1 -1
- package/dist/runtime/format.js +4 -0
- package/dist/runtime/format.js.map +1 -1
- package/dist/runtime/formula-factory.d.ts +26 -0
- package/dist/runtime/formula-factory.d.ts.map +1 -0
- package/dist/runtime/formula-factory.js +67 -0
- package/dist/runtime/formula-factory.js.map +1 -0
- package/dist/runtime/interpreter.d.ts +45 -4
- package/dist/runtime/interpreter.d.ts.map +1 -1
- package/dist/runtime/interpreter.js +1114 -609
- package/dist/runtime/interpreter.js.map +1 -1
- package/dist/tests/arithmetic.test.js +2 -1
- package/dist/tests/arithmetic.test.js.map +1 -1
- package/dist/tests/examples.test.js +12 -1
- package/dist/tests/examples.test.js.map +1 -1
- package/dist/tests/exhaustive-matrix.test.js +1 -1
- package/dist/tests/exhaustive-matrix.test.js.map +1 -1
- package/dist/tests/limits.test.js +67 -5
- package/dist/tests/limits.test.js.map +1 -1
- package/dist/tests/profiles.test.js +14 -6
- package/dist/tests/profiles.test.js.map +1 -1
- package/dist/tests/result-bindings.test.d.ts +2 -0
- package/dist/tests/result-bindings.test.d.ts.map +1 -0
- package/dist/tests/result-bindings.test.js +59 -0
- package/dist/tests/result-bindings.test.js.map +1 -0
- package/dist/tests/stress-hardware.test.d.ts +2 -0
- package/dist/tests/stress-hardware.test.d.ts.map +1 -0
- package/dist/tests/stress-hardware.test.js +516 -0
- package/dist/tests/stress-hardware.test.js.map +1 -0
- package/dist/tests/v1-features.test.js +1 -1
- package/dist/tests/v1-features.test.js.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/memo.d.ts +3 -0
- package/dist/utils/memo.d.ts.map +1 -1
- package/dist/utils/memo.js +30 -0
- package/dist/utils/memo.js.map +1 -1
- package/package.json +2 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// ============================================================
|
|
3
|
-
// ST Classical First-Order — Motor de Tableau Sistematico (
|
|
3
|
+
// ST Classical First-Order — Motor de Tableau Sistematico (Hardened)
|
|
4
4
|
// ============================================================
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.ClassicalFirstOrder = void 0;
|
|
@@ -79,14 +79,11 @@ function skolemize(f) {
|
|
|
79
79
|
}
|
|
80
80
|
return node;
|
|
81
81
|
}
|
|
82
|
-
|
|
83
|
-
// Restore remaining foralls (Skolemization removes existentials, keeps foralls usually implicitly but we make them explicit)
|
|
84
|
-
const finalRes = rawMatrix;
|
|
85
|
-
return finalRes; // Simplification: we just return the matrix, standard skolemization implicitly universalizes
|
|
82
|
+
return process(prenex);
|
|
86
83
|
}
|
|
87
84
|
class ClassicalFirstOrder {
|
|
88
85
|
name = 'classical.first_order';
|
|
89
|
-
description = 'Logica clasica de primer orden (FOL) — Motor de Tableau
|
|
86
|
+
description = 'Logica clasica de primer orden (FOL) — Motor de Tableau Hardened';
|
|
90
87
|
checkWellFormed(formula) {
|
|
91
88
|
const diags = [];
|
|
92
89
|
const walk = (f) => {
|
|
@@ -102,12 +99,11 @@ class ClassicalFirstOrder {
|
|
|
102
99
|
checkValid(formula) {
|
|
103
100
|
const negated = (0, propositional_1.toNNF)({ kind: 'not', args: [formula] });
|
|
104
101
|
const res = this.solve([{ formula: negated }]);
|
|
105
|
-
const isClosed = res.closed;
|
|
106
102
|
return {
|
|
107
|
-
status:
|
|
108
|
-
output:
|
|
109
|
-
? `${(0, propositional_1.formulaToString)(formula)} es VÁLIDA
|
|
110
|
-
: `${(0, propositional_1.formulaToString)(formula)} NO es válida
|
|
103
|
+
status: res.closed ? 'valid' : 'invalid',
|
|
104
|
+
output: res.closed
|
|
105
|
+
? `${(0, propositional_1.formulaToString)(formula)} es VÁLIDA`
|
|
106
|
+
: `${(0, propositional_1.formulaToString)(formula)} NO es válida`,
|
|
111
107
|
tableauTrace: res.trace,
|
|
112
108
|
diagnostics: [],
|
|
113
109
|
formula,
|
|
@@ -130,10 +126,9 @@ class ClassicalFirstOrder {
|
|
|
130
126
|
{ formula: (0, propositional_1.toNNF)({ kind: 'not', args: [goal] }) },
|
|
131
127
|
];
|
|
132
128
|
const res = this.solve(nodes);
|
|
133
|
-
const isClosed = res.closed;
|
|
134
129
|
return {
|
|
135
|
-
status:
|
|
136
|
-
output:
|
|
130
|
+
status: res.closed ? 'provable' : 'refutable',
|
|
131
|
+
output: res.closed ? 'Demostrado' : 'No demostrable',
|
|
137
132
|
tableauTrace: res.trace,
|
|
138
133
|
diagnostics: [],
|
|
139
134
|
formula: goal,
|
|
@@ -148,68 +143,10 @@ class ClassicalFirstOrder {
|
|
|
148
143
|
{ formula: (0, propositional_1.toNNF)({ kind: 'not', args: [goal] }) },
|
|
149
144
|
];
|
|
150
145
|
const res = this.solve(nodes);
|
|
151
|
-
// Build proof steps from tableau trace
|
|
152
|
-
const proof = { goal, steps: [], status: 'incomplete' };
|
|
153
|
-
let stepNum = 0;
|
|
154
|
-
// Add premises as steps
|
|
155
|
-
for (let i = 0; i < premises.length; i++) {
|
|
156
|
-
stepNum++;
|
|
157
|
-
const f = formulas[i];
|
|
158
|
-
if (f) {
|
|
159
|
-
proof.steps.push({
|
|
160
|
-
stepNumber: stepNum,
|
|
161
|
-
formula: f,
|
|
162
|
-
justification: `Premisa (${premises[i]})`,
|
|
163
|
-
premises: [],
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
// Parse tableau trace to extract named quantifier rules
|
|
168
|
-
for (const traceLine of res.trace) {
|
|
169
|
-
if (traceLine.includes('Instanciación Universal UI') ||
|
|
170
|
-
traceLine.includes('Gamma') ||
|
|
171
|
-
traceLine.includes('gamma')) {
|
|
172
|
-
stepNum++;
|
|
173
|
-
proof.steps.push({
|
|
174
|
-
stepNumber: stepNum,
|
|
175
|
-
formula: goal,
|
|
176
|
-
justification: 'Instanciación Universal (UI): ∀xφ(x) ⊢ φ(a)',
|
|
177
|
-
premises: [],
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
if (traceLine.includes('Instanciación Existencial EI') ||
|
|
181
|
-
traceLine.includes('Delta') ||
|
|
182
|
-
traceLine.includes('delta')) {
|
|
183
|
-
stepNum++;
|
|
184
|
-
proof.steps.push({
|
|
185
|
-
stepNumber: stepNum,
|
|
186
|
-
formula: goal,
|
|
187
|
-
justification: 'Instanciación Existencial (EI): ∃xφ(x) ⊢ φ(c) [c nueva]',
|
|
188
|
-
premises: [],
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
// Final step
|
|
193
|
-
if (res.closed) {
|
|
194
|
-
stepNum++;
|
|
195
|
-
proof.steps.push({
|
|
196
|
-
stepNumber: stepNum,
|
|
197
|
-
formula: goal,
|
|
198
|
-
justification: 'Demostrado por refutación — todas las ramas del tableau cerradas',
|
|
199
|
-
premises: [],
|
|
200
|
-
});
|
|
201
|
-
proof.status = 'complete';
|
|
202
|
-
}
|
|
203
|
-
const output = res.closed
|
|
204
|
-
? `Derivado con éxito mediante tableau de primer orden.\n Reglas de cuantificadores aplicadas:\n UI: Instanciación Universal — ∀xφ(x) ⊢ φ(a)\n EI: Instanciación Existencial — ∃xφ(x) ⊢ φ(c) [c nueva]\n UG: Generalización Universal — φ(a) [a arbitrario] ⊢ ∀xφ(x)\n EG: Generalización Existencial — φ(a) ⊢ ∃xφ(x)`
|
|
205
|
-
: 'No se pudo derivar.';
|
|
206
146
|
return {
|
|
207
147
|
status: res.closed ? 'provable' : 'refutable',
|
|
208
|
-
output,
|
|
209
|
-
proof: res.closed ? proof : undefined,
|
|
148
|
+
output: res.closed ? 'Derivado' : 'No derivable',
|
|
210
149
|
tableauTrace: res.trace,
|
|
211
|
-
reasoningType: res.closed ? 'Tableau de primer orden (refutación)' : undefined,
|
|
212
|
-
reasoningSchema: res.closed ? 'Γ, ¬φ ⊢ ⊥ ∴ Γ ⊢ φ' : undefined,
|
|
213
150
|
diagnostics: [],
|
|
214
151
|
formula: goal,
|
|
215
152
|
};
|
|
@@ -217,212 +154,73 @@ class ClassicalFirstOrder {
|
|
|
217
154
|
countermodel(formula) {
|
|
218
155
|
const nnf = (0, propositional_1.toNNF)(formula);
|
|
219
156
|
const res = this.solve([{ formula: { kind: 'not', args: [nnf] } }]);
|
|
220
|
-
if (!res.closed) {
|
|
221
|
-
// Extract domain and interpretation from open branch trace
|
|
222
|
-
const domain = new Set();
|
|
223
|
-
const interpretations = new Map();
|
|
224
|
-
for (const line of res.trace) {
|
|
225
|
-
// Collect constants mentioned in trace
|
|
226
|
-
const constMatch = line.match(/\b(c\d+|[a-d])\b/g);
|
|
227
|
-
if (constMatch) {
|
|
228
|
-
for (const c of constMatch)
|
|
229
|
-
domain.add(c);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
if (domain.size === 0)
|
|
233
|
-
domain.add('a');
|
|
234
|
-
// Build output
|
|
235
|
-
let output = `Contramodelo encontrado:\n`;
|
|
236
|
-
output += ` Dominio D = {${Array.from(domain).join(', ')}}\n`;
|
|
237
|
-
output += ` Interpretación:\n`;
|
|
238
|
-
// Collect predicates from formula
|
|
239
|
-
const preds = new Map();
|
|
240
|
-
const collectPreds = (f) => {
|
|
241
|
-
if (f.kind === 'predicate' && f.name) {
|
|
242
|
-
preds.set(f.name, (f.params || []).length);
|
|
243
|
-
}
|
|
244
|
-
f.args?.forEach(collectPreds);
|
|
245
|
-
};
|
|
246
|
-
collectPreds(formula);
|
|
247
|
-
for (const [pred, arity] of preds) {
|
|
248
|
-
const interp = interpretations.get(pred);
|
|
249
|
-
if (interp && interp.size > 0) {
|
|
250
|
-
output += ` ${pred} = {${Array.from(interp).join(', ')}} (aridad ${arity})\n`;
|
|
251
|
-
}
|
|
252
|
-
else {
|
|
253
|
-
output += ` ${pred} = {} (vacío, aridad ${arity})\n`;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
output += ` → La fórmula no es válida`;
|
|
257
|
-
return {
|
|
258
|
-
status: 'invalid',
|
|
259
|
-
output,
|
|
260
|
-
model: {
|
|
261
|
-
type: 'first_order',
|
|
262
|
-
valuation: Object.fromEntries(Array.from(preds.keys()).map((p) => [p, false])),
|
|
263
|
-
},
|
|
264
|
-
tableauTrace: res.trace,
|
|
265
|
-
diagnostics: [],
|
|
266
|
-
formula,
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
157
|
return {
|
|
270
|
-
status: 'valid',
|
|
271
|
-
output:
|
|
158
|
+
status: res.closed ? 'valid' : 'invalid',
|
|
159
|
+
output: res.closed ? 'Válida' : 'Inválida',
|
|
272
160
|
tableauTrace: res.trace,
|
|
273
161
|
diagnostics: [],
|
|
274
162
|
formula,
|
|
275
163
|
};
|
|
276
164
|
}
|
|
277
|
-
explain(
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
quantifiers.push({
|
|
291
|
-
kind: f.kind === 'forall' ? '∀' : '∃',
|
|
292
|
-
variable: v,
|
|
293
|
-
scope: innerStr,
|
|
294
|
-
});
|
|
295
|
-
const newBound = new Set(bound);
|
|
296
|
-
newBound.add(v);
|
|
297
|
-
boundVars.add(v);
|
|
298
|
-
if (f.args)
|
|
299
|
-
f.args.forEach((a) => collectInfo(a, newBound));
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
if (f.kind === 'predicate' && f.name) {
|
|
303
|
-
const arity = (f.params || []).length;
|
|
304
|
-
predicates.set(f.name, arity);
|
|
305
|
-
for (const p of f.params || []) {
|
|
306
|
-
if (!bound.has(p))
|
|
307
|
-
freeVars.add(p);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
if (f.kind === 'atom' && f.name) {
|
|
311
|
-
predicates.set(f.name, 0);
|
|
312
|
-
}
|
|
313
|
-
if (f.args)
|
|
314
|
-
f.args.forEach((a) => collectInfo(a, bound));
|
|
315
|
-
};
|
|
316
|
-
collectInfo(formula, new Set());
|
|
317
|
-
let out = `Análisis de Fórmula en Primer Orden:\n`;
|
|
318
|
-
out += ` Fórmula original: ${(0, propositional_1.formulaToString)(formula)}\n`;
|
|
319
|
-
// Syntactic analysis
|
|
320
|
-
out += `\nAnálisis sintáctico:\n`;
|
|
321
|
-
if (quantifiers.length > 0) {
|
|
322
|
-
const qStrs = quantifiers.map((q) => `${q.kind}${q.variable} (${q.kind === '∀' ? 'universal' : 'existencial'})`);
|
|
323
|
-
out += ` Cuantificadores: ${qStrs.join(', ')}\n`;
|
|
324
|
-
for (const q of quantifiers) {
|
|
325
|
-
out += ` Alcance de ${q.kind}${q.variable}: ${q.scope}\n`;
|
|
165
|
+
explain(_formula) {
|
|
166
|
+
return { status: 'unknown', output: 'Explain no implementado en Hardened', diagnostics: [] };
|
|
167
|
+
}
|
|
168
|
+
checkEquivalent(a, b) {
|
|
169
|
+
const biconditional = { kind: 'biconditional', args: [a, b] };
|
|
170
|
+
return this.checkValid(biconditional);
|
|
171
|
+
}
|
|
172
|
+
collectConstants(f, bound) {
|
|
173
|
+
const result = new Set();
|
|
174
|
+
if (f.kind === 'predicate' && f.params) {
|
|
175
|
+
for (const p of f.params) {
|
|
176
|
+
if (!bound.has(p))
|
|
177
|
+
result.add(p);
|
|
326
178
|
}
|
|
327
179
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
}
|
|
331
|
-
if (predicates.size > 0) {
|
|
332
|
-
const predStrs = Array.from(predicates.entries()).map(([n, a]) => `${n}/${a} (aridad ${a})`);
|
|
333
|
-
out += ` Predicados: ${predStrs.join(', ')}\n`;
|
|
180
|
+
if (f.kind === 'atom' && f.name && !bound.has(f.name)) {
|
|
181
|
+
// atoms used as term-like references (e.g. P(a) where a is a constant)
|
|
334
182
|
}
|
|
335
|
-
if (
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
183
|
+
if (f.kind === 'forall' || f.kind === 'exists') {
|
|
184
|
+
const inner = new Set(bound);
|
|
185
|
+
if (f.variable)
|
|
186
|
+
inner.add(f.variable);
|
|
187
|
+
for (const s of this.collectConstants((f.args || [])[0], inner))
|
|
188
|
+
result.add(s);
|
|
339
189
|
}
|
|
340
|
-
else {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
let lastQ = '';
|
|
346
|
-
for (const q of quantifiers) {
|
|
347
|
-
if (lastQ && lastQ !== q.kind)
|
|
348
|
-
altDepth++;
|
|
349
|
-
lastQ = q.kind;
|
|
350
|
-
}
|
|
351
|
-
out += ` Alternancia de cuantificadores: ${altDepth}\n`;
|
|
352
|
-
out += ` Profundidad de cuantificadores: ${quantifiers.length}\n`;
|
|
353
|
-
// Normal forms
|
|
354
|
-
out += `\nFormas normales:\n`;
|
|
355
|
-
out += ` NNF: ${(0, propositional_1.formulaToString)(nnf)}\n`;
|
|
356
|
-
out += ` PNF: ${(0, propositional_1.formulaToString)(prenex)}\n`;
|
|
357
|
-
out += ` Skolem: ${(0, propositional_1.formulaToString)(skolem)}\n`;
|
|
358
|
-
// Natural language interpretation
|
|
359
|
-
if (quantifiers.length === 1 &&
|
|
360
|
-
quantifiers[0].kind === '∀' &&
|
|
361
|
-
formula.args?.[0]?.kind === 'implies' &&
|
|
362
|
-
predicates.size === 2) {
|
|
363
|
-
const pNames = Array.from(predicates.keys());
|
|
364
|
-
out += `\nInterpretación natural: "Para todo ${quantifiers[0].variable}, si ${quantifiers[0].variable} es ${pNames[0]} entonces ${quantifiers[0].variable} es ${pNames[1]}"\n`;
|
|
365
|
-
out += `Lectura categórica: "Todo ${pNames[0]} es ${pNames[1]}" (proposición tipo A)\n`;
|
|
190
|
+
else if (f.args) {
|
|
191
|
+
for (const a of f.args) {
|
|
192
|
+
for (const s of this.collectConstants(a, bound))
|
|
193
|
+
result.add(s);
|
|
194
|
+
}
|
|
366
195
|
}
|
|
367
|
-
|
|
368
|
-
const res = this.solve([{ formula: (0, propositional_1.toNNF)({ kind: 'not', args: [formula] }) }]);
|
|
369
|
-
out += `\nEstatus: ${res.closed ? 'VÁLIDA (demostrada por tableau)' : 'INVÁLIDA (rama abierta encontrada)'}`;
|
|
370
|
-
return {
|
|
371
|
-
status: res.closed ? 'valid' : 'invalid',
|
|
372
|
-
output: out,
|
|
373
|
-
tableauTrace: res.trace,
|
|
374
|
-
normalForms: {
|
|
375
|
-
nnf: (0, propositional_1.formulaToString)(nnf),
|
|
376
|
-
pnf: (0, propositional_1.formulaToString)(prenex),
|
|
377
|
-
skolem: (0, propositional_1.formulaToString)(skolem),
|
|
378
|
-
},
|
|
379
|
-
diagnostics: [],
|
|
380
|
-
formula,
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
|
-
checkEquivalent(a, b) {
|
|
384
|
-
const biconditional = { kind: 'biconditional', args: [a, b] };
|
|
385
|
-
const result = this.checkValid(biconditional);
|
|
386
|
-
const fA = (0, propositional_1.formulaToString)(a);
|
|
387
|
-
const fB = (0, propositional_1.formulaToString)(b);
|
|
388
|
-
return {
|
|
389
|
-
status: result.status === 'valid' ? 'valid' : 'invalid',
|
|
390
|
-
output: result.status === 'valid'
|
|
391
|
-
? `${fA} y ${fB} son EQUIVALENTES en FOL`
|
|
392
|
-
: `${fA} y ${fB} NO son equivalentes en FOL`,
|
|
393
|
-
diagnostics: [],
|
|
394
|
-
};
|
|
196
|
+
return result;
|
|
395
197
|
}
|
|
396
198
|
solve(initialNodes) {
|
|
397
199
|
varCounter = 0;
|
|
398
200
|
skolemCounter = 0;
|
|
399
201
|
const constants = new Set(['c0']);
|
|
400
|
-
const
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
if (!p.match(/^[xyz]/))
|
|
404
|
-
constants.add(p);
|
|
202
|
+
for (const node of initialNodes) {
|
|
203
|
+
for (const c of this.collectConstants(node.formula, new Set())) {
|
|
204
|
+
constants.add(c);
|
|
405
205
|
}
|
|
406
|
-
|
|
407
|
-
};
|
|
408
|
-
initialNodes.forEach((n) => collect(n.formula));
|
|
206
|
+
}
|
|
409
207
|
const trace = [];
|
|
410
208
|
const closed = this.solveRecursive(initialNodes, constants, new Set(), 0, trace);
|
|
411
209
|
return { closed, trace };
|
|
412
210
|
}
|
|
413
211
|
solveRecursive(nodes, constants, processed, depth, trace) {
|
|
414
|
-
if (depth >
|
|
415
|
-
trace.push(`[${depth}] ⚠ Se
|
|
212
|
+
if (depth > 200) {
|
|
213
|
+
trace.push(`[${depth}] ⚠ Se alcanzó profundidad máxima de seguridad (200).`);
|
|
416
214
|
return false;
|
|
417
215
|
}
|
|
418
216
|
if (nodes.length === 0)
|
|
419
217
|
return false;
|
|
420
|
-
//
|
|
218
|
+
// Contradicción
|
|
421
219
|
for (const n1 of nodes) {
|
|
422
220
|
if (n1.formula.kind === 'not' && n1.formula.args) {
|
|
423
221
|
const atom = n1.formula.args[0];
|
|
424
222
|
if (nodes.some((n2) => this.isEqual(n2.formula, atom))) {
|
|
425
|
-
trace.push(`[${depth}] ✕ Rama cerrada por contradicción
|
|
223
|
+
trace.push(`[${depth}] ✕ Rama cerrada por contradicción`);
|
|
426
224
|
return true;
|
|
427
225
|
}
|
|
428
226
|
}
|
|
@@ -430,29 +228,12 @@ class ClassicalFirstOrder {
|
|
|
430
228
|
const type = (f) => {
|
|
431
229
|
if (f.kind === 'and')
|
|
432
230
|
return 'alfa';
|
|
433
|
-
if (f.kind === 'or' || f.kind === 'implies')
|
|
434
|
-
return 'beta';
|
|
435
|
-
if (f.kind === 'biconditional')
|
|
436
|
-
return 'beta';
|
|
437
231
|
if (f.kind === 'exists')
|
|
438
232
|
return 'delta';
|
|
439
233
|
if (f.kind === 'forall')
|
|
440
234
|
return 'gamma';
|
|
441
|
-
if (f.kind === '
|
|
442
|
-
|
|
443
|
-
if (inner) {
|
|
444
|
-
if (inner.kind === 'and')
|
|
445
|
-
return 'beta';
|
|
446
|
-
if (inner.kind === 'or')
|
|
447
|
-
return 'alfa';
|
|
448
|
-
if (inner.kind === 'implies')
|
|
449
|
-
return 'alfa';
|
|
450
|
-
if (inner.kind === 'exists')
|
|
451
|
-
return 'gamma';
|
|
452
|
-
if (inner.kind === 'forall')
|
|
453
|
-
return 'delta';
|
|
454
|
-
}
|
|
455
|
-
}
|
|
235
|
+
if (f.kind === 'or' || f.kind === 'implies')
|
|
236
|
+
return 'beta';
|
|
456
237
|
return 'atom';
|
|
457
238
|
};
|
|
458
239
|
const priorities = ['alfa', 'delta', 'gamma', 'beta'];
|
|
@@ -463,8 +244,7 @@ class ClassicalFirstOrder {
|
|
|
463
244
|
const node = nodes[idx];
|
|
464
245
|
const rest = nodes.filter((_, i) => i !== idx);
|
|
465
246
|
const { formula: f } = node;
|
|
466
|
-
const
|
|
467
|
-
const key = this.formulaHash(f);
|
|
247
|
+
const key = (0, propositional_1.formulaToString)(f);
|
|
468
248
|
if (p !== 'gamma' && p !== 'atom' && processed.has(key))
|
|
469
249
|
return this.solveRecursive(rest, constants, processed, depth, trace);
|
|
470
250
|
const nextProcessed = new Set(processed);
|
|
@@ -472,127 +252,45 @@ class ClassicalFirstOrder {
|
|
|
472
252
|
nextProcessed.add(key);
|
|
473
253
|
switch (f.kind) {
|
|
474
254
|
case 'and':
|
|
475
|
-
|
|
476
|
-
return this.solveRecursive([{ formula: args[0] }, { formula: args[1] }, ...rest], constants, nextProcessed, depth + 1, trace);
|
|
255
|
+
return this.solveRecursive([{ formula: f.args[0] }, { formula: f.args[1] }, ...rest], constants, nextProcessed, depth + 1, trace);
|
|
477
256
|
case 'exists': {
|
|
478
|
-
const variable = f.variable;
|
|
479
|
-
if (!args[0] || !variable)
|
|
480
|
-
return false;
|
|
481
257
|
const newC = `c${constants.size}`;
|
|
482
|
-
trace.push(`[${depth}] Delta (∃ - Instanciación Existencial EI): ${(0, propositional_1.formulaToString)(f)} -> asignando cte nueva ${newC}`);
|
|
483
258
|
const nextConstants = new Set(constants).add(newC);
|
|
484
|
-
return this.solveRecursive([{ formula: this.substitute(args[0], variable, newC) }, ...rest], nextConstants, nextProcessed, depth + 1, trace);
|
|
259
|
+
return this.solveRecursive([{ formula: this.substitute(f.args[0], f.variable, newC) }, ...rest], nextConstants, nextProcessed, depth + 1, trace);
|
|
485
260
|
}
|
|
486
261
|
case 'forall': {
|
|
487
|
-
const
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
if (!processed.has(instKey)) {
|
|
494
|
-
newInsts.push({ formula: this.substitute(args[0], variable, c) });
|
|
495
|
-
processed.add(instKey);
|
|
496
|
-
}
|
|
262
|
+
const gammaKey = `gamma_count:${key}`;
|
|
263
|
+
const proc = processed;
|
|
264
|
+
const gammaCount = proc.__gammaCounters?.get(gammaKey) ?? 0;
|
|
265
|
+
if (gammaCount >= 20) {
|
|
266
|
+
trace.push(`[${depth}] ⚠ Límite Gamma alcanzado.`);
|
|
267
|
+
return this.solveRecursive(rest, constants, nextProcessed, depth, trace);
|
|
497
268
|
}
|
|
498
|
-
if (
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
269
|
+
if (!proc.__gammaCounters)
|
|
270
|
+
proc.__gammaCounters = new Map();
|
|
271
|
+
proc.__gammaCounters.set(gammaKey, gammaCount + 1);
|
|
272
|
+
const newInsts = Array.from(constants).map((c) => ({
|
|
273
|
+
formula: this.substitute(f.args[0], f.variable, c),
|
|
274
|
+
}));
|
|
275
|
+
return this.solveRecursive([...newInsts, ...nodes], constants, processed, depth + 1, trace);
|
|
503
276
|
}
|
|
504
277
|
case 'or':
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
this.solveRecursive([{ formula: args[1] }, ...rest], constants, nextProcessed, depth + 1, [...trace, `[${depth}] -> Rama 2: ${(0, propositional_1.formulaToString)(args[1])}`]));
|
|
278
|
+
return (this.solveRecursive([{ formula: f.args[0] }, ...rest], constants, nextProcessed, depth + 1, trace) &&
|
|
279
|
+
this.solveRecursive([{ formula: f.args[1] }, ...rest], constants, nextProcessed, depth + 1, trace));
|
|
508
280
|
case 'implies':
|
|
509
|
-
|
|
510
|
-
return (this.solveRecursive([{ formula: { kind: 'not', args: [args[0]] } }, ...rest], constants, nextProcessed, depth + 1,
|
|
511
|
-
this.solveRecursive([{ formula: args[1] }, ...rest], constants, nextProcessed, depth + 1,
|
|
512
|
-
case 'biconditional':
|
|
513
|
-
trace.push(`[${depth}] Beta (↔): ${(0, propositional_1.formulaToString)(f)}`);
|
|
514
|
-
return (this.solveRecursive([{ formula: args[0] }, { formula: args[1] }, ...rest], constants, nextProcessed, depth + 1, [...trace]) &&
|
|
515
|
-
this.solveRecursive([
|
|
516
|
-
{ formula: { kind: 'not', args: [args[0]] } },
|
|
517
|
-
{ formula: { kind: 'not', args: [args[1]] } },
|
|
518
|
-
...rest,
|
|
519
|
-
], constants, nextProcessed, depth + 1, [...trace]));
|
|
520
|
-
case 'not': {
|
|
521
|
-
const inner = args[0];
|
|
522
|
-
if (!inner)
|
|
523
|
-
return false;
|
|
524
|
-
switch (inner.kind) {
|
|
525
|
-
case 'and':
|
|
526
|
-
trace.push(`[${depth}] Beta (¬∧): ${(0, propositional_1.formulaToString)(f)}`);
|
|
527
|
-
return ((inner.args || []).length >= 2 &&
|
|
528
|
-
this.solveRecursive([{ formula: { kind: 'not', args: [inner.args[0]] } }, ...rest], constants, nextProcessed, depth + 1, [...trace]) &&
|
|
529
|
-
this.solveRecursive([{ formula: { kind: 'not', args: [inner.args[1]] } }, ...rest], constants, nextProcessed, depth + 1, [...trace]));
|
|
530
|
-
case 'or':
|
|
531
|
-
trace.push(`[${depth}] Alfa (¬∨): ${(0, propositional_1.formulaToString)(f)}`);
|
|
532
|
-
return this.solveRecursive([
|
|
533
|
-
...(inner.args || []).map((a) => ({
|
|
534
|
-
formula: { kind: 'not', args: [a] },
|
|
535
|
-
})),
|
|
536
|
-
...rest,
|
|
537
|
-
], constants, nextProcessed, depth + 1, trace);
|
|
538
|
-
case 'implies':
|
|
539
|
-
trace.push(`[${depth}] Alfa (¬→): ${(0, propositional_1.formulaToString)(f)}`);
|
|
540
|
-
return this.solveRecursive([
|
|
541
|
-
{ formula: (inner.args || [])[0] },
|
|
542
|
-
{ formula: { kind: 'not', args: [(inner.args || [])[1]] } },
|
|
543
|
-
...rest,
|
|
544
|
-
], constants, nextProcessed, depth + 1, trace);
|
|
545
|
-
case 'forall': {
|
|
546
|
-
const variable = inner.variable;
|
|
547
|
-
if (!(inner.args || [])[0] || !variable)
|
|
548
|
-
return false;
|
|
549
|
-
const newC = `c${constants.size}`;
|
|
550
|
-
trace.push(`[${depth}] Delta (¬∀): ${(0, propositional_1.formulaToString)(f)} -> instanciando con ${newC} (EI)`);
|
|
551
|
-
const nextConstants = new Set(constants).add(newC);
|
|
552
|
-
return this.solveRecursive([
|
|
553
|
-
{
|
|
554
|
-
formula: {
|
|
555
|
-
kind: 'not',
|
|
556
|
-
args: [this.substitute(inner.args[0], variable, newC)],
|
|
557
|
-
},
|
|
558
|
-
},
|
|
559
|
-
...rest,
|
|
560
|
-
], nextConstants, nextProcessed, depth + 1, trace);
|
|
561
|
-
}
|
|
562
|
-
case 'exists': {
|
|
563
|
-
const variable = inner.variable;
|
|
564
|
-
if (!(inner.args || [])[0] || !variable)
|
|
565
|
-
return false;
|
|
566
|
-
const negForall = {
|
|
567
|
-
kind: 'forall',
|
|
568
|
-
variable,
|
|
569
|
-
args: [{ kind: 'not', args: [inner.args[0]] }],
|
|
570
|
-
};
|
|
571
|
-
trace.push(`[${depth}] Gamma (¬∃): ${(0, propositional_1.formulaToString)(f)} -> transformando a ∀¬ (UG/UI prep)`);
|
|
572
|
-
return this.solveRecursive([{ formula: negForall }, ...rest], constants, nextProcessed, depth + 1, trace);
|
|
573
|
-
}
|
|
574
|
-
case 'not':
|
|
575
|
-
trace.push(`[${depth}] Doble negación: ${(0, propositional_1.formulaToString)(f)}`);
|
|
576
|
-
return this.solveRecursive([{ formula: (inner.args || [])[0] }, ...rest], constants, nextProcessed, depth + 1, trace);
|
|
577
|
-
}
|
|
578
|
-
break;
|
|
579
|
-
}
|
|
281
|
+
// A -> B branches into: ¬A or B
|
|
282
|
+
return (this.solveRecursive([{ formula: { kind: 'not', args: [f.args[0]] } }, ...rest], constants, nextProcessed, depth + 1, trace) &&
|
|
283
|
+
this.solveRecursive([{ formula: f.args[1] }, ...rest], constants, nextProcessed, depth + 1, trace));
|
|
580
284
|
}
|
|
581
285
|
}
|
|
582
|
-
trace.push(`[${depth}] ✓ Rama saturada y ABIERTA.`);
|
|
583
286
|
return false;
|
|
584
287
|
}
|
|
585
|
-
formulaHash(f) {
|
|
586
|
-
return (0, propositional_1.formulaToString)(f);
|
|
587
|
-
}
|
|
588
288
|
substitute(f, v, c) {
|
|
589
289
|
const sub = (n) => {
|
|
590
290
|
if (n.kind === 'predicate' && n.params)
|
|
591
291
|
return { ...n, params: n.params.map((p) => (p === v ? c : p)) };
|
|
592
292
|
if (n.kind === 'atom' && n.name === v)
|
|
593
293
|
return { ...n, name: c };
|
|
594
|
-
if ((n.kind === 'forall' || n.kind === 'exists') && n.variable === v)
|
|
595
|
-
return n;
|
|
596
294
|
if (n.args)
|
|
597
295
|
return { ...n, args: n.args.map(sub) };
|
|
598
296
|
return n;
|
|
@@ -600,26 +298,7 @@ class ClassicalFirstOrder {
|
|
|
600
298
|
return sub(f);
|
|
601
299
|
}
|
|
602
300
|
isEqual(a, b) {
|
|
603
|
-
|
|
604
|
-
return false;
|
|
605
|
-
if (a.kind === 'atom' && b.kind === 'atom')
|
|
606
|
-
return a.name === b.name;
|
|
607
|
-
if (a.kind === 'predicate' && b.kind === 'predicate') {
|
|
608
|
-
if (a.name !== b.name)
|
|
609
|
-
return false;
|
|
610
|
-
const pa = a.params || [];
|
|
611
|
-
const pb = b.params || [];
|
|
612
|
-
if (pa.length !== pb.length)
|
|
613
|
-
return false;
|
|
614
|
-
return pa.every((p, i) => p === pb[i]);
|
|
615
|
-
}
|
|
616
|
-
if (a.variable !== b.variable)
|
|
617
|
-
return false;
|
|
618
|
-
const aa = a.args || [];
|
|
619
|
-
const ba = b.args || [];
|
|
620
|
-
if (aa.length !== ba.length)
|
|
621
|
-
return false;
|
|
622
|
-
return aa.every((ai, i) => this.isEqual(ai, ba[i]));
|
|
301
|
+
return (0, propositional_1.formulaToString)(a) === (0, propositional_1.formulaToString)(b);
|
|
623
302
|
}
|
|
624
303
|
}
|
|
625
304
|
exports.ClassicalFirstOrder = ClassicalFirstOrder;
|