Procesadores de Lenguajes

3º. 2º cuatrimestre. Itinerario de Computación. Grado en Ingeniería Informática. ULL


Organization ULL-ESIT-PL-1920   Github Classroom ULL-ESIT-PL-1920   Campus Virtual PL   Chat Chat   Profesor Casiano

Table of Contents

Práctica: Egg. A Programming Language. Continuación. (p6-t3-egg-1)

Metodologia

  1. Trabaje partiendo de la práctica anterior. Puede usar la misma working copy.
  2. Añada como remoto el repo de GitHub dado por la asignación de esta tarea. Quizá el nombre del remoto podría ser el nombre de la práctica.
  3. Haga también una branch con el nombre de cada práctica y manténgala actualizada hasta el último push de entrega de esa práctica.
  4. Mantenga los nombres de los ejemplos que aparecen en las descripciones de las prácticas.

Design: Smells, The Switch Smell, The Open Closed Principle and the Strategy Pattern

Lea esta sección:

procurando entender los principios SOLID, el problema del Switch Smell y el
Strategy Pattern. Vea el vídeo de Elijah Manor.

AST con Clases: evaluate como método del nodo

Aplique el Strategy Pattern para eliminar el Switch Smell en el código de evaluación del AST.

Modifique el AST para dar una solución OOP con clases:

de manera que cada clase de objeto dispone de un método evaluate.

[~/ull-pl1718-campus-virtual/tema3-analisis-sintactico/src/egg/crguezl-egg(private)]$ cat lib/ast.js
  // The AST classes
  const {specialForms} = require("./registry.js");

  class  Value {
    constructor(token) {
      ...
    }
    evaluate() {
      ...
    }
  }

  class  Word {
    constructor(token) {
      ...
    }
    evaluate(env) {
      ...
    }
  }

  class  Apply {
    constructor(tree) {
      ...
    }
    evaluate(env) {
      ...
    }
  }

  module.exports = {Value, Word, Apply};

Por supuesto, ahora, cuando el parser detecta un nuevo nodo en su construcción del árbol, crea un objeto de la clase correspondiente:

  parseExpression() {
    let expr;
    if (this.lookahead.type === "STRING") {
      expr = new Value(this.lookahead);
    } else if (this.lookahead.type === "NUMBER") {
      ...
    } else if (this.lookahead.type === "WORD") {
      expr = new Word(this.lookahead);
    } else {
      throw ...
    }

    return this.parseApply(expr);
  }

Aisle estas clases en un fichero lib/ast.js. La función evaluate con el switch que estaba inicialmente en lib/eggvm.js desaparece en esta versión

Una Solución:

Jerarquía de Ficheros y Organización

Actualice la máquina virtual evm para que pueda ejecutar los JSON

Despúes de que hayamos definido las clases de nodos del AST y hayamos añadido evaluate como método en las clases creadas nos encontramos con que bin/eggvm deja de funcionar. Esto es así porque:

  1. bin/eggc prog.egg produce como salida un JSON prog.egg.evm conteniendo el mapa/hash del árbol descrita en JSON. En JSON no se puede describir que un objeto pertenece a una cierta clase. Ni siquiera existe el concepto de clase.
  2. Cuando ejecutamos bin/eggvm prog.egg.evm falla porque la estructura del JSON es un mapa y ahora evaluate es un método definido en las clases de nodos VALUE, WORDy APPLY

Solución

Escriba una función json2AST que convierta la estructura de datos plana en un AST en los que cada nodo pertenece a la clase correspondiente. Modifique la función runFromEVM que ejecuta el código de la máquina virtual para que siga funcionando.

Una Solucion: (repo privado)

STRINGS y NUMBERS y … todos pueden llamar

Modifique la gramática de Egg para que las String y los Number puedan hacer llamadas como si fuera applications:

[.../p6-t3-egg-1-04-16-2020-03-13-25/davafons(casiano)]$ cat examples/string-apply.egg
do {
  print("hello"("length")),
  print(4("toFixed")(2))
}

Que cuando se ejecuta debería dar una salida como esta:

[.../p6-t3-egg-1-04-16-2020-03-13-25/davafons(casiano)]$ bin/egg.js examples/string-apply.egg
5
4.00

Un String o un Number o cualquier objeto resultante de una llamada a una application puede ser llamado con argumento un string con el nombre de una propiedad JS del objeto y retorna el valor de esa propiedad .

Se sigue que, en particular, si el objeto JavaScript obj resultado de la llamada a una función tiene una propiedad con nombre "meth" que es el nombre de un método, este podrá ser llamado usando la sintáxis:

  obj("meth")(args)

Por ejemplo:

.../p6-t3-egg-1-04-16-2020-03-13-25/davafons(casiano)]$ cat examples/array-properties.egg
do(
  def(x, array[1, 4, array[5, 3]]),
  print(x[0]),   # 1
  print(x[2]),   # [5, 3]
  print(x[2][1]) # 3
)

