Created
October 23, 2023 13:04
-
-
Save juanbono/485d08eab79c6d3f2f084758599d815b to your computer and use it in GitHub Desktop.
Ejemplo de interprete usando Lark
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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