@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.
Files changed (93) hide show
  1. package/README.md +3 -3
  2. package/dist/api.d.ts.map +1 -1
  3. package/dist/api.js +44 -9
  4. package/dist/api.js.map +1 -1
  5. package/dist/lexer/lexer.d.ts.map +1 -1
  6. package/dist/lexer/lexer.js +8 -0
  7. package/dist/lexer/lexer.js.map +1 -1
  8. package/dist/lexer/tokens.d.ts +2 -0
  9. package/dist/lexer/tokens.d.ts.map +1 -1
  10. package/dist/lexer/tokens.js +6 -0
  11. package/dist/lexer/tokens.js.map +1 -1
  12. package/dist/parser/parser.d.ts +4 -0
  13. package/dist/parser/parser.d.ts.map +1 -1
  14. package/dist/parser/parser.js +224 -92
  15. package/dist/parser/parser.js.map +1 -1
  16. package/dist/profiles/aristotelian/syllogistic.d.ts +1 -1
  17. package/dist/profiles/aristotelian/syllogistic.d.ts.map +1 -1
  18. package/dist/profiles/aristotelian/syllogistic.js +26 -8
  19. package/dist/profiles/aristotelian/syllogistic.js.map +1 -1
  20. package/dist/profiles/arithmetic/index.d.ts +1 -1
  21. package/dist/profiles/arithmetic/index.d.ts.map +1 -1
  22. package/dist/profiles/arithmetic/index.js +29 -7
  23. package/dist/profiles/arithmetic/index.js.map +1 -1
  24. package/dist/profiles/classical/first-order.d.ts +1 -1
  25. package/dist/profiles/classical/first-order.d.ts.map +1 -1
  26. package/dist/profiles/classical/first-order.js +32 -8
  27. package/dist/profiles/classical/first-order.js.map +1 -1
  28. package/dist/profiles/classical/propositional.d.ts +1 -1
  29. package/dist/profiles/classical/propositional.d.ts.map +1 -1
  30. package/dist/profiles/classical/propositional.js +464 -69
  31. package/dist/profiles/classical/propositional.js.map +1 -1
  32. package/dist/profiles/intuitionistic/propositional.d.ts +1 -1
  33. package/dist/profiles/intuitionistic/propositional.d.ts.map +1 -1
  34. package/dist/profiles/intuitionistic/propositional.js +33 -5
  35. package/dist/profiles/intuitionistic/propositional.js.map +1 -1
  36. package/dist/profiles/paraconsistent/belnap.d.ts +1 -2
  37. package/dist/profiles/paraconsistent/belnap.d.ts.map +1 -1
  38. package/dist/profiles/paraconsistent/belnap.js +25 -11
  39. package/dist/profiles/paraconsistent/belnap.js.map +1 -1
  40. package/dist/profiles/probabilistic/basic.d.ts +1 -1
  41. package/dist/profiles/probabilistic/basic.d.ts.map +1 -1
  42. package/dist/profiles/probabilistic/basic.js +29 -5
  43. package/dist/profiles/probabilistic/basic.js.map +1 -1
  44. package/dist/profiles/shared/base-profile.d.ts +1 -1
  45. package/dist/profiles/shared/base-profile.d.ts.map +1 -1
  46. package/dist/profiles/shared/base-profile.js +20 -3
  47. package/dist/profiles/shared/base-profile.js.map +1 -1
  48. package/dist/profiles/shared/tableau-engine.d.ts +4 -3
  49. package/dist/profiles/shared/tableau-engine.d.ts.map +1 -1
  50. package/dist/profiles/shared/tableau-engine.js +107 -15
  51. package/dist/profiles/shared/tableau-engine.js.map +1 -1
  52. package/dist/protocol/handler.d.ts +4 -0
  53. package/dist/protocol/handler.d.ts.map +1 -1
  54. package/dist/protocol/handler.js +133 -41
  55. package/dist/protocol/handler.js.map +1 -1
  56. package/dist/repl/repl.d.ts +2 -0
  57. package/dist/repl/repl.d.ts.map +1 -1
  58. package/dist/repl/repl.js +44 -19
  59. package/dist/repl/repl.js.map +1 -1
  60. package/dist/runtime/compat.d.ts +29 -0
  61. package/dist/runtime/compat.d.ts.map +1 -0
  62. package/dist/runtime/compat.js +819 -0
  63. package/dist/runtime/compat.js.map +1 -0
  64. package/dist/runtime/interpreter.d.ts +3 -0
  65. package/dist/runtime/interpreter.d.ts.map +1 -1
  66. package/dist/runtime/interpreter.js +133 -30
  67. package/dist/runtime/interpreter.js.map +1 -1
  68. package/dist/tests/cli.test.js +5 -1
  69. package/dist/tests/cli.test.js.map +1 -1
  70. package/dist/tests/compat.test.d.ts +2 -0
  71. package/dist/tests/compat.test.d.ts.map +1 -0
  72. package/dist/tests/compat.test.js +247 -0
  73. package/dist/tests/compat.test.js.map +1 -0
  74. package/dist/tests/core.test.js +41 -1
  75. package/dist/tests/core.test.js.map +1 -1
  76. package/dist/tests/parser.test.js +38 -0
  77. package/dist/tests/parser.test.js.map +1 -1
  78. package/dist/tests/profiles.test.js +8 -0
  79. package/dist/tests/profiles.test.js.map +1 -1
  80. package/dist/tests/protocol-text-layer.test.d.ts +2 -0
  81. package/dist/tests/protocol-text-layer.test.d.ts.map +1 -0
  82. package/dist/tests/protocol-text-layer.test.js +54 -0
  83. package/dist/tests/protocol-text-layer.test.js.map +1 -0
  84. package/dist/tests/stress-exhaustive.test.js +5 -1
  85. package/dist/tests/stress-exhaustive.test.js.map +1 -1
  86. package/dist/text-layer/compiler.d.ts.map +1 -1
  87. package/dist/text-layer/compiler.js +35 -8
  88. package/dist/text-layer/compiler.js.map +1 -1
  89. package/dist/types/index.d.ts +37 -3
  90. package/dist/types/index.d.ts.map +1 -1
  91. package/dist/types/index.js +42 -0
  92. package/dist/types/index.js.map +1 -1
  93. 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
