Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Analizador Lexico Sintactico
Analizador Lexico Sintactico
ANALIZADOR LEXICO
Y SINTACTICO
LENGUAJES y AUTOMATAS I
Antecedentes Históricos
Los analizadores tienen su origen en la teoría de autómatas y la
lingüística computacional. A lo largo de los años, han evolucionado para
abordar lenguajes de programación más complejos y diversas aplicaciones.
Analizador Léxico
Conceptos Básicos
El analizador léxico se encarga de dividir el código fuente en una serie de
tokens. Estos tokens representan las unidades mínimas de significado en un
programa.
Algoritmos y Herramientas
Existen varios algoritmos para el análisis léxico, como los autómatas
finitos y las expresiones regulares. Herramientas como Lex facilitan este
proceso.
Desafíos y Soluciones
El manejo de ambigüedades y la optimización del rendimiento son desafíos
comunes en el diseño de analizadores léxicos. Soluciones como el uso de
algoritmos de retroceso o la implementación de tablas de símbolos pueden
ayudar a mitigar estos problemas.
class TokenDefinitions:
"""
Esta clase encapsula todas las definiciones de tokens.
"""
# Asignación de palabras clave a sus tipos de tokens
PALABRAS_CLAVE = {
'if': 'IF',
'else': 'ELSE',
'int': 'INT',
'var': 'VAR',
'print': 'PRINT',
'true': 'TRUE',
'false': 'FALSE',
'while': 'WHILE',
}
@staticmethod
def get_compiled_regex():
"""
Devuelve un regex compilado para la coincidencia de tokens, combinando
tokens simples y complejos.
"""
# Nota: Los tokens complejos deben evaluarse primero para identificarlos
correctamente antes que los tokens simples.
all_tokens = TokenDefinitions.COMPLEX_TOKENS +
TokenDefinitions.SIMPLE_TOKENS
token_patterns = '|'.join(f'(?P<{name}>{pattern})' for name, pattern in
all_tokens)
return re.compile(token_patterns, re.DOTALL | re.MULTILINE | re.UNICODE)
class Lexer:
"""
Esta clase convierte el código fuente en una secuencia de tokens.
Se basa en un patrón regex precompilado para identificar tipos de tokens.
"""
def __init__(self, error_handler=None):
"""
Inicializa el lexer con un manejador de errores.
"""
self.linea_num = 1
self.linea_inicio = 0
self.error_handler = error_handler if error_handler else ErrorHandler()
self.comments = []
if tipo == 'SKIP':
continue
elif tipo == 'COMENTARIO':
self.comments.append((valor, self.linea_num, columna + 1))
continue
elif tipo == 'NEWLINE':
self.linea_inicio = mo.end()
self.linea_num += 1
elif tipo == 'MISMATCH':
self.error_handler.handle(f'Carácter inesperado {valor!r}',
self.linea_num, columna)
elif tipo == 'ID' and valor in TokenDefinitions.PALABRAS_CLAVE:
tipo = TokenDefinitions.PALABRAS_CLAVE[valor]
yield Token(tipo, valor, self.linea_num, columna + 1)
Conceptos Básicos
El analizador sintáctico toma los tokens generados por el analizador léxico
y construye un Árbol de Sintaxis Abstracta (AST).
Algoritmos y Herramientas
Los algoritmos como el análisis LL, LR y LALR son comúnmente utilizados en
el análisis sintáctico. Herramientas como Yacc o Bison se emplean para
generar analizadores sintácticos.
Desafíos y Soluciones
Los principales desafíos incluyen el manejo de errores sintácticos y la
optimización del análisis. Técnicas como el análisis de recuperación o la
predicción pueden ser útiles aquí.
Casos de Uso
Los analizadores se utilizan en compiladores, intérpretes, editores de
texto con resaltado de sintaxis, y en diversas aplicaciones de
procesamiento de texto y datos.
class Parser:
def __init__(self, tokens):
self.tokens = tokens
self.index = 0
self.next_token = None
self._advance()
def _advance(self):
self.next_token = self.tokens[self.index] if self.index <
len(self.tokens) else None
self.index += 1
def parse_expression(self):
return self._parse_binary_expression(0)
left_expr = self._parse_primary()
while self.next_token and self.next_token.tipo in operator_precedence:
operator = self.next_token.tipo
precedence = operator_precedence[operator]
self._advance()
right_expr = self._parse_binary_expression(precedence + 1)
left_expr = BinaryOperation(left_expr, operator, right_expr)
def _parse_primary(self):
if self.next_token.tipo == 'NUMERO':
node = Number(self.next_token.valor)
self._advance()
return node
elif self.next_token.tipo == 'ID':
node = Variable(self.next_token.valor)
self._advance()
return node
elif self.next_token.tipo == 'CADENA':
node = String(self.next_token.valor)
self._advance()
return node
elif self.next_token.tipo == 'TRUE':
node = Number(1)
self._advance()
return node
elif self.next_token.tipo == 'FALSE':
node = Number(0)
self._advance()
return node
else:
raise ValueError(f'Error de sintaxis: token inesperado
{self.next_token.tipo}')
def parse_statement(self):
if self.next_token.tipo == 'NEWLINE':
self._advance()
return None
elif self.next_token.tipo == 'CADENA':
node = String(self.next_token.valor)
self._advance()
return node
elif self.next_token.tipo == 'VAR':
return self._parse_var_declaration()
elif self.next_token.tipo == 'IF':
return self._parse_if_statement()
elif self.next_token.tipo == 'WHILE':
return self._parse_while_statement()
elif self.next_token.tipo == 'PRINT':
return self._parse_print_statement()
elif self.next_token.tipo == 'ID':
def _parse_id_related_statement(self):
var_name = self.next_token.valor
self._advance()
if self.next_token.tipo == 'IGUAL':
self._advance()
expr = self.parse_expression()
return Assignment(var_name, expr)
else:
raise ValueError(f'Error de sintaxis: token inesperado
{self.next_token.tipo}')
def _parse_var_declaration(self):
var_type = self.next_token.tipo
self._advance()
var_name = self.next_token.valor
self._advance()
initial_value = None
if self.next_token.tipo == 'IGUAL':
self._advance()
initial_value = self.parse_expression()
self._consume('PUNTO_Y_COMA')
def _parse_block_statements(self):
body_statements = []
while self.next_token.tipo != 'LLAVE_DER':
stmt = self.parse_statement()
if stmt:
body_statements.append(stmt)
return body_statements
def _parse_if_statement(self):
self._consume('IF')
self._consume('LPAREN')
condition = self._parse_binary_expression(0)
else_body = None
if self.next_token and self.next_token.tipo == 'ELSE':
self._consume('ELSE')
self._consume('LLAVE_IZQ')
else_body = self._parse_block_statements()
self._consume('LLAVE_DER')
def _parse_while_statement(self):
self._consume('WHILE')
self._consume('LPAREN')
condition = self.parse_expression()
self._consume('RPAREN')
self._consume('LLAVE_IZQ')
body = self._parse_block_statements()
self._consume('LLAVE_DER')
return WhileStatement(condition, body)
def _parse_print_statement(self):
self._consume('PRINT')
self._consume('LPAREN')
expression = self.parse_expression()
self._consume('RPAREN')
self._consume('PUNTO_Y_COMA')
return PrintStatement(expression)
def parse_block(self):
statements = []
while self.next_token and self.next_token.tipo != 'LLAVE_DER':
stmt = self.parse_statement()
if stmt:
statements.append(stmt)
return statements
def parse_program(self):
return self.parse_block()
parser.py
● Objetivo: Realizar el análisis sintáctico para construir un Árbol de
Sintaxis Abstracta (AST).
● Clase Parser: Utiliza los tokens generados por Lexer para crear un
AST. Incluye métodos como _advance y _consume para manipular la
secuencia de tokens.
ast_nodes.py
● Objetivo: Definir los nodos que compondrán el AST.
● Clases como ASTNode, BinaryOperation, etc.: Estas clases representan
diferentes tipos de nodos en el AST.
evaluator.py
● Objetivo: Evaluar el AST para ejecutar el programa.
● Clase Evaluator: Evalúa los nodos del AST. Utiliza un diccionario
para almacenar variables y su estado.