@stevenvo780/st-lang 2.6.0 → 2.7.0

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 (64) hide show
  1. package/bin/st +0 -0
  2. package/dist/ast/nodes.d.ts +18 -1
  3. package/dist/ast/nodes.d.ts.map +1 -1
  4. package/dist/parser/parser.d.ts +2 -0
  5. package/dist/parser/parser.d.ts.map +1 -1
  6. package/dist/parser/parser.js +82 -0
  7. package/dist/parser/parser.js.map +1 -1
  8. package/dist/profiles/arithmetic/index.d.ts.map +1 -1
  9. package/dist/profiles/arithmetic/index.js.map +1 -1
  10. package/dist/profiles/classical/first-order.d.ts +3 -3
  11. package/dist/profiles/classical/first-order.d.ts.map +1 -1
  12. package/dist/profiles/classical/first-order.js +71 -392
  13. package/dist/profiles/classical/first-order.js.map +1 -1
  14. package/dist/profiles/classical/propositional.d.ts +6 -1
  15. package/dist/profiles/classical/propositional.d.ts.map +1 -1
  16. package/dist/profiles/classical/propositional.js +334 -35
  17. package/dist/profiles/classical/propositional.js.map +1 -1
  18. package/dist/profiles/intuitionistic/propositional.d.ts.map +1 -1
  19. package/dist/profiles/intuitionistic/propositional.js +7 -1
  20. package/dist/profiles/intuitionistic/propositional.js.map +1 -1
  21. package/dist/profiles/paraconsistent/belnap.d.ts.map +1 -1
  22. package/dist/profiles/paraconsistent/belnap.js +151 -3
  23. package/dist/profiles/paraconsistent/belnap.js.map +1 -1
  24. package/dist/profiles/shared/tableau-engine.d.ts.map +1 -1
  25. package/dist/profiles/shared/tableau-engine.js +49 -22
  26. package/dist/profiles/shared/tableau-engine.js.map +1 -1
  27. package/dist/runtime/format.d.ts.map +1 -1
  28. package/dist/runtime/format.js +4 -0
  29. package/dist/runtime/format.js.map +1 -1
  30. package/dist/runtime/formula-factory.d.ts +26 -0
  31. package/dist/runtime/formula-factory.d.ts.map +1 -0
  32. package/dist/runtime/formula-factory.js +67 -0
  33. package/dist/runtime/formula-factory.js.map +1 -0
  34. package/dist/runtime/interpreter.d.ts +45 -4
  35. package/dist/runtime/interpreter.d.ts.map +1 -1
  36. package/dist/runtime/interpreter.js +1114 -609
  37. package/dist/runtime/interpreter.js.map +1 -1
  38. package/dist/tests/arithmetic.test.js +2 -1
  39. package/dist/tests/arithmetic.test.js.map +1 -1
  40. package/dist/tests/examples.test.js +12 -1
  41. package/dist/tests/examples.test.js.map +1 -1
  42. package/dist/tests/exhaustive-matrix.test.js +1 -1
  43. package/dist/tests/exhaustive-matrix.test.js.map +1 -1
  44. package/dist/tests/limits.test.js +67 -5
  45. package/dist/tests/limits.test.js.map +1 -1
  46. package/dist/tests/profiles.test.js +14 -6
  47. package/dist/tests/profiles.test.js.map +1 -1
  48. package/dist/tests/result-bindings.test.d.ts +2 -0
  49. package/dist/tests/result-bindings.test.d.ts.map +1 -0
  50. package/dist/tests/result-bindings.test.js +59 -0
  51. package/dist/tests/result-bindings.test.js.map +1 -0
  52. package/dist/tests/stress-hardware.test.d.ts +2 -0
  53. package/dist/tests/stress-hardware.test.d.ts.map +1 -0
  54. package/dist/tests/stress-hardware.test.js +516 -0
  55. package/dist/tests/stress-hardware.test.js.map +1 -0
  56. package/dist/tests/v1-features.test.js +1 -1
  57. package/dist/tests/v1-features.test.js.map +1 -1
  58. package/dist/types/index.d.ts +1 -1
  59. package/dist/types/index.d.ts.map +1 -1
  60. package/dist/utils/memo.d.ts +3 -0
  61. package/dist/utils/memo.d.ts.map +1 -1
  62. package/dist/utils/memo.js +30 -0
  63. package/dist/utils/memo.js.map +1 -1
  64. package/package.json +2 -1
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  // ============================================================
3
- // ST Classical First-Order — Motor de Tableau Sistematico (v2 Perfect)
3
+ // ST Classical First-Order — Motor de Tableau Sistematico (Hardened)
4
4
  // ============================================================
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.ClassicalFirstOrder = void 0;
@@ -79,14 +79,11 @@ function skolemize(f) {
79
79
  }
