@stevenvo780/st-lang 1.5.8 → 2.0.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 (41) hide show
  1. package/README.md +346 -69
  2. package/dist/ast/nodes.d.ts +7 -2
  3. package/dist/ast/nodes.d.ts.map +1 -1
  4. package/dist/lexer/lexer.d.ts.map +1 -1
  5. package/dist/lexer/lexer.js +30 -2
  6. package/dist/lexer/lexer.js.map +1 -1
  7. package/dist/lexer/tokens.d.ts +4 -0
  8. package/dist/lexer/tokens.d.ts.map +1 -1
  9. package/dist/lexer/tokens.js +9 -0
  10. package/dist/lexer/tokens.js.map +1 -1
  11. package/dist/parser/parser.d.ts +4 -0
  12. package/dist/parser/parser.d.ts.map +1 -1
  13. package/dist/parser/parser.js +111 -10
  14. package/dist/parser/parser.js.map +1 -1
  15. package/dist/profiles/classical/propositional.d.ts.map +1 -1
  16. package/dist/profiles/classical/propositional.js +45 -0
  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 +27 -0
  20. package/dist/profiles/intuitionistic/propositional.js.map +1 -1
  21. package/dist/profiles/probabilistic/basic.d.ts.map +1 -1
  22. package/dist/profiles/probabilistic/basic.js +12 -0
  23. package/dist/profiles/probabilistic/basic.js.map +1 -1
  24. package/dist/protocol/handler.d.ts +1 -0
  25. package/dist/protocol/handler.d.ts.map +1 -1
  26. package/dist/protocol/handler.js +318 -2
  27. package/dist/protocol/handler.js.map +1 -1
  28. package/dist/runtime/format.d.ts.map +1 -1
  29. package/dist/runtime/format.js +16 -2
  30. package/dist/runtime/format.js.map +1 -1
  31. package/dist/runtime/interpreter.d.ts +19 -1
  32. package/dist/runtime/interpreter.d.ts.map +1 -1
  33. package/dist/runtime/interpreter.js +365 -92
  34. package/dist/runtime/interpreter.js.map +1 -1
  35. package/dist/tests/examples.test.d.ts +2 -0
  36. package/dist/tests/examples.test.d.ts.map +1 -0
  37. package/dist/tests/examples.test.js +85 -0
  38. package/dist/tests/examples.test.js.map +1 -0
  39. package/dist/types/index.d.ts +2 -1
  40. package/dist/types/index.d.ts.map +1 -1
  41. package/package.json +2 -1
