Está en la página 1de 1

Lexer Mini-Lua

Fecha Límite: Septiembre 3 de 2020

1. Introducción
Su primera tarea es implementar un lexer (o escáner) para Mini-Lua, que convertirá un flujo de entrada de caracteres en un
flujo de tokens. Si bien estos programas a menudo se escriben mejor con un generador de lexer (por ejemplo, ML-Lex o
Flex), para esta tarea escribirás un escáner desde cero.

2. Convenciones Léxicas Mini-Lua


Mini-Lua tiene cuatro clases de tokens: identificadores, delimitadores y operadores, números y literales de cadena. Los
tokens pueden estar separados por espacios en blanco y/o comentarios.

Los identificadores en Mini-Lua pueden ser cualquier cadena de letras, dígitos y guion bajo, sin comenzar con un dígito.
Los identificadores distinguen entre mayúsculas y minúsculas (por ejemplo, foo es diferente de Foo). Los siguientes
identificadores están reservados como palabras clave:

Tenga en cuenta que estas son las palabras clave de Lua; repeat y until que estén reservados en Mini-Lua, pero no
son usadas.

Mini-Lua también tiene una colección de delimitadores y operadores, que son los siguientes:

Los números en Mini-Lua son enteros y sus literales están escritos usando notación decimal (sin signo).

Los literales de cadena están delimitados por la coincidencia de comillas dobles y pueden contener las siguientes
secuencias de escape tipo C:

Un carácter en una cadena literal también puede especificarse por su valor numérico usando la secuencia de escape
"\ddd", donde ddd es una secuencia de tres dígitos decimales. Las cadenas en Lua pueden contener cualquier valor de 8
bits, incluidos ceros incrustados, que se pueden especificar como "\000".

Los comentarios comienzan en cualquier lugar fuera de una cadena con un guión doble ( -- ). Si el texto inmediatamente
después de -- es diferente de [[ , el comentario es un comentario corto, que se extiende hasta el final de la línea. De lo
contrario, es un comentario largo, que corre hasta el correspondiente ]] . Los comentarios largos pueden abarcar varias
líneas y pueden contener pares [[ / ]] anidados.

Los espacios en blanco son cualquier secuencia de espacios no vacíos (código ASCII 32), tabuladores horizontales o
verticales (VT, HT), nueva página (FF) o retornos de carro (CR). Cualquier otro carácter no imprimible debe tratarse como un
error.

In [ ]: import re
from .errors import error

In [ ]: NAME_PAT = re.compile('[a-zA-Z_][a-zA-Z0-9_]*')
FLOAT_PAT = re.compile(r'(\d+\.\d*)|(\d*\.\d+)')
INT_PAT = re.compile(r'\d+')

TWO_CHAR = {
'<=' : 'LE',
'>=' : 'GE',
'==' : 'EQ',
'~=' : 'NE',
}

ONE_CHAR = {
'+' : 'PLUS',
'-' : 'MINUS',
'*' : 'TIMES',
'/' : 'DIVIDE',
'^' : 'POWER',
'=' : 'ASSIGN',
'<' : 'LT',
'>' : 'GT',
'(' : 'LPAREN',
')' : 'RPAREN',
'{' : 'LBRACE',
'}' : 'RBRACE',
',' : 'COMMA',
';' : 'SEMI',
}

In [ ]: KEYWORDS = {
'and', 'break', 'do', 'else', 'elseif',
'end', 'false', 'for', 'function', 'if',
'in', 'local', 'nil', 'not', 'or',
'repeat', 'return', 'then', 'true', 'until', 'while'
}

class Token:
def __init__(self, type, value, lineno):
self.type = type
self.value = value
self.lineno = lineno

def __repr__(self):
return f'Token({self.type!r}, {self.value!r}, {self.lineno})'

def tokenize(text):
index = 0 # Posición actual
lineno = 1

while index < len(text):


# Produce un token
if text[index] in ' \t':
index += 1
continue
elif text[index] == '\n':
lineno += 1
index += 1
continue
# Comentarios Largos

# Comentarios Cortos

# Coincidencia de simbolos a traves de expresiones regulares


m = NAME_PAT.match(text, index)
if m:
value = m.group(0)
if value in KEYWORDS:
yield Token(value.upper(), value, lineno)
else:
yield Token('NAME', m.group(0), lineno)
index += len(value)
continue

In [ ]: def main(argv):
if len(argv) != 2:
raise SystemExit(f'Usage: {argv[0]} filename')
with open(argv[1]) as file:
for tok in tokenize(file.read()):
print(tok)

if __name__ == '__main__':
import sys
main(sys.argv)

In [ ]: # errors.py
#
# Soporte de manejo de errores del compilador.
#
# Una de las partes más importantes (y molestas) de escribir un compilador
# es la notificación confiable de los mensajes de error al usuario. Este
# archivo debería consolidar algunas funciones básicas de manejo de errores
# en un solo lugar. Facilite la notificación de errores. Facilite la
# búsqueda de errores.

# Podría expandir esto para que sea más poderoso más adelante

# Variable global que indica si se ha producido algún error. El compilador


# puede ver esto más tarde para decidir si morir o no.

_errors_detected = 0

def error(message, lineno=None):


global _errors_detected
if lineno:
print(f'{lineno}: {message}')
else:
print(message)
_errors_detected += 1

def errors_detected():
return _errors_detected

def clear_errors():
global _errors_detected
_errors_detected = 0

También podría gustarte