@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.
- package/README.md +346 -69
- package/dist/ast/nodes.d.ts +7 -2
- package/dist/ast/nodes.d.ts.map +1 -1
- package/dist/lexer/lexer.d.ts.map +1 -1
- package/dist/lexer/lexer.js +30 -2
- package/dist/lexer/lexer.js.map +1 -1
- package/dist/lexer/tokens.d.ts +4 -0
- package/dist/lexer/tokens.d.ts.map +1 -1
- package/dist/lexer/tokens.js +9 -0
- package/dist/lexer/tokens.js.map +1 -1
- package/dist/parser/parser.d.ts +4 -0
- package/dist/parser/parser.d.ts.map +1 -1
- package/dist/parser/parser.js +111 -10
- package/dist/parser/parser.js.map +1 -1
- package/dist/profiles/classical/propositional.d.ts.map +1 -1
- package/dist/profiles/classical/propositional.js +45 -0
- 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 +27 -0
- package/dist/profiles/intuitionistic/propositional.js.map +1 -1
- package/dist/profiles/probabilistic/basic.d.ts.map +1 -1
- package/dist/profiles/probabilistic/basic.js +12 -0
- package/dist/profiles/probabilistic/basic.js.map +1 -1
- package/dist/protocol/handler.d.ts +1 -0
- package/dist/protocol/handler.d.ts.map +1 -1
- package/dist/protocol/handler.js +318 -2
- package/dist/protocol/handler.js.map +1 -1
- package/dist/runtime/format.d.ts.map +1 -1
- package/dist/runtime/format.js +16 -2
- package/dist/runtime/format.js.map +1 -1
- package/dist/runtime/interpreter.d.ts +19 -1
- package/dist/runtime/interpreter.d.ts.map +1 -1
- package/dist/runtime/interpreter.js +365 -92
- package/dist/runtime/interpreter.js.map +1 -1
- package/dist/tests/examples.test.d.ts +2 -0
- package/dist/tests/examples.test.d.ts.map +1 -0
- package/dist/tests/examples.test.js +85 -0
- package/dist/tests/examples.test.js.map +1 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.d.ts.map +1 -1
- 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
|
-
|
|
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 [
|
|
232
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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
|
-
|
|
625
|
-
|
|
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 '${
|
|
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
|
-
//
|
|
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}
|
|
766
|
+
this.emit(`── Instanciando Theory ${theoryName} ──`);
|
|
673
767
|
// Ejecutar los miembros del body
|
|
674
|
-
for (const member of
|
|
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:
|
|
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
|
|
696
|
-
|
|
697
|
-
|
|
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
|
|
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.
|
|
846
|
-
this.
|
|
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
|
-
|
|
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
|
|
886
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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();
|