@stevenvo780/st-lang 3.1.2 → 3.1.3

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 (53) hide show
  1. package/dist/cli/index.js +6 -11
  2. package/dist/cli/index.js.map +1 -1
  3. package/dist/parser/parser.d.ts.map +1 -1
  4. package/dist/parser/parser.js +29 -6
  5. package/dist/parser/parser.js.map +1 -1
  6. package/dist/profiles/classical/first-order.d.ts.map +1 -1
  7. package/dist/profiles/classical/first-order.js +1 -0
  8. package/dist/profiles/classical/first-order.js.map +1 -1
  9. package/dist/profiles/classical/propositional.d.ts.map +1 -1
  10. package/dist/profiles/classical/propositional.js +5 -2
  11. package/dist/profiles/classical/propositional.js.map +1 -1
  12. package/dist/profiles/intuitionistic/propositional.d.ts.map +1 -1
  13. package/dist/profiles/intuitionistic/propositional.js +21 -2
  14. package/dist/profiles/intuitionistic/propositional.js.map +1 -1
  15. package/dist/profiles/paraconsistent/belnap.d.ts.map +1 -1
  16. package/dist/profiles/paraconsistent/belnap.js +1 -0
  17. package/dist/profiles/paraconsistent/belnap.js.map +1 -1
  18. package/dist/profiles/probabilistic/basic.d.ts.map +1 -1
  19. package/dist/profiles/probabilistic/basic.js +1 -0
  20. package/dist/profiles/probabilistic/basic.js.map +1 -1
  21. package/dist/profiles/shared/base-profile.d.ts.map +1 -1
  22. package/dist/profiles/shared/base-profile.js +1 -0
  23. package/dist/profiles/shared/base-profile.js.map +1 -1
  24. package/dist/profiles/shared/tableau-engine.d.ts +6 -0
  25. package/dist/profiles/shared/tableau-engine.d.ts.map +1 -1
  26. package/dist/profiles/shared/tableau-engine.js +33 -6
  27. package/dist/profiles/shared/tableau-engine.js.map +1 -1
  28. package/dist/profiles/temporal/ltl.d.ts +2 -1
  29. package/dist/profiles/temporal/ltl.d.ts.map +1 -1
  30. package/dist/profiles/temporal/ltl.js +5 -1
  31. package/dist/profiles/temporal/ltl.js.map +1 -1
  32. package/dist/protocol/handler.d.ts +0 -2
  33. package/dist/protocol/handler.d.ts.map +1 -1
  34. package/dist/protocol/handler.js +2 -6
  35. package/dist/protocol/handler.js.map +1 -1
  36. package/dist/runtime/interpreter.d.ts +8 -0
  37. package/dist/runtime/interpreter.d.ts.map +1 -1
  38. package/dist/runtime/interpreter.js +188 -49
  39. package/dist/runtime/interpreter.js.map +1 -1
  40. package/dist/runtime/known-theorems.d.ts.map +1 -1
  41. package/dist/runtime/known-theorems.js +17 -5
  42. package/dist/runtime/known-theorems.js.map +1 -1
  43. package/dist/tests/parser.test.js +10 -0
  44. package/dist/tests/parser.test.js.map +1 -1
  45. package/dist/tests/profiles.test.js +16 -0
  46. package/dist/tests/profiles.test.js.map +1 -1
  47. package/dist/tests/protocol-text-layer.test.js +18 -0
  48. package/dist/tests/protocol-text-layer.test.js.map +1 -1
  49. package/dist/tests/regressions.test.d.ts +2 -0
  50. package/dist/tests/regressions.test.d.ts.map +1 -0
  51. package/dist/tests/regressions.test.js +167 -0
  52. package/dist/tests/regressions.test.js.map +1 -0
  53. package/package.json +1 -1
@@ -19,6 +19,78 @@ const formula_factory_1 = require("./formula-factory");
19
19
  const MAX_CALL_DEPTH = 50000;
