# Práctica Espree Logging

# Descripción de la Tarea

Esta práctica usa como template el repo crguezl/espree-logging-template (opens new window).

En el repo Repo ULL-ESIT-GRADOII-PL/esprima-pegjs-jsconfeu-talk (opens new window) encontrará el programa logging-espree.js el cual implementa una función addLogging que:

  • cuando se llama analiza el código JS que se la da como entrada
  • produciendo como salida un código JS equivalente que inserta mensajes de console.log a la entrada de cada función.

Por ejemplo, cuando se llama con esta entrada:

addLogging(`
function foo(a, b) {   
  var x = 'blah';   
  var y = (function () {
    return 3;
  })();
}     
foo(1, 'wut', 3);
`);
1
2
3
4
5
6
7
8
9

produce una salida como esta:

[~/javascript-learning/esprima-pegjs-jsconfeu-talk(private)]$ node logging-espree.js 

1
2
function foo(a, b) {
    console.log('Entering foo()');
    var x = 'blah';
    var y = function () {
        console.log('Entering <anonymous function>()');
        return 3;
    }();
}
foo(1, 'wut', 3);
1
2
3
4
5
6
7
8
9

Este es el código de logging-espree.js:

[~/javascript-learning/esprima-pegjs-jsconfeu-talk(private)]$ cat logging-espree.js 

1
2
const escodegen = require('escodegen');
const espree = require('espree');
const estraverse = require('estraverse');

function addLogging(code) {
    const ast = espree.parse(code);
    estraverse.traverse(ast, {
        enter: function(node, parent) {
            if (node.type === 'FunctionDeclaration' ||
                node.type === 'FunctionExpression') {
                addBeforeCode(node);
            }
        }
    });
    return escodegen.generate(ast);
}

function addBeforeCode(node) {
    const name = node.id ? node.id.name : '<anonymous function>';
    const beforeCode = "console.log('Entering " + name + "()');";
    const beforeNodes = espree.parse(beforeCode).body;
    node.body.body = beforeNodes.concat(node.body.body);
}

console.log(addLogging(`
function foo(a, b) {   
  var x = 'blah';   
  var y = (function () {
    return 3;
  })();
}
foo(1, 'wut', 3);
`));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

Le ayudarán a entender el código estos recursos:

En esta práctica, se pide:

  1. Acepte la asignación Classroom de esta tarea
  2. En la tarea del Campus basta con entregar el enlace al repositorio
  3. Ejecute paso a paso el código de logging.js usando el debugger de chrome, intentando comprender el funcionamiento de la transformación realizada. Haga un resumen de lo que ha aprendido en el fichero Markdown: README.md
  4. Modifique el programa para que los console.log insertados informen de los valores de los parámetros pasados a la función.

Vea el siguiente ejemplo de como debe funcionar una solución:

$ ./p0-t0-esprima-logging-sol.js 
Usage: p0-t0-esprima-logging-sol [options] <filename> [...]
 
Options:
  -V, --version            output the version number
  -o, --output <filename>  
  -h, --help               output usage information
[~/javascript-learning/esprima-pegjs-jsconfeu-talk(private)]$ cat input.js 
1
2
3
4
5
6
7
8

El programa usado hace un parsing de la línea de comandos mediante el módulo npm commander.js (opens new window). Puede encontrar ejemplos en el directorio examples (opens new window) del repo del modulo commander.js (opens new window).

Cuando lo ejecutamos con la opción -V nos da la versión:

$ ./p0-t0-esprima-logging-sol.js -V
0.1.0
1
2

Con la opción -o input-log.js especificamos el fichero de salida. El programa muestra los contenidos del fichero de entrada:

$ ./p0-t0-esprima-logging-sol.js -o input-log.js input.js 
input:
1
2
function foo(a, b) {
  var x = 'blah';
  var y = (function (z) {
    return z+3;
  })(2);
}
foo(1, 'wut', 3);
1
2
3
4
5
6
7

Al volcar la salida, vemos que el fichero de entrada ha sido transformado correctamente:

---
Output in file 'input-log.js'
$ cat input-log.js
1
2
3
function foo(a, b) {
    console.log(`Entering foo(${ a },${ b })`);
    var x = 'blah';
    var y = function (z) {
        console.log(`Entering <anonymous function>(${ z })`);
        return z + 3;
    }(2);
}
foo(1, 'wut', 3);
---
1
2
3
4
5
6
7
8
9
10

Si ejecutamos la salida obtenemos la traza esperada:

$ node input-log.js 
Entering foo(1,wut)
Entering <anonymous function>(2)
1
2
3

# Q & A

# Question: Backticks in espree

Trabajando y experimentando con el método parse() del compilador espree, he comprobado que es incapaz de procesar cadenas de caracteres que posean en su interior el signo `, que es usado en JS para crear cadenas de caracteres que pueden aprovecharse de la interpolación de expresiones.

En concreto, y a modo de ejemplo, el error me ha surgido al intentar ejecutar parse() pasando como argumento:

"console.log(`prueba`)"
1

Me preguntaba si el analizador léxico carece verdaderamente de la capacidad para interpretar dicho símbolo y, en caso afirmativo, cómo aprovechar la mecánica de interpolación de expresiones al utilizar el analizador. En concreto, el error que se obtiene es:

SyntaxError: Unexpected character '`'.
1

