@stevenvo780/st-lang 3.0.2 → 3.1.1
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/api.d.ts.map +1 -1
- package/dist/api.js +44 -9
- package/dist/api.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 +213 -92
- package/dist/parser/parser.js.map +1 -1
- package/dist/profiles/classical/first-order.d.ts.map +1 -1
- package/dist/profiles/classical/first-order.js +12 -5
- package/dist/profiles/classical/first-order.js.map +1 -1
- package/dist/profiles/classical/propositional.d.ts.map +1 -1
- package/dist/profiles/classical/propositional.js +583 -14
- package/dist/profiles/classical/propositional.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 +101 -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 +39 -18
- 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 +821 -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 +128 -28
- package/dist/runtime/interpreter.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 +37 -0
- 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/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 +27 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +7 -0
- package/dist/types/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -704,7 +704,78 @@ function addDerivedFormula(state, formula, justification, premises) {
|
|
|
704
704
|
state.known.set(hash, formula);
|
|
705
705
|
return true;
|
|
706
706
|
}
|
|
707
|
-
function
|
|
707
|
+
function buildPremiseRefs(theory, premiseNames) {
|
|
708
|
+
return premiseNames.map((name) => ({
|
|
709
|
+
name,
|
|
710
|
+
location: (theory.axioms.get(name) || theory.theorems.get(name))?.source,
|
|
711
|
+
}));
|
|
712
|
+
}
|
|
713
|
+
function buildProof(goal, steps, premiseNames, theory, method = 'natural_deduction', subproofs) {
|
|
714
|
+
return {
|
|
715
|
+
goal,
|
|
716
|
+
steps,
|
|
717
|
+
status: 'complete',
|
|
718
|
+
derivedFrom: premiseNames,
|
|
719
|
+
premiseRefs: buildPremiseRefs(theory, premiseNames),
|
|
720
|
+
method,
|
|
721
|
+
subproofs,
|
|
722
|
+
metadata: {
|
|
723
|
+
createdAt: new Date().toISOString(),
|
|
724
|
+
profile: theory.profile,
|
|
725
|
+
},
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
function isNegationOf(a, b) {
|
|
729
|
+
return a.kind === 'not' && !!a.args?.[0] && formulasEqual(a.args[0], b);
|
|
730
|
+
}
|
|
731
|
+
function isExcludedMiddleFormula(formula) {
|
|
732
|
+
if (formula.kind !== 'or' || !formula.args?.[0] || !formula.args?.[1])
|
|
733
|
+
return false;
|
|
734
|
+
return (isNegationOf(formula.args[0], formula.args[1]) || isNegationOf(formula.args[1], formula.args[0]));
|
|
735
|
+
}
|
|
736
|
+
function getCommutativeVariant(formula) {
|
|
737
|
+
if ((formula.kind === 'and' || formula.kind === 'or') && formula.args?.[0] && formula.args?.[1]) {
|
|
738
|
+
return { kind: formula.kind, args: [formula.args[1], formula.args[0]] };
|
|
739
|
+
}
|
|
740
|
+
return null;
|
|
741
|
+
}
|
|
742
|
+
function getAssociativeVariants(formula) {
|
|
743
|
+
const variants = [];
|
|
744
|
+
if ((formula.kind === 'and' || formula.kind === 'or') && formula.args?.[0] && formula.args?.[1]) {
|
|
745
|
+
const [left, right] = formula.args;
|
|
746
|
+
if (left.kind === formula.kind && left.args?.[0] && left.args?.[1]) {
|
|
747
|
+
variants.push({
|
|
748
|
+
kind: formula.kind,
|
|
749
|
+
args: [left.args[0], { kind: formula.kind, args: [left.args[1], right] }],
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
if (right.kind === formula.kind && right.args?.[0] && right.args?.[1]) {
|
|
753
|
+
variants.push({
|
|
754
|
+
kind: formula.kind,
|
|
755
|
+
args: [{ kind: formula.kind, args: [left, right.args[0]] }, right.args[1]],
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
return variants;
|
|
760
|
+
}
|
|
761
|
+
function getAbsorptionResult(formula) {
|
|
762
|
+
if (formula.kind === 'and' && formula.args?.[0] && formula.args?.[1]) {
|
|
763
|
+
const [left, right] = formula.args;
|
|
764
|
+
if (right.kind === 'or' && right.args?.some((arg) => formulasEqual(arg, left)))
|
|
765
|
+
return left;
|
|
766
|
+
if (left.kind === 'or' && left.args?.some((arg) => formulasEqual(arg, right)))
|
|
767
|
+
return right;
|
|
768
|
+
}
|
|
769
|
+
if (formula.kind === 'or' && formula.args?.[0] && formula.args?.[1]) {
|
|
770
|
+
const [left, right] = formula.args;
|
|
771
|
+
if (right.kind === 'and' && right.args?.some((arg) => formulasEqual(arg, left)))
|
|
772
|
+
return left;
|
|
773
|
+
if (left.kind === 'and' && left.args?.some((arg) => formulasEqual(arg, right)))
|
|
774
|
+
return right;
|
|
775
|
+
}
|
|
776
|
+
return null;
|
|
777
|
+
}
|
|
778
|
+
function tryDerive(goal, theory, premiseNames, depth = 0) {
|
|
708
779
|
const state = {
|
|
709
780
|
known: new Map(),
|
|
710
781
|
steps: [],
|
|
@@ -712,7 +783,25 @@ function tryDerive(goal, theory, premiseNames) {
|
|
|
712
783
|
};
|
|
713
784
|
// Cargar premisas
|
|
714
785
|
for (const name of premiseNames) {
|
|
715
|
-
|
|
786
|
+
let f = theory.axioms.get(name) || theory.theorems.get(name);
|
|
787
|
+
// Fallback: if name not found directly, search for an axiom/theorem whose formula
|
|
788
|
+
// matches the bare atom name (e.g., premise "Q" matches a theorem whose formula is atom Q)
|
|
789
|
+
if (!f) {
|
|
790
|
+
for (const [, formula] of theory.axioms) {
|
|
791
|
+
if (formula.kind === 'atom' && formula.name === name) {
|
|
792
|
+
f = formula;
|
|
793
|
+
break;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
if (!f) {
|
|
797
|
+
for (const [, formula] of theory.theorems) {
|
|
798
|
+
if (formula.kind === 'atom' && formula.name === name) {
|
|
799
|
+
f = formula;
|
|
800
|
+
break;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
}
|
|
716
805
|
if (f) {
|
|
717
806
|
state.stepCount++;
|
|
718
807
|
state.steps.push({
|
|
@@ -724,6 +813,9 @@ function tryDerive(goal, theory, premiseNames) {
|
|
|
724
813
|
state.known.set(formulaHash(f), f);
|
|
725
814
|
}
|
|
726
815
|
}
|
|
816
|
+
if (isExcludedMiddleFormula(goal)) {
|
|
817
|
+
addDerivedFormula(state, goal, 'Tercero excluido', []);
|
|
818
|
+
}
|
|
727
819
|
// Intentar derivar con BFS aplicando reglas (optimizado)
|
|
728
820
|
const maxIterations = 1000;
|
|
729
821
|
let changed = true;
|
|
@@ -910,6 +1002,35 @@ function tryDerive(goal, theory, premiseNames) {
|
|
|
910
1002
|
}
|
|
911
1003
|
}
|
|
912
1004
|
}
|
|
1005
|
+
// Dilema Constructivo (implicaciones separadas): P->Q, R->S, P|R ⊢ Q|S
|
|
1006
|
+
// No requiere que las implicaciones estén en conjunción
|
|
1007
|
+
if (f1.kind === 'implies' &&
|
|
1008
|
+
f1.args?.[0] &&
|
|
1009
|
+
f1.args?.[1] &&
|
|
1010
|
+
f2.kind === 'implies' &&
|
|
1011
|
+
f2.args?.[0] &&
|
|
1012
|
+
f2.args?.[1] &&
|
|
1013
|
+
!formulasEqual(f1, f2)) {
|
|
1014
|
+
// Search for a disjunction P|R in known formulas
|
|
1015
|
+
const p = f1.args[0];
|
|
1016
|
+
const q = f1.args[1];
|
|
1017
|
+
const r = f2.args[0];
|
|
1018
|
+
const s = f2.args[1];
|
|
1019
|
+
const disjHash = formulaHash({ kind: 'or', args: [p, r] });
|
|
1020
|
+
const disjHashRev = formulaHash({ kind: 'or', args: [r, p] });
|
|
1021
|
+
if (state.known.has(disjHash) || state.known.has(disjHashRev)) {
|
|
1022
|
+
const qs = { kind: 'or', args: [q, s] };
|
|
1023
|
+
const disjFormula = state.known.get(disjHash) ?? state.known.get(disjHashRev);
|
|
1024
|
+
if (!disjFormula)
|
|
1025
|
+
continue;
|
|
1026
|
+
changed =
|
|
1027
|
+
addDerivedFormula(state, qs, 'Dilema Constructivo', [
|
|
1028
|
+
findStep(state.steps, f1),
|
|
1029
|
+
findStep(state.steps, f2),
|
|
1030
|
+
findStep(state.steps, disjFormula),
|
|
1031
|
+
]) || changed;
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
913
1034
|
// Resolución: P|Q, !P|R derivar Q|R
|
|
914
1035
|
if (f1.kind === 'or' &&
|
|
915
1036
|
f1.args?.[0] &&
|
|
@@ -948,6 +1069,32 @@ function tryDerive(goal, theory, premiseNames) {
|
|
|
948
1069
|
]) || changed;
|
|
949
1070
|
}
|
|
950
1071
|
}
|
|
1072
|
+
const commutative = getCommutativeVariant(f1);
|
|
1073
|
+
if (commutative && isRelevantToGoal(commutative, goal)) {
|
|
1074
|
+
changed =
|
|
1075
|
+
addDerivedFormula(state, commutative, 'Conmutatividad', [findStep(state.steps, f1)]) ||
|
|
1076
|
+
changed;
|
|
1077
|
+
}
|
|
1078
|
+
for (const associative of getAssociativeVariants(f1)) {
|
|
1079
|
+
if (isRelevantToGoal(associative, goal)) {
|
|
1080
|
+
changed =
|
|
1081
|
+
addDerivedFormula(state, associative, 'Asociatividad', [findStep(state.steps, f1)]) ||
|
|
1082
|
+
changed;
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
if ((f1.kind === 'and' || f1.kind === 'or') &&
|
|
1086
|
+
f1.args?.[0] &&
|
|
1087
|
+
f1.args?.[1] &&
|
|
1088
|
+
formulasEqual(f1.args[0], f1.args[1])) {
|
|
1089
|
+
changed =
|
|
1090
|
+
addDerivedFormula(state, f1.args[0], 'Idempotencia', [findStep(state.steps, f1)]) ||
|
|
1091
|
+
changed;
|
|
1092
|
+
}
|
|
1093
|
+
const absorbed = getAbsorptionResult(f1);
|
|
1094
|
+
if (absorbed) {
|
|
1095
|
+
changed =
|
|
1096
|
+
addDerivedFormula(state, absorbed, 'Absorcion', [findStep(state.steps, f1)]) || changed;
|
|
1097
|
+
}
|
|
951
1098
|
// Disjunction Introduction: de A, derivar A | B
|
|
952
1099
|
// Relaxed: also allow intermediate disjunctions that are relevant to goal
|
|
953
1100
|
if (goal.kind === 'or' && goal.args?.[0] && goal.args?.[1]) {
|
|
@@ -1000,6 +1147,35 @@ function tryDerive(goal, theory, premiseNames) {
|
|
|
1000
1147
|
findStep(state.steps, f1),
|
|
1001
1148
|
]) || changed;
|
|
1002
1149
|
}
|
|
1150
|
+
// Implicación material (→ a ∨): de A->B, derivar !A|B
|
|
1151
|
+
if (f1.kind === 'implies' && f1.args?.[0] && f1.args?.[1]) {
|
|
1152
|
+
const matImpl = {
|
|
1153
|
+
kind: 'or',
|
|
1154
|
+
args: [{ kind: 'not', args: [f1.args[0]] }, f1.args[1]],
|
|
1155
|
+
};
|
|
1156
|
+
if (isRelevantToGoal(matImpl, goal)) {
|
|
1157
|
+
changed =
|
|
1158
|
+
addDerivedFormula(state, matImpl, 'Implicacion material (→ a ∨)', [
|
|
1159
|
+
findStep(state.steps, f1),
|
|
1160
|
+
]) || changed;
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
// Implicación material inversa (∨ a →): de !A|B, derivar A->B
|
|
1164
|
+
if (f1.kind === 'or' &&
|
|
1165
|
+
f1.args?.[0]?.kind === 'not' &&
|
|
1166
|
+
f1.args[0].args?.[0] &&
|
|
1167
|
+
f1.args?.[1]) {
|
|
1168
|
+
const impl = {
|
|
1169
|
+
kind: 'implies',
|
|
1170
|
+
args: [f1.args[0].args[0], f1.args[1]],
|
|
1171
|
+
};
|
|
1172
|
+
if (isRelevantToGoal(impl, goal)) {
|
|
1173
|
+
changed =
|
|
1174
|
+
addDerivedFormula(state, impl, 'Implicacion material (∨ a →)', [
|
|
1175
|
+
findStep(state.steps, f1),
|
|
1176
|
+
]) || changed;
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1003
1179
|
// Contraposition: de A->B, derivar !B->!A
|
|
1004
1180
|
// Restringir a fórmulas con profundidad de negación baja para evitar
|
|
1005
1181
|
// cadenas infinitas de contrapositivas (!!A→!!B → !!!B→!!!A → ...)
|
|
@@ -1101,6 +1277,82 @@ function tryDerive(goal, theory, premiseNames) {
|
|
|
1101
1277
|
changed =
|
|
1102
1278
|
addDerivedFormula(state, dm2, 'De Morgan (OR)', [findStep(state.steps, f1)]) || changed;
|
|
1103
1279
|
}
|
|
1280
|
+
// Distribución 1: P & (Q | R) ⊢ (P & Q) | (P & R)
|
|
1281
|
+
if (f1.kind === 'and' &&
|
|
1282
|
+
f1.args?.[0] &&
|
|
1283
|
+
f1.args?.[1]?.kind === 'or' &&
|
|
1284
|
+
f1.args[1].args?.[0] &&
|
|
1285
|
+
f1.args[1].args?.[1]) {
|
|
1286
|
+
const dist = {
|
|
1287
|
+
kind: 'or',
|
|
1288
|
+
args: [
|
|
1289
|
+
{ kind: 'and', args: [f1.args[0], f1.args[1].args[0]] },
|
|
1290
|
+
{ kind: 'and', args: [f1.args[0], f1.args[1].args[1]] },
|
|
1291
|
+
],
|
|
1292
|
+
};
|
|
1293
|
+
changed =
|
|
1294
|
+
addDerivedFormula(state, dist, 'Distribucion (AND sobre OR)', [
|
|
1295
|
+
findStep(state.steps, f1),
|
|
1296
|
+
]) || changed;
|
|
1297
|
+
}
|
|
1298
|
+
// Distribución 1b: (Q | R) & P ⊢ (Q & P) | (R & P)
|
|
1299
|
+
if (f1.kind === 'and' &&
|
|
1300
|
+
f1.args?.[0]?.kind === 'or' &&
|
|
1301
|
+
f1.args[0].args?.[0] &&
|
|
1302
|
+
f1.args[0].args?.[1] &&
|
|
1303
|
+
f1.args?.[1]) {
|
|
1304
|
+
const dist = {
|
|
1305
|
+
kind: 'or',
|
|
1306
|
+
args: [
|
|
1307
|
+
{ kind: 'and', args: [f1.args[0].args[0], f1.args[1]] },
|
|
1308
|
+
{ kind: 'and', args: [f1.args[0].args[1], f1.args[1]] },
|
|
1309
|
+
],
|
|
1310
|
+
};
|
|
1311
|
+
changed =
|
|
1312
|
+
addDerivedFormula(state, dist, 'Distribucion (AND sobre OR)', [
|
|
1313
|
+
findStep(state.steps, f1),
|
|
1314
|
+
]) || changed;
|
|
1315
|
+
}
|
|
1316
|
+
// Distribución 2: P | (Q & R) ⊢ (P | Q) & (P | R)
|
|
1317
|
+
if (f1.kind === 'or' &&
|
|
1318
|
+
f1.args?.[0] &&
|
|
1319
|
+
f1.args?.[1]?.kind === 'and' &&
|
|
1320
|
+
f1.args[1].args?.[0] &&
|
|
1321
|
+
f1.args[1].args?.[1]) {
|
|
1322
|
+
const dist = {
|
|
1323
|
+
kind: 'and',
|
|
1324
|
+
args: [
|
|
1325
|
+
{ kind: 'or', args: [f1.args[0], f1.args[1].args[0]] },
|
|
1326
|
+
{ kind: 'or', args: [f1.args[0], f1.args[1].args[1]] },
|
|
1327
|
+
],
|
|
1328
|
+
};
|
|
1329
|
+
if (isRelevantToGoal(dist, goal)) {
|
|
1330
|
+
changed =
|
|
1331
|
+
addDerivedFormula(state, dist, 'Distribucion (OR sobre AND)', [
|
|
1332
|
+
findStep(state.steps, f1),
|
|
1333
|
+
]) || changed;
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
// Distribución 2b: (Q & R) | P ⊢ (Q | P) & (R | P)
|
|
1337
|
+
if (f1.kind === 'or' &&
|
|
1338
|
+
f1.args?.[0]?.kind === 'and' &&
|
|
1339
|
+
f1.args[0].args?.[0] &&
|
|
1340
|
+
f1.args[0].args?.[1] &&
|
|
1341
|
+
f1.args?.[1]) {
|
|
1342
|
+
const dist = {
|
|
1343
|
+
kind: 'and',
|
|
1344
|
+
args: [
|
|
1345
|
+
{ kind: 'or', args: [f1.args[0].args[0], f1.args[1]] },
|
|
1346
|
+
{ kind: 'or', args: [f1.args[0].args[1], f1.args[1]] },
|
|
1347
|
+
],
|
|
1348
|
+
};
|
|
1349
|
+
if (isRelevantToGoal(dist, goal)) {
|
|
1350
|
+
changed =
|
|
1351
|
+
addDerivedFormula(state, dist, 'Distribucion (OR sobre AND)', [
|
|
1352
|
+
findStep(state.steps, f1),
|
|
1353
|
+
]) || changed;
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1104
1356
|
// RAA (Reductio ad Absurdum) #29:
|
|
1105
1357
|
// Si tenemos P→Q y P→¬Q (o ¬Q→P y Q→P), derivar ¬P
|
|
1106
1358
|
if (f1.kind === 'implies' && f1.args?.[0] && f1.args?.[1]) {
|
|
@@ -1148,12 +1400,220 @@ function tryDerive(goal, theory, premiseNames) {
|
|
|
1148
1400
|
if (state.known.has(formulaHash(goal))) {
|
|
1149
1401
|
// Filtrar solo pasos relevantes para la derivación
|
|
1150
1402
|
const relevantSteps = traceBack(state.steps, goal);
|
|
1151
|
-
return
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1403
|
+
return buildProof(goal, relevantSteps, premiseNames, theory);
|
|
1404
|
+
}
|
|
1405
|
+
// --- Sub-derivaciones recursivas (antes del fallback semántico) ---
|
|
1406
|
+
const MAX_SUB_DEPTH = 2;
|
|
1407
|
+
// Prueba Condicional real (→-Introducción / Deduction Theorem):
|
|
1408
|
+
// Para derivar A→B, asumimos A como premisa temporal y derivamos B.
|
|
1409
|
+
if (depth < MAX_SUB_DEPTH && goal.kind === 'implies' && goal.args?.[0] && goal.args?.[1]) {
|
|
1410
|
+
const assumption = goal.args[0];
|
|
1411
|
+
const subGoal = goal.args[1];
|
|
1412
|
+
// Create a temporary theory with the assumption added
|
|
1413
|
+
const tempTheory = {
|
|
1414
|
+
profile: theory.profile,
|
|
1415
|
+
axioms: new Map(theory.axioms),
|
|
1416
|
+
theorems: new Map(theory.theorems),
|
|
1417
|
+
claims: theory.claims,
|
|
1418
|
+
judgments: theory.judgments,
|
|
1156
1419
|
};
|
|
1420
|
+
const assumptionName = `__assumption_${depth}_${formulaHash(assumption)}`;
|
|
1421
|
+
tempTheory.axioms.set(assumptionName, assumption);
|
|
1422
|
+
const subPremises = [...premiseNames, assumptionName];
|
|
1423
|
+
const subProof = tryDerive(subGoal, tempTheory, subPremises, depth + 1);
|
|
1424
|
+
if (subProof && subProof.status === 'complete') {
|
|
1425
|
+
// Check the sub-proof doesn't rely solely on semantic fallback
|
|
1426
|
+
const isSyntactic = subProof.steps.every((s) => !s.justification.startsWith('Verificacion semantica'));
|
|
1427
|
+
if (isSyntactic) {
|
|
1428
|
+
// Build the main proof: premises + sub-derivation steps + conditional proof conclusion
|
|
1429
|
+
const mainSteps = [];
|
|
1430
|
+
let stepNum = 0;
|
|
1431
|
+
// Copy premise steps from current state
|
|
1432
|
+
for (const s of state.steps) {
|
|
1433
|
+
if (s.justification.startsWith('Premisa')) {
|
|
1434
|
+
stepNum++;
|
|
1435
|
+
mainSteps.push({ ...s, stepNumber: stepNum, premises: [] });
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
// Add assumption step
|
|
1439
|
+
stepNum++;
|
|
1440
|
+
const assumptionStepNum = stepNum;
|
|
1441
|
+
mainSteps.push({
|
|
1442
|
+
stepNumber: stepNum,
|
|
1443
|
+
formula: assumption,
|
|
1444
|
+
justification: 'Supuesto (para prueba condicional)',
|
|
1445
|
+
premises: [],
|
|
1446
|
+
});
|
|
1447
|
+
// Add sub-derivation steps (renumber, adjusting premise references)
|
|
1448
|
+
const subStepMap = new Map();
|
|
1449
|
+
for (const s of subProof.steps) {
|
|
1450
|
+
if (s.justification.startsWith('Premisa') && formulasEqual(s.formula, assumption)) {
|
|
1451
|
+
subStepMap.set(s.stepNumber, assumptionStepNum);
|
|
1452
|
+
continue;
|
|
1453
|
+
}
|
|
1454
|
+
if (s.justification.startsWith('Premisa')) {
|
|
1455
|
+
// Find existing premise step in main
|
|
1456
|
+
const existing = mainSteps.find((ms) => ms.justification.startsWith('Premisa') && formulasEqual(ms.formula, s.formula));
|
|
1457
|
+
if (existing) {
|
|
1458
|
+
subStepMap.set(s.stepNumber, existing.stepNumber);
|
|
1459
|
+
continue;
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
stepNum++;
|
|
1463
|
+
subStepMap.set(s.stepNumber, stepNum);
|
|
1464
|
+
mainSteps.push({
|
|
1465
|
+
stepNumber: stepNum,
|
|
1466
|
+
formula: s.formula,
|
|
1467
|
+
justification: s.justification,
|
|
1468
|
+
premises: s.premises.map((p) => subStepMap.get(p) || p),
|
|
1469
|
+
});
|
|
1470
|
+
}
|
|
1471
|
+
// Add final conditional proof step
|
|
1472
|
+
stepNum++;
|
|
1473
|
+
const subGoalStepNum = subStepMap.get(subProof.steps[subProof.steps.length - 1]?.stepNumber ?? 0) ?? stepNum - 1;
|
|
1474
|
+
mainSteps.push({
|
|
1475
|
+
stepNumber: stepNum,
|
|
1476
|
+
formula: goal,
|
|
1477
|
+
justification: 'Prueba Condicional (Teorema de Deduccion)',
|
|
1478
|
+
premises: [assumptionStepNum, subGoalStepNum],
|
|
1479
|
+
subproofs: [subProof],
|
|
1480
|
+
});
|
|
1481
|
+
return buildProof(goal, mainSteps, premiseNames, theory, 'natural_deduction', [subProof]);
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
// Prueba por Casos (∨-Eliminación / Disjunction Elimination):
|
|
1486
|
+
// Si tenemos A|B y queremos derivar C, asumimos A→C y B→C por separado.
|
|
1487
|
+
if (depth < MAX_SUB_DEPTH) {
|
|
1488
|
+
const disjunctions = Array.from(state.known.values()).filter((f) => f.kind === 'or' && f.args?.[0] && f.args?.[1]);
|
|
1489
|
+
for (const disj of disjunctions) {
|
|
1490
|
+
const left = disj.args?.[0];
|
|
1491
|
+
const right = disj.args?.[1];
|
|
1492
|
+
if (!left || !right)
|
|
1493
|
+
continue;
|
|
1494
|
+
// Try to derive goal assuming left
|
|
1495
|
+
const tempTheoryL = {
|
|
1496
|
+
profile: theory.profile,
|
|
1497
|
+
axioms: new Map(theory.axioms),
|
|
1498
|
+
theorems: new Map(theory.theorems),
|
|
1499
|
+
claims: theory.claims,
|
|
1500
|
+
judgments: theory.judgments,
|
|
1501
|
+
};
|
|
1502
|
+
const leftName = `__case_left_${depth}_${formulaHash(left)}`;
|
|
1503
|
+
tempTheoryL.axioms.set(leftName, left);
|
|
1504
|
+
const subPremisesL = [...premiseNames, leftName];
|
|
1505
|
+
const subProofL = tryDerive(goal, tempTheoryL, subPremisesL, depth + 1);
|
|
1506
|
+
if (!subProofL || subProofL.status !== 'complete')
|
|
1507
|
+
continue;
|
|
1508
|
+
const isSyntacticL = subProofL.steps.every((s) => !s.justification.startsWith('Verificacion semantica'));
|
|
1509
|
+
if (!isSyntacticL)
|
|
1510
|
+
continue;
|
|
1511
|
+
// Try to derive goal assuming right
|
|
1512
|
+
const tempTheoryR = {
|
|
1513
|
+
profile: theory.profile,
|
|
1514
|
+
axioms: new Map(theory.axioms),
|
|
1515
|
+
theorems: new Map(theory.theorems),
|
|
1516
|
+
claims: theory.claims,
|
|
1517
|
+
judgments: theory.judgments,
|
|
1518
|
+
};
|
|
1519
|
+
const rightName = `__case_right_${depth}_${formulaHash(right)}`;
|
|
1520
|
+
tempTheoryR.axioms.set(rightName, right);
|
|
1521
|
+
const subPremisesR = [...premiseNames, rightName];
|
|
1522
|
+
const subProofR = tryDerive(goal, tempTheoryR, subPremisesR, depth + 1);
|
|
1523
|
+
if (!subProofR || subProofR.status !== 'complete')
|
|
1524
|
+
continue;
|
|
1525
|
+
const isSyntacticR = subProofR.steps.every((s) => !s.justification.startsWith('Verificacion semantica'));
|
|
1526
|
+
if (!isSyntacticR)
|
|
1527
|
+
continue;
|
|
1528
|
+
// Both cases succeed — build proof by cases
|
|
1529
|
+
const mainSteps = [];
|
|
1530
|
+
let stepNum = 0;
|
|
1531
|
+
// Copy premise steps
|
|
1532
|
+
for (const s of state.steps) {
|
|
1533
|
+
if (s.justification.startsWith('Premisa')) {
|
|
1534
|
+
stepNum++;
|
|
1535
|
+
mainSteps.push({ ...s, stepNumber: stepNum, premises: [] });
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
const disjStepNum = mainSteps.find((ms) => formulasEqual(ms.formula, disj))?.stepNumber ?? 0;
|
|
1539
|
+
// Left case sub-derivation
|
|
1540
|
+
stepNum++;
|
|
1541
|
+
const leftAssumptionStep = stepNum;
|
|
1542
|
+
mainSteps.push({
|
|
1543
|
+
stepNumber: stepNum,
|
|
1544
|
+
formula: left,
|
|
1545
|
+
justification: 'Supuesto (caso izquierdo)',
|
|
1546
|
+
premises: [],
|
|
1547
|
+
});
|
|
1548
|
+
const leftStepMap = new Map();
|
|
1549
|
+
for (const s of subProofL.steps) {
|
|
1550
|
+
if (s.justification.startsWith('Premisa') && formulasEqual(s.formula, left)) {
|
|
1551
|
+
leftStepMap.set(s.stepNumber, leftAssumptionStep);
|
|
1552
|
+
continue;
|
|
1553
|
+
}
|
|
1554
|
+
if (s.justification.startsWith('Premisa')) {
|
|
1555
|
+
const existing = mainSteps.find((ms) => ms.justification.startsWith('Premisa') && formulasEqual(ms.formula, s.formula));
|
|
1556
|
+
if (existing) {
|
|
1557
|
+
leftStepMap.set(s.stepNumber, existing.stepNumber);
|
|
1558
|
+
continue;
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
stepNum++;
|
|
1562
|
+
leftStepMap.set(s.stepNumber, stepNum);
|
|
1563
|
+
mainSteps.push({
|
|
1564
|
+
stepNumber: stepNum,
|
|
1565
|
+
formula: s.formula,
|
|
1566
|
+
justification: s.justification,
|
|
1567
|
+
premises: s.premises.map((p) => leftStepMap.get(p) || p),
|
|
1568
|
+
});
|
|
1569
|
+
}
|
|
1570
|
+
const leftGoalStep = leftStepMap.get(subProofL.steps[subProofL.steps.length - 1]?.stepNumber ?? 0) ?? stepNum;
|
|
1571
|
+
// Right case sub-derivation
|
|
1572
|
+
stepNum++;
|
|
1573
|
+
const rightAssumptionStep = stepNum;
|
|
1574
|
+
mainSteps.push({
|
|
1575
|
+
stepNumber: stepNum,
|
|
1576
|
+
formula: right,
|
|
1577
|
+
justification: 'Supuesto (caso derecho)',
|
|
1578
|
+
premises: [],
|
|
1579
|
+
});
|
|
1580
|
+
const rightStepMap = new Map();
|
|
1581
|
+
for (const s of subProofR.steps) {
|
|
1582
|
+
if (s.justification.startsWith('Premisa') && formulasEqual(s.formula, right)) {
|
|
1583
|
+
rightStepMap.set(s.stepNumber, rightAssumptionStep);
|
|
1584
|
+
continue;
|
|
1585
|
+
}
|
|
1586
|
+
if (s.justification.startsWith('Premisa')) {
|
|
1587
|
+
const existing = mainSteps.find((ms) => ms.justification.startsWith('Premisa') && formulasEqual(ms.formula, s.formula));
|
|
1588
|
+
if (existing) {
|
|
1589
|
+
rightStepMap.set(s.stepNumber, existing.stepNumber);
|
|
1590
|
+
continue;
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
stepNum++;
|
|
1594
|
+
rightStepMap.set(s.stepNumber, stepNum);
|
|
1595
|
+
mainSteps.push({
|
|
1596
|
+
stepNumber: stepNum,
|
|
1597
|
+
formula: s.formula,
|
|
1598
|
+
justification: s.justification,
|
|
1599
|
+
premises: s.premises.map((p) => rightStepMap.get(p) || p),
|
|
1600
|
+
});
|
|
1601
|
+
}
|
|
1602
|
+
const rightGoalStep = rightStepMap.get(subProofR.steps[subProofR.steps.length - 1]?.stepNumber ?? 0) ?? stepNum;
|
|
1603
|
+
// Final disjunction elimination step
|
|
1604
|
+
stepNum++;
|
|
1605
|
+
mainSteps.push({
|
|
1606
|
+
stepNumber: stepNum,
|
|
1607
|
+
formula: goal,
|
|
1608
|
+
justification: 'Eliminacion de disyuncion (prueba por casos)',
|
|
1609
|
+
premises: [disjStepNum, leftGoalStep, rightGoalStep],
|
|
1610
|
+
subproofs: [subProofL, subProofR],
|
|
1611
|
+
});
|
|
1612
|
+
return buildProof(goal, mainSteps, premiseNames, theory, 'natural_deduction', [
|
|
1613
|
+
subProofL,
|
|
1614
|
+
subProofR,
|
|
1615
|
+
]);
|
|
1616
|
+
}
|
|
1157
1617
|
}
|
|
1158
1618
|
// Fallback: verificar semánticamente
|
|
1159
1619
|
const allAxiomFormulas = premiseNames
|
|
@@ -1224,12 +1684,7 @@ function tryDerive(goal, theory, premiseNames) {
|
|
|
1224
1684
|
state.known.set(goalHash, goal);
|
|
1225
1685
|
}
|
|
1226
1686
|
const relevantSteps = traceBack(state.steps, goal);
|
|
1227
|
-
return
|
|
1228
|
-
goal,
|
|
1229
|
-
steps: relevantSteps,
|
|
1230
|
-
status: 'complete',
|
|
1231
|
-
derivedFrom: premiseNames,
|
|
1232
|
-
};
|
|
1687
|
+
return buildProof(goal, relevantSteps, premiseNames, theory, 'semantic');
|
|
1233
1688
|
}
|
|
1234
1689
|
}
|
|
1235
1690
|
return null;
|
|
@@ -1260,7 +1715,17 @@ function traceBack(steps, goal) {
|
|
|
1260
1715
|
}
|
|
1261
1716
|
}
|
|
1262
1717
|
trace(goalStep.stepNumber);
|
|
1263
|
-
|
|
1718
|
+
const filtered = steps.filter((s) => needed.has(s.stepNumber));
|
|
1719
|
+
// Compact renumbering: eliminate gaps in step numbers
|
|
1720
|
+
const oldToNew = new Map();
|
|
1721
|
+
filtered.forEach((s, i) => {
|
|
1722
|
+
oldToNew.set(s.stepNumber, i + 1);
|
|
1723
|
+
});
|
|
1724
|
+
return filtered.map((s) => ({
|
|
1725
|
+
...s,
|
|
1726
|
+
stepNumber: oldToNew.get(s.stepNumber) ?? s.stepNumber,
|
|
1727
|
+
premises: s.premises.map((p) => oldToNew.get(p) ?? p),
|
|
1728
|
+
}));
|
|
1264
1729
|
}
|
|
1265
1730
|
// --- Perfil Classical Propositional ---
|
|
1266
1731
|
class ClassicalPropositional {
|
|
@@ -1456,6 +1921,96 @@ class ClassicalPropositional {
|
|
|
1456
1921
|
formula: goal,
|
|
1457
1922
|
};
|
|
1458
1923
|
}
|
|
1924
|
+
// Semantic fallback: verify via SAT/truth-table whether goal follows from axioms
|
|
1925
|
+
const allAxiomFormulas = premiseNames
|
|
1926
|
+
.map((n) => theory.axioms.get(n) || theory.theorems.get(n))
|
|
1927
|
+
.filter((f) => f !== undefined);
|
|
1928
|
+
let semanticResult;
|
|
1929
|
+
const atoms = new Set();
|
|
1930
|
+
for (const f of allAxiomFormulas)
|
|
1931
|
+
collectAtoms(f).forEach((a) => atoms.add(a));
|
|
1932
|
+
collectAtoms(goal).forEach((a) => atoms.add(a));
|
|
1933
|
+
const atomList = Array.from(atoms).sort();
|
|
1934
|
+
if (allAxiomFormulas.length > 0) {
|
|
1935
|
+
// With premises: check if premises entail goal
|
|
1936
|
+
const allPure = allAxiomFormulas.every(isPurePropositional) && isPurePropositional(goal);
|
|
1937
|
+
if (allPure && atomList.length <= 26) {
|
|
1938
|
+
const premiseBits = allAxiomFormulas.map((f) => evaluateBitset(f, atomList).result);
|
|
1939
|
+
const goalBits = evaluateBitset(goal, atomList).result;
|
|
1940
|
+
const allOnes = bvOnes(1 << atomList.length);
|
|
1941
|
+
let premisesConj = allOnes;
|
|
1942
|
+
for (const pb of premiseBits)
|
|
1943
|
+
premisesConj = bvAnd(premisesConj, pb);
|
|
1944
|
+
semanticResult = bvIsZero(bvAnd(premisesConj, bvNot(goalBits, allOnes)));
|
|
1945
|
+
}
|
|
1946
|
+
else if (allPure && atomList.length > 26) {
|
|
1947
|
+
let conjunction = allAxiomFormulas[0];
|
|
1948
|
+
for (let i = 1; i < allAxiomFormulas.length; i++) {
|
|
1949
|
+
conjunction = { kind: 'and', args: [conjunction, allAxiomFormulas[i]] };
|
|
1950
|
+
}
|
|
1951
|
+
const negGoal = { kind: 'not', args: [goal] };
|
|
1952
|
+
const check = { kind: 'and', args: [conjunction, negGoal] };
|
|
1953
|
+
semanticResult = !(0, dpll_1.dpll)(check).satisfiable;
|
|
1954
|
+
}
|
|
1955
|
+
else {
|
|
1956
|
+
const valuations = generateValuations(atomList);
|
|
1957
|
+
semanticResult = true;
|
|
1958
|
+
for (const v of valuations) {
|
|
1959
|
+
const premisesTrue = allAxiomFormulas.every((f) => evaluateClassical(f, v));
|
|
1960
|
+
if (premisesTrue && !evaluateClassical(goal, v)) {
|
|
1961
|
+
semanticResult = false;
|
|
1962
|
+
break;
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
else {
|
|
1968
|
+
// No premises: check if goal is a tautology
|
|
1969
|
+
if (isPurePropositional(goal) && atomList.length <= 26) {
|
|
1970
|
+
const { result, allOnes } = evaluateBitset(goal, atomList);
|
|
1971
|
+
semanticResult = bvEquals(result, allOnes);
|
|
1972
|
+
}
|
|
1973
|
+
else if (isPurePropositional(goal) && atomList.length > 26) {
|
|
1974
|
+
const negated = { kind: 'not', args: [goal] };
|
|
1975
|
+
semanticResult = !(0, dpll_1.dpll)(negated).satisfiable;
|
|
1976
|
+
}
|
|
1977
|
+
else {
|
|
1978
|
+
const valuations = generateValuations(atomList);
|
|
1979
|
+
semanticResult = valuations.every((v) => evaluateClassical(goal, v));
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
if (semanticResult) {
|
|
1983
|
+
const semanticProofSteps = [
|
|
1984
|
+
...premiseNames
|
|
1985
|
+
.map((n, i) => {
|
|
1986
|
+
const f = theory.axioms.get(n) || theory.theorems.get(n);
|
|
1987
|
+
return f
|
|
1988
|
+
? {
|
|
1989
|
+
stepNumber: i + 1,
|
|
1990
|
+
formula: f,
|
|
1991
|
+
justification: `Premisa (${n})`,
|
|
1992
|
+
premises: [],
|
|
1993
|
+
}
|
|
1994
|
+
: null;
|
|
1995
|
+
})
|
|
1996
|
+
.filter((s) => s !== null),
|
|
1997
|
+
{
|
|
1998
|
+
stepNumber: premiseNames.length + 1,
|
|
1999
|
+
formula: goal,
|
|
2000
|
+
justification: 'Verificacion semantica (tautologia o consecuencia logica)',
|
|
2001
|
+
premises: premiseNames.map((_, i) => i + 1),
|
|
2002
|
+
},
|
|
2003
|
+
];
|
|
2004
|
+
const semanticProof = buildProof(goal, semanticProofSteps, premiseNames, theory, 'semantic');
|
|
2005
|
+
return {
|
|
2006
|
+
status: 'provable',
|
|
2007
|
+
output: `${formulaToString(goal)} es DEMOSTRABLE desde la teoria`,
|
|
2008
|
+
proof: semanticProof,
|
|
2009
|
+
educationalNote: (0, educational_notes_1.pickEducationalNote)({ op: 'prove', ok: true }),
|
|
2010
|
+
diagnostics: [],
|
|
2011
|
+
formula: goal,
|
|
2012
|
+
};
|
|
2013
|
+
}
|
|
1459
2014
|
return {
|
|
1460
2015
|
status: 'refutable',
|
|
1461
2016
|
output: `${formulaToString(goal)} NO es demostrable desde la teoria dada`,
|
|
@@ -1592,6 +2147,20 @@ class ClassicalPropositional {
|
|
|
1592
2147
|
};
|
|
1593
2148
|
}
|
|
1594
2149
|
explain(formula) {
|
|
2150
|
+
if (!isPurePropositional(formula)) {
|
|
2151
|
+
return {
|
|
2152
|
+
status: 'error',
|
|
2153
|
+
output: `explain solo esta disponible para formulas puramente proposicionales: ${(0, format_1.formulaToUnicode)(formula)}`,
|
|
2154
|
+
diagnostics: [
|
|
2155
|
+
{
|
|
2156
|
+
severity: 'error',
|
|
2157
|
+
message: 'La formula incluye operadores no proposicionales; use un perfil con explain semantico especifico.',
|
|
2158
|
+
},
|
|
2159
|
+
...this.checkWellFormed(formula),
|
|
2160
|
+
],
|
|
2161
|
+
formula,
|
|
2162
|
+
};
|
|
2163
|
+
}
|
|
1595
2164
|
const wf = this.checkWellFormed(formula);
|
|
1596
2165
|
if (wf.length > 0) {
|
|
1597
2166
|
return { status: 'error', diagnostics: wf, formula };
|