Este es el resultado de la ejecución:

[.../p6-t3-egg-1-04-16-2020-03-13-25/davafons(casiano)]$ bin/egg.js  examples/array-properties.egg
1
[ 5, 3 ]
3

Este ejemplo funciona en parte porque en JS los índices son propiedades del array.

Otro ejemplo:

.../p6-t3-egg-1-04-16-2020-03-13-25/davafons(casiano)]$ cat examples/method.egg
print(array[1,4,5]("join")("-"))

que cuando se ejecuta da:

[.../p6-t3-egg-1-04-16-2020-03-13-25/davafons(casiano)]$ bin/egg.js examples/method.egg 
1-4-5

Otro ejemplo:

[.../p6-t3-egg-1-04-16-2020-03-13-25/davafons(casiano)]$ cat examples/method2.egg
do(
    def(x, "hello"),
    print(x("toUpperCase")())
)

que cuando se ejecuta da:

[.../p6-t3-egg-1-04-16-2020-03-13-25/davafons(casiano)]$ bin/egg.js examples/method2.egg 
HELLO

Se debería poder concatenar las llamadas de métodos:

.../p6-t3-egg-1-04-16-2020-03-13-25/davafons(casiano)]$ cat examples/method3.egg
do{
  def(x, array["a", "b", "c"]),
  print(x("join")("-")("toUpperCase")())
}

que cuando se ejecuta da:

[.../p6-t3-egg-1-04-16-2020-03-13-25/davafons(casiano)]$ bin/egg.js examples/method3.egg 
A-B-C

la concatenación puede ser larga

[.../p6-t3-egg-1-04-16-2020-03-13-25/davafons(casiano)]$ cat examples/method-concatenation.egg
do(
  print(array[1,4,5]("join")("-")("substring")(0,2)("concat")("hello egg"))
)

cuya ejecución resulta en:

[.../p6-t3-egg-1-04-16-2020-03-13-25/davafons(casiano)]$ bin/egg.js examples/method-concatenation.egg
1-hello egg

Esto nos permite disponer del mapde JS para los arrays:

.../p6-t3-egg-1-04-16-2020-03-13-25/davafons(casiano)]$ cat examples/map-js-chain.egg 
do(
    define(x, array[1,2,3,4]),
    define(inc, fun(x,i,g, +(x,1))),
    print(x("map")[inc])
)

cuya ejecución resulta en:

[.../p6-t3-egg-1-04-16-2020-03-13-25/davafons(casiano)]$ bin/egg.js examples/map-js-chain.egg 
[ 2, 3, 4, 5 ]

Monkey Patching Objetos JS

Utilizando las extensiones anteriores y haciendo Monkey patching de las clases principales de JS, podemos añadir propiedades y métodos a los objetos JavaScript, de manera que programas como este funcionen:

[.../p6-t3-egg-1-04-16-2020-03-13-25/davafons(master)]$ cat examples/multi-sub-array.egg
do{
  define(x, arr[1, arr[3, 4]]),
  print(x["sub"](1, 1)),
  print(element[x, 1, 1])
}
[.../p6-t3-egg-1-04-16-2020-03-13-25/davafons(master)]$ bin/egg.js examples/multi-sub-array.egg
4
4

Aquí se ha extendido la clase JS Object con un método sub que permite la indexación del objeto.

[.../p6-t3-egg-1-04-16-2020-03-13-25/davafons(master)]$ cat examples/set-multiarray.egg

do{
  :=(w, array[array[1,2], array[3,4]]),
  w["="](5, 0, 1),
  print(w)
}

En este ejemplo hemos añadido el método = a la clase JS Object que asigna el primer argumento al elemento del array indexado por los subsiguientes argumentos.

Cuando se ejecuta da:

[.../p6-t3-egg-1-04-16-2020-03-13-25/davafons(master)]$ bin/egg.js examples/set-multiarray.egg
[ [ 1, 5 ], [ 3, 4 ] ]

A monkey patch is a way for a program to extend or modify supporting system software locally (affecting only the running instance of the program).

.../p6-t3-egg-1-04-16-2020-03-13-25/davafons(casiano)]$ cat lib/monkey-patching.js //  SUB
Object.prototype["="] = function(value, ...indices) {
  ...
};

...

Recursos

Referencias

Recursos del Profesor

Debugging Simple Examples

[.../p6-t3-egg-1-04-16-2020-03-13-25/davafons(casiano)]$ cat examples/debug-special-form.egg
if(true, 
  print(2), 
  print("not 2")
)
.../p6-t3-egg-1-04-16-2020-03-13-25/davafons(casiano)]$ cat examples/string-apply-simple.egg 
"hello"("length")
.../p6-t3-egg-1-04-16-2020-03-13-25/davafons(casiano)]$ cat examples/number-apply.egg 
4("toFixed")(2)

Your Comments

Comment with Disqus