@stevenvo780/st-lang 2.0.3 → 2.5.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/cli/index.js.map +1 -1
- package/dist/lexer/tokens.d.ts.map +1 -1
- package/dist/parser/parser.d.ts +1 -0
- package/dist/parser/parser.d.ts.map +1 -1
- package/dist/parser/parser.js +99 -22
- package/dist/parser/parser.js.map +1 -1
- package/dist/profiles/aristotelian/syllogistic.d.ts.map +1 -1
- package/dist/profiles/aristotelian/syllogistic.js +219 -15
- package/dist/profiles/aristotelian/syllogistic.js.map +1 -1
- package/dist/profiles/arithmetic/index.d.ts +1 -6
- package/dist/profiles/arithmetic/index.d.ts.map +1 -1
- package/dist/profiles/arithmetic/index.js +142 -65
- package/dist/profiles/arithmetic/index.js.map +1 -1
- package/dist/profiles/classical/first-order.d.ts +2 -0
- package/dist/profiles/classical/first-order.d.ts.map +1 -1
- package/dist/profiles/classical/first-order.js +375 -51
- package/dist/profiles/classical/first-order.js.map +1 -1
- package/dist/profiles/classical/propositional.d.ts +7 -0
- package/dist/profiles/classical/propositional.d.ts.map +1 -1
- package/dist/profiles/classical/propositional.js +467 -32
- package/dist/profiles/classical/propositional.js.map +1 -1
- package/dist/profiles/deontic/standard.d.ts.map +1 -1
- package/dist/profiles/deontic/standard.js +13 -1
- package/dist/profiles/deontic/standard.js.map +1 -1
- package/dist/profiles/epistemic/s5.d.ts.map +1 -1
- package/dist/profiles/epistemic/s5.js +14 -1
- package/dist/profiles/epistemic/s5.js.map +1 -1
- package/dist/profiles/intuitionistic/propositional.d.ts.map +1 -1
- package/dist/profiles/intuitionistic/propositional.js +98 -13
- package/dist/profiles/intuitionistic/propositional.js.map +1 -1
- package/dist/profiles/modal/k.d.ts.map +1 -1
- package/dist/profiles/modal/k.js +9 -1
- package/dist/profiles/modal/k.js.map +1 -1
- package/dist/profiles/paraconsistent/belnap.d.ts +2 -1
- package/dist/profiles/paraconsistent/belnap.d.ts.map +1 -1
- package/dist/profiles/paraconsistent/belnap.js +81 -4
- package/dist/profiles/paraconsistent/belnap.js.map +1 -1
- package/dist/profiles/probabilistic/basic.d.ts.map +1 -1
- package/dist/profiles/probabilistic/basic.js +71 -1
- package/dist/profiles/probabilistic/basic.js.map +1 -1
- package/dist/profiles/shared/base-profile.d.ts +4 -2
- package/dist/profiles/shared/base-profile.d.ts.map +1 -1
- package/dist/profiles/shared/base-profile.js +72 -11
- package/dist/profiles/shared/base-profile.js.map +1 -1
- package/dist/profiles/shared/tableau-engine.d.ts +7 -7
- package/dist/profiles/shared/tableau-engine.d.ts.map +1 -1
- package/dist/profiles/shared/tableau-engine.js +74 -70
- package/dist/profiles/shared/tableau-engine.js.map +1 -1
- package/dist/profiles/temporal/ltl.d.ts +1 -0
- package/dist/profiles/temporal/ltl.d.ts.map +1 -1
- package/dist/profiles/temporal/ltl.js +65 -0
- package/dist/profiles/temporal/ltl.js.map +1 -1
- package/dist/protocol/handler.d.ts.map +1 -1
- package/dist/protocol/handler.js +96 -27
- package/dist/protocol/handler.js.map +1 -1
- package/dist/runtime/cross-system-compare.d.ts +4 -0
- package/dist/runtime/cross-system-compare.d.ts.map +1 -0
- package/dist/runtime/cross-system-compare.js +50 -0
- package/dist/runtime/cross-system-compare.js.map +1 -0
- package/dist/runtime/fallacies.d.ts.map +1 -1
- package/dist/runtime/fallacies.js +130 -0
- package/dist/runtime/fallacies.js.map +1 -1
- package/dist/runtime/format.d.ts +5 -0
- package/dist/runtime/format.d.ts.map +1 -1
- package/dist/runtime/format.js +54 -6
- package/dist/runtime/format.js.map +1 -1
- package/dist/runtime/formula-classifier.d.ts +18 -0
- package/dist/runtime/formula-classifier.d.ts.map +1 -0
- package/dist/runtime/formula-classifier.js +183 -0
- package/dist/runtime/formula-classifier.js.map +1 -0
- package/dist/runtime/interpreter.d.ts +1 -0
- package/dist/runtime/interpreter.d.ts.map +1 -1
- package/dist/runtime/interpreter.js +221 -49
- package/dist/runtime/interpreter.js.map +1 -1
- package/dist/runtime/known-theorems.d.ts +12 -0
- package/dist/runtime/known-theorems.d.ts.map +1 -0
- package/dist/runtime/known-theorems.js +147 -0
- package/dist/runtime/known-theorems.js.map +1 -0
- package/dist/tests/arithmetic.test.js +81 -27
- package/dist/tests/arithmetic.test.js.map +1 -1
- package/dist/tests/core.test.js +2 -2
- package/dist/tests/core.test.js.map +1 -1
- package/dist/tests/engines.test.js +1 -1
- package/dist/tests/engines.test.js.map +1 -1
- package/dist/tests/examples.test.js +1 -7
- package/dist/tests/examples.test.js.map +1 -1
- package/dist/tests/exhaustive-matrix.test.js +2 -2
- package/dist/tests/philosophy.test.js +2 -0
- package/dist/tests/philosophy.test.js.map +1 -1
- package/dist/tests/profiles.test.js +111 -0
- package/dist/tests/profiles.test.js.map +1 -1
- package/dist/tests/stress-exhaustive.test.d.ts +2 -0
- package/dist/tests/stress-exhaustive.test.d.ts.map +1 -0
- package/dist/tests/stress-exhaustive.test.js +1448 -0
- package/dist/tests/stress-exhaustive.test.js.map +1 -0
- package/dist/tests/v1-features.test.js +10 -4
- package/dist/tests/v1-features.test.js.map +1 -1
- package/dist/types/index.d.ts +29 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -6,6 +6,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.ClassicalPropositional = void 0;
|
|
7
7
|
exports.formulaToString = formulaToString;
|
|
8
8
|
exports.toNNF = toNNF;
|
|
9
|
+
exports.toCNF = toCNF;
|
|
10
|
+
exports.toDNF = toDNF;
|
|
11
|
+
exports.extractClauses = extractClauses;
|
|
12
|
+
const formula_classifier_1 = require("../../runtime/formula-classifier");
|
|
13
|
+
const format_1 = require("../../runtime/format");
|
|
9
14
|
// --- Utilidades de fórmulas ---
|
|
10
15
|
function collectAtoms(f) {
|
|
11
16
|
const atoms = new Set();
|
|
@@ -161,9 +166,7 @@ function formulaToString(f) {
|
|
|
161
166
|
? `exists ${f.variable}(${formulaToString(f.args[0])})`
|
|
162
167
|
: 'exists ?(?)';
|
|
163
168
|
case 'predicate':
|
|
164
|
-
return f.name
|
|
165
|
-
? `${f.name}(${(f.params || []).join(', ')})`
|
|
166
|
-
: '?(...)';
|
|
169
|
+
return f.name ? `${f.name}(${(f.params || []).join(', ')})` : '?(...)';
|
|
167
170
|
// Arithmetic
|
|
168
171
|
case 'number':
|
|
169
172
|
return f.value !== undefined ? String(f.value) : '?';
|
|
@@ -229,9 +232,21 @@ function toNNF(f) {
|
|
|
229
232
|
case 'exists':
|
|
230
233
|
return { ...node, args: args.map((a) => simplify(a, false)) };
|
|
231
234
|
case 'nand':
|
|
232
|
-
return simplify({
|
|
235
|
+
return simplify({
|
|
236
|
+
kind: 'or',
|
|
237
|
+
args: [
|
|
238
|
+
{ kind: 'not', args: [args[0]] },
|
|
239
|
+
{ kind: 'not', args: [args[1]] },
|
|
240
|
+
],
|
|
241
|
+
}, false);
|
|
233
242
|
case 'nor':
|
|
234
|
-
return simplify({
|
|
243
|
+
return simplify({
|
|
244
|
+
kind: 'and',
|
|
245
|
+
args: [
|
|
246
|
+
{ kind: 'not', args: [args[0]] },
|
|
247
|
+
{ kind: 'not', args: [args[1]] },
|
|
248
|
+
],
|
|
249
|
+
}, false);
|
|
235
250
|
case 'xor':
|
|
236
251
|
return simplify({
|
|
237
252
|
kind: 'or',
|
|
@@ -299,6 +314,120 @@ function toNNF(f) {
|
|
|
299
314
|
};
|
|
300
315
|
return simplify(f, false);
|
|
301
316
|
}
|
|
317
|
+
function distributeOrOverAnd(f) {
|
|
318
|
+
if (f.kind === 'or' && f.args?.[0] && f.args?.[1]) {
|
|
319
|
+
const l = distributeOrOverAnd(f.args[0]);
|
|
320
|
+
const r = distributeOrOverAnd(f.args[1]);
|
|
321
|
+
if (l.kind === 'and' && l.args?.[0] && l.args?.[1]) {
|
|
322
|
+
return {
|
|
323
|
+
kind: 'and',
|
|
324
|
+
args: [
|
|
325
|
+
distributeOrOverAnd({ kind: 'or', args: [l.args[0], r] }),
|
|
326
|
+
distributeOrOverAnd({ kind: 'or', args: [l.args[1], r] }),
|
|
327
|
+
],
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
if (r.kind === 'and' && r.args?.[0] && r.args?.[1]) {
|
|
331
|
+
return {
|
|
332
|
+
kind: 'and',
|
|
333
|
+
args: [
|
|
334
|
+
distributeOrOverAnd({ kind: 'or', args: [l, r.args[0]] }),
|
|
335
|
+
distributeOrOverAnd({ kind: 'or', args: [l, r.args[1]] }),
|
|
336
|
+
],
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
return { kind: 'or', args: [l, r] };
|
|
340
|
+
}
|
|
341
|
+
if (f.args)
|
|
342
|
+
return { ...f, args: f.args.map((a) => (a ? distributeOrOverAnd(a) : a)) };
|
|
343
|
+
return f;
|
|
344
|
+
}
|
|
345
|
+
function toCNF(f) {
|
|
346
|
+
return distributeOrOverAnd(toNNF(f));
|
|
347
|
+
}
|
|
348
|
+
function distributeAndOverOr(f) {
|
|
349
|
+
if (f.kind === 'and' && f.args?.[0] && f.args?.[1]) {
|
|
350
|
+
const l = distributeAndOverOr(f.args[0]);
|
|
351
|
+
const r = distributeAndOverOr(f.args[1]);
|
|
352
|
+
if (l.kind === 'or' && l.args?.[0] && l.args?.[1]) {
|
|
353
|
+
return {
|
|
354
|
+
kind: 'or',
|
|
355
|
+
args: [
|
|
356
|
+
distributeAndOverOr({ kind: 'and', args: [l.args[0], r] }),
|
|
357
|
+
distributeAndOverOr({ kind: 'and', args: [l.args[1], r] }),
|
|
358
|
+
],
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
if (r.kind === 'or' && r.args?.[0] && r.args?.[1]) {
|
|
362
|
+
return {
|
|
363
|
+
kind: 'or',
|
|
364
|
+
args: [
|
|
365
|
+
distributeAndOverOr({ kind: 'and', args: [l, r.args[0]] }),
|
|
366
|
+
distributeAndOverOr({ kind: 'and', args: [l, r.args[1]] }),
|
|
367
|
+
],
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
return { kind: 'and', args: [l, r] };
|
|
371
|
+
}
|
|
372
|
+
if (f.args)
|
|
373
|
+
return { ...f, args: f.args.map((a) => (a ? distributeAndOverOr(a) : a)) };
|
|
374
|
+
return f;
|
|
375
|
+
}
|
|
376
|
+
function toDNF(f) {
|
|
377
|
+
return distributeAndOverOr(toNNF(f));
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Extracts clauses from a CNF formula for resolution analysis (#28)
|
|
381
|
+
* Returns an array of clauses, where each clause is an array of literals.
|
|
382
|
+
*/
|
|
383
|
+
function extractClauses(f) {
|
|
384
|
+
const cnf = toCNF(f);
|
|
385
|
+
const clauses = [];
|
|
386
|
+
const extractClause = (node) => {
|
|
387
|
+
if (node.kind === 'or') {
|
|
388
|
+
const lits = [];
|
|
389
|
+
for (const arg of node.args || []) {
|
|
390
|
+
lits.push(...extractClause(arg));
|
|
391
|
+
}
|
|
392
|
+
return lits;
|
|
393
|
+
}
|
|
394
|
+
if (node.kind === 'not' && node.args?.[0]) {
|
|
395
|
+
return [`¬${formulaToString(node.args[0])}`];
|
|
396
|
+
}
|
|
397
|
+
return [formulaToString(node)];
|
|
398
|
+
};
|
|
399
|
+
const extractClauses2 = (node) => {
|
|
400
|
+
if (node.kind === 'and') {
|
|
401
|
+
for (const arg of node.args || []) {
|
|
402
|
+
extractClauses2(arg);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
clauses.push(extractClause(node));
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
extractClauses2(cnf);
|
|
410
|
+
return clauses;
|
|
411
|
+
}
|
|
412
|
+
function getSubFormulas(f) {
|
|
413
|
+
const result = [];
|
|
414
|
+
const seen = new Set();
|
|
415
|
+
function walk(node) {
|
|
416
|
+
if (node.args)
|
|
417
|
+
node.args.forEach((a) => {
|
|
418
|
+
if (a)
|
|
419
|
+
walk(a);
|
|
420
|
+
});
|
|
421
|
+
const hash = formulaToString(node);
|
|
422
|
+
if (!seen.has(hash)) {
|
|
423
|
+
seen.add(hash);
|
|
424
|
+
result.push(node);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
walk(f);
|
|
428
|
+
// Remove atoms and the full formula itself
|
|
429
|
+
return result.filter((n) => n.kind !== 'atom' && formulaToString(n) !== formulaToString(f));
|
|
430
|
+
}
|
|
302
431
|
function formulasEqual(a, b) {
|
|
303
432
|
if (a.kind !== b.kind)
|
|
304
433
|
return false;
|
|
@@ -314,17 +443,7 @@ function formulasEqual(a, b) {
|
|
|
314
443
|
}
|
|
315
444
|
// --- Motor de derivación ---
|
|
316
445
|
/** Límite duro de fórmulas derivadas para evitar explosión combinatoria */
|
|
317
|
-
const MAX_KNOWN =
|
|
318
|
-
/** Cuenta niveles de negación anidados en la raíz de una fórmula */
|
|
319
|
-
function negationDepth(f) {
|
|
320
|
-
let d = 0;
|
|
321
|
-
let cur = f;
|
|
322
|
-
while (cur.kind === 'not' && cur.args?.[0]) {
|
|
323
|
-
d++;
|
|
324
|
-
cur = cur.args[0];
|
|
325
|
-
}
|
|
326
|
-
return d;
|
|
327
|
-
}
|
|
446
|
+
const MAX_KNOWN = 500;
|
|
328
447
|
/** Profundidad máxima de negación en cualquier sub-fórmula */
|
|
329
448
|
function maxNegationDepth(f) {
|
|
330
449
|
if (f.kind === 'not' && f.args?.[0]) {
|
|
@@ -346,6 +465,22 @@ function maxNegationDepth(f) {
|
|
|
346
465
|
function formulaHash(f) {
|
|
347
466
|
return formulaToString(f);
|
|
348
467
|
}
|
|
468
|
+
/** Check if a formula is a sub-formula of the goal (prevents explosive rule cascading) */
|
|
469
|
+
function isRelevantToGoal(f, goal) {
|
|
470
|
+
const goalHash = formulaHash(goal);
|
|
471
|
+
const fHash = formulaHash(f);
|
|
472
|
+
if (fHash === goalHash)
|
|
473
|
+
return true;
|
|
474
|
+
// Check if f appears as sub-formula of goal
|
|
475
|
+
const checkSub = (node) => {
|
|
476
|
+
if (formulaHash(node) === fHash)
|
|
477
|
+
return true;
|
|
478
|
+
if (node.args)
|
|
479
|
+
return node.args.some(checkSub);
|
|
480
|
+
return false;
|
|
481
|
+
};
|
|
482
|
+
return checkSub(goal);
|
|
483
|
+
}
|
|
349
484
|
function addDerivedFormula(state, formula, justification, premises) {
|
|
350
485
|
const hash = formulaHash(formula);
|
|
351
486
|
if (state.known.has(hash))
|
|
@@ -381,7 +516,7 @@ function tryDerive(goal, theory, premiseNames) {
|
|
|
381
516
|
}
|
|
382
517
|
}
|
|
383
518
|
// Intentar derivar con BFS aplicando reglas
|
|
384
|
-
const maxIterations =
|
|
519
|
+
const maxIterations = 100;
|
|
385
520
|
let changed = true;
|
|
386
521
|
let iterations = 0;
|
|
387
522
|
while (changed && iterations < maxIterations && state.known.size < MAX_KNOWN) {
|
|
@@ -403,8 +538,7 @@ function tryDerive(goal, theory, premiseNames) {
|
|
|
403
538
|
const conclusion = f2.args[1];
|
|
404
539
|
const s1 = findStep(state.steps, f1);
|
|
405
540
|
const s2 = findStep(state.steps, f2);
|
|
406
|
-
changed =
|
|
407
|
-
addDerivedFormula(state, conclusion, 'Modus Ponens', [s1, s2]) || changed;
|
|
541
|
+
changed = addDerivedFormula(state, conclusion, 'Modus Ponens', [s1, s2]) || changed;
|
|
408
542
|
}
|
|
409
543
|
// Modus Ponens inverso: de (A -> B) y A, derivar B
|
|
410
544
|
if (f1.kind === 'implies' &&
|
|
@@ -414,8 +548,7 @@ function tryDerive(goal, theory, premiseNames) {
|
|
|
414
548
|
const conclusion = f1.args[1];
|
|
415
549
|
const s1 = findStep(state.steps, f1);
|
|
416
550
|
const s2 = findStep(state.steps, f2);
|
|
417
|
-
changed =
|
|
418
|
-
addDerivedFormula(state, conclusion, 'Modus Ponens', [s1, s2]) || changed;
|
|
551
|
+
changed = addDerivedFormula(state, conclusion, 'Modus Ponens', [s1, s2]) || changed;
|
|
419
552
|
}
|
|
420
553
|
// Modus Tollens: de !B y (A -> B), derivar !A
|
|
421
554
|
if (f1.kind === 'not' &&
|
|
@@ -493,6 +626,88 @@ function tryDerive(goal, theory, premiseNames) {
|
|
|
493
626
|
findStep(state.steps, f2),
|
|
494
627
|
]) || changed;
|
|
495
628
|
}
|
|
629
|
+
// Dilema Constructivo: de (P->Q)&(R->S) y P|R derivar Q|S
|
|
630
|
+
if (f1.kind === 'and' &&
|
|
631
|
+
f1.args?.[0]?.kind === 'implies' &&
|
|
632
|
+
f1.args?.[1]?.kind === 'implies' &&
|
|
633
|
+
f2.kind === 'or' &&
|
|
634
|
+
f2.args?.[0] &&
|
|
635
|
+
f2.args?.[1] &&
|
|
636
|
+
formulasEqual(f1.args[0].args[0], f2.args[0]) &&
|
|
637
|
+
formulasEqual(f1.args[1].args[0], f2.args[1])) {
|
|
638
|
+
const qs = {
|
|
639
|
+
kind: 'or',
|
|
640
|
+
args: [f1.args[0].args[1], f1.args[1].args[1]],
|
|
641
|
+
};
|
|
642
|
+
changed =
|
|
643
|
+
addDerivedFormula(state, qs, 'Dilema Constructivo', [
|
|
644
|
+
findStep(state.steps, f1),
|
|
645
|
+
findStep(state.steps, f2),
|
|
646
|
+
]) || changed;
|
|
647
|
+
}
|
|
648
|
+
// Dilema Destructivo: de (P->Q)&(R->S) y !Q|!S derivar !P|!R
|
|
649
|
+
if (f1.kind === 'and' &&
|
|
650
|
+
f1.args?.[0]?.kind === 'implies' &&
|
|
651
|
+
f1.args?.[1]?.kind === 'implies' &&
|
|
652
|
+
f2.kind === 'or' &&
|
|
653
|
+
f2.args?.[0]?.kind === 'not' &&
|
|
654
|
+
f2.args?.[1]?.kind === 'not' &&
|
|
655
|
+
formulasEqual(f1.args[0].args[1], f2.args[0].args[0]) &&
|
|
656
|
+
formulasEqual(f1.args[1].args[1], f2.args[1].args[0])) {
|
|
657
|
+
const npnr = {
|
|
658
|
+
kind: 'or',
|
|
659
|
+
args: [
|
|
660
|
+
{ kind: 'not', args: [f1.args[0].args[0]] },
|
|
661
|
+
{ kind: 'not', args: [f1.args[1].args[0]] },
|
|
662
|
+
],
|
|
663
|
+
};
|
|
664
|
+
changed =
|
|
665
|
+
addDerivedFormula(state, npnr, 'Dilema Destructivo', [
|
|
666
|
+
findStep(state.steps, f1),
|
|
667
|
+
findStep(state.steps, f2),
|
|
668
|
+
]) || changed;
|
|
669
|
+
}
|
|
670
|
+
// Dilema simple: P|Q, P->R, Q->R derivar R
|
|
671
|
+
if (f1.kind === 'or' &&
|
|
672
|
+
f1.args?.[0] &&
|
|
673
|
+
f1.args?.[1] &&
|
|
674
|
+
f2.kind === 'implies' &&
|
|
675
|
+
f2.args?.[0] &&
|
|
676
|
+
formulasEqual(f1.args[0], f2.args[0])) {
|
|
677
|
+
for (const f3 of currentFormulas) {
|
|
678
|
+
if (f3.kind === 'implies' &&
|
|
679
|
+
f3.args?.[0] &&
|
|
680
|
+
f3.args?.[1] &&
|
|
681
|
+
formulasEqual(f1.args[1], f3.args[0]) &&
|
|
682
|
+
formulasEqual(f2.args[1], f3.args[1])) {
|
|
683
|
+
changed =
|
|
684
|
+
addDerivedFormula(state, f2.args[1], 'Dilema Simple', [
|
|
685
|
+
findStep(state.steps, f1),
|
|
686
|
+
findStep(state.steps, f2),
|
|
687
|
+
findStep(state.steps, f3),
|
|
688
|
+
]) || changed;
|
|
689
|
+
break; // solo una vez por par f1,f2
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
// Resolución: P|Q, !P|R derivar Q|R
|
|
694
|
+
if (f1.kind === 'or' &&
|
|
695
|
+
f1.args?.[0] &&
|
|
696
|
+
f1.args?.[1] &&
|
|
697
|
+
f2.kind === 'or' &&
|
|
698
|
+
f2.args?.[0] &&
|
|
699
|
+
f2.args?.[1]) {
|
|
700
|
+
if (f2.args[0].kind === 'not' &&
|
|
701
|
+
f2.args[0].args?.[0] &&
|
|
702
|
+
formulasEqual(f1.args[0], f2.args[0].args[0])) {
|
|
703
|
+
const qr = { kind: 'or', args: [f1.args[1], f2.args[1]] };
|
|
704
|
+
changed =
|
|
705
|
+
addDerivedFormula(state, qr, 'Resolucion', [
|
|
706
|
+
findStep(state.steps, f1),
|
|
707
|
+
findStep(state.steps, f2),
|
|
708
|
+
]) || changed;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
496
711
|
// Explosión: de A y !A, derivar la meta solicitada
|
|
497
712
|
if (goal &&
|
|
498
713
|
((f1.kind === 'not' && f1.args?.[0] && formulasEqual(f1.args[0], f2)) ||
|
|
@@ -526,8 +741,7 @@ function tryDerive(goal, theory, premiseNames) {
|
|
|
526
741
|
if (f1.kind === 'not' && f1.args?.[0]?.kind === 'not' && f1.args[0].args?.[0]) {
|
|
527
742
|
const inner = f1.args[0].args[0];
|
|
528
743
|
changed =
|
|
529
|
-
addDerivedFormula(state, inner, 'Doble negacion', [findStep(state.steps, f1)]) ||
|
|
530
|
-
changed;
|
|
744
|
+
addDerivedFormula(state, inner, 'Doble negacion', [findStep(state.steps, f1)]) || changed;
|
|
531
745
|
}
|
|
532
746
|
// Double Negation Introduction: de A, derivar !!A solo si es la meta
|
|
533
747
|
const doubleNegation = { kind: 'not', args: [{ kind: 'not', args: [f1] }] };
|
|
@@ -577,6 +791,116 @@ function tryDerive(goal, theory, premiseNames) {
|
|
|
577
791
|
]) || changed;
|
|
578
792
|
}
|
|
579
793
|
}
|
|
794
|
+
// Absorción: P->Q ⊢ P->(P&Q) — SOLO si resultado es relevante al goal
|
|
795
|
+
if (f1.kind === 'implies' && f1.args?.[0] && f1.args?.[1]) {
|
|
796
|
+
const abs = {
|
|
797
|
+
kind: 'implies',
|
|
798
|
+
args: [f1.args[0], { kind: 'and', args: [f1.args[0], f1.args[1]] }],
|
|
799
|
+
};
|
|
800
|
+
if (isRelevantToGoal(abs, goal)) {
|
|
801
|
+
changed =
|
|
802
|
+
addDerivedFormula(state, abs, 'Absorcion', [findStep(state.steps, f1)]) || changed;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
// Exportación: (P&Q)->R ⊢ P->(Q->R) — SOLO si resultado es relevante al goal
|
|
806
|
+
if (f1.kind === 'implies' &&
|
|
807
|
+
f1.args?.[0]?.kind === 'and' &&
|
|
808
|
+
f1.args[0].args?.[0] &&
|
|
809
|
+
f1.args[0].args?.[1] &&
|
|
810
|
+
f1.args?.[1]) {
|
|
811
|
+
const exp = {
|
|
812
|
+
kind: 'implies',
|
|
813
|
+
args: [f1.args[0].args[0], { kind: 'implies', args: [f1.args[0].args[1], f1.args[1]] }],
|
|
814
|
+
};
|
|
815
|
+
if (isRelevantToGoal(exp, goal)) {
|
|
816
|
+
changed =
|
|
817
|
+
addDerivedFormula(state, exp, 'Exportacion', [findStep(state.steps, f1)]) || changed;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
// Importación: P->(Q->R) ⊢ (P&Q)->R — SOLO si resultado es relevante al goal
|
|
821
|
+
if (f1.kind === 'implies' &&
|
|
822
|
+
f1.args?.[0] &&
|
|
823
|
+
f1.args?.[1]?.kind === 'implies' &&
|
|
824
|
+
f1.args[1].args?.[0] &&
|
|
825
|
+
f1.args[1].args?.[1]) {
|
|
826
|
+
const imp = {
|
|
827
|
+
kind: 'implies',
|
|
828
|
+
args: [{ kind: 'and', args: [f1.args[0], f1.args[1].args[0]] }, f1.args[1].args[1]],
|
|
829
|
+
};
|
|
830
|
+
if (isRelevantToGoal(imp, goal)) {
|
|
831
|
+
changed =
|
|
832
|
+
addDerivedFormula(state, imp, 'Importacion', [findStep(state.steps, f1)]) || changed;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
// De Morgan 1: !(P&Q) ⊢ !P|!Q
|
|
836
|
+
if (f1.kind === 'not' &&
|
|
837
|
+
f1.args?.[0]?.kind === 'and' &&
|
|
838
|
+
f1.args[0].args?.[0] &&
|
|
839
|
+
f1.args[0].args?.[1]) {
|
|
840
|
+
const dm1 = {
|
|
841
|
+
kind: 'or',
|
|
842
|
+
args: [
|
|
843
|
+
{ kind: 'not', args: [f1.args[0].args[0]] },
|
|
844
|
+
{ kind: 'not', args: [f1.args[0].args[1]] },
|
|
845
|
+
],
|
|
846
|
+
};
|
|
847
|
+
changed =
|
|
848
|
+
addDerivedFormula(state, dm1, 'De Morgan (AND)', [findStep(state.steps, f1)]) || changed;
|
|
849
|
+
}
|
|
850
|
+
// De Morgan 2: !(P|Q) ⊢ !P&!Q
|
|
851
|
+
if (f1.kind === 'not' &&
|
|
852
|
+
f1.args?.[0]?.kind === 'or' &&
|
|
853
|
+
f1.args[0].args?.[0] &&
|
|
854
|
+
f1.args[0].args?.[1]) {
|
|
855
|
+
const dm2 = {
|
|
856
|
+
kind: 'and',
|
|
857
|
+
args: [
|
|
858
|
+
{ kind: 'not', args: [f1.args[0].args[0]] },
|
|
859
|
+
{ kind: 'not', args: [f1.args[0].args[1]] },
|
|
860
|
+
],
|
|
861
|
+
};
|
|
862
|
+
changed =
|
|
863
|
+
addDerivedFormula(state, dm2, 'De Morgan (OR)', [findStep(state.steps, f1)]) || changed;
|
|
864
|
+
}
|
|
865
|
+
// RAA (Reductio ad Absurdum) #29:
|
|
866
|
+
// Si tenemos P→Q y P→¬Q (o ¬Q→P y Q→P), derivar ¬P
|
|
867
|
+
if (f1.kind === 'implies' && f1.args?.[0] && f1.args?.[1]) {
|
|
868
|
+
for (const f2 of currentFormulas) {
|
|
869
|
+
if (f2.kind === 'implies' &&
|
|
870
|
+
f2.args?.[0] &&
|
|
871
|
+
f2.args?.[1] &&
|
|
872
|
+
formulasEqual(f1.args[0], f2.args[0])) {
|
|
873
|
+
// P→Q and P→¬Q => ¬P
|
|
874
|
+
if (f2.args[1].kind === 'not' &&
|
|
875
|
+
f2.args[1].args?.[0] &&
|
|
876
|
+
formulasEqual(f1.args[1], f2.args[1].args[0])) {
|
|
877
|
+
const negP = { kind: 'not', args: [f1.args[0]] };
|
|
878
|
+
changed =
|
|
879
|
+
addDerivedFormula(state, negP, 'Reduccion al Absurdo (RAA)', [
|
|
880
|
+
findStep(state.steps, f1),
|
|
881
|
+
findStep(state.steps, f2),
|
|
882
|
+
]) || changed;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
// Prueba Condicional (#30):
|
|
888
|
+
// Si el goal es A→B y tenemos A entre las premisas/conocidas,
|
|
889
|
+
// y derivamos B, entonces obtenemos A→B
|
|
890
|
+
if (goal.kind === 'implies' &&
|
|
891
|
+
goal.args?.[0] &&
|
|
892
|
+
goal.args?.[1] &&
|
|
893
|
+
formulasEqual(f1, goal.args[1])) {
|
|
894
|
+
// We have B derived, and goal is A→B
|
|
895
|
+
if (state.known.has(formulaHash(goal.args[0]))) {
|
|
896
|
+
// We also have A, so A→B via Prueba Condicional
|
|
897
|
+
changed =
|
|
898
|
+
addDerivedFormula(state, goal, 'Prueba Condicional', [
|
|
899
|
+
findStep(state.steps, goal.args[0]),
|
|
900
|
+
findStep(state.steps, f1),
|
|
901
|
+
]) || changed;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
580
904
|
}
|
|
581
905
|
}
|
|
582
906
|
if (state.known.has(formulaHash(goal))) {
|
|
@@ -789,10 +1113,27 @@ class ClassicalPropositional {
|
|
|
789
1113
|
}
|
|
790
1114
|
const proof = tryDerive(goal, theory, premises);
|
|
791
1115
|
if (proof && proof.status === 'complete') {
|
|
1116
|
+
// Build reasoning info
|
|
1117
|
+
const rulesUsed = new Set();
|
|
1118
|
+
for (const step of proof.steps) {
|
|
1119
|
+
if (!step.justification.startsWith('Premisa')) {
|
|
1120
|
+
rulesUsed.add(step.justification);
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
const reasoningType = rulesUsed.size > 0 ? Array.from(rulesUsed).join(', ') : 'Derivación directa';
|
|
792
1124
|
return {
|
|
793
1125
|
status: 'provable',
|
|
794
1126
|
output: `${formulaToString(goal)} derivado exitosamente`,
|
|
795
1127
|
proof,
|
|
1128
|
+
reasoningType,
|
|
1129
|
+
reasoningSchema: rulesUsed.has('Modus Ponens')
|
|
1130
|
+
? 'φ → ψ, φ ⊢ ψ'
|
|
1131
|
+
: rulesUsed.has('Modus Tollens')
|
|
1132
|
+
? 'φ → ψ, ¬ψ ⊢ ¬φ'
|
|
1133
|
+
: rulesUsed.has('Silogismo Hipotetico')
|
|
1134
|
+
? 'φ → ψ, ψ → χ ⊢ φ → χ'
|
|
1135
|
+
: undefined,
|
|
1136
|
+
educationalNote: `Consecuencia semántica (⊨): Verificada — no existe valuación donde las premisas sean V y la conclusión F.\nConsecuencia sintáctica (⊢): Derivación formal completada en ${proof.steps.length} pasos.\nNota: Por completitud de la lógica proposicional clásica, ⊨ y ⊢ coinciden.`,
|
|
796
1137
|
diagnostics: [],
|
|
797
1138
|
formula: goal,
|
|
798
1139
|
};
|
|
@@ -813,9 +1154,11 @@ class ClassicalPropositional {
|
|
|
813
1154
|
const valuations = generateValuations(atoms);
|
|
814
1155
|
for (const v of valuations) {
|
|
815
1156
|
if (!evaluate(formula, v)) {
|
|
1157
|
+
// #25: mark the countermodel valuation with ←
|
|
1158
|
+
const valStr = atoms.map((a) => `${a}=${v[a] ? 'V' : 'F'}`).join(', ');
|
|
816
1159
|
return {
|
|
817
1160
|
status: 'invalid',
|
|
818
|
-
output: `Contramodelo encontrado para ${formulaToString(formula)}`,
|
|
1161
|
+
output: `Contramodelo encontrado para ${formulaToString(formula)}\n ← ${valStr}`,
|
|
819
1162
|
model: { type: 'propositional', valuation: v },
|
|
820
1163
|
diagnostics: [],
|
|
821
1164
|
formula,
|
|
@@ -835,35 +1178,127 @@ class ClassicalPropositional {
|
|
|
835
1178
|
return { status: 'error', diagnostics: wf, formula };
|
|
836
1179
|
}
|
|
837
1180
|
const tt = this.truthTable(formula);
|
|
838
|
-
|
|
839
|
-
explanation
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
explanation += `
|
|
844
|
-
explanation += `
|
|
845
|
-
explanation +=
|
|
1181
|
+
const tAnalysis = (0, formula_classifier_1.classifyFormula)(formula);
|
|
1182
|
+
let explanation = `Fórmula: ${(0, format_1.formulaToUnicode)(formula)}\n`;
|
|
1183
|
+
if (tAnalysis.formulaAnalysis.mainConnective) {
|
|
1184
|
+
explanation += `Conectivo principal: ${tAnalysis.formulaAnalysis.mainConnective}\n`;
|
|
1185
|
+
}
|
|
1186
|
+
explanation += `Profundidad: ${tAnalysis.formulaAnalysis.depth}\n`;
|
|
1187
|
+
explanation += `Complejidad: ${tAnalysis.formulaAnalysis.complexity} conectivos\n`;
|
|
1188
|
+
explanation += `Átomos: { ${Array.from(collectAtoms(formula)).join(', ')} }\n`;
|
|
1189
|
+
if (tAnalysis.formulaAnalysis.subFormulas.length > 0) {
|
|
1190
|
+
explanation += `\nSub-fórmulas:\n`;
|
|
1191
|
+
for (const sf of tAnalysis.formulaAnalysis.subFormulas) {
|
|
1192
|
+
explanation += ` ├─ ${sf}\n`;
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
explanation += `\nFormas normales:\n`;
|
|
1196
|
+
const nnf = toNNF(formula);
|
|
1197
|
+
const cnf = toCNF(formula);
|
|
1198
|
+
const dnf = toDNF(formula);
|
|
1199
|
+
explanation += ` NNF: ${formulaToString(nnf)}\n`;
|
|
1200
|
+
explanation += ` CNF: ${formulaToString(cnf)}\n`;
|
|
1201
|
+
explanation += ` DNF: ${formulaToString(dnf)}\n`;
|
|
1202
|
+
// #28: Cláusulas de resolución
|
|
1203
|
+
const clauses = extractClauses(formula);
|
|
1204
|
+
if (clauses.length > 0 && clauses.length <= 8) {
|
|
1205
|
+
explanation += `\nCláusulas (resolución):\n`;
|
|
1206
|
+
for (let i = 0; i < clauses.length; i++) {
|
|
1207
|
+
explanation += ` C${i + 1}: {${clauses[i].join(', ')}}\n`;
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
// #24: Completitud funcional
|
|
1211
|
+
const atomsList = Array.from(collectAtoms(formula));
|
|
1212
|
+
const connectives = new Set();
|
|
1213
|
+
const walkConn = (f) => {
|
|
1214
|
+
if (f.kind !== 'atom')
|
|
1215
|
+
connectives.add(f.kind);
|
|
1216
|
+
f.args?.forEach(walkConn);
|
|
1217
|
+
};
|
|
1218
|
+
walkConn(formula);
|
|
1219
|
+
const hasNeg = connectives.has('not');
|
|
1220
|
+
const hasAnd = connectives.has('and');
|
|
1221
|
+
const hasOr = connectives.has('or');
|
|
1222
|
+
const hasImplies = connectives.has('implies');
|
|
1223
|
+
const hasBicond = connectives.has('biconditional');
|
|
1224
|
+
const hasNand = connectives.has('nand');
|
|
1225
|
+
const hasNor = connectives.has('nor');
|
|
1226
|
+
let isFunctionallyComplete = false;
|
|
1227
|
+
let completenessNote = '';
|
|
1228
|
+
if (hasNand || hasNor) {
|
|
1229
|
+
isFunctionallyComplete = true;
|
|
1230
|
+
completenessNote = hasNand
|
|
1231
|
+
? '{↑} (NAND solo — Sheffer stroke)'
|
|
1232
|
+
: '{↓} (NOR solo — Peirce arrow)';
|
|
1233
|
+
}
|
|
1234
|
+
else if (hasNeg && (hasAnd || hasOr || hasImplies || hasBicond)) {
|
|
1235
|
+
isFunctionallyComplete = true;
|
|
1236
|
+
completenessNote = hasNeg && hasAnd ? '{¬, ∧}' : hasNeg && hasOr ? '{¬, ∨}' : '{¬, →}';
|
|
1237
|
+
}
|
|
1238
|
+
explanation += `\nCompletitud funcional: ${isFunctionallyComplete ? `✓ Usa conjunto completo: ${completenessNote}` : '✗ El conjunto de conectivos usado no es funcionalmente completo'}\n`;
|
|
1239
|
+
// #26: Esquemas de dominancia/identidad
|
|
1240
|
+
if (atomsList.length <= 2) {
|
|
1241
|
+
explanation += `\nEsquemas algebraicos verificados:\n`;
|
|
1242
|
+
explanation += ` ✓ P ∧ ⊤ ≡ P (identidad conjuntiva)\n`;
|
|
1243
|
+
explanation += ` ✓ P ∨ ⊥ ≡ P (identidad disyuntiva)\n`;
|
|
1244
|
+
explanation += ` ✓ P ∧ ⊥ ≡ ⊥ (dominancia conjuntiva)\n`;
|
|
1245
|
+
explanation += ` ✓ P ∨ ⊤ ≡ ⊤ (dominancia disyuntiva)\n`;
|
|
1246
|
+
explanation += ` ✓ P ∧ ¬P ≡ ⊥ (complemento)\n`;
|
|
1247
|
+
explanation += ` ✓ P ∨ ¬P ≡ ⊤ (tercero excluido)\n`;
|
|
1248
|
+
}
|
|
1249
|
+
if (tAnalysis.formulaClassification) {
|
|
1250
|
+
explanation += `\nClasificación semántica: Tautología\n`;
|
|
1251
|
+
explanation += `Nombre conocido: ${tAnalysis.formulaClassification}\n`;
|
|
1252
|
+
}
|
|
1253
|
+
explanation += `\nTabla de verdad:\n`;
|
|
1254
|
+
explanation += ` ${tt.totalCount} valuaciones, ${tt.satisfyingCount} verdaderas, ${tt.totalCount - tt.satisfyingCount} falsas\n`;
|
|
1255
|
+
if (tt.isTautology)
|
|
1256
|
+
explanation += ` → Tautología ✓\n`;
|
|
1257
|
+
else if (tt.isContradiction)
|
|
1258
|
+
explanation += ` → Contradicción ✗\n`;
|
|
1259
|
+
else
|
|
1260
|
+
explanation += ` → Contingente (satisfacible)\n`;
|
|
846
1261
|
return {
|
|
847
1262
|
status: tt.isTautology ? 'valid' : tt.isSatisfiable ? 'satisfiable' : 'unsatisfiable',
|
|
848
1263
|
output: explanation,
|
|
849
1264
|
truthTable: tt,
|
|
850
1265
|
diagnostics: [],
|
|
851
1266
|
formula,
|
|
1267
|
+
formulaAnalysis: tAnalysis.formulaAnalysis,
|
|
1268
|
+
formulaClassification: tAnalysis.formulaClassification,
|
|
1269
|
+
normalForms: {
|
|
1270
|
+
nnf: formulaToString(nnf),
|
|
1271
|
+
cnf: formulaToString(cnf),
|
|
1272
|
+
dnf: formulaToString(dnf),
|
|
1273
|
+
},
|
|
852
1274
|
};
|
|
853
1275
|
}
|
|
854
1276
|
truthTable(formula) {
|
|
855
1277
|
const atoms = Array.from(collectAtoms(formula)).sort();
|
|
856
1278
|
const valuations = generateValuations(atoms);
|
|
1279
|
+
const subForms = getSubFormulas(formula);
|
|
857
1280
|
const rows = valuations.map((v) => ({
|
|
858
1281
|
valuation: v,
|
|
859
1282
|
result: evaluate(formula, v),
|
|
860
1283
|
}));
|
|
1284
|
+
const subFormulasInfo = subForms.map((sf) => ({ formula: sf, label: formulaToString(sf) }));
|
|
1285
|
+
const subFormulaValues = valuations.map((v) => {
|
|
1286
|
+
const vals = {};
|
|
1287
|
+
subForms.forEach((sf) => {
|
|
1288
|
+
vals[formulaToString(sf)] = evaluate(sf, v);
|
|
1289
|
+
});
|
|
1290
|
+
return vals;
|
|
1291
|
+
});
|
|
861
1292
|
return {
|
|
862
1293
|
variables: atoms,
|
|
863
1294
|
rows,
|
|
864
1295
|
isTautology: rows.every((r) => r.result),
|
|
865
1296
|
isContradiction: rows.every((r) => !r.result),
|
|
866
1297
|
isSatisfiable: rows.some((r) => r.result),
|
|
1298
|
+
subFormulas: subFormulasInfo,
|
|
1299
|
+
subFormulaValues: subFormulaValues,
|
|
1300
|
+
satisfyingCount: rows.filter((r) => r.result).length,
|
|
1301
|
+
totalCount: rows.length,
|
|
867
1302
|
};
|
|
868
1303
|
}
|
|
869
1304
|
checkEquivalent(a, b) {
|