@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.
Files changed (55) hide show
  1. package/dist/api.d.ts.map +1 -1
  2. package/dist/api.js +44 -9
  3. package/dist/api.js.map +1 -1
  4. package/dist/parser/parser.d.ts +4 -0
  5. package/dist/parser/parser.d.ts.map +1 -1
  6. package/dist/parser/parser.js +213 -92
  7. package/dist/parser/parser.js.map +1 -1
  8. package/dist/profiles/classical/first-order.d.ts.map +1 -1
  9. package/dist/profiles/classical/first-order.js +12 -5
  10. package/dist/profiles/classical/first-order.js.map +1 -1
  11. package/dist/profiles/classical/propositional.d.ts.map +1 -1
  12. package/dist/profiles/classical/propositional.js +583 -14
  13. package/dist/profiles/classical/propositional.js.map +1 -1
  14. package/dist/profiles/shared/tableau-engine.d.ts +4 -3
  15. package/dist/profiles/shared/tableau-engine.d.ts.map +1 -1
  16. package/dist/profiles/shared/tableau-engine.js +101 -15
  17. package/dist/profiles/shared/tableau-engine.js.map +1 -1
  18. package/dist/protocol/handler.d.ts +4 -0
  19. package/dist/protocol/handler.d.ts.map +1 -1
  20. package/dist/protocol/handler.js +133 -41
  21. package/dist/protocol/handler.js.map +1 -1
  22. package/dist/repl/repl.d.ts +2 -0
  23. package/dist/repl/repl.d.ts.map +1 -1
  24. package/dist/repl/repl.js +39 -18
  25. package/dist/repl/repl.js.map +1 -1
  26. package/dist/runtime/compat.d.ts +29 -0
  27. package/dist/runtime/compat.d.ts.map +1 -0
  28. package/dist/runtime/compat.js +821 -0
  29. package/dist/runtime/compat.js.map +1 -0
  30. package/dist/runtime/interpreter.d.ts +3 -0
  31. package/dist/runtime/interpreter.d.ts.map +1 -1
  32. package/dist/runtime/interpreter.js +128 -28
  33. package/dist/runtime/interpreter.js.map +1 -1
  34. package/dist/tests/compat.test.d.ts +2 -0
  35. package/dist/tests/compat.test.d.ts.map +1 -0
  36. package/dist/tests/compat.test.js +247 -0
  37. package/dist/tests/compat.test.js.map +1 -0
  38. package/dist/tests/core.test.js +37 -0
  39. package/dist/tests/core.test.js.map +1 -1
  40. package/dist/tests/parser.test.js +38 -0
  41. package/dist/tests/parser.test.js.map +1 -1
  42. package/dist/tests/profiles.test.js +8 -0
  43. package/dist/tests/profiles.test.js.map +1 -1
  44. package/dist/tests/protocol-text-layer.test.d.ts +2 -0
  45. package/dist/tests/protocol-text-layer.test.d.ts.map +1 -0
  46. package/dist/tests/protocol-text-layer.test.js +54 -0
  47. package/dist/tests/protocol-text-layer.test.js.map +1 -0
  48. package/dist/text-layer/compiler.d.ts.map +1 -1
  49. package/dist/text-layer/compiler.js +35 -8
  50. package/dist/text-layer/compiler.js.map +1 -1
  51. package/dist/types/index.d.ts +27 -1
  52. package/dist/types/index.d.ts.map +1 -1
  53. package/dist/types/index.js +7 -0
  54. package/dist/types/index.js.map +1 -1
  55. 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 tryDerive(goal, theory, premiseNames) {
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
- const f = theory.axioms.get(name) || theory.theorems.get(name);
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
- goal,
1153
- steps: relevantSteps,
1154
- status: 'complete',
1155
- derivedFrom: premiseNames,
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
- return steps.filter((s) => needed.has(s.stepNumber));
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 };