Skip to content

Instantly share code, notes, and snippets.

@juanbono
Created October 23, 2023 13:04
Show Gist options
  • Save juanbono/485d08eab79c6d3f2f084758599d815b to your computer and use it in GitHub Desktop.
Save juanbono/485d08eab79c6d3f2f084758599d815b to your computer and use it in GitHub Desktop.
Ejemplo de interprete usando Lark
# Tener en cuenta que requiere la instalacion previa de lark:
# pip install lark
# correr con el comando:
# python3 interprete.py
# Basado en el ejemplo de la documentacion de Lark, le agregue algunas funciones para hacer la evaluacion.
import sys
from typing import List
from dataclasses import dataclass
from lark import Lark, ast_utils, Transformer, v_args
from lark.tree import Meta
this_module = sys.modules[__name__]
#
# Defino la clase para el AST, esta debe heredar de `ast_utils.Ast`
#
class _Ast(ast_utils.Ast):
# This will be skipped by create_transformer(), because it starts with an underscore
pass
class _Statement(_Ast):
# This will be skipped by create_transformer(), because it starts with an underscore
pass
@dataclass
class Value(_Ast):
value: object
def evaluate(self, environment):
# Para evaluar un Value, lo que debo hacer es:
# 1. Si el valor es una variable, buscar su valor en el environment.
# 2. Si el valor es un valor literal, devolverlo tal cual.
if isinstance(self.value, Name):
return environment[self.value.name]
else:
return self.value
@dataclass
class Name(_Ast):
# esta clase representa una variable.
name: str
@dataclass
class CodeBlock(_Ast, ast_utils.AsList):
# Se corresponde a `code_block` en la gramatica
# un bloque de codigo es basicamente una lista de Statements.
statements: List[_Statement]
def evaluate(self, environment):
for statement in self.statements:
statement.evaluate(environment)
@dataclass
class If(_Statement):
cond: Value # condicion
then: CodeBlock # bloque de codigo a ejecutar si la condicion es verdadera
def evaluate(self, environment):
# Para evaluar un If, lo que debo hacer es:
# 1. Evaluar la condicion
# 2. Si la condicion es verdadera, ejecutar el bloque de codigo
# que se encuentra en `then`.
if self.cond.evaluate(environment):
self.then.evaluate(environment)
@dataclass
class SetVar(_Statement):
# Se corresponde a `set_var` en la gramatica
name: str # el nombre de la variable
value: Value # el valor que tiene asignado
def evaluate(self, environment):
# Para evaluar un SetVar, lo que debo hacer es:
# 1. Evaluar el value
# 2. A ese resultado guardarlo en el environment bajo la key `name`.
environment[self.name] = self.value.evaluate(environment)
@dataclass
class Print(_Statement):
# Se corresponde a `print` en la gramatica
value: Value
def evaluate(self, environment):
# Al evaluar el print, simplemente imprimo el valor.
# delego la evaluacion del valor a la clase Value.
# Paso el environment para que en caso de que Value sea una variable,
# pueda buscar su valor en el environment.
print(self.value.evaluate(environment))
class ToAst(Transformer):
# Define extra transformation functions, for rules that don't correspond to an AST class.
def STRING(self, s):
""" Define como se convierte de un string parseado a uno del lenguaje"""
return s[1:-1]
def DEC_NUMBER(self, n):
""" Define como se convierte un numero parseado a un numero del lenguaje """
return int(n)
@v_args(inline=True)
def start(self, x):
"""Define que se hace con el nodo `start`, que es el nodo raiz del arbol"""
return x
#
# Define Parser
#
gramatica = """
start: code_block
code_block: statement+
?statement: if | set_var | print
if: "if" value "{" code_block "}"
set_var: NAME "=" value ";"
print: "print" value ";"
value: name | STRING | DEC_NUMBER
name: NAME
%import python (NAME, STRING, DEC_NUMBER)
%import common.WS
%ignore WS
"""
# defino el parser, pasandole la gramatica y especificando que se genere
# un parser LALR
parser = Lark(grammar=gramatica, parser="lalr")
# especifico como se van a transformar los nodos parseados a nodos del AST
transformer = ast_utils.create_transformer(this_module, ToAst())
def parse(text):
"""
funcion utilitaria que parsea un string, lo transforma a un parse tree y luego
usando el transformer lo transforma a un arbol de sintaxis abstracta que podemos
recorrer y evaluar.
"""
tree = parser.parse(text)
return transformer.transform(tree)
def evaluate_program(program, environment):
ast = parse(program)
ast.evaluate(environment)
#
# Test
#
if __name__ == '__main__':
program = """
a = 1;
if a {
print "a is 1";
a = 2;
print "now: a is 2";
}
"""
ast = parse(program)
print(ast)
ast.evaluate({})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment