diff options
-rw-r--r-- | compilador/errors.py | 7 | ||||
-rw-r--r-- | compilador/lexer.py | 26 | ||||
-rw-r--r-- | compilador/parser.py | 261 | ||||
-rw-r--r-- | compilador/tabla.py | 58 |
4 files changed, 113 insertions, 239 deletions
diff --git a/compilador/errors.py b/compilador/errors.py index f5b4203..87c925e 100644 --- a/compilador/errors.py +++ b/compilador/errors.py @@ -1,4 +1,7 @@ import sys +from typing import List + +from tabla import Token class Error: errors = { @@ -14,3 +17,7 @@ class Error: def __init__(self, error, numlinea): print("Error en línea %d: %s" % (numlinea, self.errors[error]), file=sys.stderr) + + def __init__(self, got: Token, expects: List[Token], numlinea = int): + strexp = ', '.join(['`%s\'' % e.value for e in expects]) + self.message = "Error en la línea %d, se encontró `%s', pero se esperaba %s" % (numlinea, got.value, strexp) diff --git a/compilador/lexer.py b/compilador/lexer.py index 8bb7c13..2106c3f 100644 --- a/compilador/lexer.py +++ b/compilador/lexer.py @@ -1,5 +1,5 @@ from enum import Enum -from tabla import LexToken, TablaLex, tokens +from tabla import LexToken, TablaLex, Token, tokens from parser import Parser from shared import Control from errors import Error @@ -34,6 +34,8 @@ class Selector(Enum): ENTERO = 5 class Lexer: + tabla: TablaLex = None + def __init__(self, input_file: str): self.tabla = TablaLex() self.numlinea = 1 @@ -94,7 +96,7 @@ class Lexer: return Control.SIGUIENTE # Entrada a tokens simples elif c in otros_tokens or (c == '*' and self.recol_comentario == ''): - self.insertar_tabla(c, None, None) + self.insertar_tabla(Token(c), None, None) return Control.SIGUIENTE # Apertura de comentario @@ -108,14 +110,14 @@ class Lexer: rc = self.recol_operador + c if rc in op_compuestos: # Operador compuesto - self.insertar_tabla(rc, None, None) + self.insertar_tabla(Token(rc), None, None) self.recol_operador = '' return Control.SIGUIENTE else: # Operador simple - self.insertar_tabla(self.recol_operador, None, None) + self.insertar_tabla(Token(self.recol_operador), None, None) if c in op_simples: - self.insertar_tabla(c, None, None) + self.insertar_tabla(Token(c), None, None) self.recol_operador = '' return Control.SIGUIENTE @@ -143,7 +145,7 @@ class Lexer: def procesar_cadena(self, c): if c == '"': - self.insertar_tabla('STRING_LIT', None, self.recol_string) + self.insertar_tabla(Token.STRING_LIT, None, self.recol_string) self.selector = Selector.NINGUNO self.recol_string = '' else: @@ -158,7 +160,7 @@ class Lexer: if len(self.recol_caracter) == 0: Error('L_CAR_VACIO', self.numlinea) return Control.ERROR - self.insertar_tabla('CHAR_LIT', None, self.recol_caracter) + self.insertar_tabla(Token.CHAR_LIT, None, self.recol_caracter) self.selector = Selector.NINGUNO self.recol_caracter = '' else: @@ -181,13 +183,13 @@ class Lexer: self.recol_ident += c else: if self.recol_ident in reservadas.keys(): - self.insertar_tabla(reservadas[self.recol_ident], None, None) + self.insertar_tabla(Token(self.recol_ident), None, None) elif self.recol_ident == 'verdadero': - self.insertar_tabla('BOOLEAN_LIT', None, True) + self.insertar_tabla(Token.BOOLEAN_LIT, None, True) elif self.recol_ident == 'falso': - self.insertar_tabla('BOOLEAN_LIT', None, False) + self.insertar_tabla(Token.BOOLEAN_LIT, None, False) else: - self.insertar_tabla('IDENT', self.recol_ident, None) + self.insertar_tabla(Token.IDENT, self.recol_ident, None) self.recol_ident = '' self.selector = Selector.NINGUNO return Control.REPETIR @@ -197,7 +199,7 @@ class Lexer: if c.isdigit(): self.recol_entero += c else: - self.insertar_tabla('INT_LIT', None, int(self.recol_entero)) + self.insertar_tabla(Token.INT_LIT, None, int(self.recol_entero)) self.recol_entero = '' self.selector = Selector.NINGUNO return Control.REPETIR diff --git a/compilador/parser.py b/compilador/parser.py index f75f817..b1fd411 100644 --- a/compilador/parser.py +++ b/compilador/parser.py @@ -4,231 +4,54 @@ from arbol import Arbol, Nodo from shared import Control from pprint import pprint from errors import Error +from typing import NoReturn -valores = ['IDENT', 'BOOLEAN_LIT', 'CHAR_LIT', 'INT_LIT', 'STRING_LIT'] - -operadores = [ - '>=', '<=', '==', '!=', '&&', '||', '++', '--', - '=', '+', '-', '&', '|', '!', '<', '>' -] - -class Selector(Enum): - NINGUNO = 0 - DEF_VARIABLE = 1 - DIRECTIVA = 2 - EXPRESION = 3 - IF = 4 - FOR = 5 - WHILE = 6 - FUNCION = 7 +from tabla import TablaLex, Token +from errors import Error class Parser: def __init__(self, input_file: str): - self.input_file = input_file - self.arbol = Arbol() - self.pila_selector = [ - [Selector.NINGUNO, []] # selector, recolector - ] - self.pila_arbol = [self.arbol.raiz] - self.expresion = None self.tabla = TablaLex() self.tabla.importar(input_file + '.tab') - - def inicio (self): - for t in self.tabla.tabla: - r = self.procesar(t) - if r == Control.ERROR: return 1 - while r != Control.SIGUIENTE: - r = self.procesar(t) - if r == Control.ERROR: return 1 + self.iterador = self.tabla.iterar() - self.arbol.render(self.input_file + '.gv') + def inicio(self): + tok = self.want(Token.STRING_LIT, Token.BOOLEAN_LIT) + if type(tok) == Error: + print(tok.message) + return 1 return 0 - def procesar (self, t: LexToken): - if len(self.pila_selector) == 0: - return Control.SIGUIENTE - - pprint (self.pila_selector[-1]) - - cima = self.pila_selector[-1] - - if cima[0] == Selector.NINGUNO: - # Entrada a definición de variable (o función) - if t.tipo in ['BOOLEAN', 'CHAR', 'INT', 'VOID']: - self.pila_selector.pop() - self.pila_selector.append([Selector.DEF_VARIABLE, [t]]) - return Control.SIGUIENTE - # Entrada a directiva del lenguaje - elif t.tipo in ['PRINT', 'READ', 'RETURN']: - self.pila_selector.pop() - self.pila_selector.append([Selector.DIRECTIVA, [t]]) - return Control.SIGUIENTE - # Entrada a expresión - elif t.tipo in valores: - self.pila_selector.pop() - self.pila_selector.append([Selector.EXPRESION, [t]]) - return Control.SIGUIENTE - # Entrada a if - elif t.tipo == 'IF': - self.pila_selector.pop() - self.pila_selector.append([Selector.IF, []]) - return Control.SIGUIENTE - # Entrada a for - elif t.tipo == 'FOR': - self.pila_selector.pop() - self.pila_selector.append([Selector.FOR, []]) - return Control.SIGUIENTE - # Entrada a while - elif t.tipo == 'WHILE': - self.pila_selector.pop() - self.pila_selector.append([Selector.WHILE, []]) - return Control.SIGUIENTE - - if cima[0] == Selector.DEF_VARIABLE: - return self.procesar_def_variable(t) - - if cima[0] == Selector.DIRECTIVA: - return self.procesar_directiva(t) - - if cima[0] == Selector.EXPRESION: - return self.procesar_expresion(t) - - if cima[0] == Selector.IF: - return self.procesar_if(t) - - if cima[0] == Selector.FOR: - return self.procesar_for(t) - - if cima[0] == Selector.WHILE: - return self.procesar_while(t) - - if cima[0] == Selector.FUNCION: - return self.procesar_funcion(t) - - return Control.SIGUIENTE - - def procesar_def_variable(self, t): - recol = self.pila_selector[-1][1] - - # tipo - if len(recol) == 1: - if t.tipo != 'IDENT': - Error('S_ESPERA_IDENT', t.numlinea) - return Control.ERROR - recol.append(t) - return Control.SIGUIENTE - - # tipo + ident - if len(recol) == 2: - if t.tipo == ';': - self.pila_arbol[-1].hijos.append(Nodo({ - 'selector': Selector.DEF_VARIABLE, - 'tipo': recol[0].tipo, - 'nombre': recol[1].nombre - })) - self.pila_selector.pop() - self.pila_selector.append([Selector.NINGUNO, []]) - elif t.tipo == '=': - recol.append(t) - else: - Error('S_ESPERA_PC_O_IGUAL', t.numlinea) - return Control.ERROR - return Control.SIGUIENTE - - # tipo + ident + = - if len(recol) == 3: - if t.tipo in valores: - self.pila_selector.append([Selector.EXPRESION, [t]]) - recol.append(t) - else: - Error('S_ESPERA_EXPR', t.numlinea) - return Control.ERROR - return Control.SIGUIENTE - - # tipo + ident + = + expr - if len(recol) == 4: - if t.tipo == ';': - self.pila_arbol[-1].hijos.append(Nodo({ - 'selector': Selector.DEF_VARIABLE, - 'tipo': recol[0].tipo, - 'nombre': recol[1].nombre, - 'valor': self.expresion - })) - self.expresion = None - self.pila_selector.pop() - self.pila_selector.append([Selector.NINGUNO, []]) - else: - Error('S_ESPERA_PC', t.numlinea) - return Control.ERROR - - return Control.SIGUIENTE - - def procesar_directiva(self, t): - recol = self.pila_selector[-1][1] - - # directiva - if len(recol) == 1: - if t.tipo in valores: - self.pila_selector.append([Selector.EXPRESION, [t]]) - recol.append(t) - else: - Error('S_ESPERA_EXPR', t.numlinea) - return Control.ERROR - return Control.SIGUIENTE - - # directiva + expr - if len(recol) == 2: - if t.tipo == ';': - self.pila_arbol[-1].hijos.append(Nodo({ - 'selector': Selector.DIRECTIVA, - 'expresion': self.expresion - })) - self.expresion = None - self.pila_selector.pop() - self.pila_selector.append([Selector.NINGUNO, []]) - else: - Error('S_ESPERA_EXPR', t.numlinea) - return Control.ERROR - - return Control.SIGUIENTE - - def procesar_expresion(self, t): - recol = self.pila_selector[-1][1] - tipo_ultimo = recol[-1].tipo - - if len(recol) == 1: - if tipo_ultimo in valores: - recol.append(recol[-1]) - else: - Error('S_ESPERA_IDENT_O_LIT', t.numlinea) - return Control.ERROR - - if tipo_ultimo in valores and t.tipo in operadores: - recol.append(t) - elif tipo_ultimo in operadores and t.tipo in valores: - recol.append(t) - elif tipo_ultimo in valores and t.tipo in valores: - Error('S_ESPERA_OPER', t.numlinea) - return Control.ERROR - elif tipo_ultimo in operadores and t.tipo in operadores: - Error('S_ESPERA_IDENT_O_LIT', t.numlinea) - return Control.ERROR - else: - self.expresion = recol[1:] - self.pila_selector.pop() - return Control.REPETIR - - return Control.SIGUIENTE - - def procesar_if(self, t): - return - - def procesar_for(self, t): - return - - def procesar_while(self, t): - return - - def procesar_funcion(self, t): - return + ''' Requires the next token to have a matching ltok. Returns that + token, or an error. ''' + def want(self, *want: Token) -> (Token | Error): + tok: LexToken = next(self.iterador) + if len(want) == 0: + return tok + for w in want: + if tok.tipo == w: + return tok + return Error(got = tok.tipo, expects = want, numlinea = tok.numlinea) + + ''' Looks for a matching ltok from the lexer, and if not present, + unlexes the token and returns void. If found, the token is + consumed from the lexer and is returned. ''' + def _try(self, *want: Token) -> (Token | NoReturn): + tok: LexToken = next(self.iterador) + if len(want) == 0: + return tok + for w in want: + if tok.tipo == w: + return tok + self.iterador.seek(-1) + + ''' Looks for a matching ltok from the lexer, unlexes the token, + and returns it; or void if it was not an ltok. ''' + def peek(self, *want: Token) -> (Token | NoReturn): + tok: LexToken = next(self.iterador) + self.iterador.seek(-1) + if len(want) == 0: + return tok + for w in want: + if tok.tipo == w: + return tok diff --git a/compilador/tabla.py b/compilador/tabla.py index ebe6225..62e1b9d 100644 --- a/compilador/tabla.py +++ b/compilador/tabla.py @@ -1,15 +1,14 @@ import json, os -from enum import Enum +from enum import Enum, auto from dataclasses import dataclass from typing import Any +from more_itertools import seekable reservadas = [ 'BOOLEAN', - 'BREAK', 'CHAR', 'DOUBLE', 'ELSE', - 'FOR' 'IDENT', 'IF', 'INT', @@ -29,23 +28,63 @@ literales = [ 'STRING_LIT' ] +class Token(Enum): + BOOLEAN = 'booleano' + CHAR = 'caracter' + DOUBLE = 'doble' + ELSE = 'sino' + IDENT = 'IDENT' + IF = 'si' + INT = 'entero' + PRINT = 'imprimir' + READ = 'leer' + RETURN = 'retorna' + STRING = 'cadena' + VOID = 'vacio' + WHILE = 'mientras' + BOOLEAN_LIT = 'BOOLEAN_LIT' + INT_LIT = 'INT_LIT' + CHAR_LIT = 'CHAR_LIT' + STRING_LIT = 'STRING_LIT' + L_BRACKET = '{' + R_BRACKET = '}' + L_PAREN = '(' + R_PAREN = ')' + COMMA = ',' + SQUOTE = '\'' + DQUOTE = '"' + SEMICOLON = ';' + EQUAL = '=' + TIMES = '*' + SLASH = '/' + PLUS = '+' + MINUS = '-' + GT = '>' + LT = '<' + GEQ = '>=' + LEQ = '<=' + AND = '&&' + OR = '||' + EQEQ = '==' + NOTEQ = '!=' + tokens = reservadas + literales + [ '{', '}', '(', ')', ',', '\'', '"', ';', '=', '*', '/', '+', '-', '>', '<', '>=', '<=', '&&', - '||', '==', '!=', '++', '--', '//' + '||', '==', '!=' ] @dataclass class LexToken: - tipo: str + tipo: Token nombre: str valor: Any numlinea: int def __str__(self): return "LexToken(%s,%s,%s,%i)" % ( - self.tipo, self.nombre, self.valor, self.numlinea + self.tipo.name, self.nombre, self.valor, self.numlinea ) class TablaLex: @@ -58,6 +97,9 @@ class TablaLex: def buscar(self, nombre: str): return [t for t in self.tabla if t.nombre == nombre][0] + def iterar(self): + return seekable(self.tabla) + def actualizar(self, nombre: str, tok: LexToken): for i, t in enumerate(self.tabla): if t.nombre == nombre: @@ -68,7 +110,7 @@ class TablaLex: data = [] for t in self.tabla: data.append({ - 'tipo': t.tipo, + 'tipo': t.tipo.value, 'nombre': t.nombre, 'valor': t.valor, 'numlinea': t.numlinea @@ -84,7 +126,7 @@ class TablaLex: with open(input_file, 'r') as f: data = json.loads(f.read()) for t in data: - self.insertar(LexToken(t['tipo'], + self.insertar(LexToken(Token(t['tipo']), t['nombre'], t['valor'], t['numlinea'])) |