@stevenvo780/st-lang 3.1.0 → 3.1.2
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/README.md +3 -3
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +44 -9
- package/dist/api.js.map +1 -1
- package/dist/lexer/lexer.d.ts.map +1 -1
- package/dist/lexer/lexer.js +8 -0
- package/dist/lexer/lexer.js.map +1 -1
- package/dist/lexer/tokens.d.ts +2 -0
- package/dist/lexer/tokens.d.ts.map +1 -1
- package/dist/lexer/tokens.js +6 -0
- package/dist/lexer/tokens.js.map +1 -1
- package/dist/parser/parser.d.ts +4 -0
- package/dist/parser/parser.d.ts.map +1 -1
- package/dist/parser/parser.js +224 -92
- package/dist/parser/parser.js.map +1 -1
- package/dist/profiles/aristotelian/syllogistic.d.ts +1 -1
- package/dist/profiles/aristotelian/syllogistic.d.ts.map +1 -1
- package/dist/profiles/aristotelian/syllogistic.js +26 -8
- package/dist/profiles/aristotelian/syllogistic.js.map +1 -1
- package/dist/profiles/arithmetic/index.d.ts +1 -1
- package/dist/profiles/arithmetic/index.d.ts.map +1 -1
- package/dist/profiles/arithmetic/index.js +29 -7
- package/dist/profiles/arithmetic/index.js.map +1 -1
- package/dist/profiles/classical/first-order.d.ts +1 -1
- package/dist/profiles/classical/first-order.d.ts.map +1 -1
- package/dist/profiles/classical/first-order.js +32 -8
- package/dist/profiles/classical/first-order.js.map +1 -1
- package/dist/profiles/classical/propositional.d.ts +1 -1
- package/dist/profiles/classical/propositional.d.ts.map +1 -1
- package/dist/profiles/classical/propositional.js +464 -69
- package/dist/profiles/classical/propositional.js.map +1 -1
- package/dist/profiles/intuitionistic/propositional.d.ts +1 -1
- package/dist/profiles/intuitionistic/propositional.d.ts.map +1 -1
- package/dist/profiles/intuitionistic/propositional.js +33 -5
- package/dist/profiles/intuitionistic/propositional.js.map +1 -1
- package/dist/profiles/paraconsistent/belnap.d.ts +1 -2
- package/dist/profiles/paraconsistent/belnap.d.ts.map +1 -1
- package/dist/profiles/paraconsistent/belnap.js +25 -11
- package/dist/profiles/paraconsistent/belnap.js.map +1 -1
- package/dist/profiles/probabilistic/basic.d.ts +1 -1
- package/dist/profiles/probabilistic/basic.d.ts.map +1 -1
- package/dist/profiles/probabilistic/basic.js +29 -5
- package/dist/profiles/probabilistic/basic.js.map +1 -1
- package/dist/profiles/shared/base-profile.d.ts +1 -1
- package/dist/profiles/shared/base-profile.d.ts.map +1 -1
- package/dist/profiles/shared/base-profile.js +20 -3
- package/dist/profiles/shared/base-profile.js.map +1 -1
- package/dist/profiles/shared/tableau-engine.d.ts +4 -3
- package/dist/profiles/shared/tableau-engine.d.ts.map +1 -1
- package/dist/profiles/shared/tableau-engine.js +107 -15
- package/dist/profiles/shared/tableau-engine.js.map +1 -1
- package/dist/protocol/handler.d.ts +4 -0
- package/dist/protocol/handler.d.ts.map +1 -1
- package/dist/protocol/handler.js +133 -41
- package/dist/protocol/handler.js.map +1 -1
- package/dist/repl/repl.d.ts +2 -0
- package/dist/repl/repl.d.ts.map +1 -1
- package/dist/repl/repl.js +44 -19
- package/dist/repl/repl.js.map +1 -1
- package/dist/runtime/compat.d.ts +29 -0
- package/dist/runtime/compat.d.ts.map +1 -0
- package/dist/runtime/compat.js +819 -0
- package/dist/runtime/compat.js.map +1 -0
- package/dist/runtime/interpreter.d.ts +3 -0
- package/dist/runtime/interpreter.d.ts.map +1 -1
- package/dist/runtime/interpreter.js +133 -30
- package/dist/runtime/interpreter.js.map +1 -1
- package/dist/tests/cli.test.js +5 -1
- package/dist/tests/cli.test.js.map +1 -1
- package/dist/tests/compat.test.d.ts +2 -0
- package/dist/tests/compat.test.d.ts.map +1 -0
- package/dist/tests/compat.test.js +247 -0
- package/dist/tests/compat.test.js.map +1 -0
- package/dist/tests/core.test.js +41 -1
- package/dist/tests/core.test.js.map +1 -1
- package/dist/tests/parser.test.js +38 -0
- package/dist/tests/parser.test.js.map +1 -1
- package/dist/tests/profiles.test.js +8 -0
- package/dist/tests/profiles.test.js.map +1 -1
- package/dist/tests/protocol-text-layer.test.d.ts +2 -0
- package/dist/tests/protocol-text-layer.test.d.ts.map +1 -0
- package/dist/tests/protocol-text-layer.test.js +54 -0
- package/dist/tests/protocol-text-layer.test.js.map +1 -0
- package/dist/tests/stress-exhaustive.test.js +5 -1
- package/dist/tests/stress-exhaustive.test.js.map +1 -1
- package/dist/text-layer/compiler.d.ts.map +1 -1
- package/dist/text-layer/compiler.js +35 -8
- package/dist/text-layer/compiler.js.map +1 -1
- package/dist/types/index.d.ts +37 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +42 -0
- package/dist/types/index.js.map +1 -1
- package/package.json +1 -3
|
@@ -38,6 +38,10 @@ function computeCollectAtoms(f) {
|
|
|
38
38
|
}
|
|
39
39
|
function evaluateClassical(f, v) {
|
|
40
40
|
switch (f.kind) {
|
|
41
|
+
case 'true':
|
|
42
|
+
return true;
|
|
43
|
+
case 'false':
|
|
44
|
+
return false;
|
|
41
45
|
case 'atom':
|
|
42
46
|
return f.name ? (v[f.name] ?? false) : false;
|
|
43
47
|
case 'not':
|
|
@@ -311,6 +315,10 @@ function formulaToString(f) {
|
|
|
311
315
|
}
|
|
312
316
|
function computeFormulaToString(f) {
|
|
313
317
|
switch (f.kind) {
|
|
318
|
+
case 'true':
|
|
319
|
+
return '⊤';
|
|
320
|
+
case 'false':
|
|
321
|
+
return '⊥';
|
|
314
322
|
case 'atom':
|
|
315
323
|
return f.name || '?';
|
|
316
324
|
case 'not': {
|
|
@@ -425,6 +433,8 @@ function computeNNF(f) {
|
|
|
425
433
|
const args = node.args || [];
|
|
426
434
|
if (!negated) {
|
|
427
435
|
switch (k) {
|
|
436
|
+
case 'true':
|
|
437
|
+
case 'false':
|
|
428
438
|
case 'atom':
|
|
429
439
|
case 'predicate':
|
|
430
440
|
return node;
|
|
@@ -468,6 +478,10 @@ function computeNNF(f) {
|
|
|
468
478
|
}
|
|
469
479
|
else {
|
|
470
480
|
switch (k) {
|
|
481
|
+
case 'true':
|
|
482
|
+
return { kind: 'false' };
|
|
483
|
+
case 'false':
|
|
484
|
+
return { kind: 'true' };
|
|
471
485
|
case 'atom':
|
|
472
486
|
case 'predicate':
|
|
473
487
|
return { kind: 'not', args: [node] };
|
|
@@ -637,18 +651,105 @@ function getSubFormulas(f) {
|
|
|
637
651
|
// Remove atoms and the full formula itself
|
|
638
652
|
return result.filter((n) => n.kind !== 'atom' && formulaToString(n) !== formulaToString(f));
|
|
639
653
|
}
|
|
654
|
+
/**
|
|
655
|
+
* Igualdad estructural con alpha-equivalencia sobre variables cuantificadas.
|
|
656
|
+
* Cubre átomos, predicados, cuantificadores (∀/∃), modales y constantes.
|
|
657
|
+
*/
|
|
640
658
|
function formulasEqual(a, b) {
|
|
659
|
+
return alphaEqualFormulas(a, b, new Map(), new Map());
|
|
660
|
+
}
|
|
661
|
+
function alphaEqualFormulas(a, b, bindA, bindB, depth = 0) {
|
|
641
662
|
if (a.kind !== b.kind)
|
|
642
663
|
return false;
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
664
|
+
switch (a.kind) {
|
|
665
|
+
case 'true':
|
|
666
|
+
case 'false':
|
|
667
|
+
return true;
|
|
668
|
+
case 'atom': {
|
|
669
|
+
const nameA = a.name;
|
|
670
|
+
const nameB = b.name;
|
|
671
|
+
if (nameA === undefined || nameB === undefined)
|
|
672
|
+
return nameA === nameB;
|
|
673
|
+
const bA = bindA.get(nameA);
|
|
674
|
+
const bB = bindB.get(nameB);
|
|
675
|
+
if (bA !== undefined || bB !== undefined)
|
|
676
|
+
return bA === bB;
|
|
677
|
+
return nameA === nameB;
|
|
678
|
+
}
|
|
679
|
+
case 'number':
|
|
680
|
+
return a.value === b.value;
|
|
681
|
+
case 'predicate': {
|
|
682
|
+
if (a.name !== b.name)
|
|
683
|
+
return false;
|
|
684
|
+
const paramsA = a.params || a.terms || [];
|
|
685
|
+
const paramsB = b.params || b.terms || [];
|
|
686
|
+
if (paramsA.length !== paramsB.length)
|
|
687
|
+
return false;
|
|
688
|
+
for (let i = 0; i < paramsA.length; i++) {
|
|
689
|
+
const pA = paramsA[i];
|
|
690
|
+
const pB = paramsB[i];
|
|
691
|
+
const bndA = bindA.get(pA);
|
|
692
|
+
const bndB = bindB.get(pB);
|
|
693
|
+
if (bndA !== undefined || bndB !== undefined) {
|
|
694
|
+
if (bndA !== bndB)
|
|
695
|
+
return false;
|
|
696
|
+
}
|
|
697
|
+
else if (pA !== pB) {
|
|
698
|
+
return false;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
return true;
|
|
702
|
+
}
|
|
703
|
+
case 'forall':
|
|
704
|
+
case 'exists': {
|
|
705
|
+
const vA = a.variable;
|
|
706
|
+
const vB = b.variable;
|
|
707
|
+
if (!vA || !vB)
|
|
708
|
+
return vA === vB;
|
|
709
|
+
const innerA = a.args?.[0];
|
|
710
|
+
const innerB = b.args?.[0];
|
|
711
|
+
if (!innerA || !innerB)
|
|
712
|
+
return false;
|
|
713
|
+
const prevA = bindA.get(vA);
|
|
714
|
+
const prevB = bindB.get(vB);
|
|
715
|
+
bindA.set(vA, depth);
|
|
716
|
+
bindB.set(vB, depth);
|
|
717
|
+
const eq = alphaEqualFormulas(innerA, innerB, bindA, bindB, depth + 1);
|
|
718
|
+
if (prevA === undefined)
|
|
719
|
+
bindA.delete(vA);
|
|
720
|
+
else
|
|
721
|
+
bindA.set(vA, prevA);
|
|
722
|
+
if (prevB === undefined)
|
|
723
|
+
bindB.delete(vB);
|
|
724
|
+
else
|
|
725
|
+
bindB.set(vB, prevB);
|
|
726
|
+
return eq;
|
|
727
|
+
}
|
|
728
|
+
case 'modal_necessity':
|
|
729
|
+
case 'modal_possibility':
|
|
730
|
+
case 'temporal_next':
|
|
731
|
+
case 'temporal_until': {
|
|
732
|
+
if (a.name !== b.name)
|
|
733
|
+
return false;
|
|
734
|
+
const argsA = a.args || [];
|
|
735
|
+
const argsB = b.args || [];
|
|
736
|
+
if (argsA.length !== argsB.length)
|
|
737
|
+
return false;
|
|
738
|
+
return argsA.every((arg, i) => alphaEqualFormulas(arg, argsB[i], bindA, bindB, depth));
|
|
739
|
+
}
|
|
740
|
+
default: {
|
|
741
|
+
const argsA = a.args || [];
|
|
742
|
+
const argsB = b.args || [];
|
|
743
|
+
if (argsA.length !== argsB.length)
|
|
744
|
+
return false;
|
|
745
|
+
if (argsA.length === 0) {
|
|
746
|
+
if (a.name !== b.name)
|
|
747
|
+
return false;
|
|
748
|
+
return a.value === b.value;
|
|
749
|
+
}
|
|
750
|
+
return argsA.every((arg, i) => alphaEqualFormulas(arg, argsB[i], bindA, bindB, depth));
|
|
751
|
+
}
|
|
650
752
|
}
|
|
651
|
-
return false;
|
|
652
753
|
}
|
|
653
754
|
// --- Motor de derivación ---
|
|
654
755
|
/** Límite duro de fórmulas derivadas para evitar explosión combinatoria */
|
|
@@ -690,7 +791,7 @@ function isRelevantToGoal(f, goal) {
|
|
|
690
791
|
};
|
|
691
792
|
return checkSub(goal);
|
|
692
793
|
}
|
|
693
|
-
function addDerivedFormula(state, formula, justification, premises) {
|
|
794
|
+
function addDerivedFormula(state, formula, justification, premises, source = 'rule') {
|
|
694
795
|
const hash = formulaHash(formula);
|
|
695
796
|
if (state.known.has(hash))
|
|
696
797
|
return false;
|
|
@@ -700,10 +801,82 @@ function addDerivedFormula(state, formula, justification, premises) {
|
|
|
700
801
|
formula,
|
|
701
802
|
justification,
|
|
702
803
|
premises,
|
|
804
|
+
source,
|
|
703
805
|
});
|
|
704
806
|
state.known.set(hash, formula);
|
|
705
807
|
return true;
|
|
706
808
|
}
|
|
809
|
+
function buildPremiseRefs(theory, premiseNames) {
|
|
810
|
+
return premiseNames.map((name) => ({
|
|
811
|
+
name,
|
|
812
|
+
location: (theory.axioms.get(name) || theory.theorems.get(name))?.source,
|
|
813
|
+
}));
|
|
814
|
+
}
|
|
815
|
+
function buildProof(goal, steps, premiseNames, theory, method = 'natural_deduction', subproofs) {
|
|
816
|
+
return {
|
|
817
|
+
goal,
|
|
818
|
+
steps,
|
|
819
|
+
status: 'complete',
|
|
820
|
+
derivedFrom: premiseNames,
|
|
821
|
+
premiseRefs: buildPremiseRefs(theory, premiseNames),
|
|
822
|
+
method,
|
|
823
|
+
subproofs,
|
|
824
|
+
metadata: {
|
|
825
|
+
createdAt: new Date().toISOString(),
|
|
826
|
+
profile: theory.profile,
|
|
827
|
+
},
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
function isNegationOf(a, b) {
|
|
831
|
+
return a.kind === 'not' && !!a.args?.[0] && formulasEqual(a.args[0], b);
|
|
832
|
+
}
|
|
833
|
+
function isExcludedMiddleFormula(formula) {
|
|
834
|
+
if (formula.kind !== 'or' || !formula.args?.[0] || !formula.args?.[1])
|
|
835
|
+
return false;
|
|
836
|
+
return (isNegationOf(formula.args[0], formula.args[1]) || isNegationOf(formula.args[1], formula.args[0]));
|
|
837
|
+
}
|
|
838
|
+
function getCommutativeVariant(formula) {
|
|
839
|
+
if ((formula.kind === 'and' || formula.kind === 'or') && formula.args?.[0] && formula.args?.[1]) {
|
|
840
|
+
return { kind: formula.kind, args: [formula.args[1], formula.args[0]] };
|
|
841
|
+
}
|
|
842
|
+
return null;
|
|
843
|
+
}
|
|
844
|
+
function getAssociativeVariants(formula) {
|
|
845
|
+
const variants = [];
|
|
846
|
+
if ((formula.kind === 'and' || formula.kind === 'or') && formula.args?.[0] && formula.args?.[1]) {
|
|
847
|
+
const [left, right] = formula.args;
|
|
848
|
+
if (left.kind === formula.kind && left.args?.[0] && left.args?.[1]) {
|
|
849
|
+
variants.push({
|
|
850
|
+
kind: formula.kind,
|
|
851
|
+
args: [left.args[0], { kind: formula.kind, args: [left.args[1], right] }],
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
if (right.kind === formula.kind && right.args?.[0] && right.args?.[1]) {
|
|
855
|
+
variants.push({
|
|
856
|
+
kind: formula.kind,
|
|
857
|
+
args: [{ kind: formula.kind, args: [left, right.args[0]] }, right.args[1]],
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
return variants;
|
|
862
|
+
}
|
|
863
|
+
function getAbsorptionResult(formula) {
|
|
864
|
+
if (formula.kind === 'and' && formula.args?.[0] && formula.args?.[1]) {
|
|
865
|
+
const [left, right] = formula.args;
|
|
866
|
+
if (right.kind === 'or' && right.args?.some((arg) => formulasEqual(arg, left)))
|
|
867
|
+
return left;
|
|
868
|
+
if (left.kind === 'or' && left.args?.some((arg) => formulasEqual(arg, right)))
|
|
869
|
+
return right;
|
|
870
|
+
}
|
|
871
|
+
if (formula.kind === 'or' && formula.args?.[0] && formula.args?.[1]) {
|
|
872
|
+
const [left, right] = formula.args;
|
|
873
|
+
if (right.kind === 'and' && right.args?.some((arg) => formulasEqual(arg, left)))
|
|
874
|
+
return left;
|
|
875
|
+
if (left.kind === 'and' && left.args?.some((arg) => formulasEqual(arg, right)))
|
|
876
|
+
return right;
|
|
877
|
+
}
|
|
878
|
+
return null;
|
|
879
|
+
}
|
|
707
880
|
function tryDerive(goal, theory, premiseNames, depth = 0) {
|
|
708
881
|
const state = {
|
|
709
882
|
known: new Map(),
|
|
@@ -712,7 +885,25 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
|
|
|
712
885
|
};
|
|
713
886
|
// Cargar premisas
|
|
714
887
|
for (const name of premiseNames) {
|
|
715
|
-
|
|
888
|
+
let f = theory.axioms.get(name) || theory.theorems.get(name);
|
|
889
|
+
// Fallback: if name not found directly, search for an axiom/theorem whose formula
|
|
890
|
+
// matches the bare atom name (e.g., premise "Q" matches a theorem whose formula is atom Q)
|
|
891
|
+
if (!f) {
|
|
892
|
+
for (const [, formula] of theory.axioms) {
|
|
893
|
+
if (formula.kind === 'atom' && formula.name === name) {
|
|
894
|
+
f = formula;
|
|
895
|
+
break;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
if (!f) {
|
|
899
|
+
for (const [, formula] of theory.theorems) {
|
|
900
|
+
if (formula.kind === 'atom' && formula.name === name) {
|
|
901
|
+
f = formula;
|
|
902
|
+
break;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
}
|
|
716
907
|
if (f) {
|
|
717
908
|
state.stepCount++;
|
|
718
909
|
state.steps.push({
|
|
@@ -720,10 +911,14 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
|
|
|
720
911
|
formula: f,
|
|
721
912
|
justification: `Premisa (${name})`,
|
|
722
913
|
premises: [],
|
|
914
|
+
source: 'premise',
|
|
723
915
|
});
|
|
724
916
|
state.known.set(formulaHash(f), f);
|
|
725
917
|
}
|
|
726
918
|
}
|
|
919
|
+
if (isExcludedMiddleFormula(goal)) {
|
|
920
|
+
addDerivedFormula(state, goal, 'Tercero excluido', []);
|
|
921
|
+
}
|
|
727
922
|
// Intentar derivar con BFS aplicando reglas (optimizado)
|
|
728
923
|
const maxIterations = 1000;
|
|
729
924
|
let changed = true;
|
|
@@ -928,7 +1123,9 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
|
|
|
928
1123
|
const disjHashRev = formulaHash({ kind: 'or', args: [r, p] });
|
|
929
1124
|
if (state.known.has(disjHash) || state.known.has(disjHashRev)) {
|
|
930
1125
|
const qs = { kind: 'or', args: [q, s] };
|
|
931
|
-
const disjFormula = state.known.get(disjHash)
|
|
1126
|
+
const disjFormula = state.known.get(disjHash) ?? state.known.get(disjHashRev);
|
|
1127
|
+
if (!disjFormula)
|
|
1128
|
+
continue;
|
|
932
1129
|
changed =
|
|
933
1130
|
addDerivedFormula(state, qs, 'Dilema Constructivo', [
|
|
934
1131
|
findStep(state.steps, f1),
|
|
@@ -975,6 +1172,32 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
|
|
|
975
1172
|
]) || changed;
|
|
976
1173
|
}
|
|
977
1174
|
}
|
|
1175
|
+
const commutative = getCommutativeVariant(f1);
|
|
1176
|
+
if (commutative && isRelevantToGoal(commutative, goal)) {
|
|
1177
|
+
changed =
|
|
1178
|
+
addDerivedFormula(state, commutative, 'Conmutatividad', [findStep(state.steps, f1)]) ||
|
|
1179
|
+
changed;
|
|
1180
|
+
}
|
|
1181
|
+
for (const associative of getAssociativeVariants(f1)) {
|
|
1182
|
+
if (isRelevantToGoal(associative, goal)) {
|
|
1183
|
+
changed =
|
|
1184
|
+
addDerivedFormula(state, associative, 'Asociatividad', [findStep(state.steps, f1)]) ||
|
|
1185
|
+
changed;
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
if ((f1.kind === 'and' || f1.kind === 'or') &&
|
|
1189
|
+
f1.args?.[0] &&
|
|
1190
|
+
f1.args?.[1] &&
|
|
1191
|
+
formulasEqual(f1.args[0], f1.args[1])) {
|
|
1192
|
+
changed =
|
|
1193
|
+
addDerivedFormula(state, f1.args[0], 'Idempotencia', [findStep(state.steps, f1)]) ||
|
|
1194
|
+
changed;
|
|
1195
|
+
}
|
|
1196
|
+
const absorbed = getAbsorptionResult(f1);
|
|
1197
|
+
if (absorbed) {
|
|
1198
|
+
changed =
|
|
1199
|
+
addDerivedFormula(state, absorbed, 'Absorcion', [findStep(state.steps, f1)]) || changed;
|
|
1200
|
+
}
|
|
978
1201
|
// Disjunction Introduction: de A, derivar A | B
|
|
979
1202
|
// Relaxed: also allow intermediate disjunctions that are relevant to goal
|
|
980
1203
|
if (goal.kind === 'or' && goal.args?.[0] && goal.args?.[1]) {
|
|
@@ -1027,6 +1250,35 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
|
|
|
1027
1250
|
findStep(state.steps, f1),
|
|
1028
1251
|
]) || changed;
|
|
1029
1252
|
}
|
|
1253
|
+
// Implicación material (→ a ∨): de A->B, derivar !A|B
|
|
1254
|
+
if (f1.kind === 'implies' && f1.args?.[0] && f1.args?.[1]) {
|
|
1255
|
+
const matImpl = {
|
|
1256
|
+
kind: 'or',
|
|
1257
|
+
args: [{ kind: 'not', args: [f1.args[0]] }, f1.args[1]],
|
|
1258
|
+
};
|
|
1259
|
+
if (isRelevantToGoal(matImpl, goal)) {
|
|
1260
|
+
changed =
|
|
1261
|
+
addDerivedFormula(state, matImpl, 'Implicacion material (→ a ∨)', [
|
|
1262
|
+
findStep(state.steps, f1),
|
|
1263
|
+
]) || changed;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
// Implicación material inversa (∨ a →): de !A|B, derivar A->B
|
|
1267
|
+
if (f1.kind === 'or' &&
|
|
1268
|
+
f1.args?.[0]?.kind === 'not' &&
|
|
1269
|
+
f1.args[0].args?.[0] &&
|
|
1270
|
+
f1.args?.[1]) {
|
|
1271
|
+
const impl = {
|
|
1272
|
+
kind: 'implies',
|
|
1273
|
+
args: [f1.args[0].args[0], f1.args[1]],
|
|
1274
|
+
};
|
|
1275
|
+
if (isRelevantToGoal(impl, goal)) {
|
|
1276
|
+
changed =
|
|
1277
|
+
addDerivedFormula(state, impl, 'Implicacion material (∨ a →)', [
|
|
1278
|
+
findStep(state.steps, f1),
|
|
1279
|
+
]) || changed;
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1030
1282
|
// Contraposition: de A->B, derivar !B->!A
|
|
1031
1283
|
// Restringir a fórmulas con profundidad de negación baja para evitar
|
|
1032
1284
|
// cadenas infinitas de contrapositivas (!!A→!!B → !!!B→!!!A → ...)
|
|
@@ -1251,21 +1503,13 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
|
|
|
1251
1503
|
if (state.known.has(formulaHash(goal))) {
|
|
1252
1504
|
// Filtrar solo pasos relevantes para la derivación
|
|
1253
1505
|
const relevantSteps = traceBack(state.steps, goal);
|
|
1254
|
-
return
|
|
1255
|
-
goal,
|
|
1256
|
-
steps: relevantSteps,
|
|
1257
|
-
status: 'complete',
|
|
1258
|
-
derivedFrom: premiseNames,
|
|
1259
|
-
};
|
|
1506
|
+
return buildProof(goal, relevantSteps, premiseNames, theory);
|
|
1260
1507
|
}
|
|
1261
1508
|
// --- Sub-derivaciones recursivas (antes del fallback semántico) ---
|
|
1262
1509
|
const MAX_SUB_DEPTH = 2;
|
|
1263
1510
|
// Prueba Condicional real (→-Introducción / Deduction Theorem):
|
|
1264
1511
|
// Para derivar A→B, asumimos A como premisa temporal y derivamos B.
|
|
1265
|
-
if (depth < MAX_SUB_DEPTH &&
|
|
1266
|
-
goal.kind === 'implies' &&
|
|
1267
|
-
goal.args?.[0] &&
|
|
1268
|
-
goal.args?.[1]) {
|
|
1512
|
+
if (depth < MAX_SUB_DEPTH && goal.kind === 'implies' && goal.args?.[0] && goal.args?.[1]) {
|
|
1269
1513
|
const assumption = goal.args[0];
|
|
1270
1514
|
const subGoal = goal.args[1];
|
|
1271
1515
|
// Create a temporary theory with the assumption added
|
|
@@ -1282,14 +1526,14 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
|
|
|
1282
1526
|
const subProof = tryDerive(subGoal, tempTheory, subPremises, depth + 1);
|
|
1283
1527
|
if (subProof && subProof.status === 'complete') {
|
|
1284
1528
|
// Check the sub-proof doesn't rely solely on semantic fallback
|
|
1285
|
-
const isSyntactic = subProof.steps.every((s) =>
|
|
1529
|
+
const isSyntactic = subProof.steps.every((s) => s.source !== 'semantic');
|
|
1286
1530
|
if (isSyntactic) {
|
|
1287
1531
|
// Build the main proof: premises + sub-derivation steps + conditional proof conclusion
|
|
1288
1532
|
const mainSteps = [];
|
|
1289
1533
|
let stepNum = 0;
|
|
1290
1534
|
// Copy premise steps from current state
|
|
1291
1535
|
for (const s of state.steps) {
|
|
1292
|
-
if (s.
|
|
1536
|
+
if (s.source === 'premise') {
|
|
1293
1537
|
stepNum++;
|
|
1294
1538
|
mainSteps.push({ ...s, stepNumber: stepNum, premises: [] });
|
|
1295
1539
|
}
|
|
@@ -1302,18 +1546,18 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
|
|
|
1302
1546
|
formula: assumption,
|
|
1303
1547
|
justification: 'Supuesto (para prueba condicional)',
|
|
1304
1548
|
premises: [],
|
|
1549
|
+
source: 'assumption',
|
|
1305
1550
|
});
|
|
1306
1551
|
// Add sub-derivation steps (renumber, adjusting premise references)
|
|
1307
|
-
const subStepOffset = stepNum;
|
|
1308
1552
|
const subStepMap = new Map();
|
|
1309
1553
|
for (const s of subProof.steps) {
|
|
1310
|
-
if (s.
|
|
1554
|
+
if (s.source === 'premise' && formulasEqual(s.formula, assumption)) {
|
|
1311
1555
|
subStepMap.set(s.stepNumber, assumptionStepNum);
|
|
1312
1556
|
continue;
|
|
1313
1557
|
}
|
|
1314
|
-
if (s.
|
|
1558
|
+
if (s.source === 'premise') {
|
|
1315
1559
|
// Find existing premise step in main
|
|
1316
|
-
const existing = mainSteps.find((ms) => ms.
|
|
1560
|
+
const existing = mainSteps.find((ms) => ms.source === 'premise' && formulasEqual(ms.formula, s.formula));
|
|
1317
1561
|
if (existing) {
|
|
1318
1562
|
subStepMap.set(s.stepNumber, existing.stepNumber);
|
|
1319
1563
|
continue;
|
|
@@ -1326,23 +1570,21 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
|
|
|
1326
1570
|
formula: s.formula,
|
|
1327
1571
|
justification: s.justification,
|
|
1328
1572
|
premises: s.premises.map((p) => subStepMap.get(p) || p),
|
|
1573
|
+
source: s.source,
|
|
1329
1574
|
});
|
|
1330
1575
|
}
|
|
1331
1576
|
// Add final conditional proof step
|
|
1332
1577
|
stepNum++;
|
|
1333
|
-
const subGoalStepNum = subStepMap.get(subProof.steps[subProof.steps.length - 1]?.stepNumber ?? 0) ??
|
|
1578
|
+
const subGoalStepNum = subStepMap.get(subProof.steps[subProof.steps.length - 1]?.stepNumber ?? 0) ?? stepNum - 1;
|
|
1334
1579
|
mainSteps.push({
|
|
1335
1580
|
stepNumber: stepNum,
|
|
1336
1581
|
formula: goal,
|
|
1337
1582
|
justification: 'Prueba Condicional (Teorema de Deduccion)',
|
|
1338
1583
|
premises: [assumptionStepNum, subGoalStepNum],
|
|
1584
|
+
subproofs: [subProof],
|
|
1585
|
+
source: 'rule',
|
|
1339
1586
|
});
|
|
1340
|
-
return
|
|
1341
|
-
goal,
|
|
1342
|
-
steps: mainSteps,
|
|
1343
|
-
status: 'complete',
|
|
1344
|
-
derivedFrom: premiseNames,
|
|
1345
|
-
};
|
|
1587
|
+
return buildProof(goal, mainSteps, premiseNames, theory, 'natural_deduction', [subProof]);
|
|
1346
1588
|
}
|
|
1347
1589
|
}
|
|
1348
1590
|
}
|
|
@@ -1351,8 +1593,10 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
|
|
|
1351
1593
|
if (depth < MAX_SUB_DEPTH) {
|
|
1352
1594
|
const disjunctions = Array.from(state.known.values()).filter((f) => f.kind === 'or' && f.args?.[0] && f.args?.[1]);
|
|
1353
1595
|
for (const disj of disjunctions) {
|
|
1354
|
-
const left = disj.args[0];
|
|
1355
|
-
const right = disj.args[1];
|
|
1596
|
+
const left = disj.args?.[0];
|
|
1597
|
+
const right = disj.args?.[1];
|
|
1598
|
+
if (!left || !right)
|
|
1599
|
+
continue;
|
|
1356
1600
|
// Try to derive goal assuming left
|
|
1357
1601
|
const tempTheoryL = {
|
|
1358
1602
|
profile: theory.profile,
|
|
@@ -1367,7 +1611,7 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
|
|
|
1367
1611
|
const subProofL = tryDerive(goal, tempTheoryL, subPremisesL, depth + 1);
|
|
1368
1612
|
if (!subProofL || subProofL.status !== 'complete')
|
|
1369
1613
|
continue;
|
|
1370
|
-
const isSyntacticL = subProofL.steps.every((s) =>
|
|
1614
|
+
const isSyntacticL = subProofL.steps.every((s) => s.source !== 'semantic');
|
|
1371
1615
|
if (!isSyntacticL)
|
|
1372
1616
|
continue;
|
|
1373
1617
|
// Try to derive goal assuming right
|
|
@@ -1384,7 +1628,7 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
|
|
|
1384
1628
|
const subProofR = tryDerive(goal, tempTheoryR, subPremisesR, depth + 1);
|
|
1385
1629
|
if (!subProofR || subProofR.status !== 'complete')
|
|
1386
1630
|
continue;
|
|
1387
|
-
const isSyntacticR = subProofR.steps.every((s) =>
|
|
1631
|
+
const isSyntacticR = subProofR.steps.every((s) => s.source !== 'semantic');
|
|
1388
1632
|
if (!isSyntacticR)
|
|
1389
1633
|
continue;
|
|
1390
1634
|
// Both cases succeed — build proof by cases
|
|
@@ -1392,7 +1636,7 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
|
|
|
1392
1636
|
let stepNum = 0;
|
|
1393
1637
|
// Copy premise steps
|
|
1394
1638
|
for (const s of state.steps) {
|
|
1395
|
-
if (s.
|
|
1639
|
+
if (s.source === 'premise') {
|
|
1396
1640
|
stepNum++;
|
|
1397
1641
|
mainSteps.push({ ...s, stepNumber: stepNum, premises: [] });
|
|
1398
1642
|
}
|
|
@@ -1406,15 +1650,16 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
|
|
|
1406
1650
|
formula: left,
|
|
1407
1651
|
justification: 'Supuesto (caso izquierdo)',
|
|
1408
1652
|
premises: [],
|
|
1653
|
+
source: 'assumption',
|
|
1409
1654
|
});
|
|
1410
1655
|
const leftStepMap = new Map();
|
|
1411
1656
|
for (const s of subProofL.steps) {
|
|
1412
|
-
if (s.
|
|
1657
|
+
if (s.source === 'premise' && formulasEqual(s.formula, left)) {
|
|
1413
1658
|
leftStepMap.set(s.stepNumber, leftAssumptionStep);
|
|
1414
1659
|
continue;
|
|
1415
1660
|
}
|
|
1416
|
-
if (s.
|
|
1417
|
-
const existing = mainSteps.find((ms) => ms.
|
|
1661
|
+
if (s.source === 'premise') {
|
|
1662
|
+
const existing = mainSteps.find((ms) => ms.source === 'premise' && formulasEqual(ms.formula, s.formula));
|
|
1418
1663
|
if (existing) {
|
|
1419
1664
|
leftStepMap.set(s.stepNumber, existing.stepNumber);
|
|
1420
1665
|
continue;
|
|
@@ -1427,6 +1672,7 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
|
|
|
1427
1672
|
formula: s.formula,
|
|
1428
1673
|
justification: s.justification,
|
|
1429
1674
|
premises: s.premises.map((p) => leftStepMap.get(p) || p),
|
|
1675
|
+
source: s.source,
|
|
1430
1676
|
});
|
|
1431
1677
|
}
|
|
1432
1678
|
const leftGoalStep = leftStepMap.get(subProofL.steps[subProofL.steps.length - 1]?.stepNumber ?? 0) ?? stepNum;
|
|
@@ -1438,15 +1684,16 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
|
|
|
1438
1684
|
formula: right,
|
|
1439
1685
|
justification: 'Supuesto (caso derecho)',
|
|
1440
1686
|
premises: [],
|
|
1687
|
+
source: 'assumption',
|
|
1441
1688
|
});
|
|
1442
1689
|
const rightStepMap = new Map();
|
|
1443
1690
|
for (const s of subProofR.steps) {
|
|
1444
|
-
if (s.
|
|
1691
|
+
if (s.source === 'premise' && formulasEqual(s.formula, right)) {
|
|
1445
1692
|
rightStepMap.set(s.stepNumber, rightAssumptionStep);
|
|
1446
1693
|
continue;
|
|
1447
1694
|
}
|
|
1448
|
-
if (s.
|
|
1449
|
-
const existing = mainSteps.find((ms) => ms.
|
|
1695
|
+
if (s.source === 'premise') {
|
|
1696
|
+
const existing = mainSteps.find((ms) => ms.source === 'premise' && formulasEqual(ms.formula, s.formula));
|
|
1450
1697
|
if (existing) {
|
|
1451
1698
|
rightStepMap.set(s.stepNumber, existing.stepNumber);
|
|
1452
1699
|
continue;
|
|
@@ -1459,6 +1706,7 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
|
|
|
1459
1706
|
formula: s.formula,
|
|
1460
1707
|
justification: s.justification,
|
|
1461
1708
|
premises: s.premises.map((p) => rightStepMap.get(p) || p),
|
|
1709
|
+
source: s.source,
|
|
1462
1710
|
});
|
|
1463
1711
|
}
|
|
1464
1712
|
const rightGoalStep = rightStepMap.get(subProofR.steps[subProofR.steps.length - 1]?.stepNumber ?? 0) ?? stepNum;
|
|
@@ -1469,13 +1717,13 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
|
|
|
1469
1717
|
formula: goal,
|
|
1470
1718
|
justification: 'Eliminacion de disyuncion (prueba por casos)',
|
|
1471
1719
|
premises: [disjStepNum, leftGoalStep, rightGoalStep],
|
|
1720
|
+
subproofs: [subProofL, subProofR],
|
|
1721
|
+
source: 'rule',
|
|
1472
1722
|
});
|
|
1473
|
-
return
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
derivedFrom: premiseNames,
|
|
1478
|
-
};
|
|
1723
|
+
return buildProof(goal, mainSteps, premiseNames, theory, 'natural_deduction', [
|
|
1724
|
+
subProofL,
|
|
1725
|
+
subProofR,
|
|
1726
|
+
]);
|
|
1479
1727
|
}
|
|
1480
1728
|
}
|
|
1481
1729
|
// Fallback: verificar semánticamente
|
|
@@ -1543,16 +1791,12 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
|
|
|
1543
1791
|
formula: goal,
|
|
1544
1792
|
justification: 'Verificacion semantica (todas las valuaciones satisfacen la consecuencia)',
|
|
1545
1793
|
premises: premiseStepNums,
|
|
1794
|
+
source: 'semantic',
|
|
1546
1795
|
});
|
|
1547
1796
|
state.known.set(goalHash, goal);
|
|
1548
1797
|
}
|
|
1549
1798
|
const relevantSteps = traceBack(state.steps, goal);
|
|
1550
|
-
return
|
|
1551
|
-
goal,
|
|
1552
|
-
steps: relevantSteps,
|
|
1553
|
-
status: 'complete',
|
|
1554
|
-
derivedFrom: premiseNames,
|
|
1555
|
-
};
|
|
1799
|
+
return buildProof(goal, relevantSteps, premiseNames, theory, 'semantic');
|
|
1556
1800
|
}
|
|
1557
1801
|
}
|
|
1558
1802
|
return null;
|
|
@@ -1583,7 +1827,17 @@ function traceBack(steps, goal) {
|
|
|
1583
1827
|
}
|
|
1584
1828
|
}
|
|
1585
1829
|
trace(goalStep.stepNumber);
|
|
1586
|
-
|
|
1830
|
+
const filtered = steps.filter((s) => needed.has(s.stepNumber));
|
|
1831
|
+
// Compact renumbering: eliminate gaps in step numbers
|
|
1832
|
+
const oldToNew = new Map();
|
|
1833
|
+
filtered.forEach((s, i) => {
|
|
1834
|
+
oldToNew.set(s.stepNumber, i + 1);
|
|
1835
|
+
});
|
|
1836
|
+
return filtered.map((s) => ({
|
|
1837
|
+
...s,
|
|
1838
|
+
stepNumber: oldToNew.get(s.stepNumber) ?? s.stepNumber,
|
|
1839
|
+
premises: s.premises.map((p) => oldToNew.get(p) ?? p),
|
|
1840
|
+
}));
|
|
1587
1841
|
}
|
|
1588
1842
|
// --- Perfil Classical Propositional ---
|
|
1589
1843
|
class ClassicalPropositional {
|
|
@@ -1762,28 +2016,152 @@ class ClassicalPropositional {
|
|
|
1762
2016
|
};
|
|
1763
2017
|
}
|
|
1764
2018
|
}
|
|
1765
|
-
prove(goal, theory) {
|
|
2019
|
+
prove(goal, theory, premises) {
|
|
1766
2020
|
const wf = this.checkWellFormed(goal);
|
|
1767
2021
|
if (wf.length > 0) {
|
|
1768
2022
|
return { status: 'error', diagnostics: wf, formula: goal };
|
|
1769
2023
|
}
|
|
1770
|
-
const
|
|
1771
|
-
const
|
|
2024
|
+
const usingRestricted = premises !== undefined && premises.length > 0;
|
|
2025
|
+
const premiseNames = usingRestricted
|
|
2026
|
+
? premises.filter((n) => theory.axioms.has(n) || theory.theorems.has(n))
|
|
2027
|
+
: Array.from(theory.axioms.keys());
|
|
2028
|
+
const diagnostics = [];
|
|
2029
|
+
if (usingRestricted) {
|
|
2030
|
+
for (const n of premises) {
|
|
2031
|
+
if (!theory.axioms.has(n) && !theory.theorems.has(n)) {
|
|
2032
|
+
diagnostics.push({
|
|
2033
|
+
severity: 'warning',
|
|
2034
|
+
message: `Premisa '${n}' no encontrada en la teoría; será ignorada en prove`,
|
|
2035
|
+
});
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
const effectiveTheory = usingRestricted
|
|
2040
|
+
? {
|
|
2041
|
+
profile: theory.profile,
|
|
2042
|
+
axioms: new Map(premiseNames
|
|
2043
|
+
.filter((n) => theory.axioms.has(n))
|
|
2044
|
+
.map((n) => [n, theory.axioms.get(n)])),
|
|
2045
|
+
theorems: new Map(premiseNames
|
|
2046
|
+
.filter((n) => theory.theorems.has(n))
|
|
2047
|
+
.map((n) => [n, theory.theorems.get(n)])),
|
|
2048
|
+
claims: theory.claims,
|
|
2049
|
+
judgments: theory.judgments,
|
|
2050
|
+
}
|
|
2051
|
+
: theory;
|
|
2052
|
+
const proof = tryDerive(goal, effectiveTheory, premiseNames);
|
|
1772
2053
|
if (proof && proof.status === 'complete') {
|
|
2054
|
+
const isSemantic = proof.method === 'semantic';
|
|
1773
2055
|
return {
|
|
1774
2056
|
status: 'provable',
|
|
1775
|
-
output:
|
|
2057
|
+
output: isSemantic
|
|
2058
|
+
? `${formulaToString(goal)} es DEMOSTRABLE desde la teoria (verificación semántica, sin derivación sintáctica)`
|
|
2059
|
+
: `${formulaToString(goal)} es DEMOSTRABLE desde la teoria`,
|
|
1776
2060
|
proof,
|
|
1777
2061
|
educationalNote: (0, educational_notes_1.pickEducationalNote)({ op: 'prove', ok: true }),
|
|
1778
|
-
diagnostics
|
|
2062
|
+
diagnostics,
|
|
1779
2063
|
formula: goal,
|
|
1780
2064
|
};
|
|
1781
2065
|
}
|
|
2066
|
+
// Semantic fallback: verify via SAT/truth-table whether goal follows from axioms
|
|
2067
|
+
const allAxiomFormulas = premiseNames
|
|
2068
|
+
.map((n) => effectiveTheory.axioms.get(n) || effectiveTheory.theorems.get(n))
|
|
2069
|
+
.filter((f) => f !== undefined);
|
|
2070
|
+
let semanticResult;
|
|
2071
|
+
const atoms = new Set();
|
|
2072
|
+
for (const f of allAxiomFormulas)
|
|
2073
|
+
collectAtoms(f).forEach((a) => atoms.add(a));
|
|
2074
|
+
collectAtoms(goal).forEach((a) => atoms.add(a));
|
|
2075
|
+
const atomList = Array.from(atoms).sort();
|
|
2076
|
+
if (allAxiomFormulas.length > 0) {
|
|
2077
|
+
// With premises: check if premises entail goal
|
|
2078
|
+
const allPure = allAxiomFormulas.every(isPurePropositional) && isPurePropositional(goal);
|
|
2079
|
+
if (allPure && atomList.length <= 26) {
|
|
2080
|
+
const premiseBits = allAxiomFormulas.map((f) => evaluateBitset(f, atomList).result);
|
|
2081
|
+
const goalBits = evaluateBitset(goal, atomList).result;
|
|
2082
|
+
const allOnes = bvOnes(1 << atomList.length);
|
|
2083
|
+
let premisesConj = allOnes;
|
|
2084
|
+
for (const pb of premiseBits)
|
|
2085
|
+
premisesConj = bvAnd(premisesConj, pb);
|
|
2086
|
+
semanticResult = bvIsZero(bvAnd(premisesConj, bvNot(goalBits, allOnes)));
|
|
2087
|
+
}
|
|
2088
|
+
else if (allPure && atomList.length > 26) {
|
|
2089
|
+
let conjunction = allAxiomFormulas[0];
|
|
2090
|
+
for (let i = 1; i < allAxiomFormulas.length; i++) {
|
|
2091
|
+
conjunction = { kind: 'and', args: [conjunction, allAxiomFormulas[i]] };
|
|
2092
|
+
}
|
|
2093
|
+
const negGoal = { kind: 'not', args: [goal] };
|
|
2094
|
+
const check = { kind: 'and', args: [conjunction, negGoal] };
|
|
2095
|
+
semanticResult = !(0, dpll_1.dpll)(check).satisfiable;
|
|
2096
|
+
}
|
|
2097
|
+
else {
|
|
2098
|
+
const valuations = generateValuations(atomList);
|
|
2099
|
+
semanticResult = true;
|
|
2100
|
+
for (const v of valuations) {
|
|
2101
|
+
const premisesTrue = allAxiomFormulas.every((f) => evaluateClassical(f, v));
|
|
2102
|
+
if (premisesTrue && !evaluateClassical(goal, v)) {
|
|
2103
|
+
semanticResult = false;
|
|
2104
|
+
break;
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
else {
|
|
2110
|
+
// No premises: check if goal is a tautology
|
|
2111
|
+
if (isPurePropositional(goal) && atomList.length <= 26) {
|
|
2112
|
+
const { result, allOnes } = evaluateBitset(goal, atomList);
|
|
2113
|
+
semanticResult = bvEquals(result, allOnes);
|
|
2114
|
+
}
|
|
2115
|
+
else if (isPurePropositional(goal) && atomList.length > 26) {
|
|
2116
|
+
const negated = { kind: 'not', args: [goal] };
|
|
2117
|
+
semanticResult = !(0, dpll_1.dpll)(negated).satisfiable;
|
|
2118
|
+
}
|
|
2119
|
+
else {
|
|
2120
|
+
const valuations = generateValuations(atomList);
|
|
2121
|
+
semanticResult = valuations.every((v) => evaluateClassical(goal, v));
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
if (semanticResult) {
|
|
2125
|
+
const premiseSteps = [];
|
|
2126
|
+
premiseNames.forEach((n, i) => {
|
|
2127
|
+
const f = effectiveTheory.axioms.get(n) || effectiveTheory.theorems.get(n);
|
|
2128
|
+
if (f) {
|
|
2129
|
+
premiseSteps.push({
|
|
2130
|
+
stepNumber: i + 1,
|
|
2131
|
+
formula: f,
|
|
2132
|
+
justification: `Premisa (${n})`,
|
|
2133
|
+
premises: [],
|
|
2134
|
+
source: 'premise',
|
|
2135
|
+
});
|
|
2136
|
+
}
|
|
2137
|
+
});
|
|
2138
|
+
const semanticProofSteps = [
|
|
2139
|
+
...premiseSteps,
|
|
2140
|
+
{
|
|
2141
|
+
stepNumber: premiseNames.length + 1,
|
|
2142
|
+
formula: goal,
|
|
2143
|
+
justification: 'Verificacion semantica (tautologia o consecuencia logica)',
|
|
2144
|
+
premises: premiseNames.map((_, i) => i + 1),
|
|
2145
|
+
source: 'semantic',
|
|
2146
|
+
},
|
|
2147
|
+
];
|
|
2148
|
+
const semanticProof = buildProof(goal, semanticProofSteps, premiseNames, effectiveTheory, 'semantic');
|
|
2149
|
+
return {
|
|
2150
|
+
status: 'provable',
|
|
2151
|
+
output: `${formulaToString(goal)} es DEMOSTRABLE desde la teoria (verificación semántica, sin derivación sintáctica)`,
|
|
2152
|
+
proof: semanticProof,
|
|
2153
|
+
educationalNote: (0, educational_notes_1.pickEducationalNote)({ op: 'prove', ok: true }),
|
|
2154
|
+
diagnostics,
|
|
2155
|
+
formula: goal,
|
|
2156
|
+
};
|
|
2157
|
+
}
|
|
2158
|
+
// Ni derivación sintáctica ni consecuencia semántica: existe contramodelo.
|
|
2159
|
+
// Esto sí es refutable en el sentido fuerte.
|
|
1782
2160
|
return {
|
|
1783
2161
|
status: 'refutable',
|
|
1784
|
-
output: `${formulaToString(goal)} NO
|
|
2162
|
+
output: `${formulaToString(goal)} NO se sigue de la teoría (existe contramodelo en ${formulaToString(goal)})`,
|
|
1785
2163
|
educationalNote: (0, educational_notes_1.pickEducationalNote)({ op: 'prove', ok: false }),
|
|
1786
|
-
diagnostics
|
|
2164
|
+
diagnostics,
|
|
1787
2165
|
formula: goal,
|
|
1788
2166
|
};
|
|
1789
2167
|
}
|
|
@@ -1797,14 +2175,17 @@ class ClassicalPropositional {
|
|
|
1797
2175
|
// Build reasoning info
|
|
1798
2176
|
const rulesUsed = new Set();
|
|
1799
2177
|
for (const step of proof.steps) {
|
|
1800
|
-
if (
|
|
2178
|
+
if (step.source !== 'premise') {
|
|
1801
2179
|
rulesUsed.add(step.justification);
|
|
1802
2180
|
}
|
|
1803
2181
|
}
|
|
1804
2182
|
const reasoningType = rulesUsed.size > 0 ? Array.from(rulesUsed).join(', ') : 'Derivación directa';
|
|
2183
|
+
const isSemantic = proof.method === 'semantic';
|
|
1805
2184
|
return {
|
|
1806
2185
|
status: 'provable',
|
|
1807
|
-
output:
|
|
2186
|
+
output: isSemantic
|
|
2187
|
+
? `${formulaToString(goal)} derivado (verificación semántica, sin derivación sintáctica)`
|
|
2188
|
+
: `${formulaToString(goal)} derivado exitosamente`,
|
|
1808
2189
|
proof,
|
|
1809
2190
|
reasoningType,
|
|
1810
2191
|
reasoningSchema: rulesUsed.has('Modus Ponens')
|
|
@@ -1825,8 +2206,8 @@ class ClassicalPropositional {
|
|
|
1825
2206
|
};
|
|
1826
2207
|
}
|
|
1827
2208
|
return {
|
|
1828
|
-
status: '
|
|
1829
|
-
output: `No se
|
|
2209
|
+
status: 'unknown',
|
|
2210
|
+
output: `No se pudo derivar ${formulaToString(goal)} desde las premisas dadas (sin refutación)`,
|
|
1830
2211
|
educationalNote: (0, educational_notes_1.pickEducationalNote)({ op: 'derive', ok: false }),
|
|
1831
2212
|
diagnostics: [],
|
|
1832
2213
|
formula: goal,
|
|
@@ -1915,6 +2296,20 @@ class ClassicalPropositional {
|
|
|
1915
2296
|
};
|
|
1916
2297
|
}
|
|
1917
2298
|
explain(formula) {
|
|
2299
|
+
if (!isPurePropositional(formula)) {
|
|
2300
|
+
return {
|
|
2301
|
+
status: 'error',
|
|
2302
|
+
output: `explain solo esta disponible para formulas puramente proposicionales: ${(0, format_1.formulaToUnicode)(formula)}`,
|
|
2303
|
+
diagnostics: [
|
|
2304
|
+
{
|
|
2305
|
+
severity: 'error',
|
|
2306
|
+
message: 'La formula incluye operadores no proposicionales; use un perfil con explain semantico especifico.',
|
|
2307
|
+
},
|
|
2308
|
+
...this.checkWellFormed(formula),
|
|
2309
|
+
],
|
|
2310
|
+
formula,
|
|
2311
|
+
};
|
|
2312
|
+
}
|
|
1918
2313
|
const wf = this.checkWellFormed(formula);
|
|
1919
2314
|
if (wf.length > 0) {
|
|
1920
2315
|
return { status: 'error', diagnostics: wf, formula };
|