# Answer: Use option {ecmaVersion:6}


[~/javascript-learning/esprima-pegjs-jsconfeu-talk(private)]$  node
Welcome to Node.js v12.10.0.
Type ".help" for more information.
> code3 = "console.log(`prueba`)"
'console.log(`prueba`)'
> const { parse } = require('espree')
undefined
> parse(code3, {ecmaVersion:6})
Node {
  type: 'Program',
  start: 0,
  end: 21,
  body: [
    Node {
      type: 'ExpressionStatement',
      start: 0,
      end: 21,
      expression: [Node]
    }
  ],
  sourceType: 'script'
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# Reto 1: Funciones Flecha Gorda

Añada la capacidad de procesar funciones con sintáxis ECMA6 flecha gorda con bloque como en este ejemplo:

let z = (e => { 
  return e +1 
})(4);
1
2
3

Ejemplo de ejecución:

[~/.../eval/p0-t0-esprima-logging(master)]$ ./logging-espree.js input.js -o output.js 
input:
function foo(a, b, c) {
  let x = 'tutu';
  let y = (function (x) { return x*x })(2);
  let z = (e => { return e +1 })(4);
  console.log(x,y,z);
}
foo(1, 'wut', 3);
---
1
2
3
4
5
6
7
8
9
10
[~/.../eval/p0-t0-esprima-logging(master)]$ cat output.js 
1
function foo(a, b, c) {
    console.log(`Entering foo(${ a }, ${ b }, ${ c }) at line 1`);
    let x = 'tutu';
    let y = function (x) {
        console.log(`Entering <anonymous function>(${ x }) at line 3`);
        return x * x;
    }(2);
    let z = (e => {
        console.log(`Entering <anonymous function>(${ e }) at line 4`);
        return e + 1;
    })(4);
    console.log(x, y, z);
}
1
2
3
4
5
6
7
8
9
10
11
12
13

Ejecución de la salida:

foo(1, 'wut', 3);[~/.../eval/p0-t0-esprima-logging-CristoNavarro(master)]$ node output.js 
Entering foo(1, wut, 3) at line 1
Entering <anonymous function>(2) at line 3
Entering <anonymous function>(4) at line 4
tutu 4 5
1
2
3
4
5

Vea aquí El AST Espree del ejemplo (opens new window) usado como entrada en la ejecución anterior. Use el parser de espree pasándole la opción ecmaVersion:

const ast = espree.parse(code, {ecmaVersion:6});
1

# Reto 2: Número de Línea

Añada el número de línea a la información de log de la función en la que se entra:

[~/javascript-learning/esprima-pegjs-jsconfeu-talk(develop)]$ ./p0-t0-esprima-logging-sol.js input.js -o salida.js
input:
1
2
function foo(a, b) {
  var x = 'blah';
  var y = (function (z) {
    return z+3;
  })(2);
}
foo(1, 'wut', 3);
1
2
3
4
5
6
7
---
Output in file 'salida.js'
[esprima-pegjs-jsconfeu-talk(develop)]$ cat salida.js
1
2
3
function foo(a, b) {
    console.log(`Entering foo(${ a },${ b }) at line 1`);
    var x = 'blah';
    var y = function (z) {
        console.log(`Entering <anonymous function>(${ z }) at line 3`);
        return z + 3;
    }(2);
}
foo(1, 'wut', 3);
1
2
3
4
5
6
7
8
9
[esprima-pegjs-jsconfeu-talk(develop)]$ node salida.js 
Entering foo(1,wut) at line 1
Entering <anonymous function>(2) at line 3
1
2
3

# Recursos

# The Shape of the AST for console.log

Here is the AST for

console.log(`Entering foo(${ a },${ b }) at line 2`);
1

See also JAVASCRIPT AST VISUALIZER (opens new window) jointjs demos

# Material para la Práctica

# Debugging

# Commander

# Soluciones (No disponibles)

# Pruebas

Aprovechando el script bin/all-test.sh introduzca una entrada scripts/test en su package.json que ejecute su solución contra todos los programas de prueba

✗ npm test        

> ./bin/all-test.sh

test/test1.js  was successful
test/test2.js  was successful
test/test3.js  was successful
1
2
3
4
5
6
7

# Cuestionario

En la práctica de esta semana (y en futuras prácticas) tendremos que realizar un cuestionario. Este cuestionario se realiza bajo las siguientes condiciones:

  • Aprobar este cuestionario es requisito para la corrección de la práctica. En caso de suspenderse, la práctica se considerará NO APTA.
  • Posee un tiempo límite desde que se inicia el cuestionario.
  • Sólo está permitido 1 intento.
  • Una pregunta sin respuesta se considerará errónea.

# References

Grading Rubric#

#Labs

  1. Task GitHub-AluXXXX Form
  2. Lab GitHub Campus Expert
  3. Lab GitHub Project Board
  4. Lab GitPod and Visual Studio Code
  5. Lab IAAS
  6. Lab Espree Logging
  7. Lab Hello Compilers
  8. Lab Constant Folding
  9. Lab ast-types
  10. Lab egg-parser
  11. Lab Lexer Generator
  12. Lab The Egg Interpreter
  13. Lab Adding OOP to the Egg Parser
  14. Lab Extending the Egg Interpreter
  15. Lab TFA: Final Project PL
  16. Task Training Exam for PL

Comments#

Last Updated: 10 months ago