20
20
  const DEFAULT_MAX_RUNTIME_STEPS = 500000;
21
21
  const DEFAULT_MAX_RUNTIME_CALLS = 100000;
22
+ /**
23
+ * Reemplaza el contenido de strings y comentarios con espacios,
24
+ * preservando newlines y longitudes. Se usa para escaneos léxicos previos
25
+ * (p.ej. detección de `logic`) sin falsos positivos dentro de literales o
26
+ * comentarios.
27
+ */
28
+ function stripStringsAndComments(source) {
29
+ const out = [];
30
+ let i = 0;
31
+ const n = source.length;
32
+ while (i < n) {
33
+ const c = source[i];
34
+ const next = i + 1 < n ? source[i + 1] : '';
35
+ if (c === '/' && next === '/') {
36
+ out.push(' ');
37
+ i += 2;
38
+ while (i < n && source[i] !== '\n') {
39
+ out.push(' ');
40
+ i++;
41
+ }
42
+ continue;
43
+ }
44
+ if (c === '/' && next === '*') {
45
+ out.push(' ');
46
+ i += 2;
47
+ while (i < n && !(source[i] === '*' && source[i + 1] === '/')) {
48
+ out.push(source[i] === '\n' ? '\n' : ' ');
49
+ i++;
50
+ }
51
+ if (i < n) {
52
+ out.push(' ');
53
+ i += 2;
54
+ }
55
+ continue;
56
+ }
57
+ if (c === '"' || c === "'") {
58
+ const quote = c;
59
+ out.push(' ');
60
+ i++;
61
+ while (i < n && source[i] !== quote) {
62
+ if (source[i] === '\\' && i + 1 < n) {
63
+ out.push(source[i + 1] === '\n' ? ' \n' : ' ');
64
+ i += 2;
65
+ continue;
66
+ }
67
+ out.push(source[i] === '\n' ? '\n' : ' ');
68
+ i++;
69
+ }
70
+ if (i < n) {
71
+ out.push(' ');
72
+ i++;
73
+ }
74
+ continue;
75
+ }
76
+ if (c === '[' && next === '[') {
77
+ out.push(' ');
78
+ i += 2;
79
+ while (i < n && !(source[i] === ']' && source[i + 1] === ']')) {
80
+ out.push(source[i] === '\n' ? '\n' : ' ');
81
+ i++;
82
+ }
83
+ if (i < n) {
84
+ out.push(' ');
85
+ i += 2;
86
+ }
87
+ continue;
88
+ }
89
+ out.push(c);
90
+ i++;
91
+ }
92
+ return out.join('');
93
+ }
22
94
  class Interpreter {
23
95
  theory;
24
96
  profile = null;
@@ -98,7 +170,8 @@ class Interpreter {
98
170
  judgments: [],
99
171
  };
100
172
  }