@@ -22,6 +22,8 @@ class Interpreter {
22
22
  letBindings = new Map();
23
23
  letDescriptions = new Map();
24
24
  theories = new Map();
25
+ /** Plantillas de teorías (clases) */
26
+ theoryTemplates = new Map();
25
27
  /** Nombre de la teoría actual (si estamos dentro de una) */
26
28
  currentTheoryName = null;
27
29
  /** Funciones declaradas */
@@ -29,9 +31,31 @@ class Interpreter {
29
31
  /** Señal de return activa (para salir de funciones) */
30
32
  returnSignal = false;
31
33
  returnValue = undefined;
34
+ /** Modo importación: solo registrar exports */
35
+ isImporting = false;
36
+ /** Elementos exportados por el archivo actual */
37
+ exportedBindings = new Map();
38
+ exportedAxioms = new Map();
39
+ exportedTheorems = new Map();
40
+ exportedFunctions = new Map();
41
+ exportedTheories = new Map();
32
42
  constructor() {
33
43
  this.theory = this.createEmptyTheory();
34
44
  this.textLayer = (0, compiler_1.createTextLayerState)();
45
+ this.registerBuiltins();
46
+ }
47
+ /** Registra funciones nativas (Built-ins) para metaprogramación e interactividad */
48
+ registerBuiltins() {
49
+ const builtins = ['typeof', 'is_valid', 'is_satisfiable', 'get_atoms', 'input'];
50
+ for (const name of builtins) {
51
+ this.functions.set(name, {
52
+ kind: 'fn_decl',
53
+ name,
54
+ params: ['arg'],
55
+ body: [],
56
+ source: { line: 0, column: 0 }
57
+ });
58
+ }
35
59
  }
36
60
  createEmptyTheory() {
37
61
  return {
@@ -56,8 +80,15 @@ class Interpreter {
56
80
  this.theories.clear();
57
81
  this.currentTheoryName = null;
58
82
  this.functions.clear();
83
+ this.registerBuiltins();
59
84
  this.returnSignal = false;
60
85
  this.returnValue = undefined;
86
+ this.isImporting = false;
87
+ this.exportedBindings.clear();
88
+ this.exportedAxioms.clear();
89
+ this.exportedTheorems.clear();
90
+ this.exportedFunctions.clear();
91
+ this.exportedTheories.clear();
61
92
  }
62
93
  execute(source, file = '<stdin>') {
63
94
  this.diagnostics = [];
@@ -148,6 +179,23 @@ class Interpreter {
148
179
  executeStatement(stmt) {
149
180
  if (this.returnSignal)
150
181
  return;
182
+ // Si estamos importando, ignoramos comandos que no sean declaraciones
183
+ const sideEffects = [
184
+ 'derive_cmd',
185
+ 'check_valid_cmd',
186
+ 'check_satisfiable_cmd',
187
+ 'check_equivalent_cmd',
188
+ 'prove_cmd',
189
+ 'countermodel_cmd',
190
+ 'truth_table_cmd',
191
+ 'print_cmd',
192
+ 'analyze_cmd',
193
+ 'explain_cmd',
194
+ 'render_cmd',
195
+ ];
196
+ if (this.isImporting && (sideEffects.includes(stmt.kind) || stmt.kind === 'logic_decl')) {
197
+ return;
198
+ }
151
199
  switch (stmt.kind) {
152
200
  case 'logic_decl':
153
201
  return this.execLogicDecl(stmt);
@@ -206,7 +254,36 @@ class Interpreter {
206
254
  case 'return_stmt':
207
255
  return this.execReturnStmt(stmt);
208
256
  case 'fn_call':
209
- return this.execFnCall(stmt);
257
+ this.executeFnCall(stmt);
258
+ return;
259
+ case 'export_decl':
260
+ return this.execExportDecl(stmt);
261
+ }
262
+ }
263
+ execExportDecl(stmt) {
264
+ // Ejecutar la declaración interna
265
+ this.executeStatement(stmt.statement);
266
+ // Registrarla como exportada
267
+ const s = stmt.statement;
268
+ switch (s.kind) {
269
+ case 'let_decl':
270
+ if (s.letType === 'formula') {
271
+ this.exportedBindings.set(s.name, this.letBindings.get(s.name));
272
+ this.exportedAxioms.set(s.name, this.theory.axioms.get(s.name));
273
+ }
274
+ break;
275
+ case 'axiom_decl':
276
+ this.exportedAxioms.set(s.name, this.theory.axioms.get(s.name));
277
+ break;
278
+ case 'theorem_decl':
279
+ this.exportedTheorems.set(s.name, this.theory.theorems.get(s.name));
280
+ break;
281
+ case 'fn_decl':
282
+ this.exportedFunctions.set(s.name, this.functions.get(s.name));
283
+ break;
284
+ case 'theory_decl':
285
+ this.exportedTheories.set(s.name, this.theories.get(s.name));
286
+ break;
210
287
  }
211
288
  }
212
289
  requireProfile() {
@@ -226,39 +303,41 @@ class Interpreter {
226
303
  return f;
227
304
  // Si es un átomo, intentar resolver
228
305
  if (f.kind === 'atom' && f.name) {
229
- // Dot notation: Theory.member
306
+ // Dot notation: Theory.member o instance.member
230
307
  if (f.name.includes('.')) {
231
- const [theoryName, memberName] = f.name.split('.', 2);
232
- const scope = this.theories.get(theoryName);
308
+ const [prefix, memberName] = f.name.split('.', 2);
309
+ // 1. Intentar resolver el prefijo como una variable local (instancia)
310
+ // Ej: let f1 = Familia("Socrates") -> f1.amor
311
+ if (this.letBindings.has(prefix)) {
312
+ const resolvedPrefix = this.letBindings.get(prefix);
313
+ if (resolvedPrefix.kind === 'atom' && resolvedPrefix.name) {
314
+ // Si el prefijo se resuelve a un nombre de teoría/instancia
315
+ const actualInstanceName = resolvedPrefix.name;
316
+ const scope = this.theories.get(actualInstanceName);
317
+ if (scope) {
318
+ if (scope.privateMembers.has(memberName) && this.currentTheoryName !== actualInstanceName)
319
+ return f;
320
+ if (scope.letBindings.has(memberName))
321
+ return this.resolveFormula(scope.letBindings.get(memberName), new Set(visited));
322
+ if (scope.axioms.has(memberName))
323
+ return this.resolveFormula(scope.axioms.get(memberName), new Set(visited));
324
+ if (scope.theorems.has(memberName))
325
+ return this.resolveFormula(scope.theorems.get(memberName), new Set(visited));
326
+ }
327
+ }
328
+ }
329
+ // 2. Intentar resolver como nombre de teoría global (Singleton)
330
+ const scope = this.theories.get(prefix);
233
331
  if (scope) {
234
- // Verificar encapsulamiento: miembros privados no accesibles desde fuera
235
- if (scope.privateMembers.has(memberName) && this.currentTheoryName !== theoryName) {
236
- // Miembro privado — no resolver, dejar como átomo
332
+ if (scope.privateMembers.has(memberName) && this.currentTheoryName !== prefix)
237
333
  return f;
238
- }
239
- // Buscar en letBindings de la teoría
240
- if (scope.letBindings.has(memberName)) {
241
- if (visited.has(f.name))
242
- return f;
243
- visited.add(f.name);
334
+ if (scope.letBindings.has(memberName))
244
335
  return this.resolveFormula(scope.letBindings.get(memberName), new Set(visited));
245
- }
246
- // Buscar en axiomas de la teoría
247
- if (scope.axioms.has(memberName)) {
248
- if (visited.has(f.name))
249
- return f;
250
- visited.add(f.name);
336
+ if (scope.axioms.has(memberName))
251
337
  return this.resolveFormula(scope.axioms.get(memberName), new Set(visited));
252
- }
253
- // Buscar en teoremas de la teoría
254
- if (scope.theorems.has(memberName)) {
255
- if (visited.has(f.name))
256
- return f;
257
- visited.add(f.name);
338
+ if (scope.theorems.has(memberName))
258
339
  return this.resolveFormula(scope.theorems.get(memberName), new Set(visited));
259
- }
260
340
  }
261
- // No se encontró — dejar como átomo con punto
262
341
  return f;
263
342
  }
264
343
  // Binding local normal
@@ -283,6 +362,11 @@ class Interpreter {
283
362
  return this.resolveFormula(this.theory.theorems.get(f.name), new Set(visited));
284
363
  }
285
364
  }
365
+ // Llamada a función como expresión
366
+ if (f.kind === 'fn_call' && f.name) {
367
+ const result = this.executeFnCall({ name: f.name, args: f.args || [] });
368
+ return result || { kind: 'atom', name: 'undefined', source: f.source };
369
+ }
286
370
  // Recorrer hijos recursivamente
287
371
  if (f.args && f.args.length > 0) {
288
372
  const newArgs = f.args.map(a => a ? this.resolveFormula(a, new Set(visited)) : a);
@@ -609,11 +693,23 @@ class Interpreter {
609
693
  }
610
694
  execTheoryDecl(stmt) {
611
695
  const theoryName = stmt.name;
612
- // Si ya existe una teoría con este nombre, sobreescribirla
696
+ // Si tiene parámetros, es una plantilla (Clase)
697
+ if (stmt.params && stmt.params.length > 0) {
698
+ this.theoryTemplates.set(theoryName, { node: stmt, parent: stmt.parent });
699
+ this.emit(`Teoría (plantilla) ${theoryName}(${stmt.params.join(', ')}) declarada`);
700
+ return;
701
+ }
702
+ // Si no tiene parámetros, ejecutar como singleton/objeto inmediato
703
+ this.instantiateTheory(stmt);
704
+ }
705
+ /** Crea una instancia de una teoría y la registra en this.theories */
706
+ instantiateTheory(node, instanceName, args = []) {
707
+ const theoryName = instanceName || node.name;
708
+ const templateName = node.name;
613
709
  // Crear scope vacío
614
710
  const scope = {
615
711
  name: theoryName,
616
- parent: stmt.parent,
712
+ parent: node.parent,
617
713
  letBindings: new Map(),
618
714
  letDescriptions: new Map(),
619
715
  axioms: new Map(),
@@ -621,32 +717,25 @@ class Interpreter {
621
717
  privateMembers: new Set(),
622
718
  };
623
719
  // HERENCIA: Si extends Parent, copiar bindings/axiomas/teoremas del padre
624
- if (stmt.parent) {
625
- const parentScope = this.theories.get(stmt.parent);
720
+ // El padre puede ser otra plantilla o un singleton ya instanciado
721
+ if (node.parent) {
722
+ const parentScope = this.theories.get(node.parent);
626
723
  if (!parentScope) {
627
- throw new Error(`Teoría padre '${stmt.parent}' no encontrada. Declárela antes de '${theoryName}'.`);
724
+ throw new Error(`Teoría padre '${node.parent}' no encontrada. Debe declararse antes de '${theoryName}'.`);
628
725
  }
629
726
  // Copiar todo del padre (no los miembros privados del padre al hijo)
630
- for (const [k, v] of parentScope.letBindings) {
631
- if (!parentScope.privateMembers.has(k)) {
727
+ for (const [k, v] of parentScope.letBindings)
728
+ if (!parentScope.privateMembers.has(k))
632
729
  scope.letBindings.set(k, v);
633
- }
634
- }
635
- for (const [k, v] of parentScope.letDescriptions) {
636
- if (!parentScope.privateMembers.has(k)) {
730
+ for (const [k, v] of parentScope.letDescriptions)
731
+ if (!parentScope.privateMembers.has(k))
637
732
  scope.letDescriptions.set(k, v);
638
- }
639
- }
640
- for (const [k, v] of parentScope.axioms) {
641
- if (!parentScope.privateMembers.has(k)) {
733
+ for (const [k, v] of parentScope.axioms)
734
+ if (!parentScope.privateMembers.has(k))
642
735
  scope.axioms.set(k, v);
643
- }
644
- }
645
- for (const [k, v] of parentScope.theorems) {
646
- if (!parentScope.privateMembers.has(k)) {
736
+ for (const [k, v] of parentScope.theorems)
737
+ if (!parentScope.privateMembers.has(k))
647
738
  scope.theorems.set(k, v);
648
- }
649
- }
650
739
  }
651
740
  // Guardar estado global antes de entrar al scope de la teoría
652
741
  const savedLetBindings = new Map(this.letBindings);
@@ -654,30 +743,33 @@ class Interpreter {
654
743
  const savedAxioms = new Map(this.theory.axioms);
655
744
  const savedTheorems = new Map(this.theory.theorems);
656
745
  const savedTheoryName = this.currentTheoryName;
657
- // Establecer el scope de la teoría como contexto actual
746
+ // Inyectar argumentos en el scope local de la teoría
747
+ if (node.params && args.length > 0) {
748
+ for (let i = 0; i < node.params.length; i++) {
749
+ if (i < args.length) {
750
+ const resolvedArg = this.resolveFormula(args[i]);
751
+ this.letBindings.set(node.params[i], resolvedArg);
752
+ scope.letBindings.set(node.params[i], resolvedArg);
753
+ }
754
+ }
755
+ }
658
756
  // Inyectar bindings heredados al scope local para que los statements internos los vean
659
- for (const [k, v] of scope.letBindings) {
757
+ for (const [k, v] of scope.letBindings)
660
758
  this.letBindings.set(k, v);
661
- }
662
- for (const [k, v] of scope.letDescriptions) {
759
+ for (const [k, v] of scope.letDescriptions)
663
760
  this.letDescriptions.set(k, v);
664
- }
665
- for (const [k, v] of scope.axioms) {
761
+ for (const [k, v] of scope.axioms)
666
762
  this.theory.axioms.set(k, v);
667
- }
668
- for (const [k, v] of scope.theorems) {
763
+ for (const [k, v] of scope.theorems)
669
764
  this.theory.theorems.set(k, v);
670
- }
671
765
  this.currentTheoryName = theoryName;
672
- this.emit(`── Theory ${theoryName}${stmt.parent ? ` extends ${stmt.parent}` : ''} ──`);
766
+ this.emit(`── Instanciando Theory ${theoryName} ──`);
673
767
  // Ejecutar los miembros del body
674
- for (const member of stmt.members) {
675
- // Registrar visibilidad
768
+ for (const member of node.members) {
676
769
  const memberStmt = member.statement;
677
770
  const memberName = 'name' in memberStmt ? memberStmt.name : null;
678
- if (member.visibility === 'private' && memberName) {
771
+ if (member.visibility === 'private' && memberName)
679
772
  scope.privateMembers.add(memberName);
680
- }
681
773
  try {
682
774
  this.executeStatement(memberStmt);
683
775
  }
@@ -686,43 +778,35 @@ class Interpreter {
686
778
  this.diagnostics.push({
687
779
  severity: 'error',
688
780
  message: `[theory ${theoryName}] ${message}`,
689
- file: stmt.source.file,
781
+ file: node.source.file,
690
782
  line: memberStmt.source.line,
691
783
  column: memberStmt.source.column,
692
784
  });
693
785
  }
694
786
  }
695
- // Capturar lo que los statements internos produjeron en el scope
696
- // (nuevos letBindings, axiomas, teoremas, descriptions)
697
- for (const [k, v] of this.letBindings) {
698
- if (!savedLetBindings.has(k)) {
787
+ // Capturar lo producido
788
+ for (const [k, v] of this.letBindings)
789
+ if (!savedLetBindings.has(k))
699
790
  scope.letBindings.set(k, v);
700
- }
701
- }
702
- for (const [k, v] of this.letDescriptions) {
703
- if (!savedLetDescriptions.has(k)) {
791
+ for (const [k, v] of this.letDescriptions)
792
+ if (!savedLetDescriptions.has(k))
704
793
  scope.letDescriptions.set(k, v);
705
- }
706
- }
707
- for (const [k, v] of this.theory.axioms) {
708
- if (!savedAxioms.has(k)) {
794
+ for (const [k, v] of this.theory.axioms)
795
+ if (!savedAxioms.has(k))
709
796
  scope.axioms.set(k, v);
710
- }
711
- }
712
- for (const [k, v] of this.theory.theorems) {
713
- if (!savedTheorems.has(k)) {
797
+ for (const [k, v] of this.theory.theorems)
798
+ if (!savedTheorems.has(k))
714
799
  scope.theorems.set(k, v);
715
- }
716
- }
717
- // Restaurar estado global (encapsulamiento — los internos no escapan)
800
+ // Restaurar estado global
718
801
  this.letBindings = savedLetBindings;
719
802
  this.letDescriptions = savedLetDescriptions;
720
803
  this.theory.axioms = savedAxioms;
721
804
  this.theory.theorems = savedTheorems;
722
805
  this.currentTheoryName = savedTheoryName;
723
- // Registrar la teoría
806
+ // Registrar la instancia
724
807
  this.theories.set(theoryName, scope);
725
- this.emit(`── End Theory ${theoryName} ──`);
808
+ this.emit(`── End Theory Instance ${theoryName} ──`);
809
+ return theoryName;
726
810
  }
727
811
  // =============================================
728
812
  // Control flow & funciones (v1.5.8)
@@ -842,8 +926,14 @@ class Interpreter {
842
926
  }
843
927
  }
844
928
  execFnDecl(stmt) {
845
- this.functions.set(stmt.name, stmt);
846
- this.emit(`Función ${stmt.name}(${stmt.params.join(', ')}) declarada`);
929
+ const name = this.currentTheoryName ? `${this.currentTheoryName}.${stmt.name}` : stmt.name;
930
+ this.functions.set(name, stmt);
931
+ if (this.currentTheoryName) {
932
+ this.emit(`Función de instancia ${name}(${stmt.params.join(', ')}) declarada`);
933
+ }
934
+ else {
935
+ this.emit(`Función ${name}(${stmt.params.join(', ')}) declarada`);
936
+ }
847
937
  }
848
938
  execReturnStmt(stmt) {
849
939
  if (stmt.formula) {
@@ -854,10 +944,48 @@ class Interpreter {
854
944
  }
855
945
  this.returnSignal = true;
856
946
  }
857
- execFnCall(stmt) {
947
+ executeFnCall(stmt) {
948
+ // 0. Funciones Nativas (Built-ins) para Metaprogramación
949
+ if (['typeof', 'is_valid', 'is_satisfiable', 'get_atoms', 'input'].includes(stmt.name)) {
950
+ return this.executeBuiltin(stmt.name, stmt.args);
951
+ }
952
+ // 0.5 Si es un método (obj.metodo), resolver el objeto primero
953
+ if (stmt.name.includes('.')) {
954
+ const [prefix, methodName] = stmt.name.split('.', 2);
955
+ let actualInstanceName = prefix;
956
+ // Intentar resolver prefijo si es una variable local
957
+ if (this.letBindings.has(prefix)) {
958
+ const resolved = this.letBindings.get(prefix);
959
+ if (resolved.kind === 'atom' && resolved.name) {
960
+ actualInstanceName = resolved.name;
961
+ }
962
+ }
963
+ const scope = this.theories.get(actualInstanceName);
964
+ if (scope) {
965
+ // En ST actual, las funciones no se guardan en TheoryScope sino que se ejecutan
966
+ // en el contexto del intérprete. Para soportar métodos de instancia,
967
+ // necesitamos que las funciones estén vinculadas al scope o prefijadas.
968
+ // Como solución rápida y potente: buscamos la función prefijada en el mapa global.
969
+ const internalFnName = `${actualInstanceName}.${methodName}`;
970
+ const fn = this.functions.get(internalFnName);
971
+ if (fn) {
972
+ // Ejecutar función con el scope de la instancia inyectado
973
+ return this.executeFunctionInScope(fn, stmt.args, scope);
974
+ }
975
+ }
976
+ }
977
+ // 1. Intentar instanciación de teoría (Clase)
978
+ const template = this.theoryTemplates.get(stmt.name);
979
+ if (template) {
980
+ // Nombre de instancia único si no estamos en un let (o usar un contador)
981
+ const instanceId = `inst_${stmt.name}_${this.theories.size}`;
982
+ this.instantiateTheory(template.node, instanceId, stmt.args);
983
+ return { kind: 'atom', name: instanceId };
984
+ }
985
+ // 2. Intentar llamada a función normal
858
986
  const fn = this.functions.get(stmt.name);
859
987
  if (!fn) {
860
- throw new Error(`Función '${stmt.name}' no declarada`);
988
+ throw new Error(`Función o Teoría '${stmt.name}' no declarada`);
861
989
  }
862
990
  if (stmt.args.length !== fn.params.length) {
863
991
  throw new Error(`Función '${stmt.name}' espera ${fn.params.length} argumento(s), recibió ${stmt.args.length}`);
@@ -882,8 +1010,8 @@ class Interpreter {
882
1010
  break;
883
1011
  this.executeStatement(bodyStmt);
884
1012
  }
885
- // Capturar valor de retorno (si lo hay) — queda en this.returnValue
886
- // Por ahora no hacemos nada con él, pero está disponible para extensión futura
1013
+ // Capturar valor de retorno
1014
+ const result = this.returnValue;
887
1015
  this.returnSignal = savedReturnSignal;
888
1016
  this.returnValue = savedReturnValue;
889
1017
  // Restaurar bindings
@@ -896,6 +1024,94 @@ class Interpreter {
896
1024
  this.letBindings.delete(param);
897
1025
  }
898
1026
  }
1027
+ return result;
1028
+ }
1029
+ /** Ejecuta una función inyectando bindings de un scope (para métodos de instancia) */
1030
+ executeFunctionInScope(fn, args, scope) {
1031
+ if (args.length !== fn.params.length) {
1032
+ throw new Error(`Método '${fn.name}' espera ${fn.params.length} argumento(s), recibió ${args.length}`);
1033
+ }
1034
+ // 1. Guardar estado global
1035
+ const savedBindings = new Map(this.letBindings);
1036
+ const savedAxioms = new Map(this.theory.axioms);
1037
+ const savedTheorems = new Map(this.theory.theorems);
1038
+ const savedTheoryName = this.currentTheoryName;
1039
+ // 2. Inyectar scope de la instancia
1040
+ for (const [k, v] of scope.letBindings)
1041
+ this.letBindings.set(k, v);
1042
+ for (const [k, v] of scope.axioms)
1043
+ this.theory.axioms.set(k, v);
1044
+ for (const [k, v] of scope.theorems)
1045
+ this.theory.theorems.set(k, v);
1046
+ this.currentTheoryName = scope.name;
1047
+ // 3. Inyectar argumentos de la llamada
1048
+ for (let i = 0; i < fn.params.length; i++) {
1049
+ this.letBindings.set(fn.params[i], this.resolveFormula(args[i]));
1050
+ }
1051
+ // 4. Ejecutar cuerpo
1052
+ const savedReturnSignal = this.returnSignal;
1053
+ const savedReturnValue = this.returnValue;
1054
+ this.returnSignal = false;
1055
+ this.returnValue = undefined;
1056
+ for (const bodyStmt of fn.body) {
1057
+ if (this.returnSignal)
1058
+ break;
1059
+ this.executeStatement(bodyStmt);
1060
+ }
1061
+ const result = this.returnValue;
1062
+ // 5. Restaurar estado global
1063
+ this.returnSignal = savedReturnSignal;
1064
+ this.returnValue = savedReturnValue;
1065
+ this.letBindings = savedBindings;
1066
+ this.theory.axioms = savedAxioms;
1067
+ this.theory.theorems = savedTheorems;
1068
+ this.currentTheoryName = savedTheoryName;
1069
+ return result;
1070
+ }
1071
+ executeBuiltin(name, args) {
1072
+ if (args.length !== 1) {
1073
+ throw new Error(`Built-in '${name}' espera exactamente 1 argumento, recibió ${args.length}`);
1074
+ }
1075
+ const arg = this.resolveFormula(args[0]);
1076
+ if (name === 'typeof') {
1077
+ let typeStr = 'Formula';
1078
+ if (arg.kind === 'number')
1079
+ typeStr = 'Number';
1080
+ if (arg.kind === 'atom' && arg.name?.startsWith('"'))
1081
+ typeStr = 'String';
1082
+ return { kind: 'atom', name: `"${typeStr}"`, source: arg.source };
1083
+ }
1084
+ if (name === 'is_valid' || name === 'is_satisfiable') {
1085
+ const profile = this.requireProfile();
1086
+ try {
1087
+ const result = name === 'is_valid' ? profile.checkValid(arg) : profile.checkSatisfiable(arg);
1088
+ const isTrue = result.status === 'valid' || result.status === 'satisfiable';
1089
+ return { kind: 'atom', name: `"${isTrue ? 'True' : 'False'}"`, source: arg.source };
1090
+ }
1091
+ catch (e) {
1092
+ return { kind: 'atom', name: '"Error"', source: arg.source };
1093
+ }
1094
+ }
1095
+ if (name === 'get_atoms') {
1096
+ const atoms = this.collectAtoms(arg);
1097
+ return { kind: 'atom', name: `"{ ${atoms.join(', ')} }"`, source: arg.source };
1098
+ }
1099
+ if (name === 'input') {
1100
+ const prompt = arg.kind === 'atom' && arg.name?.startsWith('"') ? arg.name.replace(/(^"|"$)/g, '') : (0, propositional_1.formulaToString)(arg);
1101
+ let inputStr = '';
1102
+ try {
1103
+ process.stdout.write(prompt + ' ');
1104
+ const fs = require('fs');
1105
+ const buf = Buffer.alloc(256);
1106
+ const bytesRead = fs.readSync(process.stdin.fd, buf, 0, 256, null);
1107
+ inputStr = buf.toString('utf8', 0, bytesRead).trim();
1108
+ }
1109
+ catch (e) {
1110
+ inputStr = "interactive_not_supported";
1111
+ }
1112
+ return { kind: 'atom', name: `"${inputStr}"`, source: arg.source };
1113
+ }
1114
+ return undefined;
899
1115
  }
900
1116
  execImportDecl(stmt) {
901
1117
  let filePath = stmt.path;
@@ -929,11 +1145,68 @@ class Interpreter {
929
1145
  if (parser.diagnostics.some((d) => d.severity === 'error')) {
930
1146
  throw new Error(`Errores de parseo en '${filePath}'`);
931
1147
  }
932
- // Ejecutar statements del archivo importado en el contexto actual
1148
+ // Guardar estado de exportación del importador
1149
+ const prevIsImporting = this.isImporting;
1150
+ const prevExportedBindings = new Map(this.exportedBindings);
1151
+ const prevExportedAxioms = new Map(this.exportedAxioms);
1152
+ const prevExportedTheorems = new Map(this.exportedTheorems);
1153
+ const prevExportedFunctions = new Map(this.exportedFunctions);
1154
+ const prevExportedTheories = new Map(this.exportedTheories);
1155
+ // Guardar scope local actual del importador para no contaminarlo durante la carga
1156
+ const prevLetBindings = new Map(this.letBindings);
1157
+ const prevAxioms = new Map(this.theory.axioms);
1158
+ const prevTheorems = new Map(this.theory.theorems);
1159
+ const prevFunctions = new Map(this.functions);
1160
+ const prevTheories = new Map(this.theories);
1161
+ // Limpiar para capturar solo lo que exporta el archivo importado
1162
+ this.isImporting = true;
1163
+ this.exportedBindings.clear();
1164
+ this.exportedAxioms.clear();
1165
+ this.exportedTheorems.clear();
1166
+ this.exportedFunctions.clear();
1167
+ this.exportedTheories.clear();
1168
+ // No queremos que el archivo importado vea el scope del importador (encapsulamiento total)
1169
+ this.letBindings.clear();
1170
+ this.theory.axioms.clear();
1171
+ this.theory.theorems.clear();
1172
+ this.functions.clear();
1173
+ this.theories.clear();
1174
+ // Ejecutar statements del archivo importado
933
1175
  for (const importedStmt of program.statements) {
934
1176
  this.executeStatement(importedStmt);
935
1177
  }
936
- this.emit(`Import: ${filePath} cargado`);
1178
+ // Capturar lo exportado
1179
+ const newExports = {
1180
+ bindings: new Map(this.exportedBindings),
1181
+ axioms: new Map(this.exportedAxioms),
1182
+ theorems: new Map(this.exportedTheorems),
1183
+ functions: new Map(this.exportedFunctions),
1184
+ theories: new Map(this.exportedTheories),
1185
+ };
1186
+ // Restaurar estado del importador (incluyendo su scope original)
1187
+ this.isImporting = prevIsImporting;
1188
+ this.exportedBindings = prevExportedBindings;
1189
+ this.exportedAxioms = prevExportedAxioms;
1190
+ this.exportedTheorems = prevExportedTheorems;
1191
+ this.exportedFunctions = prevExportedFunctions;
1192
+ this.exportedTheories = prevExportedTheories;
1193
+ this.letBindings = prevLetBindings;
1194
+ this.theory.axioms = prevAxioms;
1195
+ this.theory.theorems = prevTheorems;
1196
+ this.functions = prevFunctions;
1197
+ this.theories = prevTheories;
1198
+ // Fusionar solo lo exportado al scope actual
1199
+ for (const [k, v] of newExports.bindings)
1200
+ this.letBindings.set(k, v);
1201
+ for (const [k, v] of newExports.axioms)
1202
+ this.theory.axioms.set(k, v);
1203
+ for (const [k, v] of newExports.theorems)
1204
+ this.theory.theorems.set(k, v);
1205
+ for (const [k, v] of newExports.functions)
1206
+ this.functions.set(k, v);
1207
+ for (const [k, v] of newExports.theories)
1208
+ this.theories.set(k, v);
1209
+ this.emit(`Import: ${filePath} cargado (${newExports.bindings.size + newExports.axioms.size + newExports.theorems.size + newExports.functions.size + newExports.theories.size} elementos importados)`);
937
1210
  }
938
1211
  execExplainCmd(stmt) {
939
1212
  const profile = this.requireProfile();