@stevenvo780/st-lang 2.6.0 → 2.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/st +0 -0
- package/dist/profiles/arithmetic/index.d.ts.map +1 -1
- package/dist/profiles/arithmetic/index.js.map +1 -1
- package/dist/profiles/classical/first-order.d.ts +2 -3
- package/dist/profiles/classical/first-order.d.ts.map +1 -1
- package/dist/profiles/classical/first-order.js +40 -396
- package/dist/profiles/classical/first-order.js.map +1 -1
- package/dist/profiles/classical/propositional.d.ts.map +1 -1
- package/dist/profiles/classical/propositional.js.map +1 -1
- package/dist/profiles/intuitionistic/propositional.d.ts.map +1 -1
- package/dist/profiles/intuitionistic/propositional.js +7 -1
- package/dist/profiles/intuitionistic/propositional.js.map +1 -1
- package/dist/profiles/paraconsistent/belnap.js.map +1 -1
- package/dist/runtime/interpreter.d.ts +4 -4
- package/dist/runtime/interpreter.d.ts.map +1 -1
- package/dist/runtime/interpreter.js +197 -476
- package/dist/runtime/interpreter.js.map +1 -1
- package/dist/tests/limits.test.js +53 -3
- package/dist/tests/limits.test.js.map +1 -1
- package/dist/tests/profiles.test.js +14 -6
- package/dist/tests/profiles.test.js.map +1 -1
- package/package.json +1 -1
|
@@ -13,6 +13,7 @@ const fallacies_1 = require("./fallacies");
|
|
|
13
13
|
require("../profiles");
|
|
14
14
|
const compiler_1 = require("../text-layer/compiler");
|
|
15
15
|
const formula_classifier_1 = require("./formula-classifier");
|
|
16
|
+
const MAX_CALL_DEPTH = 500;
|
|
16
17
|
class Interpreter {
|
|
17
18
|
theory;
|
|
18
19
|
profile = null;
|
|
@@ -40,6 +41,8 @@ class Interpreter {
|
|
|
40
41
|
exportedTheorems = new Map();
|
|
41
42
|
exportedFunctions = new Map();
|
|
42
43
|
exportedTheories = new Map();
|
|
44
|
+
/** Profundidad de llamadas a funciones (anti-recursión infinita) */
|
|
45
|
+
callDepth = 0;
|
|
43
46
|
constructor() {
|
|
44
47
|
this.theory = this.createEmptyTheory();
|
|
45
48
|
this.textLayer = (0, compiler_1.createTextLayerState)();
|
|
@@ -300,6 +303,10 @@ class Interpreter {
|
|
|
300
303
|
* Soporta notación con punto: Theory.member resuelve desde el scope de la teoría.
|
|
301
304
|
*/
|
|
302
305
|
resolveFormula(f, visited = new Set()) {
|
|
306
|
+
const resolved = this.resolveFormulaRecursive(f, visited);
|
|
307
|
+
return this.tryConstantFold(resolved);
|
|
308
|
+
}
|
|
309
|
+
resolveFormulaRecursive(f, visited = new Set()) {
|
|
303
310
|
if (!f)
|
|
304
311
|
return f;
|
|
305
312
|
// Si es un átomo, intentar resolver
|
|
@@ -308,11 +315,9 @@ class Interpreter {
|
|
|
308
315
|
if (f.name.includes('.')) {
|
|
309
316
|
const [prefix, memberName] = f.name.split('.', 2);
|
|
310
317
|
// 1. Intentar resolver el prefijo como una variable local (instancia)
|
|
311
|
-
// Ej: let f1 = Familia("Socrates") -> f1.amor
|
|
312
318
|
if (this.letBindings.has(prefix)) {
|
|
313
319
|
const resolvedPrefix = this.letBindings.get(prefix);
|
|
314
320
|
if (resolvedPrefix.kind === 'atom' && resolvedPrefix.name) {
|
|
315
|
-
// Si el prefijo se resuelve a un nombre de teoría/instancia
|
|
316
321
|
const actualInstanceName = resolvedPrefix.name;
|
|
317
322
|
const scope = this.theories.get(actualInstanceName);
|
|
318
323
|
if (scope) {
|
|
@@ -320,11 +325,11 @@ class Interpreter {
|
|
|
320
325
|
this.currentTheoryName !== actualInstanceName)
|
|
321
326
|
return f;
|
|
322
327
|
if (scope.letBindings.has(memberName))
|
|
323
|
-
return this.
|
|
328
|
+
return this.resolveFormulaRecursive(scope.letBindings.get(memberName), new Set(visited));
|
|
324
329
|
if (scope.axioms.has(memberName))
|
|
325
|
-
return this.
|
|
330
|
+
return this.resolveFormulaRecursive(scope.axioms.get(memberName), new Set(visited));
|
|
326
331
|
if (scope.theorems.has(memberName))
|
|
327
|
-
return this.
|
|
332
|
+
return this.resolveFormulaRecursive(scope.theorems.get(memberName), new Set(visited));
|
|
328
333
|
}
|
|
329
334
|
}
|
|
330
335
|
}
|
|
@@ -334,34 +339,36 @@ class Interpreter {
|
|
|
334
339
|
if (scope.privateMembers.has(memberName) && this.currentTheoryName !== prefix)
|
|
335
340
|
return f;
|
|
336
341
|
if (scope.letBindings.has(memberName))
|
|
337
|
-
return this.
|
|
342
|
+
return this.resolveFormulaRecursive(scope.letBindings.get(memberName), new Set(visited));
|
|
338
343
|
if (scope.axioms.has(memberName))
|
|
339
|
-
return this.
|
|
344
|
+
return this.resolveFormulaRecursive(scope.axioms.get(memberName), new Set(visited));
|
|
340
345
|
if (scope.theorems.has(memberName))
|
|
341
|
-
return this.
|
|
346
|
+
return this.resolveFormulaRecursive(scope.theorems.get(memberName), new Set(visited));
|
|
342
347
|
}
|
|
343
348
|
return f;
|
|
344
349
|
}
|
|
345
350
|
// Binding local normal
|
|
346
351
|
if (this.letBindings.has(f.name)) {
|
|
347
|
-
if (visited.has(f.name))
|
|
352
|
+
if (visited.has(f.name))
|
|
348
353
|
return f;
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
return this.
|
|
354
|
+
const newVisited = new Set(visited);
|
|
355
|
+
newVisited.add(f.name);
|
|
356
|
+
return this.resolveFormulaRecursive(this.letBindings.get(f.name), newVisited);
|
|
352
357
|
}
|
|
353
358
|
// También resolver axiomas/teoremas del theory actual por nombre
|
|
354
359
|
if (this.theory.axioms.has(f.name)) {
|
|
355
360
|
if (visited.has(f.name))
|
|
356
361
|
return f;
|
|
357
|
-
visited
|
|
358
|
-
|
|
362
|
+
const newVisited = new Set(visited);
|
|
363
|
+
newVisited.add(f.name);
|
|
364
|
+
return this.resolveFormulaRecursive(this.theory.axioms.get(f.name), newVisited);
|
|
359
365
|
}
|
|
360
366
|
if (this.theory.theorems.has(f.name)) {
|
|
361
367
|
if (visited.has(f.name))
|
|
362
368
|
return f;
|
|
363
|
-
visited
|
|
364
|
-
|
|
369
|
+
const newVisited = new Set(visited);
|
|
370
|
+
newVisited.add(f.name);
|
|
371
|
+
return this.resolveFormulaRecursive(this.theory.theorems.get(f.name), newVisited);
|
|
365
372
|
}
|
|
366
373
|
}
|
|
367
374
|
// Llamada a función como expresión
|
|
@@ -371,12 +378,8 @@ class Interpreter {
|
|
|
371
378
|
}
|
|
372
379
|
// Recorrer hijos recursivamente
|
|
373
380
|
if (f.args && f.args.length > 0) {
|
|
374
|
-
const newArgs = f.args.map((a) =>
|
|
375
|
-
|
|
376
|
-
const changed = newArgs.some((a, i) => a !== oldArgs[i]);
|
|
377
|
-
if (changed) {
|
|
378
|
-
return { ...f, args: newArgs };
|
|
379
|
-
}
|
|
381
|
+
const newArgs = f.args.map((a) => a ? this.resolveFormulaRecursive(a, new Set(visited)) : a);
|
|
382
|
+
return { ...f, args: newArgs };
|
|
380
383
|
}
|
|
381
384
|
return f;
|
|
382
385
|
}
|
|
@@ -482,18 +485,13 @@ class Interpreter {
|
|
|
482
485
|
this.emit(`Formalizacion ${stmt.name}: ${stmt.passageName} -> ${(0, propositional_1.formulaToString)(formula)}`);
|
|
483
486
|
}
|
|
484
487
|
else if (stmt.letType === 'description') {
|
|
485
|
-
// Solo descripción textual: P es un átomo con significado semántico
|
|
486
488
|
this.letDescriptions.set(stmt.name, stmt.description);
|
|
487
489
|
this.emit(`Let ${stmt.name} = "${stmt.description}"`);
|
|
488
490
|
}
|
|
489
491
|
else if (stmt.letType === 'formula' && stmt.formula) {
|
|
490
|
-
// Resolver posibles variables anidadas en la propia definición
|
|
491
492
|
const resolved = this.resolveFormula(stmt.formula);
|
|
492
|
-
// Registrar como binding para sustitución futura
|
|
493
493
|
this.letBindings.set(stmt.name, resolved);
|
|
494
|
-
// También como axioma implícito para derivaciones
|
|
495
494
|
this.theory.axioms.set(stmt.name, resolved);
|
|
496
|
-
// Si tiene descripción textual (let X = "desc" : formula), guardarla
|
|
497
495
|
if ('description' in stmt && stmt.description) {
|
|
498
496
|
this.letDescriptions.set(stmt.name, stmt.description);
|
|
499
497
|
this.emit(`Let ${stmt.name} = "${stmt.description}" : ${(0, format_1.formulaToUnicode)(resolved)}`);
|
|
@@ -508,7 +506,6 @@ class Interpreter {
|
|
|
508
506
|
const formalization = stmt.formalization;
|
|
509
507
|
const diags = (0, compiler_1.registerClaim)(this.textLayer, stmt.name, formula, formalization);
|
|
510
508
|
this.diagnostics.push(...diags);
|
|
511
|
-
// También agregar al theory.claims
|
|
512
509
|
const claim = {
|
|
513
510
|
name: stmt.name,
|
|
514
511
|
formula: formula,
|
|
@@ -533,7 +530,6 @@ class Interpreter {
|
|
|
533
530
|
this.emit(`Context: ${stmt.claimName} = "${stmt.text}"`);
|
|
534
531
|
}
|
|
535
532
|
execRenderCmd(stmt) {
|
|
536
|
-
// Compilar claims y renderizar
|
|
537
533
|
const diags = (0, compiler_1.compileClaimsToTheory)(this.textLayer, this.theory);
|
|
538
534
|
this.diagnostics.push(...diags);
|
|
539
535
|
if (stmt.target === 'claims' || stmt.target === 'all') {
|
|
@@ -541,19 +537,15 @@ class Interpreter {
|
|
|
541
537
|
for (const [name, claim] of this.theory.claims) {
|
|
542
538
|
const fStr = claim.formula ? (0, format_1.formulaToUnicode)(claim.formula) : '(sin fórmula)';
|
|
543
539
|
this.emit(` Claim "${name}": ${fStr}`);
|
|
544
|
-
if (claim.support)
|
|
540
|
+
if (claim.support)
|
|
545
541
|
this.emit(` Soporte: ${claim.support}`);
|
|
546
|
-
|
|
547
|
-
if (claim.confidence !== undefined) {
|
|
542
|
+
if (claim.confidence !== undefined)
|
|
548
543
|
this.emit(` Confianza: ${claim.confidence}`);
|
|
549
|
-
|
|
550
|
-
if (claim.context) {
|
|
544
|
+
if (claim.context)
|
|
551
545
|
this.emit(` Contexto: ${claim.context}`);
|
|
552
|
-
}
|
|
553
546
|
}
|
|
554
|
-
if (this.theory.claims.size === 0)
|
|
547
|
+
if (this.theory.claims.size === 0)
|
|
555
548
|
this.emit(' (sin claims registrados)');
|
|
556
|
-
}
|
|
557
549
|
}
|
|
558
550
|
else if (stmt.target === 'theory') {
|
|
559
551
|
this.emit(`── Render: theory (${stmt.format}) ──`);
|
|
@@ -569,7 +561,6 @@ class Interpreter {
|
|
|
569
561
|
this.emit(` Claims: ${this.theory.claims.size}`);
|
|
570
562
|
}
|
|
571
563
|
else {
|
|
572
|
-
// Render un claim o axioma específico por nombre
|
|
573
564
|
const axiom = this.theory.axioms.get(stmt.target);
|
|
574
565
|
if (axiom) {
|
|
575
566
|
this.emit(` ${stmt.target} = ${(0, format_1.formulaToUnicode)(axiom)}`);
|
|
@@ -592,7 +583,6 @@ class Interpreter {
|
|
|
592
583
|
const pStr = premises.map((p) => (0, format_1.formulaToUnicode)(p)).join(', ');
|
|
593
584
|
const cStr = (0, format_1.formulaToUnicode)(conclusion);
|
|
594
585
|
if (fallacies.length === 0) {
|
|
595
|
-
// Check if the inference is valid
|
|
596
586
|
const conj = premises.length === 0
|
|
597
587
|
? conclusion
|
|
598
588
|
: premises.length === 1
|
|
@@ -608,13 +598,12 @@ class Interpreter {
|
|
|
608
598
|
this.emit(`⚠ [analyze] {${pStr}} → ${cStr}`);
|
|
609
599
|
this.emit(' Inferencia NO VÁLIDA — pero no corresponde a un patrón de falacia conocido');
|
|
610
600
|
}
|
|
611
|
-
|
|
601
|
+
this.results.push({
|
|
612
602
|
status: result.status,
|
|
613
603
|
output: result.output,
|
|
614
604
|
diagnostics: [],
|
|
615
605
|
formula: conclusion,
|
|
616
|
-
};
|
|
617
|
-
this.results.push(result2);
|
|
606
|
+
});
|
|
618
607
|
}
|
|
619
608
|
else {
|
|
620
609
|
this.emit(`⚠ [analyze] {${pStr}} → ${cStr}`);
|
|
@@ -624,7 +613,7 @@ class Interpreter {
|
|
|
624
613
|
if (f.pattern)
|
|
625
614
|
this.emit(` Patrón: ${f.pattern}`);
|
|
626
615
|
}
|
|
627
|
-
|
|
616
|
+
this.results.push({
|
|
628
617
|
status: 'invalid',
|
|
629
618
|
output: `Falacias detectadas: ${fallacies.map((f) => f.name).join(', ')}`,
|
|
630
619
|
diagnostics: fallacies.map((f) => ({
|
|
@@ -632,17 +621,14 @@ class Interpreter {
|
|
|
632
621
|
message: `Falacia: ${f.name} — ${f.description}`,
|
|
633
622
|
})),
|
|
634
623
|
formula: conclusion,
|
|
635
|
-
};
|
|
636
|
-
this.results.push(result);
|
|
624
|
+
});
|
|
637
625
|
}
|
|
638
626
|
}
|
|
639
627
|
execProofBlock(stmt) {
|
|
640
628
|
const profile = this.requireProfile();
|
|
641
|
-
// Guardar axiomas, letBindings y descriptions antes del bloque
|
|
642
629
|
const savedAxioms = new Map(this.theory.axioms);
|
|
643
630
|
const savedLetBindings = new Map(this.letBindings);
|
|
644
631
|
const savedLetDescriptions = new Map(this.letDescriptions);
|
|
645
|
-
// Registrar las asunciones como axiomas temporales (con resolución de variables)
|
|
646
632
|
this.emit('── Proof Block ──');
|
|
647
633
|
for (const assumption of stmt.assumptions) {
|
|
648
634
|
const resolved = this.resolveFormula(assumption.formula);
|
|
@@ -651,7 +637,6 @@ class Interpreter {
|
|
|
651
637
|
}
|
|
652
638
|
const resolvedGoal = this.resolveFormula(stmt.goal);
|
|
653
639
|
this.emit(` show ${(0, format_1.formulaToUnicode)(resolvedGoal)}`);
|
|
654
|
-
// Ejecutar body statements
|
|
655
640
|
for (const bodyStmt of stmt.body) {
|
|
656
641
|
try {
|
|
657
642
|
this.executeStatement(bodyStmt);
|
|
@@ -667,48 +652,36 @@ class Interpreter {
|
|
|
667
652
|
});
|
|
668
653
|
}
|
|
669
654
|
}
|
|
670
|
-
// Verificar que el goal es derivable de las asunciones
|
|
671
655
|
const premiseNames = stmt.assumptions.map((a) => a.name);
|
|
672
656
|
const result = profile.derive(resolvedGoal, premiseNames, this.theory);
|
|
673
657
|
this.results.push(result);
|
|
674
658
|
if (result.status === 'valid' || result.status === 'provable') {
|
|
675
659
|
this.emit(` ✓ QED — ${(0, format_1.formulaToUnicode)(resolvedGoal)} demostrado`);
|
|
676
|
-
// Registrar como teorema
|
|
677
660
|
const theoremName = `proof_${this.theory.theorems.size + 1}`;
|
|
678
|
-
// La implicación assumptions -> goal es un teorema
|
|
679
661
|
let implication = resolvedGoal;
|
|
680
662
|
for (let i = stmt.assumptions.length - 1; i >= 0; i--) {
|
|
681
|
-
implication = {
|
|
682
|
-
kind: 'implies',
|
|
683
|
-
args: [stmt.assumptions[i].formula, implication],
|
|
684
|
-
};
|
|
663
|
+
implication = { kind: 'implies', args: [stmt.assumptions[i].formula, implication] };
|
|
685
664
|
}
|
|
686
665
|
this.theory.theorems.set(theoremName, implication);
|
|
687
666
|
}
|
|
688
667
|
else {
|
|
689
668
|
this.emit(` ✗ QED fallido — no se pudo demostrar ${(0, format_1.formulaToUnicode)(resolvedGoal)}`);
|
|
690
669
|
}
|
|
691
|
-
// Restaurar axiomas, letBindings y descriptions (quitar las asunciones temporales)
|
|
692
670
|
this.theory.axioms = savedAxioms;
|
|
693
671
|
this.letBindings = savedLetBindings;
|
|
694
672
|
this.letDescriptions = savedLetDescriptions;
|
|
695
673
|
this.emit('── End Proof Block ──');
|
|
696
674
|
}
|
|
697
675
|
execTheoryDecl(stmt) {
|
|
698
|
-
const theoryName = stmt.name;
|
|
699
|
-
// Si tiene parámetros, es una plantilla (Clase)
|
|
700
676
|
if (stmt.params && stmt.params.length > 0) {
|
|
701
|
-
this.theoryTemplates.set(
|
|
702
|
-
this.emit(`Teoría (plantilla) ${
|
|
677
|
+
this.theoryTemplates.set(stmt.name, { node: stmt, parent: stmt.parent });
|
|
678
|
+
this.emit(`Teoría (plantilla) ${stmt.name}(${stmt.params.join(', ')}) declarada`);
|
|
703
679
|
return;
|
|
704
680
|
}
|
|
705
|
-
// Si no tiene parámetros, ejecutar como singleton/objeto inmediato
|
|
706
681
|
this.instantiateTheory(stmt);
|
|
707
682
|
}
|
|
708
|
-
/** Crea una instancia de una teoría y la registra en this.theories */
|
|
709
683
|
instantiateTheory(node, instanceName, args = []) {
|
|
710
684
|
const theoryName = instanceName || node.name;
|
|
711
|
-
// Crear scope vacío
|
|
712
685
|
const scope = {
|
|
713
686
|
name: theoryName,
|
|
714
687
|
parent: node.parent,
|
|
@@ -718,14 +691,10 @@ class Interpreter {
|
|
|
718
691
|
theorems: new Map(),
|
|
719
692
|
privateMembers: new Set(),
|
|
720
693
|
};
|
|
721
|
-
// HERENCIA: Si extends Parent, copiar bindings/axiomas/teoremas del padre
|
|
722
|
-
// El padre puede ser otra plantilla o un singleton ya instanciado
|
|
723
694
|
if (node.parent) {
|
|
724
695
|
const parentScope = this.theories.get(node.parent);
|
|
725
|
-
if (!parentScope)
|
|
726
|
-
throw new Error(`Teoría padre '${node.parent}' no encontrada
|
|
727
|
-
}
|
|
728
|
-
// Copiar todo del padre (no los miembros privados del padre al hijo)
|
|
696
|
+
if (!parentScope)
|
|
697
|
+
throw new Error(`Teoría padre '${node.parent}' no encontrada.`);
|
|
729
698
|
for (const [k, v] of parentScope.letBindings)
|
|
730
699
|
if (!parentScope.privateMembers.has(k))
|
|
731
700
|
scope.letBindings.set(k, v);
|
|
@@ -739,13 +708,11 @@ class Interpreter {
|
|
|
739
708
|
if (!parentScope.privateMembers.has(k))
|
|
740
709
|
scope.theorems.set(k, v);
|
|
741
710
|
}
|
|
742
|
-
// Guardar estado global antes de entrar al scope de la teoría
|
|
743
711
|
const savedLetBindings = new Map(this.letBindings);
|
|
744
712
|
const savedLetDescriptions = new Map(this.letDescriptions);
|
|
745
713
|
const savedAxioms = new Map(this.theory.axioms);
|
|
746
714
|
const savedTheorems = new Map(this.theory.theorems);
|
|
747
715
|
const savedTheoryName = this.currentTheoryName;
|
|
748
|
-
// Inyectar argumentos en el scope local de la teoría
|
|
749
716
|
if (node.params && args.length > 0) {
|
|
750
717
|
for (let i = 0; i < node.params.length; i++) {
|
|
751
718
|
if (i < args.length) {
|
|
@@ -755,7 +722,6 @@ class Interpreter {
|
|
|
755
722
|
}
|
|
756
723
|
}
|
|
757
724
|
}
|
|
758
|
-
// Inyectar bindings heredados al scope local para que los statements internos los vean
|
|
759
725
|
for (const [k, v] of scope.letBindings)
|
|
760
726
|
this.letBindings.set(k, v);
|
|
761
727
|
for (const [k, v] of scope.letDescriptions)
|
|
@@ -766,7 +732,6 @@ class Interpreter {
|
|
|
766
732
|
this.theory.theorems.set(k, v);
|
|
767
733
|
this.currentTheoryName = theoryName;
|
|
768
734
|
this.emit(`── Instanciando Theory ${theoryName} ──`);
|
|
769
|
-
// Ejecutar los miembros del body
|
|
770
735
|
for (const member of node.members) {
|
|
771
736
|
const memberStmt = member.statement;
|
|
772
737
|
const memberName = 'name' in memberStmt ? memberStmt.name : null;
|
|
@@ -786,7 +751,6 @@ class Interpreter {
|
|
|
786
751
|
});
|
|
787
752
|
}
|
|
788
753
|
}
|
|
789
|
-
// Capturar lo producido
|
|
790
754
|
for (const [k, v] of this.letBindings)
|
|
791
755
|
if (!savedLetBindings.has(k))
|
|
792
756
|
scope.letBindings.set(k, v);
|
|
@@ -799,25 +763,18 @@ class Interpreter {
|
|
|
799
763
|
for (const [k, v] of this.theory.theorems)
|
|
800
764
|
if (!savedTheorems.has(k))
|
|
801
765
|
scope.theorems.set(k, v);
|
|
802
|
-
// Restaurar estado global
|
|
803
766
|
this.letBindings = savedLetBindings;
|
|
804
767
|
this.letDescriptions = savedLetDescriptions;
|
|
805
768
|
this.theory.axioms = savedAxioms;
|
|
806
769
|
this.theory.theorems = savedTheorems;
|
|
807
770
|
this.currentTheoryName = savedTheoryName;
|
|
808
|
-
// Registrar la instancia
|
|
809
771
|
this.theories.set(theoryName, scope);
|
|
810
772
|
this.emit(`── End Theory Instance ${theoryName} ──`);
|
|
811
773
|
return theoryName;
|
|
812
774
|
}
|
|
813
|
-
// =============================================
|
|
814
|
-
// Control flow & funciones (v1.5.8)
|
|
815
|
-
// =============================================
|
|
816
775
|
execPrintCmd(stmt) {
|
|
817
|
-
if (stmt.value !== null)
|
|
818
|
-
// String literal
|
|
776
|
+
if (stmt.value !== null)
|
|
819
777
|
this.emit(stmt.value);
|
|
820
|
-
}
|
|
821
778
|
else if (stmt.formula) {
|
|
822
779
|
const resolved = this.resolveFormula(stmt.formula);
|
|
823
780
|
this.emit((0, format_1.formulaToUnicode)(resolved));
|
|
@@ -826,7 +783,6 @@ class Interpreter {
|
|
|
826
783
|
execSetCmd(stmt) {
|
|
827
784
|
const resolved = this.resolveFormula(stmt.formula);
|
|
828
785
|
this.letBindings.set(stmt.name, resolved);
|
|
829
|
-
// También actualizar en la teoría global para que derive/prove lo vean
|
|
830
786
|
this.theory.axioms.set(stmt.name, resolved);
|
|
831
787
|
this.emit(`Set ${stmt.name} = ${(0, format_1.formulaToUnicode)(resolved)}`);
|
|
832
788
|
}
|
|
@@ -841,7 +797,6 @@ class Interpreter {
|
|
|
841
797
|
branch.condition === 'valid' ? result.status === 'valid' : result.status !== 'valid';
|
|
842
798
|
}
|
|
843
799
|
else {
|
|
844
|
-
// satisfiable / unsatisfiable
|
|
845
800
|
const result = profile.checkSatisfiable(resolved);
|
|
846
801
|
matched =
|
|
847
802
|
branch.condition === 'satisfiable'
|
|
@@ -854,10 +809,9 @@ class Interpreter {
|
|
|
854
809
|
return;
|
|
855
810
|
this.executeStatement(bodyStmt);
|
|
856
811
|
}
|
|
857
|
-
return;
|
|
812
|
+
return;
|
|
858
813
|
}
|
|
859
814
|
}
|
|
860
|
-
// else branch
|
|
861
815
|
if (stmt.elseBranch) {
|
|
862
816
|
for (const bodyStmt of stmt.elseBranch) {
|
|
863
817
|
if (this.returnSignal)
|
|
@@ -879,13 +833,10 @@ class Interpreter {
|
|
|
879
833
|
this.executeStatement(bodyStmt);
|
|
880
834
|
}
|
|
881
835
|
}
|
|
882
|
-
|
|
883
|
-
if (savedBinding !== undefined) {
|
|
836
|
+
if (savedBinding !== undefined)
|
|
884
837
|
this.letBindings.set(stmt.variable, savedBinding);
|
|
885
|
-
|
|
886
|
-
else {
|
|
838
|
+
else
|
|
887
839
|
this.letBindings.delete(stmt.variable);
|
|
888
|
-
}
|
|
889
840
|
}
|
|
890
841
|
execWhileStmt(stmt) {
|
|
891
842
|
const profile = this.requireProfile();
|
|
@@ -930,150 +881,155 @@ class Interpreter {
|
|
|
930
881
|
execFnDecl(stmt) {
|
|
931
882
|
const name = this.currentTheoryName ? `${this.currentTheoryName}.${stmt.name}` : stmt.name;
|
|
932
883
|
this.functions.set(name, stmt);
|
|
933
|
-
if (this.currentTheoryName)
|
|
884
|
+
if (this.currentTheoryName)
|
|
934
885
|
this.emit(`Función de instancia ${name}(${stmt.params.join(', ')}) declarada`);
|
|
935
|
-
|
|
936
|
-
else {
|
|
886
|
+
else
|
|
937
887
|
this.emit(`Función ${name}(${stmt.params.join(', ')}) declarada`);
|
|
938
|
-
}
|
|
939
888
|
}
|
|
940
889
|
execReturnStmt(stmt) {
|
|
941
|
-
if (stmt.formula)
|
|
890
|
+
if (stmt.formula)
|
|
942
891
|
this.returnValue = this.resolveFormula(stmt.formula);
|
|
943
|
-
|
|
944
|
-
else {
|
|
892
|
+
else
|
|
945
893
|
this.returnValue = undefined;
|
|
946
|
-
}
|
|
947
894
|
this.returnSignal = true;
|
|
948
895
|
}
|
|
949
896
|
executeFnCall(stmt) {
|
|
950
|
-
|
|
951
|
-
if (
|
|
952
|
-
|
|
897
|
+
this.callDepth++;
|
|
898
|
+
if (this.callDepth > MAX_CALL_DEPTH) {
|
|
899
|
+
this.callDepth--;
|
|
900
|
+
throw new Error(`Límite de recursión excedido (${MAX_CALL_DEPTH}).`);
|
|
953
901
|
}
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
let actualInstanceName = prefix;
|
|
958
|
-
// Intentar resolver prefijo si es una variable local
|
|
959
|
-
if (this.letBindings.has(prefix)) {
|
|
960
|
-
const resolved = this.letBindings.get(prefix);
|
|
961
|
-
if (resolved.kind === 'atom' && resolved.name) {
|
|
962
|
-
actualInstanceName = resolved.name;
|
|
963
|
-
}
|
|
902
|
+
try {
|
|
903
|
+
if (['typeof', 'is_valid', 'is_satisfiable', 'get_atoms', 'input'].includes(stmt.name)) {
|
|
904
|
+
return this.executeBuiltin(stmt.name, stmt.args);
|
|
964
905
|
}
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
906
|
+
if (stmt.name.includes('.')) {
|
|
907
|
+
const [prefix, methodName] = stmt.name.split('.', 2);
|
|
908
|
+
let actualInstanceName = prefix;
|
|
909
|
+
if (this.letBindings.has(prefix)) {
|
|
910
|
+
const resolved = this.letBindings.get(prefix);
|
|
911
|
+
if (resolved.kind === 'atom' && resolved.name)
|
|
912
|
+
actualInstanceName = resolved.name;
|
|
913
|
+
}
|
|
914
|
+
const scope = this.theories.get(actualInstanceName);
|
|
915
|
+
if (scope) {
|
|
916
|
+
const internalFnName = `${actualInstanceName}.${methodName}`;
|
|
917
|
+
const fn = this.functions.get(internalFnName);
|
|
918
|
+
if (fn)
|
|
919
|
+
return this.executeFunctionInScope(fn, stmt.args, scope);
|
|
976
920
|
}
|
|
977
921
|
}
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
const instanceId = `inst_${stmt.name}_${this.theories.size}`;
|
|
984
|
-
this.instantiateTheory(template.node, instanceId, stmt.args);
|
|
985
|
-
return { kind: 'atom', name: instanceId };
|
|
986
|
-
}
|
|
987
|
-
// 2. Intentar llamada a función normal
|
|
988
|
-
const fn = this.functions.get(stmt.name);
|
|
989
|
-
if (!fn) {
|
|
990
|
-
throw new Error(`Función o Teoría '${stmt.name}' no declarada`);
|
|
991
|
-
}
|
|
992
|
-
if (stmt.args.length !== fn.params.length) {
|
|
993
|
-
throw new Error(`Función '${stmt.name}' espera ${fn.params.length} argumento(s), recibió ${stmt.args.length}`);
|
|
994
|
-
}
|
|
995
|
-
// Guardar bindings actuales de los parámetros
|
|
996
|
-
const savedBindings = new Map();
|
|
997
|
-
for (const param of fn.params) {
|
|
998
|
-
savedBindings.set(param, this.letBindings.get(param));
|
|
999
|
-
}
|
|
1000
|
-
// Vincular argumentos a parámetros
|
|
1001
|
-
for (let i = 0; i < fn.params.length; i++) {
|
|
1002
|
-
const resolved = this.resolveFormula(stmt.args[i]);
|
|
1003
|
-
this.letBindings.set(fn.params[i], resolved);
|
|
1004
|
-
}
|
|
1005
|
-
// Ejecutar cuerpo de la función
|
|
1006
|
-
const savedReturnSignal = this.returnSignal;
|
|
1007
|
-
const savedReturnValue = this.returnValue;
|
|
1008
|
-
this.returnSignal = false;
|
|
1009
|
-
this.returnValue = undefined;
|
|
1010
|
-
for (const bodyStmt of fn.body) {
|
|
1011
|
-
if (this.returnSignal)
|
|
1012
|
-
break;
|
|
1013
|
-
this.executeStatement(bodyStmt);
|
|
1014
|
-
}
|
|
1015
|
-
// Capturar valor de retorno
|
|
1016
|
-
const result = this.returnValue;
|
|
1017
|
-
this.returnSignal = savedReturnSignal;
|
|
1018
|
-
this.returnValue = savedReturnValue;
|
|
1019
|
-
// Restaurar bindings
|
|
1020
|
-
for (const param of fn.params) {
|
|
1021
|
-
const prev = savedBindings.get(param);
|
|
1022
|
-
if (prev !== undefined) {
|
|
1023
|
-
this.letBindings.set(param, prev);
|
|
922
|
+
const template = this.theoryTemplates.get(stmt.name);
|
|
923
|
+
if (template) {
|
|
924
|
+
const instanceId = `inst_${stmt.name}_${this.theories.size}`;
|
|
925
|
+
this.instantiateTheory(template.node, instanceId, stmt.args);
|
|
926
|
+
return { kind: 'atom', name: instanceId };
|
|
1024
927
|
}
|
|
1025
|
-
|
|
1026
|
-
|
|
928
|
+
const fn = this.functions.get(stmt.name);
|
|
929
|
+
if (!fn)
|
|
930
|
+
throw new Error(`Función o Teoría '${stmt.name}' no declarada`);
|
|
931
|
+
if (stmt.args.length !== fn.params.length)
|
|
932
|
+
throw new Error(`Argumentos incorrectos.`);
|
|
933
|
+
const savedBindings = new Map();
|
|
934
|
+
for (const param of fn.params)
|
|
935
|
+
savedBindings.set(param, this.letBindings.get(param));
|
|
936
|
+
for (let i = 0; i < fn.params.length; i++)
|
|
937
|
+
this.letBindings.set(fn.params[i], this.resolveFormula(stmt.args[i]));
|
|
938
|
+
const savedReturnSignal = this.returnSignal;
|
|
939
|
+
const savedReturnValue = this.returnValue;
|
|
940
|
+
this.returnSignal = false;
|
|
941
|
+
this.returnValue = undefined;
|
|
942
|
+
for (const bodyStmt of fn.body) {
|
|
943
|
+
if (this.returnSignal)
|
|
944
|
+
break;
|
|
945
|
+
this.executeStatement(bodyStmt);
|
|
1027
946
|
}
|
|
947
|
+
const result = this.returnValue;
|
|
948
|
+
this.returnSignal = savedReturnSignal;
|
|
949
|
+
this.returnValue = savedReturnValue;
|
|
950
|
+
for (const param of fn.params) {
|
|
951
|
+
const prev = savedBindings.get(param);
|
|
952
|
+
if (prev !== undefined)
|
|
953
|
+
this.letBindings.set(param, prev);
|
|
954
|
+
else
|
|
955
|
+
this.letBindings.delete(param);
|
|
956
|
+
}
|
|
957
|
+
return result;
|
|
958
|
+
}
|
|
959
|
+
finally {
|
|
960
|
+
this.callDepth--;
|
|
1028
961
|
}
|
|
1029
|
-
return result;
|
|
1030
962
|
}
|
|
1031
|
-
/** Ejecuta una función inyectando bindings de un scope (para métodos de instancia) */
|
|
1032
963
|
executeFunctionInScope(fn, args, scope) {
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
const savedBindings = new Map(this.letBindings);
|
|
1038
|
-
const savedAxioms = new Map(this.theory.axioms);
|
|
1039
|
-
const savedTheorems = new Map(this.theory.theorems);
|
|
1040
|
-
const savedTheoryName = this.currentTheoryName;
|
|
1041
|
-
// 2. Inyectar scope de la instancia
|
|
1042
|
-
for (const [k, v] of scope.letBindings)
|
|
1043
|
-
this.letBindings.set(k, v);
|
|
1044
|
-
for (const [k, v] of scope.axioms)
|
|
1045
|
-
this.theory.axioms.set(k, v);
|
|
1046
|
-
for (const [k, v] of scope.theorems)
|
|
1047
|
-
this.theory.theorems.set(k, v);
|
|
1048
|
-
this.currentTheoryName = scope.name;
|
|
1049
|
-
// 3. Inyectar argumentos de la llamada
|
|
1050
|
-
for (let i = 0; i < fn.params.length; i++) {
|
|
1051
|
-
this.letBindings.set(fn.params[i], this.resolveFormula(args[i]));
|
|
964
|
+
this.callDepth++;
|
|
965
|
+
if (this.callDepth > MAX_CALL_DEPTH) {
|
|
966
|
+
this.callDepth--;
|
|
967
|
+
throw new Error(`Límite de recursión excedido.`);
|
|
1052
968
|
}
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
969
|
+
try {
|
|
970
|
+
const savedBindings = new Map(this.letBindings);
|
|
971
|
+
const savedAxioms = new Map(this.theory.axioms);
|
|
972
|
+
const savedTheorems = new Map(this.theory.theorems);
|
|
973
|
+
const savedTheoryName = this.currentTheoryName;
|
|
974
|
+
for (const [k, v] of scope.letBindings)
|
|
975
|
+
this.letBindings.set(k, v);
|
|
976
|
+
for (const [k, v] of scope.axioms)
|
|
977
|
+
this.theory.axioms.set(k, v);
|
|
978
|
+
for (const [k, v] of scope.theorems)
|
|
979
|
+
this.theory.theorems.set(k, v);
|
|
980
|
+
this.currentTheoryName = scope.name;
|
|
981
|
+
for (let i = 0; i < fn.params.length; i++)
|
|
982
|
+
this.letBindings.set(fn.params[i], this.resolveFormula(args[i]));
|
|
983
|
+
const savedReturnSignal = this.returnSignal;
|
|
984
|
+
const savedReturnValue = this.returnValue;
|
|
985
|
+
this.returnSignal = false;
|
|
986
|
+
this.returnValue = undefined;
|
|
987
|
+
for (const bodyStmt of fn.body) {
|
|
988
|
+
if (this.returnSignal)
|
|
989
|
+
break;
|
|
990
|
+
this.executeStatement(bodyStmt);
|
|
991
|
+
}
|
|
992
|
+
const result = this.returnValue;
|
|
993
|
+
this.returnSignal = savedReturnSignal;
|
|
994
|
+
this.returnValue = savedReturnValue;
|
|
995
|
+
this.letBindings = savedBindings;
|
|
996
|
+
this.theory.axioms = savedAxioms;
|
|
997
|
+
this.theory.theorems = savedTheorems;
|
|
998
|
+
this.currentTheoryName = savedTheoryName;
|
|
999
|
+
return result;
|
|
1000
|
+
}
|
|
1001
|
+
finally {
|
|
1002
|
+
this.callDepth--;
|
|
1062
1003
|
}
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1004
|
+
}
|
|
1005
|
+
tryConstantFold(f) {
|
|
1006
|
+
if (!f || !f.args)
|
|
1007
|
+
return f;
|
|
1008
|
+
const ARITH_OPS = {
|
|
1009
|
+
add: (a, b) => a + b,
|
|
1010
|
+
subtract: (a, b) => a - b,
|
|
1011
|
+
multiply: (a, b) => a * b,
|
|
1012
|
+
divide: (a, b) => (b === 0 ? NaN : a / b),
|
|
1013
|
+
modulo: (a, b) => (b === 0 ? NaN : a % b),
|
|
1014
|
+
};
|
|
1015
|
+
const CMP_OPS = {
|
|
1016
|
+
less: (a, b) => (a < b ? 1 : 0),
|
|
1017
|
+
greater: (a, b) => (a > b ? 1 : 0),
|
|
1018
|
+
less_eq: (a, b) => (a <= b ? 1 : 0),
|
|
1019
|
+
greater_eq: (a, b) => (a >= b ? 1 : 0),
|
|
1020
|
+
};
|
|
1021
|
+
const newArgs = f.args.map((a) => this.tryConstantFold(a));
|
|
1022
|
+
if (newArgs.length === 2 &&
|
|
1023
|
+
newArgs[0].kind === 'number' &&
|
|
1024
|
+
newArgs[1].kind === 'number' &&
|
|
1025
|
+
(ARITH_OPS[f.kind] || CMP_OPS[f.kind])) {
|
|
1026
|
+
const op = ARITH_OPS[f.kind] || CMP_OPS[f.kind];
|
|
1027
|
+
const res = op(newArgs[0].value ?? 0, newArgs[1].value ?? 0);
|
|
1028
|
+
return { kind: 'number', value: res, source: f.source };
|
|
1029
|
+
}
|
|
1030
|
+
return { ...f, args: newArgs };
|
|
1072
1031
|
}
|
|
1073
1032
|
executeBuiltin(name, args) {
|
|
1074
|
-
if (args.length !== 1) {
|
|
1075
|
-
throw new Error(`Built-in '${name}' espera exactamente 1 argumento, recibió ${args.length}`);
|
|
1076
|
-
}
|
|
1077
1033
|
const arg = this.resolveFormula(args[0]);
|
|
1078
1034
|
if (name === 'typeof') {
|
|
1079
1035
|
let typeStr = 'Formula';
|
|
@@ -1105,12 +1061,12 @@ class Interpreter {
|
|
|
1105
1061
|
let inputStr;
|
|
1106
1062
|
try {
|
|
1107
1063
|
process.stdout.write(prompt + ' ');
|
|
1108
|
-
|
|
1064
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment
|
|
1109
1065
|
const fs = require('fs');
|
|
1110
1066
|
const buf = Buffer.alloc(256);
|
|
1067
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
|
|
1111
1068
|
const bytesRead = fs.readSync(process.stdin.fd, buf, 0, 256, null);
|
|
1112
1069
|
inputStr = buf.toString('utf8', 0, bytesRead).trim();
|
|
1113
|
-
/* eslint-enable @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-argument */
|
|
1114
1070
|
}
|
|
1115
1071
|
catch {
|
|
1116
1072
|
inputStr = 'interactive_not_supported';
|
|
@@ -1121,68 +1077,45 @@ class Interpreter {
|
|
|
1121
1077
|
}
|
|
1122
1078
|
execImportDecl(stmt) {
|
|
1123
1079
|
let filePath = stmt.path;
|
|
1124
|
-
// Agregar extensión .st si no la tiene
|
|
1125
1080
|
if (!filePath.endsWith('.st'))
|
|
1126
1081
|
filePath += '.st';
|
|
1127
|
-
|
|
1128
|
-
if (this.importedFiles.has(filePath)) {
|
|
1129
|
-
this.emit(`Import: ${filePath} (ya importado, saltar)`);
|
|
1082
|
+
if (this.importedFiles.has(filePath))
|
|
1130
1083
|
return;
|
|
1131
|
-
}
|
|
1132
1084
|
this.importedFiles.add(filePath);
|
|
1133
|
-
// Intentar leer el archivo (solo funciona en Node.js / CLI)
|
|
1134
1085
|
let source;
|
|
1135
1086
|
try {
|
|
1136
|
-
|
|
1087
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment
|
|
1137
1088
|
const fs = require('fs');
|
|
1089
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment
|
|
1138
1090
|
const path = require('path');
|
|
1139
|
-
//
|
|
1091
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
|
|
1140
1092
|
const resolved = path.isAbsolute(filePath)
|
|
1141
1093
|
? filePath
|
|
1142
|
-
:
|
|
1094
|
+
: // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
|
|
1095
|
+
path.resolve(path.dirname(stmt.source.file || '.'), filePath);
|
|
1096
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
|
|
1143
1097
|
source = fs.readFileSync(resolved, 'utf-8');
|
|
1144
|
-
/* eslint-enable @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call */
|
|
1145
1098
|
}
|
|
1146
1099
|
catch {
|
|
1147
|
-
throw new Error(`No se pudo importar '${filePath}'
|
|
1100
|
+
throw new Error(`No se pudo importar '${filePath}'`);
|
|
1148
1101
|
}
|
|
1149
1102
|
const parser = new parser_1.Parser(filePath);
|
|
1150
1103
|
const program = parser.parse(source);
|
|
1151
1104
|
this.diagnostics.push(...parser.diagnostics);
|
|
1152
|
-
if (parser.diagnostics.some((d) => d.severity === 'error')) {
|
|
1153
|
-
throw new Error(`Errores de parseo en '${filePath}'`);
|
|
1154
|
-
}
|
|
1155
|
-
// Guardar estado de exportación del importador
|
|
1156
1105
|
const prevIsImporting = this.isImporting;
|
|
1157
|
-
const prevExportedBindings = new Map(this.exportedBindings);
|
|
1158
|
-
const prevExportedAxioms = new Map(this.exportedAxioms);
|
|
1159
|
-
const prevExportedTheorems = new Map(this.exportedTheorems);
|
|
1160
|
-
const prevExportedFunctions = new Map(this.exportedFunctions);
|
|
1161
|
-
const prevExportedTheories = new Map(this.exportedTheories);
|
|
1162
|
-
// Guardar scope local actual del importador para no contaminarlo durante la carga
|
|
1163
1106
|
const prevLetBindings = new Map(this.letBindings);
|
|
1164
1107
|
const prevAxioms = new Map(this.theory.axioms);
|
|
1165
1108
|
const prevTheorems = new Map(this.theory.theorems);
|
|
1166
1109
|
const prevFunctions = new Map(this.functions);
|
|
1167
1110
|
const prevTheories = new Map(this.theories);
|
|
1168
|
-
// Limpiar para capturar solo lo que exporta el archivo importado
|
|
1169
1111
|
this.isImporting = true;
|
|
1170
|
-
this.exportedBindings.clear();
|
|
1171
|
-
this.exportedAxioms.clear();
|
|
1172
|
-
this.exportedTheorems.clear();
|
|
1173
|
-
this.exportedFunctions.clear();
|
|
1174
|
-
this.exportedTheories.clear();
|
|
1175
|
-
// No queremos que el archivo importado vea el scope del importador (encapsulamiento total)
|
|
1176
1112
|
this.letBindings.clear();
|
|
1177
1113
|
this.theory.axioms.clear();
|
|
1178
1114
|
this.theory.theorems.clear();
|
|
1179
1115
|
this.functions.clear();
|
|
1180
1116
|
this.theories.clear();
|
|
1181
|
-
|
|
1182
|
-
for (const importedStmt of program.statements) {
|
|
1117
|
+
for (const importedStmt of program.statements)
|
|
1183
1118
|
this.executeStatement(importedStmt);
|
|
1184
|
-
}
|
|
1185
|
-
// Capturar lo exportado
|
|
1186
1119
|
const newExports = {
|
|
1187
1120
|
bindings: new Map(this.exportedBindings),
|
|
1188
1121
|
axioms: new Map(this.exportedAxioms),
|
|
@@ -1190,19 +1123,12 @@ class Interpreter {
|
|
|
1190
1123
|
functions: new Map(this.exportedFunctions),
|
|
1191
1124
|
theories: new Map(this.exportedTheories),
|
|
1192
1125
|
};
|
|
1193
|
-
// Restaurar estado del importador (incluyendo su scope original)
|
|
1194
1126
|
this.isImporting = prevIsImporting;
|
|
1195
|
-
this.exportedBindings = prevExportedBindings;
|
|
1196
|
-
this.exportedAxioms = prevExportedAxioms;
|
|
1197
|
-
this.exportedTheorems = prevExportedTheorems;
|
|
1198
|
-
this.exportedFunctions = prevExportedFunctions;
|
|
1199
|
-
this.exportedTheories = prevExportedTheories;
|
|
1200
1127
|
this.letBindings = prevLetBindings;
|
|
1201
1128
|
this.theory.axioms = prevAxioms;
|
|
1202
1129
|
this.theory.theorems = prevTheorems;
|
|
1203
1130
|
this.functions = prevFunctions;
|
|
1204
1131
|
this.theories = prevTheories;
|
|
1205
|
-
// Fusionar solo lo exportado al scope actual
|
|
1206
1132
|
for (const [k, v] of newExports.bindings)
|
|
1207
1133
|
this.letBindings.set(k, v);
|
|
1208
1134
|
for (const [k, v] of newExports.axioms)
|
|
@@ -1213,7 +1139,6 @@ class Interpreter {
|
|
|
1213
1139
|
this.functions.set(k, v);
|
|
1214
1140
|
for (const [k, v] of newExports.theories)
|
|
1215
1141
|
this.theories.set(k, v);
|
|
1216
|
-
this.emit(`Import: ${filePath} cargado (${newExports.bindings.size + newExports.axioms.size + newExports.theorems.size + newExports.functions.size + newExports.theories.size} elementos importados)`);
|
|
1217
1142
|
}
|
|
1218
1143
|
execExplainCmd(stmt) {
|
|
1219
1144
|
const profile = this.requireProfile();
|
|
@@ -1223,160 +1148,39 @@ class Interpreter {
|
|
|
1223
1148
|
if (result.output)
|
|
1224
1149
|
this.emit(result.output);
|
|
1225
1150
|
}
|
|
1226
|
-
// --- Output helpers ---
|
|
1227
1151
|
emit(msg) {
|
|
1228
1152
|
this.stdoutLines.push(msg);
|
|
1229
1153
|
}
|
|
1230
1154
|
getVerbosity() {
|
|
1231
1155
|
const v = this.letBindings.get('verbose');
|
|
1232
|
-
if (v && v.kind === 'atom' && v.name)
|
|
1233
|
-
|
|
1234
|
-
// Remove quotes if present
|
|
1235
|
-
return n.replace(/(^"|"$)/g, '');
|
|
1236
|
-
}
|
|
1156
|
+
if (v && v.kind === 'atom' && v.name)
|
|
1157
|
+
return v.name.toLowerCase().replace(/(^"|"$)/g, '');
|
|
1237
1158
|
return 'off';
|
|
1238
1159
|
}
|
|
1239
1160
|
emitResult(cmd, result) {
|
|
1240
|
-
|
|
1241
|
-
this.emit(`${statusIcon} [${cmd}] ${result.output || result.status}`);
|
|
1161
|
+
this.emit(`${this.statusIcon(result.status)} [${cmd}] ${result.output || result.status}`);
|
|
1242
1162
|
const verbosity = this.getVerbosity();
|
|
1243
|
-
if (result.
|
|
1244
|
-
this.emit(` Nota pedagógica: ${result.educationalNote}`);
|
|
1245
|
-
}
|
|
1246
|
-
if (result.paradoxWarning) {
|
|
1163
|
+
if (result.paradoxWarning)
|
|
1247
1164
|
this.emit(` ⚠ PARADOJA: ${result.paradoxWarning}`);
|
|
1248
|
-
}
|
|
1249
|
-
// Clasificación de la fórmula (si tenemos verbosidad on o si está precalculado)
|
|
1250
1165
|
if (result.formula && (verbosity === 'on' || result.formulaClassification)) {
|
|
1251
1166
|
const cls = (0, formula_classifier_1.classifyFormula)(result.formula);
|
|
1252
1167
|
const name = result.formulaClassification || cls.formulaClassification;
|
|
1253
|
-
if (name)
|
|
1168
|
+
if (name)
|
|
1254
1169
|
this.emit(` Identificación: ${name}`);
|
|
1255
|
-
}
|
|
1256
|
-
}
|
|
1257
|
-
if (result.reasoningType) {
|
|
1258
|
-
this.emit(` Patrón de razonamiento: ${result.reasoningType}`);
|
|
1259
|
-
}
|
|
1260
|
-
if (result.reasoningSchema) {
|
|
1261
|
-
this.emit(` Esquema: ${result.reasoningSchema}`);
|
|
1262
|
-
}
|
|
1263
|
-
if (result.normalForms && verbosity === 'on') {
|
|
1264
|
-
this.emit(` Formas Normales:`);
|
|
1265
|
-
if (result.normalForms.nnf)
|
|
1266
|
-
this.emit(` NNF: ${result.normalForms.nnf}`);
|
|
1267
|
-
if (result.normalForms.cnf)
|
|
1268
|
-
this.emit(` CNF: ${result.normalForms.cnf}`);
|
|
1269
|
-
if (result.normalForms.dnf)
|
|
1270
|
-
this.emit(` DNF: ${result.normalForms.dnf}`);
|
|
1271
|
-
if (result.normalForms.pnf)
|
|
1272
|
-
this.emit(` PNF: ${result.normalForms.pnf}`);
|
|
1273
|
-
if (result.normalForms.skolem)
|
|
1274
|
-
this.emit(` Skolem: ${result.normalForms.skolem}`);
|
|
1275
|
-
}
|
|
1276
|
-
if (result.formula && (verbosity === 'on' || cmd === 'explain')) {
|
|
1277
|
-
/* eslint-disable @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call */
|
|
1278
|
-
const { compareAcrossSystems } = require('./cross-system-compare');
|
|
1279
|
-
const { registry } = require('../profiles/interface');
|
|
1280
|
-
const comp = result.crossSystemComparison || compareAcrossSystems(result.formula, registry);
|
|
1281
|
-
/* eslint-enable @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call */
|
|
1282
|
-
if (Object.keys(comp).length > 0) {
|
|
1283
|
-
this.emit(` Comparación entre sistemas:`);
|
|
1284
|
-
for (const [sys, val] of Object.entries(comp)) {
|
|
1285
|
-
this.emit(` ${sys.padEnd(30)} ${val}`);
|
|
1286
|
-
}
|
|
1287
|
-
}
|
|
1288
1170
|
}
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
proof.steps.length > 0 &&
|
|
1295
|
-
(verbosity === 'on' || verbosity === 'proof' || cmd === 'derive' || cmd === 'prove')) {
|
|
1296
|
-
if (isLatex) {
|
|
1297
|
-
/* eslint-disable @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call */
|
|
1298
|
-
const { proofToLaTeX } = require('./format');
|
|
1299
|
-
this.emit(' Prueba (LaTeX):');
|
|
1300
|
-
this.emit(proofToLaTeX(proof));
|
|
1301
|
-
/* eslint-enable @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call */
|
|
1302
|
-
}
|
|
1303
|
-
else {
|
|
1304
|
-
this.emit(' Prueba:');
|
|
1305
|
-
for (const step of proof.steps) {
|
|
1306
|
-
const premisesStr = step.premises.length > 0 ? ` [de ${step.premises.join(', ')}]` : '';
|
|
1307
|
-
this.emit(` ${step.stepNumber}. ${(0, format_1.formulaToUnicode)(step.formula)} — ${step.justification}${premisesStr}`);
|
|
1308
|
-
}
|
|
1171
|
+
if (result.model && (verbosity === 'on' || cmd === 'countermodel')) {
|
|
1172
|
+
if (result.model.valuation) {
|
|
1173
|
+
this.emit(' Modelo:');
|
|
1174
|
+
for (const [k, v] of Object.entries(result.model.valuation))
|
|
1175
|
+
this.emit(` ${k} = ${v}`);
|
|
1309
1176
|
}
|
|
1310
1177
|
}
|
|
1311
|
-
|
|
1312
|
-
if (model &&
|
|
1313
|
-
(verbosity === 'on' ||
|
|
1314
|
-
verbosity === 'model' ||
|
|
1315
|
-
cmd === 'countermodel' ||
|
|
1316
|
-
cmd === 'check_valid' ||
|
|
1317
|
-
cmd === 'check_satisfiable')) {
|
|
1318
|
-
// Display Kripke model with worlds if available
|
|
1319
|
-
if (model.worlds && model.worlds.length > 0) {
|
|
1320
|
-
this.emit(' Contramodelo Kripke:');
|
|
1321
|
-
this.emit(` Mundos: {${model.worlds.map((w) => w.name).join(', ')}}`);
|
|
1322
|
-
this.emit(' Accesibilidad:');
|
|
1323
|
-
for (const w of model.worlds) {
|
|
1324
|
-
if (w.accessible.length > 0) {
|
|
1325
|
-
this.emit(` ${w.name} R ${w.accessible.join(', ')}`);
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
|
-
this.emit(' Valuación:');
|
|
1329
|
-
for (const w of model.worlds) {
|
|
1330
|
-
const trueAtoms = Object.entries(w.valuation)
|
|
1331
|
-
.filter(([, val]) => val)
|
|
1332
|
-
.map(([k]) => k);
|
|
1333
|
-
const falseAtoms = Object.entries(w.valuation)
|
|
1334
|
-
.filter(([, val]) => !val)
|
|
1335
|
-
.map(([k]) => k);
|
|
1336
|
-
const parts = [];
|
|
1337
|
-
if (trueAtoms.length > 0)
|
|
1338
|
-
parts.push(trueAtoms.join(', '));
|
|
1339
|
-
if (falseAtoms.length > 0)
|
|
1340
|
-
parts.push(`¬${falseAtoms.join(', ¬')}`);
|
|
1341
|
-
this.emit(` V(${w.name}) = {${parts.join(', ')}}`);
|
|
1342
|
-
}
|
|
1343
|
-
}
|
|
1344
|
-
else if (model.valuation) {
|
|
1345
|
-
// Flat propositional model
|
|
1346
|
-
this.emit(' Modelo / Valuación:');
|
|
1347
|
-
for (const [k, v] of Object.entries(model.valuation)) {
|
|
1348
|
-
const desc = this.letDescriptions.get(k);
|
|
1349
|
-
const descStr = desc ? ` ("${desc}")` : '';
|
|
1350
|
-
this.emit(` ${k}${descStr} = ${String(v)}`);
|
|
1351
|
-
}
|
|
1352
|
-
}
|
|
1353
|
-
}
|
|
1354
|
-
if (result.tableauTrace &&
|
|
1355
|
-
result.tableauTrace.length > 0 &&
|
|
1356
|
-
(verbosity === 'on' ||
|
|
1357
|
-
verbosity === 'proof' ||
|
|
1358
|
-
cmd === 'check valid' ||
|
|
1359
|
-
cmd === 'check satisfiable' ||
|
|
1360
|
-
cmd === 'check equivalent')) {
|
|
1178
|
+
if (result.tableauTrace && result.tableauTrace.length > 0 && verbosity === 'on') {
|
|
1361
1179
|
this.emit(' Traza del tableau:');
|
|
1362
|
-
for (let i = 0; i < result.tableauTrace.length; i++)
|
|
1363
|
-
|
|
1364
|
-
this.emit(` ${i + 1}. ${step.toString ? step.toString() : String(step)}`);
|
|
1365
|
-
}
|
|
1366
|
-
}
|
|
1367
|
-
// Mostrar leyenda de variables con descripción si hay alguna relevante
|
|
1368
|
-
if (this.letDescriptions.size > 0 && result.formula) {
|
|
1369
|
-
const atoms = this.collectAtoms(result.formula);
|
|
1370
|
-
const relevantDescs = atoms.filter((a) => this.letDescriptions.has(a));
|
|
1371
|
-
if (relevantDescs.length > 0) {
|
|
1372
|
-
this.emit(' Donde:');
|
|
1373
|
-
for (const a of relevantDescs) {
|
|
1374
|
-
this.emit(` ${a} = "${this.letDescriptions.get(a)}"`);
|
|
1375
|
-
}
|
|
1376
|
-
}
|
|
1180
|
+
for (let i = 0; i < result.tableauTrace.length; i++)
|
|
1181
|
+
this.emit(` ${i + 1}. ${String(result.tableauTrace[i])}`);
|
|
1377
1182
|
}
|
|
1378
1183
|
}
|
|
1379
|
-
/** Recolecta nombres de átomos únicos de una fórmula */
|
|
1380
1184
|
collectAtoms(f, seen = new Set()) {
|
|
1381
1185
|
if (!f)
|
|
1382
1186
|
return [];
|
|
@@ -1385,11 +1189,9 @@ class Interpreter {
|
|
|
1385
1189
|
return [f.name];
|
|
1386
1190
|
}
|
|
1387
1191
|
const result = [];
|
|
1388
|
-
if (f.args)
|
|
1389
|
-
for (const arg of f.args)
|
|
1192
|
+
if (f.args)
|
|
1193
|
+
for (const arg of f.args)
|
|
1390
1194
|
result.push(...this.collectAtoms(arg, seen));
|
|
1391
|
-
}
|
|
1392
|
-
}
|
|
1393
1195
|
return result;
|
|
1394
1196
|
}
|
|
1395
1197
|
statusIcon(status) {
|
|
@@ -1402,91 +1204,13 @@ class Interpreter {
|
|
|
1402
1204
|
return '◎';
|
|
1403
1205
|
case 'unsatisfiable':
|
|
1404
1206
|
return '⊘';
|
|
1405
|
-
case 'provable':
|
|
1406
|
-
return '✓';
|
|
1407
|
-
case 'refutable':
|
|
1408
|
-
return '✗';
|
|
1409
|
-
case 'unknown':
|
|
1410
|
-
return '?';
|
|
1411
|
-
case 'error':
|
|
1412
|
-
return '⚠';
|
|
1413
1207
|
default:
|
|
1414
1208
|
return '•';
|
|
1415
1209
|
}
|
|
1416
1210
|
}
|
|
1417
|
-
formatTruthTable(formula,
|
|
1418
|
-
|
|
1419
|
-
const isVerbose = verbosity === 'on' || verbosity === 'model';
|
|
1420
|
-
const lines = [];
|
|
1421
|
-
// Detect Belnap (4-valued) table: results are strings like 'T','F','B','N'
|
|
1422
|
-
const isBelnap = tt.rows.length > 0 &&
|
|
1423
|
-
typeof tt.rows[0].result === 'string' &&
|
|
1424
|
-
['T', 'F', 'B', 'N'].includes(String(tt.rows[0].result));
|
|
1425
|
-
if (isBelnap) {
|
|
1426
|
-
lines.push(`Tabla de verdad Belnap (4 valores) para: ${(0, propositional_1.formulaToString)(formula)}`);
|
|
1427
|
-
lines.push('');
|
|
1428
|
-
}
|
|
1429
|
-
// Header
|
|
1430
|
-
const colLabels = [...tt.variables];
|
|
1431
|
-
if (isVerbose && tt.subFormulas) {
|
|
1432
|
-
tt.subFormulas.forEach((sf) => colLabels.push(sf.label));
|
|
1433
|
-
}
|
|
1434
|
-
colLabels.push((0, propositional_1.formulaToString)(formula));
|
|
1435
|
-
const colWidths = colLabels.map((h) => Math.max(h.length, 5));
|
|
1436
|
-
lines.push(colLabels.map((h, i) => h.padEnd(colWidths[i])).join(' | '));
|
|
1437
|
-
lines.push(colWidths.map((w) => '-'.repeat(w)).join('-+-'));
|
|
1438
|
-
// Rows
|
|
1439
|
-
const designated = new Set(['T', 'B']);
|
|
1440
|
-
for (let rowIndex = 0; rowIndex < tt.rows.length; rowIndex++) {
|
|
1441
|
-
const row = tt.rows[rowIndex];
|
|
1442
|
-
const vals = tt.variables.map((v) => {
|
|
1443
|
-
const val = row.valuation[v];
|
|
1444
|
-
if (typeof val === 'string')
|
|
1445
|
-
return val;
|
|
1446
|
-
return val ? 'T' : 'F';
|
|
1447
|
-
});
|
|
1448
|
-
if (isVerbose && tt.subFormulas && tt.subFormulaValues) {
|
|
1449
|
-
const subVals = tt.subFormulaValues[rowIndex];
|
|
1450
|
-
tt.subFormulas.forEach((sf) => {
|
|
1451
|
-
let v = subVals[sf.label];
|
|
1452
|
-
if (typeof v === 'boolean')
|
|
1453
|
-
v = v ? 'T' : 'F';
|
|
1454
|
-
vals.push(String(v));
|
|
1455
|
-
});
|
|
1456
|
-
}
|
|
1457
|
-
let finalVal = row.result;
|
|
1458
|
-
if (typeof finalVal === 'boolean')
|
|
1459
|
-
finalVal = finalVal ? 'T' : 'F';
|
|
1460
|
-
vals.push(String(finalVal));
|
|
1461
|
-
const isCountermodel = (tt.isTautology === false && !row.result) || (tt.isSatisfiable && row.result);
|
|
1462
|
-
const rowStr = vals.map((v, i) => v.padEnd(colWidths[i])).join(' | ');
|
|
1463
|
-
// Belnap designation marker
|
|
1464
|
-
if (isBelnap && designated.has(String(finalVal))) {
|
|
1465
|
-
lines.push(`${rowStr} ⊛ Designado`);
|
|
1466
|
-
}
|
|
1467
|
-
else if (isVerbose && isCountermodel) {
|
|
1468
|
-
lines.push(`${rowStr} ←`);
|
|
1469
|
-
}
|
|
1470
|
-
else {
|
|
1471
|
-
lines.push(rowStr);
|
|
1472
|
-
}
|
|
1473
|
-
}
|
|
1474
|
-
lines.push('');
|
|
1475
|
-
if (tt.satisfyingCount !== undefined && tt.totalCount !== undefined) {
|
|
1476
|
-
lines.push(`${tt.satisfyingCount}/${tt.totalCount} valuaciones verdaderas`);
|
|
1477
|
-
}
|
|
1478
|
-
if (isBelnap) {
|
|
1479
|
-
lines.push('Valores designados (portadores de verdad): {T, B}');
|
|
1480
|
-
}
|
|
1481
|
-
if (tt.isTautology)
|
|
1482
|
-
lines.push('→ Tautologia ✓');
|
|
1483
|
-
else if (tt.isContradiction)
|
|
1484
|
-
lines.push('→ Contradiccion ✗');
|
|
1485
|
-
else
|
|
1486
|
-
lines.push('→ Contingente (satisfacible)');
|
|
1487
|
-
return lines.join('\n');
|
|
1211
|
+
formatTruthTable(formula, _tt) {
|
|
1212
|
+
return `Tabla de verdad para ${(0, propositional_1.formulaToString)(formula)}`;
|
|
1488
1213
|
}
|
|
1489
|
-
// Getters para el estado (usados por REPL)
|
|
1490
1214
|
getTheory() {
|
|
1491
1215
|
return this.theory;
|
|
1492
1216
|
}
|
|
@@ -1496,9 +1220,6 @@ class Interpreter {
|
|
|
1496
1220
|
getTextLayer() {
|
|
1497
1221
|
return this.textLayer;
|
|
1498
1222
|
}
|
|
1499
|
-
getLetDescriptions() {
|
|
1500
|
-
return this.letDescriptions;
|
|
1501
|
-
}
|
|
1502
1223
|
getLetBindings() {
|
|
1503
1224
|
return this.letBindings;
|
|
1504
1225
|
}
|