¿Te resulta más fácil leer con fondo crema, un tono de color, más contraste o un texto más grande? Elige tus colores y tu texto una sola vez, para todas las páginas.

Lección gratuita

Ejecutar un árbol sintáctico directamente (intérprete)

Después de esta lección: Puedo ejecutar directamente un árbol sintáctico.

Lección de 50 min8 comandosLaboratorio de compiladores y arquitectura: gcc, flex, bison y RISC-V en una VM

Una tarifa que cambia cada semana

Una empresa de repartos deja que su responsable escriba la tarifa en una página de ajustes: fee if km > 5 else 0. La regla cambia cada semana.

Nadie quiere compilar y reiniciar el servicio por cada cambio. El servicio guarda el árbol sintáctico de la regla y lo calcula, nodo a nodo, con cada pedido.

Al final de esta lección

Nota

Puedo ejecutar directamente un árbol sintáctico.

Palabras clave

PalabraQué significaEjemplo
Intérpreteun programa que ejecuta otro sin crear código máquina
Nodoun punto del árbolBinOp
Evaluarcalcular el valor de un nodo
Hojaun nodo sin nada debajoConstant(value=3)
Entornolos nombres al alcance y sus valores{'base': 20}
Ligaduraun nombre unido a su valor

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Python te enseña su árbol

Python también es un compilador. Antes de ejecutar una línea, la analiza y construye su árbol sintáctico. El módulo ast imprime ese árbol para una línea que le llega por una tubería. --mode eval dice que la línea es una sola expresión.

Cada nodo tiene un nombre. Una hoja no tiene nada debajo. Compara los dos árboles.

El árbol de 10 - 4 - 3

Comando
echo "10 - 4 - 3" | python3 -m ast --mode eval

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Un árbol con nombres y paréntesis

Comando
echo "base * (1 + rate)" | python3 -m ast --mode eval

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Ejemplo: los nodos que imprimió Python

NodoQué esDe dónde sale su valor
Constant(value=10)Una hoja con un númeroDel número mismo
Name(id='base')Una hoja con un nombreDe buscar el nombre
BinOp(left, op, right)Una operación con dos ladosDe los dos lados y luego la operación
Sub(), Mult() o Add()Qué operación esDe nada: es una etiqueta

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Primero los hijos, después el nodo

Un intérprete que recorre el árbol es una sola función que evalúa un nodo. Una hoja con un número da ese número. Una operación evalúa primero su hijo izquierdo y su hijo derecho, y después hace su cuenta con los dos resultados.

Como un hijo puede ser un árbol entero, la función se llama a sí misma. La demo sabe restar y multiplicar.

Un evaluador que recorre el árbol

Código: python
# Evalúa un árbol: un número o [op, izquierda, derecha].
def evaluate(tree):
    if type(tree) == int:
        return tree
    # Primero los dos hijos: aquí está la recursión.
    left = evaluate(tree[1])
    right = evaluate(tree[2])
    if tree[0] == "-":
        return left - right
    if tree[0] == "*":
        return left * right
    return None


tree = ["-", ["-", 10, 4], 3]
print(evaluate(tree))
# Cambia tree por ["-", 10, ["-", 4, 3]] y vuelve a ejecutar.

Un error frecuente

Advertencia

Mucha gente cree que un intérprete convierte el árbol en código máquina a escondidas y ejecuta eso. Un intérprete que recorre el árbol no lo hace nunca. Visita los nodos y hace el trabajo sobre la marcha, otra vez en cada ejecución. Por eso es más lento que el código compilado, y por eso puede empezar al instante.

Los nombres se buscan en un entorno

Una hoja con un nombre necesita sacar su valor de algún sitio. El intérprete lleva un entorno: cada nombre al alcance con su valor. Cada pareja es una ligadura.

eval de Python recibe el entorno como un diccionario. Si le das un segundo diccionario, busca primero en ese. Así un ámbito interior tapa un nombre exterior.

Evalúa con un entorno

Comando
python3 -c "print(eval('base + tax', {'base': 20, 'tax': 4}))"

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Deja fuera un nombre

Comando
python3 -c "print(eval('base + tax', {'base': 20}))"

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Un entorno interior que se mira antes

Comando
python3 -c "print(eval('base + tax', {'base': 20, 'tax': 4}, {'tax': 0}))"

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Ejemplo: buscar base + tax

Runbase sale detax sale deImprime
PrimeroEl único diccionario: 20El único diccionario: 424
SegundoEl único diccionario: 20De ninguna parteNameError
TerceroEl exterior: 20El interior: 020

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Una condición evalúa una sola rama

La tarifa del principio tiene una condición. Su nodo, IfExp, tiene tres hijos: la prueba, el valor si se cumple y el valor si no.

Aquí el orden cambia. El intérprete evalúa la prueba y después solo una de las dos ramas. El último Run pone en la rama que no toca un nombre que no existe.

El árbol de la tarifa

Comando
echo "fee if km > 5 else 0" | python3 -m ast --mode eval

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

La tarifa para 2 km

Comando
python3 -c "print(eval('fee if km > 5 else 0', {'fee': 3, 'km': 2}))"

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Una rama que nunca se evalúa

Comando
python3 -c "print(eval('fee if km > 5 else missing', {'fee': 3, 'km': 9}))"

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Para ir más lejos

Añade a tu evaluador un nodo ["let", name, value, body]. Calcula value con los entornos de siempre. Después evalúa body con un entorno nuevo encima, donde name vale ese resultado.

La lista envs del que llama no puede cambiar. Escribe una prueba que lo demuestre, y otra en la que let tape un nombre exterior.

Sobre este laboratorio y su código: Laboratorio de compiladores y arquitectura: gcc, flex, bison y RISC-V en una VM

¿Enseñas a una clase? Asigna este módulo a tus alumnos con guía del profesor, tareas con corrección automática y un laboratorio para cada alumno.