aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compilador/errors.py7
-rw-r--r--compilador/lexer.py26
-rw-r--r--compilador/parser.py261
-rw-r--r--compilador/tabla.py58
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']))