- if (a.kind === 'atom' && b.kind === 'atom')
644
- return a.name === b.name;
645
- if (a.args && b.args) {
646
- if (a.args.length !== b.args.length)
647
- return false;
648
- const bArgs = b.args;
649
- return a.args.every((arg, i) => formulasEqual(arg, bArgs[i]));
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
- const f = theory.axioms.get(name) || theory.theorems.get(name);
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) || state.known.get(disjHashRev);
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) => !s.justification.startsWith('Verificacion semantica'));
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.justification.startsWith('Premisa')) {
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.justification.startsWith('Premisa') && formulasEqual(s.formula, assumption)) {
1554
+ if (s.source === 'premise' && formulasEqual(s.formula, assumption)) {
1311
1555
  subStepMap.set(s.stepNumber, assumptionStepNum);
1312
1556
  continue;
1313
1557
  }
1314
- if (s.justification.startsWith('Premisa')) {
1558
+ if (s.source === 'premise') {
1315
1559
  // Find existing premise step in main
1316
- const existing = mainSteps.find((ms) => ms.justification.startsWith('Premisa') && formulasEqual(ms.formula, s.formula));
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) ?? (stepNum - 1);
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) => !s.justification.startsWith('Verificacion semantica'));
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) => !s.justification.startsWith('Verificacion semantica'));
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.justification.startsWith('Premisa')) {
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.justification.startsWith('Premisa') && formulasEqual(s.formula, left)) {
1657
+ if (s.source === 'premise' && formulasEqual(s.formula, left)) {
1413
1658
  leftStepMap.set(s.stepNumber, leftAssumptionStep);
1414
1659
  continue;
1415
1660
  }
1416
- if (s.justification.startsWith('Premisa')) {
1417
- const existing = mainSteps.find((ms) => ms.justification.startsWith('Premisa') && formulasEqual(ms.formula, s.formula));
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.justification.startsWith('Premisa') && formulasEqual(s.formula, right)) {
1691
+ if (s.source === 'premise' && formulasEqual(s.formula, right)) {
1445
1692
  rightStepMap.set(s.stepNumber, rightAssumptionStep);
1446
1693
  continue;
1447
1694
  }
1448
- if (s.justification.startsWith('Premisa')) {
1449
- const existing = mainSteps.find((ms) => ms.justification.startsWith('Premisa') && formulasEqual(ms.formula, s.formula));
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
- goal,
1475
- steps: mainSteps,
1476
- status: 'complete',
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
- return steps.filter((s) => needed.has(s.stepNumber));
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 premiseNames = Array.from(theory.axioms.keys());
1771
- const proof = tryDerive(goal, theory, premiseNames);
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: `${formulaToString(goal)} es DEMOSTRABLE desde la teoria`,
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 es demostrable desde la teoria dada`,
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 (!step.justification.startsWith('Premisa')) {
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: `${formulaToString(goal)} derivado exitosamente`,
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: 'refutable',
1829
- output: `No se puede derivar ${formulaToString(goal)} desde las premisas dadas`,
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 };