@stevenvo780/st-lang 1.5.7 → 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 +55 -2
- package/dist/ast/nodes.d.ts.map +1 -1
- package/dist/lexer/lexer.d.ts.map +1 -1
- package/dist/lexer/lexer.js +72 -16
- package/dist/lexer/lexer.js.map +1 -1
- package/dist/lexer/tokens.d.ts +22 -0
- package/dist/lexer/tokens.d.ts.map +1 -1
- package/dist/lexer/tokens.js +46 -0
- package/dist/lexer/tokens.js.map +1 -1
- package/dist/parser/parser.d.ts +20 -0
- package/dist/parser/parser.d.ts.map +1 -1
- package/dist/parser/parser.js +390 -10
- package/dist/parser/parser.js.map +1 -1
- package/dist/profiles/arithmetic/index.d.ts +20 -0
- package/dist/profiles/arithmetic/index.d.ts.map +1 -0
- package/dist/profiles/arithmetic/index.js +222 -0
- package/dist/profiles/arithmetic/index.js.map +1 -0
- package/dist/profiles/classical/propositional.d.ts.map +1 -1
- package/dist/profiles/classical/propositional.js +84 -0
- package/dist/profiles/classical/propositional.js.map +1 -1
- package/dist/profiles/index.d.ts +2 -1
- package/dist/profiles/index.d.ts.map +1 -1
- package/dist/profiles/index.js +4 -1
- package/dist/profiles/index.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 +94 -2
- package/dist/runtime/format.js.map +1 -1
- package/dist/runtime/interpreter.d.ts +31 -0
- package/dist/runtime/interpreter.d.ts.map +1 -1
- package/dist/runtime/interpreter.js +555 -83
- package/dist/runtime/interpreter.js.map +1 -1
- package/dist/tests/arithmetic.test.d.ts +2 -0
- package/dist/tests/arithmetic.test.d.ts.map +1 -0
- package/dist/tests/arithmetic.test.js +416 -0
- package/dist/tests/arithmetic.test.js.map +1 -0
- 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/tests/v1-features.test.js +339 -2
- package/dist/tests/v1-features.test.js.map +1 -1
- package/dist/types/index.d.ts +3 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +2 -1
package/dist/parser/parser.js
CHANGED
|
@@ -11,6 +11,8 @@ class Parser {
|
|
|
11
11
|
pos = 0;
|
|
12
12
|
file;
|
|
13
13
|
diagnostics = [];
|
|
14
|
+
knownFunctionNames = new Set(['typeof', 'is_valid', 'is_satisfiable', 'get_atoms', 'input']);
|
|
15
|
+
knownTheoryNames = new Set();
|
|
14
16
|
constructor(file = '<stdin>') {
|
|
15
17
|
this.file = file;
|
|
16
18
|
}
|
|
@@ -47,6 +49,15 @@ class Parser {
|
|
|
47
49
|
// --- Parsing de statements ---
|
|
48
50
|
parseStatement() {
|
|
49
51
|
const tok = this.current();
|
|
52
|
+
// Detección de llamada a función: nombre(...)
|
|
53
|
+
if (this.peek(1) === tokens_1.TokenType.LPAREN &&
|
|
54
|
+
(tok.type === tokens_1.TokenType.IDENTIFIER || this.knownFunctionNames.has(tok.value))) {
|
|
55
|
+
return this.parseFnCall();
|
|
56
|
+
}
|
|
57
|
+
// Detección de llamada a método: objeto.metodo(...)
|
|
58
|
+
if (tok.type === tokens_1.TokenType.IDENTIFIER && this.peek(1) === tokens_1.TokenType.DOT && this.peek(2) === tokens_1.TokenType.IDENTIFIER && this.peek(3) === tokens_1.TokenType.LPAREN) {
|
|
59
|
+
return this.parseMemberFnCall();
|
|
60
|
+
}
|
|
50
61
|
switch (tok.type) {
|
|
51
62
|
case tokens_1.TokenType.LOGIC:
|
|
52
63
|
return this.parseLogicDecl();
|
|
@@ -87,6 +98,24 @@ class Parser {
|
|
|
87
98
|
return this.parseProofBlock();
|
|
88
99
|
case tokens_1.TokenType.THEORY:
|
|
89
100
|
return this.parseTheoryDecl();
|
|
101
|
+
case tokens_1.TokenType.PRINT:
|
|
102
|
+
return this.parsePrintCmd();
|
|
103
|
+
case tokens_1.TokenType.SET:
|
|
104
|
+
return this.parseSetCmd();
|
|
105
|
+
case tokens_1.TokenType.IF:
|
|
106
|
+
return this.parseIfStmt();
|
|
107
|
+
case tokens_1.TokenType.FOR:
|
|
108
|
+
return this.parseForStmt();
|
|
109
|
+
case tokens_1.TokenType.WHILE:
|
|
110
|
+
return this.parseWhileStmt();
|
|
111
|
+
case tokens_1.TokenType.FN:
|
|
112
|
+
return this.parseFnDecl();
|
|
113
|
+
case tokens_1.TokenType.RETURN:
|
|
114
|
+
return this.parseReturnStmt();
|
|
115
|
+
case tokens_1.TokenType.EXPORT:
|
|
116
|
+
return this.parseExportDecl();
|
|
117
|
+
case tokens_1.TokenType.IDENTIFIER:
|
|
118
|
+
throw new Error(`Statement inesperado: '${tok.value}' (${tok.type})`);
|
|
90
119
|
case tokens_1.TokenType.NEWLINE:
|
|
91
120
|
this.advance();
|
|
92
121
|
return null;
|
|
@@ -403,11 +432,24 @@ class Parser {
|
|
|
403
432
|
this.expect(tokens_1.TokenType.QED);
|
|
404
433
|
return { kind: 'proof_block', assumptions, goal, body, source: src };
|
|
405
434
|
}
|
|
406
|
-
// theory Name { ... } | theory Name extends Parent { ... }
|
|
435
|
+
// theory Name(params) { ... } | theory Name extends Parent { ... }
|
|
407
436
|
parseTheoryDecl() {
|
|
408
437
|
const src = this.loc();
|
|
409
438
|
this.expect(tokens_1.TokenType.THEORY);
|
|
410
439
|
const name = this.expectName();
|
|
440
|
+
this.knownTheoryNames.add(name);
|
|
441
|
+
// Parámetros opcionales (constructor): theory Math(n)
|
|
442
|
+
let params;
|
|
443
|
+
if (this.match(tokens_1.TokenType.LPAREN)) {
|
|
444
|
+
params = [];
|
|
445
|
+
if (!this.checkType(tokens_1.TokenType.RPAREN)) {
|
|
446
|
+
params.push(this.expectName());
|
|
447
|
+
while (this.match(tokens_1.TokenType.COMMA)) {
|
|
448
|
+
params.push(this.expectName());
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
this.expect(tokens_1.TokenType.RPAREN);
|
|
452
|
+
}
|
|
411
453
|
// Herencia opcional: extends Parent
|
|
412
454
|
let parent;
|
|
413
455
|
if (this.match(tokens_1.TokenType.EXTENDS)) {
|
|
@@ -444,7 +486,220 @@ class Parser {
|
|
|
444
486
|
}
|
|
445
487
|
}
|
|
446
488
|
this.expect(tokens_1.TokenType.RBRACE);
|
|
447
|
-
return { kind: 'theory_decl', name, parent, members, source: src };
|
|
489
|
+
return { kind: 'theory_decl', name, params, parent, members, source: src };
|
|
490
|
+
}
|
|
491
|
+
// --- print "texto" | print formula ---
|
|
492
|
+
parsePrintCmd() {
|
|
493
|
+
const src = this.loc();
|
|
494
|
+
this.expect(tokens_1.TokenType.PRINT);
|
|
495
|
+
if (this.checkType(tokens_1.TokenType.STRING)) {
|
|
496
|
+
const value = this.current().value;
|
|
497
|
+
this.advance();
|
|
498
|
+
return { kind: 'print_cmd', value, source: src };
|
|
499
|
+
}
|
|
500
|
+
// Imprimir resultado de fórmula
|
|
501
|
+
const formula = this.parseFormula();
|
|
502
|
+
return { kind: 'print_cmd', value: null, formula, source: src };
|
|
503
|
+
}
|
|
504
|
+
// --- set x = formula ---
|
|
505
|
+
parseSetCmd() {
|
|
506
|
+
const src = this.loc();
|
|
507
|
+
this.expect(tokens_1.TokenType.SET);
|
|
508
|
+
const name = this.expectName();
|
|
509
|
+
this.expectOneOf(tokens_1.TokenType.EQUALS, tokens_1.TokenType.COLON);
|
|
510
|
+
const formula = this.parseFormula();
|
|
511
|
+
return { kind: 'set_cmd', name, formula, source: src };
|
|
512
|
+
}
|
|
513
|
+
// --- if valid FORMULA { } else if satisfiable FORMULA { } else { } ---
|
|
514
|
+
parseIfStmt() {
|
|
515
|
+
const src = this.loc();
|
|
516
|
+
const branches = [];
|
|
517
|
+
let elseBranch;
|
|
518
|
+
// Primera rama: if
|
|
519
|
+
this.expect(tokens_1.TokenType.IF);
|
|
520
|
+
branches.push(this.parseConditionBranch());
|
|
521
|
+
// Ramas else if
|
|
522
|
+
while (this.checkElseIf()) {
|
|
523
|
+
this.expect(tokens_1.TokenType.ELSE);
|
|
524
|
+
this.expect(tokens_1.TokenType.IF);
|
|
525
|
+
branches.push(this.parseConditionBranch());
|
|
526
|
+
}
|
|
527
|
+
// Rama else
|
|
528
|
+
if (this.match(tokens_1.TokenType.ELSE)) {
|
|
529
|
+
elseBranch = this.parseBlock();
|
|
530
|
+
}
|
|
531
|
+
return { kind: 'if_stmt', branches, elseBranch, source: src };
|
|
532
|
+
}
|
|
533
|
+
checkElseIf() {
|
|
534
|
+
// Mira si hay else seguido de if (con posibles newlines entre medio)
|
|
535
|
+
if (!this.checkType(tokens_1.TokenType.ELSE))
|
|
536
|
+
return false;
|
|
537
|
+
// Buscar if después de else (puede haber newlines)
|
|
538
|
+
let lookahead = 1;
|
|
539
|
+
while (this.peek(lookahead) === tokens_1.TokenType.NEWLINE)
|
|
540
|
+
lookahead++;
|
|
541
|
+
return this.peek(lookahead) === tokens_1.TokenType.IF;
|
|
542
|
+
}
|
|
543
|
+
parseConditionBranch() {
|
|
544
|
+
const condition = this.parseConditionKeyword();
|
|
545
|
+
const formula = this.parseFormula();
|
|
546
|
+
const body = this.parseBlock();
|
|
547
|
+
return { condition, formula, body };
|
|
548
|
+
}
|
|
549
|
+
parseConditionKeyword() {
|
|
550
|
+
if (this.match(tokens_1.TokenType.VALID))
|
|
551
|
+
return 'valid';
|
|
552
|
+
if (this.match(tokens_1.TokenType.SATISFIABLE))
|
|
553
|
+
return 'satisfiable';
|
|
554
|
+
// Aceptar "invalid" / "unsatisfiable" como identifiers
|
|
555
|
+
if (this.checkType(tokens_1.TokenType.IDENTIFIER)) {
|
|
556
|
+
const v = this.current().value.toLowerCase();
|
|
557
|
+
if (v === 'invalid' || v === 'invalido') {
|
|
558
|
+
this.advance();
|
|
559
|
+
return 'invalid';
|
|
560
|
+
}
|
|
561
|
+
if (v === 'unsatisfiable' || v === 'insatisfacible') {
|
|
562
|
+
this.advance();
|
|
563
|
+
return 'unsatisfiable';
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
// Default: valid
|
|
567
|
+
return 'valid';
|
|
568
|
+
}
|
|
569
|
+
// --- for x in {A, B, C} { body } ---
|
|
570
|
+
parseForStmt() {
|
|
571
|
+
const src = this.loc();
|
|
572
|
+
this.expect(tokens_1.TokenType.FOR);
|
|
573
|
+
const variable = this.expectName();
|
|
574
|
+
this.expect(tokens_1.TokenType.IN);
|
|
575
|
+
this.expect(tokens_1.TokenType.LBRACE);
|
|
576
|
+
const items = [];
|
|
577
|
+
if (!this.checkType(tokens_1.TokenType.RBRACE)) {
|
|
578
|
+
items.push(this.parseFormula());
|
|
579
|
+
while (this.match(tokens_1.TokenType.COMMA)) {
|
|
580
|
+
items.push(this.parseFormula());
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
this.expect(tokens_1.TokenType.RBRACE);
|
|
584
|
+
const body = this.parseBlock();
|
|
585
|
+
return { kind: 'for_stmt', variable, items, body, source: src };
|
|
586
|
+
}
|
|
587
|
+
// --- while valid FORMULA { body } ---
|
|
588
|
+
parseWhileStmt() {
|
|
589
|
+
const src = this.loc();
|
|
590
|
+
this.expect(tokens_1.TokenType.WHILE);
|
|
591
|
+
const condition = this.parseConditionKeyword();
|
|
592
|
+
const formula = this.parseFormula();
|
|
593
|
+
const body = this.parseBlock();
|
|
594
|
+
return { kind: 'while_stmt', condition, formula, body, maxIterations: 1000, source: src };
|
|
595
|
+
}
|
|
596
|
+
// --- fn nombre(param1, param2) { body } ---
|
|
597
|
+
parseFnDecl() {
|
|
598
|
+
const src = this.loc();
|
|
599
|
+
this.expect(tokens_1.TokenType.FN);
|
|
600
|
+
const name = this.expectName();
|
|
601
|
+
this.knownFunctionNames.add(name);
|
|
602
|
+
this.expect(tokens_1.TokenType.LPAREN);
|
|
603
|
+
const params = [];
|
|
604
|
+
if (!this.checkType(tokens_1.TokenType.RPAREN)) {
|
|
605
|
+
params.push(this.expectName());
|
|
606
|
+
while (this.match(tokens_1.TokenType.COMMA)) {
|
|
607
|
+
params.push(this.expectName());
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
this.expect(tokens_1.TokenType.RPAREN);
|
|
611
|
+
const body = this.parseBlock();
|
|
612
|
+
return { kind: 'fn_decl', name, params, body, source: src };
|
|
613
|
+
}
|
|
614
|
+
// --- return formula ---
|
|
615
|
+
parseReturnStmt() {
|
|
616
|
+
const src = this.loc();
|
|
617
|
+
this.expect(tokens_1.TokenType.RETURN);
|
|
618
|
+
let formula;
|
|
619
|
+
// Si hay algo después del return que no sea newline/EOF/}
|
|
620
|
+
if (!this.checkType(tokens_1.TokenType.NEWLINE) && !this.checkType(tokens_1.TokenType.EOF) &&
|
|
621
|
+
!this.checkType(tokens_1.TokenType.RBRACE)) {
|
|
622
|
+
formula = this.parseFormula();
|
|
623
|
+
}
|
|
624
|
+
return { kind: 'return_stmt', formula, source: src };
|
|
625
|
+
}
|
|
626
|
+
// --- export STATEMENT ---
|
|
627
|
+
parseExportDecl() {
|
|
628
|
+
const src = this.loc();
|
|
629
|
+
this.expect(tokens_1.TokenType.EXPORT);
|
|
630
|
+
const stmt = this.parseStatement();
|
|
631
|
+
if (!stmt) {
|
|
632
|
+
throw new Error('Se esperaba un statement después de "export"');
|
|
633
|
+
}
|
|
634
|
+
// Solo permitimos exportar declaraciones
|
|
635
|
+
const exportable = ['let_decl', 'axiom_decl', 'theorem_decl', 'fn_decl', 'theory_decl'];
|
|
636
|
+
if (!exportable.includes(stmt.kind)) {
|
|
637
|
+
throw new Error(`No se puede exportar un statement de tipo: ${stmt.kind}`);
|
|
638
|
+
}
|
|
639
|
+
return { kind: 'export_decl', statement: stmt, source: src };
|
|
640
|
+
}
|
|
641
|
+
// --- nombre(arg1, arg2) — llamada a función ---
|
|
642
|
+
parseFnCall() {
|
|
643
|
+
const src = this.loc();
|
|
644
|
+
const name = this.expectName();
|
|
645
|
+
this.expect(tokens_1.TokenType.LPAREN);
|
|
646
|
+
const args = [];
|
|
647
|
+
if (!this.checkType(tokens_1.TokenType.RPAREN)) {
|
|
648
|
+
args.push(this.parseFormula());
|
|
649
|
+
while (this.match(tokens_1.TokenType.COMMA)) {
|
|
650
|
+
args.push(this.parseFormula());
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
this.expect(tokens_1.TokenType.RPAREN);
|
|
654
|
+
return { kind: 'fn_call', name, args, source: src };
|
|
655
|
+
}
|
|
656
|
+
// --- obj.method(arg1, arg2) ---
|
|
657
|
+
parseMemberFnCall() {
|
|
658
|
+
const src = this.loc();
|
|
659
|
+
const obj = this.expectIdent();
|
|
660
|
+
this.expect(tokens_1.TokenType.DOT);
|
|
661
|
+
const method = this.expectIdent();
|
|
662
|
+
const fullName = `${obj}.${method}`;
|
|
663
|
+
this.expect(tokens_1.TokenType.LPAREN);
|
|
664
|
+
const args = [];
|
|
665
|
+
if (!this.checkType(tokens_1.TokenType.RPAREN)) {
|
|
666
|
+
args.push(this.parseFormula());
|
|
667
|
+
while (this.match(tokens_1.TokenType.COMMA)) {
|
|
668
|
+
args.push(this.parseFormula());
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
this.expect(tokens_1.TokenType.RPAREN);
|
|
672
|
+
return { kind: 'fn_call', name: fullName, args, source: src };
|
|
673
|
+
}
|
|
674
|
+
// --- Helper: parsear un bloque { statements } ---
|
|
675
|
+
parseBlock() {
|
|
676
|
+
this.skipNewlines();
|
|
677
|
+
this.expect(tokens_1.TokenType.LBRACE);
|
|
678
|
+
this.skipNewlines();
|
|
679
|
+
const body = [];
|
|
680
|
+
while (!this.checkType(tokens_1.TokenType.RBRACE) && !this.isAtEnd()) {
|
|
681
|
+
this.skipNewlines();
|
|
682
|
+
if (this.checkType(tokens_1.TokenType.RBRACE))
|
|
683
|
+
break;
|
|
684
|
+
try {
|
|
685
|
+
const stmt = this.parseStatement();
|
|
686
|
+
if (stmt)
|
|
687
|
+
body.push(stmt);
|
|
688
|
+
}
|
|
689
|
+
catch (e) {
|
|
690
|
+
const message = e instanceof Error ? e.message : 'Error de parseo en bloque';
|
|
691
|
+
this.diagnostics.push({
|
|
692
|
+
severity: 'error',
|
|
693
|
+
message,
|
|
694
|
+
file: this.file,
|
|
695
|
+
line: this.current().line,
|
|
696
|
+
column: this.current().column,
|
|
697
|
+
});
|
|
698
|
+
this.advanceToNextStatement();
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
this.expect(tokens_1.TokenType.RBRACE);
|
|
702
|
+
return body;
|
|
448
703
|
}
|
|
449
704
|
// --- Parsing de fórmulas (precedencia) ---
|
|
450
705
|
// Precedencia (de menor a mayor):
|
|
@@ -475,9 +730,11 @@ class Parser {
|
|
|
475
730
|
}
|
|
476
731
|
parseDisjunction() {
|
|
477
732
|
let left = this.parseUntil();
|
|
478
|
-
while (this.match(tokens_1.TokenType.OR)) {
|
|
733
|
+
while (this.match(tokens_1.TokenType.OR) || this.match(tokens_1.TokenType.XOR) || this.match(tokens_1.TokenType.NOR)) {
|
|
734
|
+
const type = this.previous().type;
|
|
479
735
|
const right = this.parseUntil();
|
|
480
|
-
|
|
736
|
+
const kind = type === tokens_1.TokenType.OR ? 'or' : (type === tokens_1.TokenType.XOR ? 'xor' : 'nor');
|
|
737
|
+
left = { kind, args: [left, right], source: this.loc() };
|
|
481
738
|
}
|
|
482
739
|
return left;
|
|
483
740
|
}
|
|
@@ -490,14 +747,81 @@ class Parser {
|
|
|
490
747
|
return left;
|
|
491
748
|
}
|
|
492
749
|
parseConjunction() {
|
|
750
|
+
let left = this.parseComparison();
|
|
751
|
+
while (this.match(tokens_1.TokenType.AND) || this.match(tokens_1.TokenType.NAND)) {
|
|
752
|
+
const type = this.previous().type;
|
|
753
|
+
const right = this.parseComparison();
|
|
754
|
+
const kind = type === tokens_1.TokenType.AND ? 'and' : 'nand';
|
|
755
|
+
left = { kind, args: [left, right], source: this.loc() };
|
|
756
|
+
}
|
|
757
|
+
return left;
|
|
758
|
+
}
|
|
759
|
+
// --- Arithmetic precedence ---
|
|
760
|
+
parseComparison() {
|
|
761
|
+
let left = this.parseAdditive();
|
|
762
|
+
while (this.checkType(tokens_1.TokenType.LT) ||
|
|
763
|
+
this.checkType(tokens_1.TokenType.GT) ||
|
|
764
|
+
this.checkType(tokens_1.TokenType.LTE) ||
|
|
765
|
+
this.checkType(tokens_1.TokenType.GTE)) {
|
|
766
|
+
if (this.match(tokens_1.TokenType.LT)) {
|
|
767
|
+
const right = this.parseAdditive();
|
|
768
|
+
left = { kind: 'less', args: [left, right], source: this.loc() };
|
|
769
|
+
}
|
|
770
|
+
else if (this.match(tokens_1.TokenType.GT)) {
|
|
771
|
+
const right = this.parseAdditive();
|
|
772
|
+
left = { kind: 'greater', args: [left, right], source: this.loc() };
|
|
773
|
+
}
|
|
774
|
+
else if (this.match(tokens_1.TokenType.LTE)) {
|
|
775
|
+
const right = this.parseAdditive();
|
|
776
|
+
left = { kind: 'less_eq', args: [left, right], source: this.loc() };
|
|
777
|
+
}
|
|
778
|
+
else if (this.match(tokens_1.TokenType.GTE)) {
|
|
779
|
+
const right = this.parseAdditive();
|
|
780
|
+
left = { kind: 'greater_eq', args: [left, right], source: this.loc() };
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
return left;
|
|
784
|
+
}
|
|
785
|
+
parseAdditive() {
|
|
786
|
+
let left = this.parseMultiplicative();
|
|
787
|
+
while (this.checkType(tokens_1.TokenType.PLUS) || this.checkType(tokens_1.TokenType.MINUS)) {
|
|
788
|
+
if (this.match(tokens_1.TokenType.PLUS)) {
|
|
789
|
+
const right = this.parseMultiplicative();
|
|
790
|
+
left = { kind: 'add', args: [left, right], source: this.loc() };
|
|
791
|
+
}
|
|
792
|
+
else if (this.match(tokens_1.TokenType.MINUS)) {
|
|
793
|
+
const right = this.parseMultiplicative();
|
|
794
|
+
left = { kind: 'subtract', args: [left, right], source: this.loc() };
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
return left;
|
|
798
|
+
}
|
|
799
|
+
parseMultiplicative() {
|
|
493
800
|
let left = this.parseUnary();
|
|
494
|
-
while (this.
|
|
495
|
-
|
|
496
|
-
|
|
801
|
+
while (this.checkType(tokens_1.TokenType.STAR) ||
|
|
802
|
+
this.checkType(tokens_1.TokenType.SLASH) ||
|
|
803
|
+
this.checkType(tokens_1.TokenType.PERCENT)) {
|
|
804
|
+
if (this.match(tokens_1.TokenType.STAR)) {
|
|
805
|
+
const right = this.parseUnary();
|
|
806
|
+
left = { kind: 'multiply', args: [left, right], source: this.loc() };
|
|
807
|
+
}
|
|
808
|
+
else if (this.match(tokens_1.TokenType.SLASH)) {
|
|
809
|
+
const right = this.parseUnary();
|
|
810
|
+
left = { kind: 'divide', args: [left, right], source: this.loc() };
|
|
811
|
+
}
|
|
812
|
+
else if (this.match(tokens_1.TokenType.PERCENT)) {
|
|
813
|
+
const right = this.parseUnary();
|
|
814
|
+
left = { kind: 'modulo', args: [left, right], source: this.loc() };
|
|
815
|
+
}
|
|
497
816
|
}
|
|
498
817
|
return left;
|
|
499
818
|
}
|
|
500
819
|
parseUnary() {
|
|
820
|
+
// Unary minus: -expr → subtract(0, expr)
|
|
821
|
+
if (this.match(tokens_1.TokenType.MINUS)) {
|
|
822
|
+
const operand = this.parseUnary();
|
|
823
|
+
return { kind: 'subtract', args: [{ kind: 'number', value: 0, source: this.loc() }, operand], source: this.loc() };
|
|
824
|
+
}
|
|
501
825
|
if (this.match(tokens_1.TokenType.NOT)) {
|
|
502
826
|
const operand = this.parseUnary();
|
|
503
827
|
return { kind: 'not', args: [operand], source: this.loc() };
|
|
@@ -527,6 +851,20 @@ class Parser {
|
|
|
527
851
|
return this.parseAtom();
|
|
528
852
|
}
|
|
529
853
|
parseAtom() {
|
|
854
|
+
// Literal numérico
|
|
855
|
+
if (this.checkType(tokens_1.TokenType.NUMBER)) {
|
|
856
|
+
const tok = this.current();
|
|
857
|
+
this.advance();
|
|
858
|
+
return { kind: 'number', value: parseFloat(tok.value), source: { line: tok.line, column: tok.column } };
|
|
859
|
+
}
|
|
860
|
+
// Literal de texto (String)
|
|
861
|
+
if (this.checkType(tokens_1.TokenType.STRING)) {
|
|
862
|
+
const tok = this.current();
|
|
863
|
+
this.advance();
|
|
864
|
+
// Lo representamos como un átomo especial o un nuevo kind.
|
|
865
|
+
// Por simplicidad para el motor lógico, lo tratamos como un átomo cuyo nombre es el valor del string entre comillas.
|
|
866
|
+
return { kind: 'atom', name: `"${tok.value}"`, source: { line: tok.line, column: tok.column } };
|
|
867
|
+
}
|
|
530
868
|
// Paréntesis
|
|
531
869
|
if (this.match(tokens_1.TokenType.LPAREN)) {
|
|
532
870
|
const inner = this.parseFormula();
|
|
@@ -561,19 +899,37 @@ class Parser {
|
|
|
561
899
|
return { kind: 'atom', name: `${tok.value}.${memberTok.value}`, source: { line: tok.line, column: tok.column } };
|
|
562
900
|
}
|
|
563
901
|
if (this.match(tokens_1.TokenType.LPAREN)) {
|
|
902
|
+
// Podría ser un predicado P(x, y) o una llamada a función fn(arg1, arg2)
|
|
903
|
+
if (this.knownFunctionNames.has(tok.value) || this.knownTheoryNames.has(tok.value)) {
|
|
904
|
+
const args = [];
|
|
905
|
+
if (!this.checkType(tokens_1.TokenType.RPAREN)) {
|
|
906
|
+
args.push(this.parseFormula());
|
|
907
|
+
while (this.match(tokens_1.TokenType.COMMA)) {
|
|
908
|
+
args.push(this.parseFormula());
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
this.expect(tokens_1.TokenType.RPAREN);
|
|
912
|
+
return {
|
|
913
|
+
kind: 'fn_call',
|
|
914
|
+
name: tok.value,
|
|
915
|
+
args,
|
|
916
|
+
source: { line: tok.line, column: tok.column },
|
|
917
|
+
};
|
|
918
|
+
}
|
|
564
919
|
// Predicado: P(x, y, ...)
|
|
565
920
|
const args = [];
|
|
566
921
|
if (!this.checkType(tokens_1.TokenType.RPAREN)) {
|
|
567
|
-
args.push(this.
|
|
922
|
+
args.push(this.parseFormula());
|
|
568
923
|
while (this.match(tokens_1.TokenType.COMMA)) {
|
|
569
|
-
args.push(this.
|
|
924
|
+
args.push(this.parseFormula());
|
|
570
925
|
}
|
|
571
926
|
}
|
|
572
927
|
this.expect(tokens_1.TokenType.RPAREN);
|
|
928
|
+
const paramStrings = args.map((a) => this.formulaToString(a));
|
|
573
929
|
const predFormula = {
|
|
574
930
|
kind: 'predicate',
|
|
575
931
|
name: tok.value,
|
|
576
|
-
params:
|
|
932
|
+
params: paramStrings,
|
|
577
933
|
source: { line: tok.line, column: tok.column },
|
|
578
934
|
};
|
|
579
935
|
// FOL igualdad: P(x) = Q(y) (raro pero posible)
|
|
@@ -674,6 +1030,18 @@ class Parser {
|
|
|
674
1030
|
return arg0 && arg1
|
|
675
1031
|
? `(${this.collectAssociativeArgs(f, 'or').map(a => this.formulaToString(a)).join(' | ')})`
|
|
676
1032
|
: '(? | ?)';
|
|
1033
|
+
case 'nand':
|
|
1034
|
+
return arg0 && arg1
|
|
1035
|
+
? `(${this.formulaToString(arg0)} ↑ ${this.formulaToString(arg1)})`
|
|
1036
|
+
: '(? ↑ ?)';
|
|
1037
|
+
case 'nor':
|
|
1038
|
+
return arg0 && arg1
|
|
1039
|
+
? `(${this.formulaToString(arg0)} ↓ ${this.formulaToString(arg1)})`
|
|
1040
|
+
: '(? ↓ ?)';
|
|
1041
|
+
case 'xor':
|
|
1042
|
+
return arg0 && arg1
|
|
1043
|
+
? `(${this.collectAssociativeArgs(f, 'xor').map(a => this.formulaToString(a)).join(' ⊕ ')})`
|
|
1044
|
+
: '(? ⊕ ?)';
|
|
677
1045
|
case 'implies':
|
|
678
1046
|
return arg0 && arg1
|
|
679
1047
|
? `(${this.formulaToString(arg0)} -> ${this.formulaToString(arg1)})`
|
|
@@ -702,6 +1070,11 @@ class Parser {
|
|
|
702
1070
|
}
|
|
703
1071
|
return this.tokens[this.pos];
|
|
704
1072
|
}
|
|
1073
|
+
previous() {
|
|
1074
|
+
if (this.pos === 0)
|
|
1075
|
+
return this.tokens[0];
|
|
1076
|
+
return this.tokens[this.pos - 1];
|
|
1077
|
+
}
|
|
705
1078
|
peek(offset) {
|
|
706
1079
|
const idx = this.pos + offset;
|
|
707
1080
|
if (idx >= this.tokens.length)
|
|
@@ -807,6 +1180,13 @@ class Parser {
|
|
|
807
1180
|
tokens_1.TokenType.SHOW,
|
|
808
1181
|
tokens_1.TokenType.QED,
|
|
809
1182
|
tokens_1.TokenType.THEORY,
|
|
1183
|
+
tokens_1.TokenType.PRINT,
|
|
1184
|
+
tokens_1.TokenType.SET,
|
|
1185
|
+
tokens_1.TokenType.IF,
|
|
1186
|
+
tokens_1.TokenType.FOR,
|
|
1187
|
+
tokens_1.TokenType.WHILE,
|
|
1188
|
+
tokens_1.TokenType.FN,
|
|
1189
|
+
tokens_1.TokenType.RETURN,
|
|
810
1190
|
]);
|
|
811
1191
|
while (!this.isAtEnd()) {
|
|
812
1192
|
if (this.checkType(tokens_1.TokenType.NEWLINE)) {
|