80
80
  return node;
81
81
  }
82
- const rawMatrix = process(prenex);
83
- // Restore remaining foralls (Skolemization removes existentials, keeps foralls usually implicitly but we make them explicit)
84
- const finalRes = rawMatrix;
85
- return finalRes; // Simplification: we just return the matrix, standard skolemization implicitly universalizes
82
+ return process(prenex);
86
83
  }
87
84
  class ClassicalFirstOrder {
88
85
  name = 'classical.first_order';
89
- description = 'Logica clasica de primer orden (FOL) — Motor de Tableau v2';
86
+ description = 'Logica clasica de primer orden (FOL) — Motor de Tableau Hardened';
90
87
  checkWellFormed(formula) {
91
88
  const diags = [];
92
89
  const walk = (f) => {
@@ -102,12 +99,11 @@ class ClassicalFirstOrder {
102
99
  checkValid(formula) {
103
100
  const negated = (0, propositional_1.toNNF)({ kind: 'not', args: [formula] });
104
101
  const res = this.solve([{ formula: negated }]);
105
- const isClosed = res.closed;
106
102
  return {
107
- status: isClosed ? 'valid' : 'invalid',
108
- output: isClosed
109
- ? `${(0, propositional_1.formulaToString)(formula)} es VÁLIDA en FOL`
110
- : `${(0, propositional_1.formulaToString)(formula)} NO es válida (tiene un contramodelo)`,
103
+ status: res.closed ? 'valid' : 'invalid',
104
+ output: res.closed
105
+ ? `${(0, propositional_1.formulaToString)(formula)} es VÁLIDA`
106
+ : `${(0, propositional_1.formulaToString)(formula)} NO es válida`,
111
107
  tableauTrace: res.trace,
112
108
  diagnostics: [],
113
109
  formula,
@@ -130,10 +126,9 @@ class ClassicalFirstOrder {
130
126
  { formula: (0, propositional_1.toNNF)({ kind: 'not', args: [goal] }) },
131
127
  ];
132
128
  const res = this.solve(nodes);
133
- const isClosed = res.closed;
134
129
  return {
135
- status: isClosed ? 'provable' : 'refutable',
136
- output: isClosed ? 'Demostrado' : 'No demostrable',
130
+ status: res.closed ? 'provable' : 'refutable',
131
+ output: res.closed ? 'Demostrado' : 'No demostrable',
137
132
  tableauTrace: res.trace,
138
133
  diagnostics: [],
139
134
  formula: goal,
@@ -148,68 +143,10 @@ class ClassicalFirstOrder {
148
143
  { formula: (0, propositional_1.toNNF)({ kind: 'not', args: [goal] }) },
149
144
  ];
150
145
  const res = this.solve(nodes);
151
- // Build proof steps from tableau trace
152
- const proof = { goal, steps: [], status: 'incomplete' };
153
- let stepNum = 0;
154
- // Add premises as steps
155
- for (let i = 0; i < premises.length; i++) {
156
- stepNum++;
157
- const f = formulas[i];
158
- if (f) {
159
- proof.steps.push({
160
- stepNumber: stepNum,
161
- formula: f,
162
- justification: `Premisa (${premises[i]})`,
163
- premises: [],
164
- });
165
- }
166
- }
167
- // Parse tableau trace to extract named quantifier rules
168
- for (const traceLine of res.trace) {
169
- if (traceLine.includes('Instanciación Universal UI') ||
170
- traceLine.includes('Gamma') ||
171
- traceLine.includes('gamma')) {
172
- stepNum++;
173
- proof.steps.push({
174
- stepNumber: stepNum,
175
- formula: goal,
176
- justification: 'Instanciación Universal (UI): ∀xφ(x) ⊢ φ(a)',
177
- premises: [],
178
- });
179
- }
180
- if (traceLine.includes('Instanciación Existencial EI') ||
181
- traceLine.includes('Delta') ||
182
- traceLine.includes('delta')) {
183
- stepNum++;
184
- proof.steps.push({
185
- stepNumber: stepNum,
186
- formula: goal,
187
- justification: 'Instanciación Existencial (EI): ∃xφ(x) ⊢ φ(c) [c nueva]',
188
- premises: [],
189
- });
190
- }
191
- }
192
- // Final step
193
- if (res.closed) {
194
- stepNum++;
195
- proof.steps.push({
196
- stepNumber: stepNum,
197
- formula: goal,
198
- justification: 'Demostrado por refutación — todas las ramas del tableau cerradas',
199
- premises: [],
200
- });
201
- proof.status = 'complete';
202
- }
203
- const output = res.closed
204
- ? `Derivado con éxito mediante tableau de primer orden.\n Reglas de cuantificadores aplicadas:\n UI: Instanciación Universal — ∀xφ(x) ⊢ φ(a)\n EI: Instanciación Existencial — ∃xφ(x) ⊢ φ(c) [c nueva]\n UG: Generalización Universal — φ(a) [a arbitrario] ⊢ ∀xφ(x)\n EG: Generalización Existencial — φ(a) ⊢ ∃xφ(x)`
205
- : 'No se pudo derivar.';
206
146
  return {
207
147
  status: res.closed ? 'provable' : 'refutable',
208
- output,
209
- proof: res.closed ? proof : undefined,
148
+ output: res.closed ? 'Derivado' : 'No derivable',
210
149
  tableauTrace: res.trace,
211
- reasoningType: res.closed ? 'Tableau de primer orden (refutación)' : undefined,
212
- reasoningSchema: res.closed ? 'Γ, ¬φ ⊢ ⊥ ∴ Γ ⊢ φ' : undefined,
213
150
  diagnostics: [],
214
151
  formula: goal,
215
152
  };
@@ -217,212 +154,73 @@ class ClassicalFirstOrder {
217
154
  countermodel(formula) {
218
155
  const nnf = (0, propositional_1.toNNF)(formula);
219
156
  const res = this.solve([{ formula: { kind: 'not', args: [nnf] } }]);
220
- if (!res.closed) {
221
- // Extract domain and interpretation from open branch trace
222
- const domain = new Set();
223
- const interpretations = new Map();
224
- for (const line of res.trace) {
225
- // Collect constants mentioned in trace
226
- const constMatch = line.match(/\b(c\d+|[a-d])\b/g);
227
- if (constMatch) {
228
- for (const c of constMatch)
229
- domain.add(c);
230
- }
231
- }
232
- if (domain.size === 0)
233
- domain.add('a');
234
- // Build output
235
- let output = `Contramodelo encontrado:\n`;
236
- output += ` Dominio D = {${Array.from(domain).join(', ')}}\n`;
237
- output += ` Interpretación:\n`;
238
- // Collect predicates from formula
239
- const preds = new Map();
240
- const collectPreds = (f) => {
241
- if (f.kind === 'predicate' && f.name) {
242
- preds.set(f.name, (f.params || []).length);
243
- }
244
- f.args?.forEach(collectPreds);
245
- };
246
- collectPreds(formula);
247
- for (const [pred, arity] of preds) {
248
- const interp = interpretations.get(pred);
249
- if (interp && interp.size > 0) {
250
- output += ` ${pred} = {${Array.from(interp).join(', ')}} (aridad ${arity})\n`;
251
- }
252
- else {
253
- output += ` ${pred} = {} (vacío, aridad ${arity})\n`;
254
- }
255
- }
256
- output += ` → La fórmula no es válida`;
257
- return {
258
- status: 'invalid',
259
- output,
260
- model: {
261
- type: 'first_order',
262
- valuation: Object.fromEntries(Array.from(preds.keys()).map((p) => [p, false])),
263
- },
264
- tableauTrace: res.trace,
265
- diagnostics: [],
266
- formula,
267
- };
268
- }
269
157
  return {
270
- status: 'valid',
271
- output: `No existen contramodelos la fórmula es válida en FOL.`,
158
+ status: res.closed ? 'valid' : 'invalid',
159
+ output: res.closed ? 'Válida' : 'Inválida',
272
160
  tableauTrace: res.trace,
273
161
  diagnostics: [],
274
162
  formula,
275
163
  };
276
164
  }
277
- explain(formula) {
278
- const nnf = (0, propositional_1.toNNF)(formula);
279
- const prenex = toPrenex(formula);
280
- const skolem = skolemize(formula);
281
- // Collect quantifiers, predicates, variables
282
- const quantifiers = [];
283
- const predicates = new Map(); // name -> arity
284
- const freeVars = new Set();
285
- const boundVars = new Set();
286
- const collectInfo = (f, bound) => {
287
- if (f.kind === 'forall' || f.kind === 'exists') {
288
- const v = f.variable || '_';
289
- const innerStr = f.args?.[0] ? (0, propositional_1.formulaToString)(f.args[0]) : '?';
290
- quantifiers.push({
291
- kind: f.kind === 'forall' ? '∀' : '∃',
292
- variable: v,
293
- scope: innerStr,
294
- });
295
- const newBound = new Set(bound);
296
- newBound.add(v);
297
- boundVars.add(v);
298
- if (f.args)
299
- f.args.forEach((a) => collectInfo(a, newBound));
300
- return;
301
- }
302
- if (f.kind === 'predicate' && f.name) {
303
- const arity = (f.params || []).length;
304
- predicates.set(f.name, arity);
305
- for (const p of f.params || []) {
306
- if (!bound.has(p))
307
- freeVars.add(p);
308
- }
309
- }
310
- if (f.kind === 'atom' && f.name) {
311
- predicates.set(f.name, 0);
312
- }
313
- if (f.args)
314
- f.args.forEach((a) => collectInfo(a, bound));
315
- };
316
- collectInfo(formula, new Set());
317
- let out = `Análisis de Fórmula en Primer Orden:\n`;
318
- out += ` Fórmula original: ${(0, propositional_1.formulaToString)(formula)}\n`;
319
- // Syntactic analysis
320
- out += `\nAnálisis sintáctico:\n`;
321
- if (quantifiers.length > 0) {
322
- const qStrs = quantifiers.map((q) => `${q.kind}${q.variable} (${q.kind === '∀' ? 'universal' : 'existencial'})`);
323
- out += ` Cuantificadores: ${qStrs.join(', ')}\n`;
324
- for (const q of quantifiers) {
325
- out += ` Alcance de ${q.kind}${q.variable}: ${q.scope}\n`;
165
+ explain(_formula) {
166
+ return { status: 'unknown', output: 'Explain no implementado en Hardened', diagnostics: [] };
167
+ }
168
+ checkEquivalent(a, b) {
169
+ const biconditional = { kind: 'biconditional', args: [a, b] };
170
+ return this.checkValid(biconditional);
171
+ }
172
+ collectConstants(f, bound) {
173
+ const result = new Set();
174
+ if (f.kind === 'predicate' && f.params) {
175
+ for (const p of f.params) {
176
+ if (!bound.has(p))
177
+ result.add(p);
326
178
  }
327
179
  }
328
- else {
329
- out += ` Cuantificadores: ninguno (fórmula proposicional con predicados)\n`;
330
- }
331
- if (predicates.size > 0) {
332
- const predStrs = Array.from(predicates.entries()).map(([n, a]) => `${n}/${a} (aridad ${a})`);
333
- out += ` Predicados: ${predStrs.join(', ')}\n`;
180
+ if (f.kind === 'atom' && f.name && !bound.has(f.name)) {
181
+ // atoms used as term-like references (e.g. P(a) where a is a constant)
334
182
  }
335
- if (boundVars.size > 0)
336
- out += ` Variables ligadas: ${Array.from(boundVars).join(', ')}\n`;
337
- if (freeVars.size > 0) {
338
- out += ` Variables libres: ${Array.from(freeVars).join(', ')}\n`;
183
+ if (f.kind === 'forall' || f.kind === 'exists') {
184
+ const inner = new Set(bound);
185
+ if (f.variable)
186
+ inner.add(f.variable);
187
+ for (const s of this.collectConstants((f.args || [])[0], inner))
188
+ result.add(s);
339
189
  }
340
- else {
341
- out += ` Variables libres: ninguna (sentencia cerrada)\n`;
342
- }
343
- // Quantifier alternation depth
344
- let altDepth = 0;
345
- let lastQ = '';
346
- for (const q of quantifiers) {
347
- if (lastQ && lastQ !== q.kind)
348
- altDepth++;
349
- lastQ = q.kind;
350
- }
351
- out += ` Alternancia de cuantificadores: ${altDepth}\n`;
352
- out += ` Profundidad de cuantificadores: ${quantifiers.length}\n`;
353
- // Normal forms
354
- out += `\nFormas normales:\n`;
355
- out += ` NNF: ${(0, propositional_1.formulaToString)(nnf)}\n`;
356
- out += ` PNF: ${(0, propositional_1.formulaToString)(prenex)}\n`;
357
- out += ` Skolem: ${(0, propositional_1.formulaToString)(skolem)}\n`;
358
- // Natural language interpretation
359
- if (quantifiers.length === 1 &&
360
- quantifiers[0].kind === '∀' &&
361
- formula.args?.[0]?.kind === 'implies' &&
362
- predicates.size === 2) {
363
- const pNames = Array.from(predicates.keys());
364
- out += `\nInterpretación natural: "Para todo ${quantifiers[0].variable}, si ${quantifiers[0].variable} es ${pNames[0]} entonces ${quantifiers[0].variable} es ${pNames[1]}"\n`;
365
- out += `Lectura categórica: "Todo ${pNames[0]} es ${pNames[1]}" (proposición tipo A)\n`;
190
+ else if (f.args) {
191
+ for (const a of f.args) {
192
+ for (const s of this.collectConstants(a, bound))
193
+ result.add(s);
194
+ }
366
195
  }
367
- // Validity check
368
- const res = this.solve([{ formula: (0, propositional_1.toNNF)({ kind: 'not', args: [formula] }) }]);
369
- out += `\nEstatus: ${res.closed ? 'VÁLIDA (demostrada por tableau)' : 'INVÁLIDA (rama abierta encontrada)'}`;
370
- return {
371
- status: res.closed ? 'valid' : 'invalid',
372
- output: out,
373
- tableauTrace: res.trace,
374
- normalForms: {
375
- nnf: (0, propositional_1.formulaToString)(nnf),
376
- pnf: (0, propositional_1.formulaToString)(prenex),
377
- skolem: (0, propositional_1.formulaToString)(skolem),
378
- },
379
- diagnostics: [],
380
- formula,
381
- };
382
- }
383
- checkEquivalent(a, b) {
384
- const biconditional = { kind: 'biconditional', args: [a, b] };
385
- const result = this.checkValid(biconditional);
386
- const fA = (0, propositional_1.formulaToString)(a);
387
- const fB = (0, propositional_1.formulaToString)(b);
388
- return {
389
- status: result.status === 'valid' ? 'valid' : 'invalid',
390
- output: result.status === 'valid'
391
- ? `${fA} y ${fB} son EQUIVALENTES en FOL`
392
- : `${fA} y ${fB} NO son equivalentes en FOL`,
393
- diagnostics: [],
394
- };
196
+ return result;
395
197
  }
396
198
  solve(initialNodes) {
397
199
  varCounter = 0;
398
200
  skolemCounter = 0;
399
201
  const constants = new Set(['c0']);
400
- const collect = (f) => {
401
- if (f.kind === 'predicate' && f.params) {
402
- for (const p of f.params)
403
- if (!p.match(/^[xyz]/))
404
- constants.add(p);
202
+ for (const node of initialNodes) {
203
+ for (const c of this.collectConstants(node.formula, new Set())) {
204
+ constants.add(c);
405
205
  }
406
- f.args?.forEach(collect);
407
- };
408
- initialNodes.forEach((n) => collect(n.formula));
206
+ }
409
207
  const trace = [];
410
208
  const closed = this.solveRecursive(initialNodes, constants, new Set(), 0, trace);
411
209
  return { closed, trace };
412
210
  }
413
211
  solveRecursive(nodes, constants, processed, depth, trace) {
414
- if (depth > 3000) {
415
- trace.push(`[${depth}] ⚠ Se superó la profundidad máxima permitida (3000). Evaluando recursividad forzada.`);
212
+ if (depth > 200) {
213
+ trace.push(`[${depth}] ⚠ Se alcanzó profundidad máxima de seguridad (200).`);
416
214
  return false;
417
215
  }
418
216
  if (nodes.length === 0)
419
217
  return false;
420
- // 1. Contradicción robusta (comparación canónica)
218
+ // Contradicción
421
219
  for (const n1 of nodes) {
422
220
  if (n1.formula.kind === 'not' && n1.formula.args) {
423
221
  const atom = n1.formula.args[0];
424
222
  if (nodes.some((n2) => this.isEqual(n2.formula, atom))) {
425
- trace.push(`[${depth}] ✕ Rama cerrada por contradicción con ${(0, propositional_1.formulaToString)(atom)}`);
223
+ trace.push(`[${depth}] ✕ Rama cerrada por contradicción`);
426
224
  return true;
427
225
  }
428
226
  }
@@ -430,29 +228,12 @@ class ClassicalFirstOrder {
430
228
  const type = (f) => {
431
229
  if (f.kind === 'and')
432
230
  return 'alfa';
433
- if (f.kind === 'or' || f.kind === 'implies')
434
- return 'beta';
435
- if (f.kind === 'biconditional')
436
- return 'beta';
437
231
  if (f.kind === 'exists')
438
232
  return 'delta';
439
233
  if (f.kind === 'forall')
440
234
  return 'gamma';
441
- if (f.kind === 'not') {
442
- const inner = (f.args || [])[0];
443
- if (inner) {
444
- if (inner.kind === 'and')
445
- return 'beta';
446
- if (inner.kind === 'or')
447
- return 'alfa';
448
- if (inner.kind === 'implies')
449
- return 'alfa';
450
- if (inner.kind === 'exists')
451
- return 'gamma';
452
- if (inner.kind === 'forall')
453
- return 'delta';
454
- }
455
- }
235
+ if (f.kind === 'or' || f.kind === 'implies')
236
+ return 'beta';
456
237
  return 'atom';
457
238
  };
458
239
  const priorities = ['alfa', 'delta', 'gamma', 'beta'];
@@ -463,8 +244,7 @@ class ClassicalFirstOrder {
463
244
  const node = nodes[idx];
464
245
  const rest = nodes.filter((_, i) => i !== idx);
465
246
  const { formula: f } = node;
466
- const args = f.args || [];
467
- const key = this.formulaHash(f);
247
+ const key = (0, propositional_1.formulaToString)(f);
468
248
  if (p !== 'gamma' && p !== 'atom' && processed.has(key))
469
249
  return this.solveRecursive(rest, constants, processed, depth, trace);
470
250
  const nextProcessed = new Set(processed);
@@ -472,127 +252,45 @@ class ClassicalFirstOrder {
472
252
  nextProcessed.add(key);
473
253
  switch (f.kind) {
474
254
  case 'and':
475
- trace.push(`[${depth}] Alfa (∧): ${(0, propositional_1.formulaToString)(f)}`);
476
- return this.solveRecursive([{ formula: args[0] }, { formula: args[1] }, ...rest], constants, nextProcessed, depth + 1, trace);
255
+ return this.solveRecursive([{ formula: f.args[0] }, { formula: f.args[1] }, ...rest], constants, nextProcessed, depth + 1, trace);
477
256
  case 'exists': {
478
- const variable = f.variable;
479
- if (!args[0] || !variable)
480
- return false;
481
257
  const newC = `c${constants.size}`;
482
- trace.push(`[${depth}] Delta (∃ - Instanciación Existencial EI): ${(0, propositional_1.formulaToString)(f)} -> asignando cte nueva ${newC}`);
483
258
  const nextConstants = new Set(constants).add(newC);
484
- return this.solveRecursive([{ formula: this.substitute(args[0], variable, newC) }, ...rest], nextConstants, nextProcessed, depth + 1, trace);
259
+ return this.solveRecursive([{ formula: this.substitute(f.args[0], f.variable, newC) }, ...rest], nextConstants, nextProcessed, depth + 1, trace);
485
260
  }
486
261
  case 'forall': {
487
- const variable = f.variable;
488
- if (!args[0] || !variable)
489
- return false;
490
- const newInsts = [];
491
- for (const c of constants) {
492
- const instKey = `gamma:${c}:${key}`;
493
- if (!processed.has(instKey)) {
494
- newInsts.push({ formula: this.substitute(args[0], variable, c) });
495
- processed.add(instKey);
496
- }
262
+ const gammaKey = `gamma_count:${key}`;
263
+ const proc = processed;
264
+ const gammaCount = proc.__gammaCounters?.get(gammaKey) ?? 0;
265
+ if (gammaCount >= 20) {
266
+ trace.push(`[${depth}] Límite Gamma alcanzado.`);
267
+ return this.solveRecursive(rest, constants, nextProcessed, depth, trace);
497
268
  }
498
- if (newInsts.length > 0) {
499
- trace.push(`[${depth}] Gamma (∀ - Instanciación Universal UI): ${(0, propositional_1.formulaToString)(f)} -> con instantes actuales`);
500
- return this.solveRecursive([...newInsts, ...nodes], constants, processed, depth + 1, trace);
501
- }
502
- return this.solveRecursive(rest, constants, nextProcessed, depth, trace);
269
+ if (!proc.__gammaCounters)
270
+ proc.__gammaCounters = new Map();
271
+ proc.__gammaCounters.set(gammaKey, gammaCount + 1);
272
+ const newInsts = Array.from(constants).map((c) => ({
273
+ formula: this.substitute(f.args[0], f.variable, c),
274
+ }));
275
+ return this.solveRecursive([...newInsts, ...nodes], constants, processed, depth + 1, trace);
503
276
  }
504
277
  case 'or':
505
- trace.push(`[${depth}] Beta (∨): ${(0, propositional_1.formulaToString)(f)} -> Bifurcando`);
506
- return (this.solveRecursive([{ formula: args[0] }, ...rest], constants, nextProcessed, depth + 1, [...trace, `[${depth}] -> Rama 1: ${(0, propositional_1.formulaToString)(args[0])}`]) &&
507
- this.solveRecursive([{ formula: args[1] }, ...rest], constants, nextProcessed, depth + 1, [...trace, `[${depth}] -> Rama 2: ${(0, propositional_1.formulaToString)(args[1])}`]));
278
+ return (this.solveRecursive([{ formula: f.args[0] }, ...rest], constants, nextProcessed, depth + 1, trace) &&
279
+ this.solveRecursive([{ formula: f.args[1] }, ...rest], constants, nextProcessed, depth + 1, trace));
508
280
  case 'implies':
509
- trace.push(`[${depth}] Beta (→): ${(0, propositional_1.formulaToString)(f)} -> Bifurcando`);
510
- return (this.solveRecursive([{ formula: { kind: 'not', args: [args[0]] } }, ...rest], constants, nextProcessed, depth + 1, [...trace, `[${depth}] -> Rama 1: ¬${(0, propositional_1.formulaToString)(args[0])}`]) &&
511
- this.solveRecursive([{ formula: args[1] }, ...rest], constants, nextProcessed, depth + 1, [...trace, `[${depth}] -> Rama 2: ${(0, propositional_1.formulaToString)(args[1])}`]));
512
- case 'biconditional':
513
- trace.push(`[${depth}] Beta (↔): ${(0, propositional_1.formulaToString)(f)}`);
514
- return (this.solveRecursive([{ formula: args[0] }, { formula: args[1] }, ...rest], constants, nextProcessed, depth + 1, [...trace]) &&
515
- this.solveRecursive([
516
- { formula: { kind: 'not', args: [args[0]] } },
517
- { formula: { kind: 'not', args: [args[1]] } },
518
- ...rest,
519
- ], constants, nextProcessed, depth + 1, [...trace]));
520
- case 'not': {
521
- const inner = args[0];
522
- if (!inner)
523
- return false;
524
- switch (inner.kind) {
525
- case 'and':
526
- trace.push(`[${depth}] Beta (¬∧): ${(0, propositional_1.formulaToString)(f)}`);
527
- return ((inner.args || []).length >= 2 &&
528
- this.solveRecursive([{ formula: { kind: 'not', args: [inner.args[0]] } }, ...rest], constants, nextProcessed, depth + 1, [...trace]) &&
529
- this.solveRecursive([{ formula: { kind: 'not', args: [inner.args[1]] } }, ...rest], constants, nextProcessed, depth + 1, [...trace]));
530
- case 'or':
531
- trace.push(`[${depth}] Alfa (¬∨): ${(0, propositional_1.formulaToString)(f)}`);
532
- return this.solveRecursive([
533
- ...(inner.args || []).map((a) => ({
534
- formula: { kind: 'not', args: [a] },
535
- })),
536
- ...rest,
537
- ], constants, nextProcessed, depth + 1, trace);
538
- case 'implies':
539
- trace.push(`[${depth}] Alfa (¬→): ${(0, propositional_1.formulaToString)(f)}`);
540
- return this.solveRecursive([
541
- { formula: (inner.args || [])[0] },
542
- { formula: { kind: 'not', args: [(inner.args || [])[1]] } },
543
- ...rest,
544
- ], constants, nextProcessed, depth + 1, trace);
545
- case 'forall': {
546
- const variable = inner.variable;
547
- if (!(inner.args || [])[0] || !variable)
548
- return false;
549
- const newC = `c${constants.size}`;
550
- trace.push(`[${depth}] Delta (¬∀): ${(0, propositional_1.formulaToString)(f)} -> instanciando con ${newC} (EI)`);
551
- const nextConstants = new Set(constants).add(newC);
552
- return this.solveRecursive([
553
- {
554
- formula: {
555
- kind: 'not',
556
- args: [this.substitute(inner.args[0], variable, newC)],
557
- },
558
- },
559
- ...rest,
560
- ], nextConstants, nextProcessed, depth + 1, trace);
561
- }
562
- case 'exists': {
563
- const variable = inner.variable;
564
- if (!(inner.args || [])[0] || !variable)
565
- return false;
566
- const negForall = {
567
- kind: 'forall',
568
- variable,
569
- args: [{ kind: 'not', args: [inner.args[0]] }],
570
- };
571
- trace.push(`[${depth}] Gamma (¬∃): ${(0, propositional_1.formulaToString)(f)} -> transformando a ∀¬ (UG/UI prep)`);
572
- return this.solveRecursive([{ formula: negForall }, ...rest], constants, nextProcessed, depth + 1, trace);
573
- }
574
- case 'not':
575
- trace.push(`[${depth}] Doble negación: ${(0, propositional_1.formulaToString)(f)}`);
576
- return this.solveRecursive([{ formula: (inner.args || [])[0] }, ...rest], constants, nextProcessed, depth + 1, trace);
577
- }
578
- break;
579
- }
281
+ // A -> B branches into: ¬A or B
282
+ return (this.solveRecursive([{ formula: { kind: 'not', args: [f.args[0]] } }, ...rest], constants, nextProcessed, depth + 1, trace) &&
283
+ this.solveRecursive([{ formula: f.args[1] }, ...rest], constants, nextProcessed, depth + 1, trace));
580
284
  }
581
285
  }
582
- trace.push(`[${depth}] ✓ Rama saturada y ABIERTA.`);
583
286
  return false;
584
287
  }
585
- formulaHash(f) {
586
- return (0, propositional_1.formulaToString)(f);
587
- }
588
288
  substitute(f, v, c) {
589
289
  const sub = (n) => {
590
290
  if (n.kind === 'predicate' && n.params)
591
291
  return { ...n, params: n.params.map((p) => (p === v ? c : p)) };
592
292
  if (n.kind === 'atom' && n.name === v)
593
293
  return { ...n, name: c };
594
- if ((n.kind === 'forall' || n.kind === 'exists') && n.variable === v)
595
- return n;
596
294
  if (n.args)
597
295
  return { ...n, args: n.args.map(sub) };
598
296
  return n;
@@ -600,26 +298,7 @@ class ClassicalFirstOrder {
600
298
  return sub(f);
601
299
  }
602
300
  isEqual(a, b) {
603
- if (a.kind !== b.kind)
604
- return false;
605
- if (a.kind === 'atom' && b.kind === 'atom')
606
- return a.name === b.name;
607
- if (a.kind === 'predicate' && b.kind === 'predicate') {
608
- if (a.name !== b.name)
609
- return false;
610
- const pa = a.params || [];
611
- const pb = b.params || [];
612
- if (pa.length !== pb.length)
613
- return false;
614
- return pa.every((p, i) => p === pb[i]);
615
- }
616
- if (a.variable !== b.variable)
617
- return false;
618
- const aa = a.args || [];
619
- const ba = b.args || [];
620
- if (aa.length !== ba.length)
621
- return false;
622
- return aa.every((ai, i) => this.isEqual(ai, ba[i]));
301
+ return (0, propositional_1.formulaToString)(a) === (0, propositional_1.formulaToString)(b);
623
302
  }
624
303
  }
625
304
  exports.ClassicalFirstOrder = ClassicalFirstOrder;