101
- importedFiles = new Set();
173
+ importedFiles = new Map();
174
+ activeImports = new Set();
102
175
  reset() {
103
176
  this.theory = this.createEmptyTheory();
104
177
  this.textLayer = (0, compiler_1.createTextLayerState)();
@@ -107,6 +180,7 @@ class Interpreter {
107
180
  this.stdoutLines = [];
108
181
  this.profile = null;
109
182
  this.importedFiles.clear();
183
+ this.activeImports.clear();
110
184
  this.letBindings.clear();
111
185
  this.letDescriptions.clear();
112
186
  this.theories.clear();
@@ -326,7 +400,8 @@ class Interpreter {
326
400
  // Pre-scan for profile declarations to enable profile-aware lexing.
327
401
  // Only restrict keywords when there's exactly one profile in the source.
328
402
  // Multi-profile files use all keywords for safety.
329
- const profileMatches = source.match(/(?:^|\n)\s*(?:logic|logica)\s+([\w.]+)/g);
403
+ const scanSource = stripStringsAndComments(source);
404
+ const profileMatches = scanSource.match(/(?:^|\n)\s*(?:logic|logica)\s+([\w.]+)/g);
330
405
  let detectedProfile;
331
406
  if (profileMatches && profileMatches.length === 1) {
332
407
  const m = profileMatches[0].match(/(?:logic|logica)\s+([\w.]+)/);
@@ -818,6 +893,27 @@ class Interpreter {
818
893
  }
819
894
  return p;
820
895
  }
896
+ /**
897
+ * Construye una teoría efímera que combina axiomas + teoremas + letBindings
898
+ * SIN mutar la teoría persistente. Los letBindings quedan accesibles por nombre
899
+ * como si fueran axiomas, pero solo para esta llamada a derive/prove.
900
+ * Axiomas/teoremas reales tienen prioridad sobre letBindings con el mismo nombre.
901
+ */
902
+ buildEphemeralTheory() {
903
+ const axioms = new Map(this.theory.axioms);
904
+ for (const [name, formula] of this.letBindings) {
905
+ if (!axioms.has(name) && !this.theory.theorems.has(name)) {
906
+ axioms.set(name, formula);
907
+ }
908
+ }
909
+ return {
910
+ profile: this.theory.profile,
911
+ axioms,
912
+ theorems: this.theory.theorems,
913
+ claims: this.theory.claims,
914
+ judgments: this.theory.judgments,
915
+ };
916
+ }
821
917
  /**
822
918
  * Sustituye recursivamente los átomos que coincidan con variables `let`
823
919
  * por sus fórmulas definidas. Detecta ciclos para evitar recursión infinita.
@@ -826,6 +922,7 @@ class Interpreter {
826
922
  invalidateResolveCache() {
827
923
  this.resolveCache = new WeakMap();
828
924
  this.resolveCacheGeneration++;
925
+ this.fnMemoCache.clear();
829
926
  }
830
927
  resolveFormula(f, visited = new Set()) {
831
928
  // Check resolution cache first
@@ -977,6 +1074,7 @@ class Interpreter {
977
1074
  const diags = profile.checkWellFormed(resolved);
978
1075
  this.diagnostics.push(...diags);
979
1076
  this.theory.axioms.set(stmt.name, resolved);
1077
+ this.invalidateResolveCache();
980
1078
  this.emit(`Axioma ${stmt.name} = ${(0, propositional_1.formulaToString)(resolved)}`);
981
1079
  }
982
1080
  execTheoremDecl(stmt) {
@@ -985,6 +1083,7 @@ class Interpreter {
985
1083
  const diags = profile.checkWellFormed(resolved);
986
1084
  this.diagnostics.push(...diags);
987
1085
  this.theory.theorems.set(stmt.name, resolved);
1086
+ this.invalidateResolveCache();
988
1087
  this.emit(`Teorema ${stmt.name} = ${(0, propositional_1.formulaToString)(resolved)}`);
989
1088
  }
990
1089
  execDeriveCmd(stmt) {
@@ -1023,7 +1122,7 @@ class Interpreter {
1023
1122
  });
1024
1123
  }
1025
1124
  }
1026
- const result = profile.derive(resolved, stmt.premises, this.theory);
1125
+ const result = profile.derive(resolved, stmt.premises, this.buildEphemeralTheory());
1027
1126
  this.results.push(result);
1028
1127
  this.emitResult('derive', result);
1029
1128
  // Auto-register successful derivations as theorems so they can be reused.
@@ -1033,8 +1132,6 @@ class Interpreter {
1033
1132
  result.proof?.method !== 'semantic') {
1034
1133
  const theoremName = `derived_${this.theory.theorems.size + 1}`;
1035
1134
  this.theory.theorems.set(theoremName, resolved);
1036
- // Also register as axiom so it can be used as premise in subsequent derives
1037
- this.theory.axioms.set(theoremName, resolved);
1038
1135
  }
1039
1136
  }
1040
1137
  execCheckValidCmd(stmt) {
@@ -1065,7 +1162,7 @@ class Interpreter {
1065
1162
  execProveCmd(stmt) {
1066
1163
  const profile = this.requireProfile();
1067
1164
  const resolved = this.resolveFormula(stmt.goal);
1068
- const result = profile.prove(resolved, this.theory, stmt.premises);
1165
+ const result = profile.prove(resolved, this.buildEphemeralTheory(), stmt.premises);
1069
1166
  this.results.push(result);
1070
1167
  this.emitResult('prove', result);
1071
1168
  }
@@ -1081,6 +1178,10 @@ class Interpreter {
1081
1178
  const formula = this.resolveFormula(stmt.formula);
1082
1179
  if (profile.name === 'classical.propositional') {
1083
1180
  const atoms = Array.from((0, propositional_1.collectAtoms)(formula)).sort();
1181
+ const MAX_STREAMING_TRUTH_TABLE_ATOMS = 20;
1182
+ if (atoms.length > MAX_STREAMING_TRUTH_TABLE_ATOMS) {
1183
+ throw new Error(`truth_table soporta hasta ${MAX_STREAMING_TRUTH_TABLE_ATOMS} variables en lógica clásica proposicional`);
1184
+ }
1084
1185
  // Streaming de tabla de verdad para evitar OOM
1085
1186
  this.emit(`Tabla de verdad para ${(0, propositional_1.formulaToString)(formula)}:`);
1086
1187
  this.emit(` ${atoms.join(' | ')} | Resultado`);
@@ -1184,8 +1285,8 @@ class Interpreter {
1184
1285
  // Resolve bindings but preserve symbolic structure (no constant-folding)
1185
1286
  const resolved = this.resolveFormulaRecursive(stmt.formula, new Set());
1186
1287
  this.defineBinding(stmt.name, resolved, 'description' in stmt ? stmt.description : undefined);
1187
- if (!this.currentBindingFrame)
1188
- this.theory.axioms.set(stmt.name, resolved);
1288
+ // Note: `let` es un alias semántico, no un axioma.
1289
+ // Se mantiene accesible como premisa por nombre vía letBindings (ver execDeriveCmd).
1189
1290
  if ('description' in stmt && stmt.description) {
1190
1291
  if (!this.currentBindingFrame)
1191
1292
  this.letDescriptions.set(stmt.name, stmt.description);
@@ -1422,6 +1523,7 @@ class Interpreter {
1422
1523
  execProofBlock(stmt) {
1423
1524
  const profile = this.requireProfile();
1424
1525
  const savedAxioms = new Map(this.theory.axioms);
1526
+ const savedTheorems = new Map(this.theory.theorems);
1425
1527
  const savedLetBindings = new Map(this.letBindings);
1426
1528
  const savedLetDescriptions = new Map(this.letDescriptions);
1427
1529
  this.emit('── Proof Block ──');
@@ -1440,21 +1542,31 @@ class Interpreter {
1440
1542
  ]));
1441
1543
  const result = profile.derive(resolvedGoal, premiseNames, this.theory);
1442
1544
  this.results.push(result);
1443
- if (result.status === 'valid' || result.status === 'provable') {
1545
+ const succeeded = result.status === 'valid' || result.status === 'provable';
1546
+ let theoremPayload;
1547
+ if (succeeded) {
1444
1548
  this.emit(` ✓ QED — ${(0, format_1.formulaToUnicode)(resolvedGoal)} demostrado`);
1445
- const theoremName = `proof_${this.theory.theorems.size + 1}`;
1549
+ // Build implication from resolved assumption formulas so bindings are captured
1446
1550
  let implication = resolvedGoal;
1447
1551
  for (let i = stmt.assumptions.length - 1; i >= 0; i--) {
1448
- implication = { kind: 'implies', args: [stmt.assumptions[i].formula, implication] };
1552
+ const assumedResolved = this.resolveFormula(stmt.assumptions[i].formula);
1553
+ implication = { kind: 'implies', args: [assumedResolved, implication] };
1449
1554
  }
1450
- this.theory.theorems.set(theoremName, implication);
1555
+ const theoremName = `proof_${savedTheorems.size + 1}`;
1556
+ theoremPayload = { name: theoremName, formula: implication };
1451
1557
  }
1452
1558
  else {
1453
1559
  this.emit(` ✗ QED fallido — no se pudo demostrar ${(0, format_1.formulaToUnicode)(resolvedGoal)}`);
1454
1560
  }
1561
+ // Restore full theory state — proof blocks must be hypothetical
1455
1562
  this.theory.axioms = savedAxioms;
1563
+ this.theory.theorems = savedTheorems;
1456
1564
  this.letBindings = savedLetBindings;
1457
1565
  this.letDescriptions = savedLetDescriptions;
1566
+ // Only the resulting implication (if any) survives the block
1567
+ if (theoremPayload) {
1568
+ this.theory.theorems.set(theoremPayload.name, theoremPayload.formula);
1569
+ }
1458
1570
  this.invalidateResolveCache();
1459
1571
  this.emit('── End Proof Block ──');
1460
1572
  }
@@ -2491,30 +2603,34 @@ class Interpreter {
2491
2603
  return Array.from(seen);
2492
2604
  }
2493
2605
  execImportDecl(stmt) {
2494
- let filePath = stmt.path;
2495
- if (!filePath.endsWith('.st'))
2496
- filePath += '.st';
2497
- if (this.importedFiles.has(filePath))
2498
- return;
2499
- this.importedFiles.add(filePath);
2606
+ let requestedPath = stmt.path;
2607
+ if (!requestedPath.endsWith('.st'))
2608
+ requestedPath += '.st';
2500
2609
  let source;
2610
+ let resolved;
2501
2611
  try {
2502
2612
  // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment
2503
2613
  const fs = require('fs');
2504
2614
  // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment
2505
2615
  const path = require('path');
2506
2616
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
2507
- const resolved = path.isAbsolute(filePath)
2508
- ? filePath
2617
+ resolved = path.isAbsolute(requestedPath)
2618
+ ? requestedPath
2509
2619
  : // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
2510
- path.resolve(path.dirname(stmt.source.file || '.'), filePath);
2620
+ path.resolve(path.dirname(stmt.source.file || '.'), requestedPath);
2511
2621
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
2512
2622
  source = fs.readFileSync(resolved, 'utf-8');
2513
2623
  }
2514
2624
  catch {
2515
- throw new Error(`No se pudo importar '${filePath}'`);
2625
+ throw new Error(`No se pudo importar '${requestedPath}'`);
2516
2626
  }
2517
- const parser = new parser_1.Parser(filePath);
2627
+ if (this.activeImports.has(resolved))
2628
+ return;
2629
+ if (this.importedFiles.get(resolved) === source)
2630
+ return;
2631
+ this.activeImports.add(resolved);
2632
+ this.importedFiles.set(resolved, source);
2633
+ const parser = new parser_1.Parser(resolved);
2518
2634
  const program = parser.parse(source);
2519
2635
  this.diagnostics.push(...parser.diagnostics);
2520
2636
  const prevIsImporting = this.isImporting;
@@ -2523,37 +2639,60 @@ class Interpreter {
2523
2639
  const prevTheorems = new Map(this.theory.theorems);
2524
2640
  const prevFunctions = new Map(this.functions);
2525
2641
  const prevTheories = new Map(this.theories);
2526
- this.isImporting = true;
2527
- this.letBindings.clear();
2528
- this.theory.axioms.clear();
2529
- this.theory.theorems.clear();
2530
- this.functions.clear();
2531
- this.theories.clear();
2532
- for (const importedStmt of program.statements)
2533
- this.executeStatement(importedStmt);
2534
- const newExports = {
2535
- bindings: new Map(this.exportedBindings),
2536
- axioms: new Map(this.exportedAxioms),
2537
- theorems: new Map(this.exportedTheorems),
2538
- functions: new Map(this.exportedFunctions),
2539
- theories: new Map(this.exportedTheories),
2540
- };
2541
- this.isImporting = prevIsImporting;
2542
- this.letBindings = prevLetBindings;
2543
- this.theory.axioms = prevAxioms;
2544
- this.theory.theorems = prevTheorems;
2545
- this.functions = prevFunctions;
2546
- this.theories = prevTheories;
2547
- for (const [k, v] of newExports.bindings)
2642
+ const prevExportedBindings = new Map(this.exportedBindings);
2643
+ const prevExportedAxioms = new Map(this.exportedAxioms);
2644
+ const prevExportedTheorems = new Map(this.exportedTheorems);
2645
+ const prevExportedFunctions = new Map(this.exportedFunctions);
2646
+ const prevExportedTheories = new Map(this.exportedTheories);
2647
+ // eslint-disable-next-line no-useless-assignment -- needed as fallback if try block throws
2648
+ let newExports = null;
2649
+ try {
2650
+ this.isImporting = true;
2651
+ this.letBindings.clear();
2652
+ this.theory.axioms.clear();
2653
+ this.theory.theorems.clear();
2654
+ this.functions.clear();
2655
+ this.theories.clear();
2656
+ this.exportedBindings.clear();
2657
+ this.exportedAxioms.clear();
2658
+ this.exportedTheorems.clear();
2659
+ this.exportedFunctions.clear();
2660
+ this.exportedTheories.clear();
2661
+ for (const importedStmt of program.statements)
2662
+ this.executeStatement(importedStmt);
2663
+ newExports = {
2664
+ bindings: new Map(this.exportedBindings),
2665
+ axioms: new Map(this.exportedAxioms),
2666
+ theorems: new Map(this.exportedTheorems),
2667
+ functions: new Map(this.exportedFunctions),
2668
+ theories: new Map(this.exportedTheories),
2669
+ };
2670
+ }
2671
+ finally {
2672
+ this.isImporting = prevIsImporting;
2673
+ this.letBindings = prevLetBindings;
2674
+ this.theory.axioms = prevAxioms;
2675
+ this.theory.theorems = prevTheorems;
2676
+ this.functions = prevFunctions;
2677
+ this.theories = prevTheories;
2678
+ this.exportedBindings = prevExportedBindings;
2679
+ this.exportedAxioms = prevExportedAxioms;
2680
+ this.exportedTheorems = prevExportedTheorems;
2681
+ this.exportedFunctions = prevExportedFunctions;
2682
+ this.exportedTheories = prevExportedTheories;
2683
+ this.activeImports.delete(resolved);
2684
+ }
2685
+ for (const [k, v] of newExports?.bindings ?? [])
2548
2686
  this.letBindings.set(k, v);
2549
- for (const [k, v] of newExports.axioms)
2687
+ for (const [k, v] of newExports?.axioms ?? [])
2550
2688
  this.theory.axioms.set(k, v);
2551
- for (const [k, v] of newExports.theorems)
2689
+ for (const [k, v] of newExports?.theorems ?? [])
2552
2690
  this.theory.theorems.set(k, v);
2553
- for (const [k, v] of newExports.functions)
2691
+ for (const [k, v] of newExports?.functions ?? [])
2554
2692
  this.functions.set(k, v);
2555
- for (const [k, v] of newExports.theories)
2693
+ for (const [k, v] of newExports?.theories ?? [])
2556
2694
  this.theories.set(k, v);
2695
+ this.invalidateResolveCache();
2557
2696
  }
2558
2697
  execExplainCmd(stmt) {
2559
2698
  const profile = this.requireProfile();