@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.
Files changed (55) hide show
  1. package/README.md +346 -69
  2. package/dist/ast/nodes.d.ts +55 -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 +72 -16
  6. package/dist/lexer/lexer.js.map +1 -1
  7. package/dist/lexer/tokens.d.ts +22 -0
  8. package/dist/lexer/tokens.d.ts.map +1 -1
  9. package/dist/lexer/tokens.js +46 -0
  10. package/dist/lexer/tokens.js.map +1 -1
  11. package/dist/parser/parser.d.ts +20 -0
  12. package/dist/parser/parser.d.ts.map +1 -1
  13. package/dist/parser/parser.js +390 -10
  14. package/dist/parser/parser.js.map +1 -1
  15. package/dist/profiles/arithmetic/index.d.ts +20 -0
  16. package/dist/profiles/arithmetic/index.d.ts.map +1 -0
  17. package/dist/profiles/arithmetic/index.js +222 -0
  18. package/dist/profiles/arithmetic/index.js.map +1 -0
  19. package/dist/profiles/classical/propositional.d.ts.map +1 -1
  20. package/dist/profiles/classical/propositional.js +84 -0
  21. package/dist/profiles/classical/propositional.js.map +1 -1
  22. package/dist/profiles/index.d.ts +2 -1
  23. package/dist/profiles/index.d.ts.map +1 -1
  24. package/dist/profiles/index.js +4 -1
  25. package/dist/profiles/index.js.map +1 -1
  26. package/dist/profiles/intuitionistic/propositional.d.ts.map +1 -1
  27. package/dist/profiles/intuitionistic/propositional.js +27 -0
  28. package/dist/profiles/intuitionistic/propositional.js.map +1 -1
  29. package/dist/profiles/probabilistic/basic.d.ts.map +1 -1
  30. package/dist/profiles/probabilistic/basic.js +12 -0
  31. package/dist/profiles/probabilistic/basic.js.map +1 -1
  32. package/dist/protocol/handler.d.ts +1 -0
  33. package/dist/protocol/handler.d.ts.map +1 -1
  34. package/dist/protocol/handler.js +318 -2
  35. package/dist/protocol/handler.js.map +1 -1
  36. package/dist/runtime/format.d.ts.map +1 -1
  37. package/dist/runtime/format.js +94 -2
  38. package/dist/runtime/format.js.map +1 -1
  39. package/dist/runtime/interpreter.d.ts +31 -0
  40. package/dist/runtime/interpreter.d.ts.map +1 -1
  41. package/dist/runtime/interpreter.js +555 -83
  42. package/dist/runtime/interpreter.js.map +1 -1
  43. package/dist/tests/arithmetic.test.d.ts +2 -0
  44. package/dist/tests/arithmetic.test.d.ts.map +1 -0
  45. package/dist/tests/arithmetic.test.js +416 -0
  46. package/dist/tests/arithmetic.test.js.map +1 -0
  47. package/dist/tests/examples.test.d.ts +2 -0
  48. package/dist/tests/examples.test.d.ts.map +1 -0
  49. package/dist/tests/examples.test.js +85 -0
  50. package/dist/tests/examples.test.js.map +1 -0
  51. package/dist/tests/v1-features.test.js +339 -2
  52. package/dist/tests/v1-features.test.js.map +1 -1
  53. package/dist/types/index.d.ts +3 -1
  54. package/dist/types/index.d.ts.map +1 -1
  55. package/package.json +2 -1
@@ -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
- left = { kind: 'or', args: [left, right], source: this.loc() };
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.match(tokens_1.TokenType.AND)) {
495
- const right = this.parseUnary();
496
- left = { kind: 'and', args: [left, right], source: this.loc() };
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.expectIdent());
922
+ args.push(this.parseFormula());
568
923
  while (this.match(tokens_1.TokenType.COMMA)) {
569
- args.push(this.expectIdent());
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: args,
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)) {