Egg Parser

Egg Parser

Objetivos

Usando el generador de analizadores Nearley.js escriba un parser para el lenguaje Egg que genere los árboles según la especificación que se describe en la sección ASTs: Árboles de Análisis Abstracto. Utilice el generador de analizadores léxicos moo-ignore para la fase de análisis léxico.

Construya un paquete npm y publíquelo en GitHub Registry con ámbito @ull-esit-pl-2324 y nombre el nombre del repo asignado.

El módulo exportará un objeto con al menos la propiedad { parseFromfile }.

  • parseFromFile(inputFile) es una función que devuelve el AST construído a partir de los contenidos del programa en lenguaje Egg contenido en el fichero con nombre inputFile

La gramática del lenguaje Egg se describe en la sección Gramáticas Independientes del Contexto. This is the equivalent syntax diagram:

/images/egg-syntax-diagram.png

Se deberá proveer un ejecutable eggc con una interfaz como esta:

➜  prefix-lang git:(master) ✗ bin/eggc.js -h
Usage: eggc [options] <origin>

Compile a Egg lang file

Arguments:
  origin                   The path of the file to compile

Options:
  -V, --version            output the version number
  -o, --out <destination>  Path for output file. If it isn't specified the path of the origin file will be
                           used,changing the extension to .json
  -h, --help               display help for command

Puede usar el ejecutable evm (las siglas corresponden a Egg virtual Machine) del paquete "@crguezl/eloquentjsegg" (opens in a new tab) para comprobar que los ASTs generados funcionan.

Sigue un ejemplo (compatible "@crguezl/eloquentjsegg" (opens in a new tab) versión 1.2.6) de como debería funcionar nuestro parser eggc:

➜  prefix-lang git:(master) ✗ cat test/examples/array.egg 
do(
  def(x, arr(arr(1,4),5,7)),
  print([](x,0)), # [1,4]
  print([](x,1))  # 5
)

Nuestro parser deberà por tanto producir un AST conforme a la especificación dada en la sección Anatomía de los AST para Egg. Esto es, deberá estar conforme a esta gramática árbol:

ast: VALUE
   | WORD 
   | APPLY( operator:WORD args:[ ast * ]))

Los nodos APPLY tienen dos atributos operator y args. El atributo args es un ARRAY conteniendo los ASTs que se corresponden con los argumentos de operator. Los nodos WORD son nodos hoja y tienen al menos el atributo name. Los nodos VALUE tienen al menos el atributo value.

Por ejemplo, el AST para +(a,*(4,5)) se podría describir mediante este término:

APPLY(
  operator: WORD{name: +},
  args: [
    WORD{name: a}, 
    APPLY(
      operator: WORD{name:*}, 
      args: [VALUE{value:4}, VALUE{value:5}]
    )
   ]
)

A la derecha del tipo de nodo y entre llaves escribimos las parejas atributo: valor que nos interesa resaltar.

El ejecutable bin/eggc.js deberá producir un fichero JSON con el ast:

✗ bin/eggc.js test/examples/array.egg -o test/ast/array.json

Puede ver los contenidos del ast para el ejemplo test/examples/array.egg (opens in a new tab) haciendo click sobre este enlace:

➜ prefix-lang git:(master) ✗ cat test/ast/array.json

A continuación podemos usar el ejecutable evm para interpretar el árbol:

➜  prefix-lang git:(master) ✗ npx evm test/ast/array.json
[ 1, 4 ]
5

Observe que puesto que el paquete "@crguezl/eloquentjsegg" (opens in a new tab) ha sido instalado localmente, necesitamos hacer uso de npx (opens in a new tab) para ejecutar el intérprete evm.

ℹ️

npx

npx <command>[@version] [command-arg]... executes <command> either from a local node_modules/.bin, or from a central cache, usually in ~/.npm/cacache

  npm config get cache
  /Users/casianorodriguezleon/.npm
  ✗ ls /Users/casianorodriguezleon/.npm/_cacache 
content-v2 index-v5   tmp

