Cómo implementar una evaluación perezosa de if ()

10

Actualmente estoy implementando un evaluador de expresiones (expresiones de línea única, como fórmulas) basado en lo siguiente:

  • la expresión ingresada se tokeniza para separar booleanos literales, enteros, decimales, cadenas, funciones, identificadores (variables)
  • Implementé el algoritmo Shunting-yard (ligeramente modificado para manejar funciones con un número variable de argumentos) para deshacer el paréntesis y ordenar a los operadores con una precedencia decente en un orden pospuesto
  • mi patio de maniobras simplemente produce una cola (simulada) de tokens (por medio de una matriz, mi lenguaje Powerbuilder Classic puede definir objetos, pero solo tiene matrices dinámicas como almacenamiento nativo, no una lista verdadera, ningún diccionario) que evalúo secuencialmente con un máquina de pila simple

Mi evaluador está funcionando bien, pero todavía me falta un if()y me pregunto cómo proceder.

Con mi evaluación de desviación fijada y basada en la pila, si agrego if()como otra función con partes verdaderas y falsas, un solo if(true, msgbox("ok"), msgbox("not ok"))mostrará ambos mensajes, mientras que me gustaría mostrar solo uno. Esto se debe a que cuando necesito evaluar una función, todos sus argumentos ya han sido evaluados y colocados en la pila.

¿Podría darme alguna forma de implementar if()de manera perezosa?

Pensé en procesarlos como una especie de macro, pero al principio aún no tengo la evaluación de la condición. ¿Quizás necesito usar otro tipo de estructura que no sea una cola para mantener por separado la condición y las expresiones verdadero / falso? Por ahora, la expresión se analiza antes de la evaluación, pero también planeo almacenar la representación intermedia como una especie de expresión precompilada para una evaluación futura.

Editar : después de un poco sobre el problema, creo que podría construir una representación en árbol de mi expresión (un AST en lugar de una secuencia de token lineal), desde la cual podría ignorar fácilmente una u otra rama de mi if().

Seki
fuente

Respuestas:

9

Aquí hay dos opciones.

1) No implementar ifcomo una función. Conviértalo en una función de lenguaje con semántica especial. Fácil de hacer, pero menos "puro" si quieres que todo sea una función.

2) Implemente la semántica de "llamada por nombre", que es mucho más complicada, pero permite que la magia del compilador se encargue del problema de evaluación diferida mientras se mantiene ifcomo una función en lugar de un elemento del lenguaje. Dice así:

ifes una función que toma dos parámetros, ambos declarados como "por nombre". Cuando el compilador ve que está pasando algo a un parámetro de nombre, cambia el código que se generará. En lugar de evaluar la expresión y pasar el valor, crea un cierre que evalúa la expresión y la pasa en su lugar. Y al invocar un parámetro por nombre dentro de la función, el compilador genera código para evaluar el cierre.

Mason Wheeler
fuente
No estoy seguro, pero ¿el "cierre" debe ser "golpeado"? Hmm, tal vez no; Acabo de mirar la página de Wikipedia: "un thunk es un cierre sin parámetros".
Cuando dices "llamar por nombre", ¿te refieres a nivel mundial? Alternativamente a la llamada global por nombre es simplemente implementar un tipo de cierre, luego la función if solo toma 3 cierres y evalúa dos (condición y luego o de lo contrario), pero no todo debe reconocerse como un cierre, como la llamada completa por- semántica de nombre
Jimmy Hoffa
@Matt: El término "thunk" puede significar varias otras cosas en el contexto de la programación, y "cierre sin parámetros" no es el primero en el que pienso cuando lo escucho. El "cierre" es mucho más inequívoco.
Mason Wheeler
1
@JimmyHoffa: cuando digo "llamar por nombre", me refiero a un estilo específico de configurar un argumento de función, que debería ser opcional. Al igual que muchos idiomas existentes, le permitirá elegir pasar un parámetro por valor o por referencia, para este escenario necesita la opción de pasar por nombre.
Mason Wheeler
Si bien su sugerencia sobre la semántica de "llamar por nombre" me mostró algunos puntos interesantes, es un poco exagerado para mi evaluador que no es un compilador completo, ya que mis llamadas a funciones son de una sola línea (piense en las fórmulas de MS-Excel). Estoy pensando que podría agregar un paso después de la cola de tokens haciendo una pseudoevaluación de la pila para deducir el árbol de llamadas. Debería ser más fácil saber del árbol qué ramas descartar.
Seki
3

En lugar de la función que tiene la firma:

object if(bool, object, object)

Dale la firma:

object if(bool, object function(), object function())

Luego, su iffunción llamará a la función apropiada en función de la condición, solo evaluando una de ellas.

Hand-E-Food
fuente
1

Es bastante fácil, si compila todo perezosamente. Debe tener algunos medios para ver si un valor ya está evaluado o si necesita más evaluación.

Luego puede hacer lo siguiente: Si es un literal o una variable (¿los tiene ?, es decir, nombres de funciones?), Empújelo en la pila. Si es una aplicación de una función, compílela por separado y presione el punto de entrada en la pila.

La ejecución de un programa es, simplemente, un bucle hasta que se evalúa la parte superior de la pila y no una función. Si no se evalúa o es una función, llame al código al que apunta la parte superior de la pila.

Ingo
fuente