-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathforth.py
More file actions
105 lines (90 loc) · 3.79 KB
/
forth.py
File metadata and controls
105 lines (90 loc) · 3.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
"""
Script de l'intèrpret
"""
from antlr4 import InputStream, CommonTokenStream
from forthLexer import forthLexer
from forthParser import forthParser
from visitor import TreeVisitor
from errors import ForthLexicalError, ForthSyntaxError, ForthSemanticError
import sys
import readline
# Codis ANSI pels colors dels errors
RED = '\033[91m'
RESET = '\033[0m'
BOLD = '\033[1m'
def _print_error(error_type: str, message: str):
"""Imprimeix error amb format."""
# Només usar colors si estem en un terminal real (no en tests)
if sys.stdout.isatty():
print(f"{RED}{BOLD}{error_type}:{RESET}{RED} {message}{RESET}")
else:
print(f"{error_type}: {message}")
def interpret(code: str, visitor: TreeVisitor = None):
"""
Interpreta codi FORTH.
Args:
code: String amb el codi a interpretar
visitor: Visitor opcional per manteir el context (per a REPL)
"""
try:
# 1. ANÀLISI LÈXIC
# Lexer -> Converteix el codi als tokens lèxics de la gramàtica
input_stream = InputStream(code)
lexer = forthLexer(input_stream)
lexer.removeErrorListeners() # Treure l'error listener per defecte
token_stream = CommonTokenStream(lexer)
token_stream.fill() # Forçar tokenitzacio completa
# Detectar errors lèxics (tokens no reconeguts)
for token in token_stream.tokens:
if token.type == forthLexer.LEXICAL_ERROR:
raise ForthLexicalError(f"token no reconegut: '{token.text}'")
# 2. ANÀLISI SINTÀCTIC
# Parser -> Verifica que els tokens segueixen les regles gramaticals i genera l'AST
parser = forthParser(token_stream)
parser.removeErrorListeners() # Treure l'error listener per defecte
tree = parser.root() # -> Constuir AST
# Verificar errors sintàctics, no cridem al visitor si s'ha produit algun
if parser.getNumberOfSyntaxErrors() > 0:
# print(tree.toStringTree(recog=parser))
raise ForthSyntaxError("errors sintàctics detectats")
# 3. ANÀLISI SEMÀNTIC
# Utilitzem el visitor proporcionat (si estem en mode REPL) o en creem un de nou
if visitor is None:
visitor = TreeVisitor()
visitor.visit(tree)
# print(tree.toStringTree(recog=parser))
except ForthLexicalError as e:
_print_error("Lexical error", str(e))
except ForthSyntaxError as e:
_print_error("Syntax error", str(e))
except ForthSemanticError as e:
_print_error("Semantic error", str(e))
except Exception as e:
# Fallback per errors inesperats
_print_error("Error", str(e))
if __name__ == "__main__":
# Executar un arxiu
if len(sys.argv) > 1:
with open(sys.argv[1], 'r') as f:
interpret(f.read())
# Si no volem executar un arxiu, utilitzem l'intèrpret en mode REPL (Read Eval Print Loop), és a dir, mode interactiu
# També mirem que no es vulgui executar en mode interactiu de PYTHON
elif not sys.flags.interactive:
print("mini Forth 1.0")
print("A reduced version of FORTH by Fernando Guirao Núñez")
print("Programming Languages course, FIB-UPC Q1 2025-2026")
print("Type 'bye' to exit\n")
# Crear visitor amb context persistent (la pila de FORTH es manté durant la sessió)
persistent_visitor = TreeVisitor()
while True:
try:
code = input()
if code.strip() == "bye":
break
# Interpretar usando el visitor persistente
interpret(code, persistent_visitor)
except EOFError:
break
except KeyboardInterrupt:
print("\nbye")
break