installing any packages needed in order for <command> to run. By default, npx will check whether <command> exists in $PATH, or in the local project binaries, and execute that. If <command> is not found, it will be installed prior to execution.

En el directorio node_modules/@crguezl/eloquentjsegg/examples tiene algunos ejemplos de programas egg que puede usar para comprobar el buen funcionamiento de su parser:

➜  prefix-lang git:(master) ✗ ls  node_modules/@crguezl/eloquentjsegg/examples 
array.egg       greater-x-5.egg main2.js        one.egg         sum.egg         unbalanced.egg
expcomma.egg    if.egg          one-err-2.egg   scope.egg       sum.egg.evm
fun.egg         main.js         one-err.egg     string.egg      two.egg
⚠️

Nota de Advertencia

En algunos de los ejemplos, vídeos, repos, etc. que acompañan esta práctica puede notar algunas inconsistencias en el lenguaje Egg debidas a que casi en cada curso hemos ido haciendo alias de algunos de los nombres de los constructos. Por ejemplo, a veces en un vídeo en vez de fun usamos -> y en algún ejemplo en los apuntes en vez de element se usa <-, etc. También en algún ejemplo aparecen llavitas { y } en vez de paréntesis (de nuevo una llave aquí es un alias del correspondiente paréntesis). Son cambios triviales que no deberían afectar a la comprensión del texto.

Option run

The package @crguezl/eloquentjsegg includes a function runFromEVM that can be used to run the program after compiling it. Alternatively, you can extend your executable with an option --run to run the program after compiling it.

➜  prefix-lang git:(moo-ignore) ✗ bin/eggc.js -h
Usage: eggc <origin> [options]
 
Arguments:
  origin                   The path of the file to compile
 
Options:
  -V, --version            output the version number
  -o, --out <destination>  Path for output file. If it isn't specified the path of the origin file will be
                           used,changing the extension to .json
  -r, --run                Run the program after compiling it
  -h, --help               display help for command

Simply add a code like this inside the compile function::

const compile = (origin, options) => {
  let destination = options.out;
  if (destination == undefined) {
    destination = options.out = origin.match(/^[^.]*/)[0] + '.json';
  }
  const ast = parseFromFile(origin);
  if (options.run) {
    const { runFromEVM } = require('@crguezl/eloquentjsegg/lib/eggvm.js');
    runFromEVM(options.out);
  }
  ...
}

Publicación del módulo

Una parte de los conceptos y habilidades a adquirir con esta práctica se explican en la sección Creating and publishing a node.js module en GitHub y en NPM. Léala con detenimiento antes de hacer esta práctica.

Pruebas

Deberá añadir pruebas usando Mocha y Chai o Jest. Repase las secciones Testing with Mocha and Chai y Jest.

Añada un estudio de covering. See the notes in covering.

Añada CI con GitHub Actions.

Informe y Documentación

Documente el módulo incorporando un README.md: Como se instala, como se usa el ejecutable, como se carga la librería, etc.

La documentación de la API de la función exportada usando JsDoc la puede dejar accesible en el despliegue (directorio docs/api).

Añada el informe de Covering también (directorio docs/covering o similar).

Challenge

  • Asegúrese de producir mensajes de error significativos informando de los números de línea y columna correctos

Videos

Lab egg-parser 2024

Clase del 12/03/2024. Lexical Analysis of Egg with moo-ignore. Egg grammar in Nearley.js

Retrasamos el examen parcial. El lenguaje Egg, ASTs para Egg, El generador de analizadores sintácticos Nearley.js, El generador de analizadores léxicos Moo. Moo-ignore

Videos Lab egg-parser 2023





  • Vídeo 2023/03/29: ASTs for Egg. Lexical Analysis with moo-ignore. Introduction to Nearley.js
  • Vídeo 2023/04/10: Parsing Egg. More on Nearley.js
  • Vídeo 2023/04/11: Building the Egg ASTs. White management in moo. The Earley algorithm
  • Vídeo 2023/04/12: The Earley algorithm

Rubric















egg-parser Repos

References

Nearley.js

moo

Testing

Documentation

Semantic versioning and npm