# Introduction to Scoping
# Descripción de la Tarea
Queremos extender nuestra calculadora para que soporte operaciones de coma, identificadores, asignación y print
de manera que podamos escribir una entrada como esta:
➜ calc2js-solution git:(develop) ✗ cat test/data/input/test-id.calc
a = 4+i,
b = 2-2i,
print(a*b)
2
3
4
Cuando se llame a nuestro transpiler con esta entrada, se debe generar un programa JS que al ejecutarse en NodeJS muestre por pantalla el resultado de la operación:
➜ calc2js-solution git:(develop) ✗ bin/calc2js.mjs test/data/input/test-id.calc
#!/usr/bin/env node
const Complex = require("/Users/casianorodriguezleon/campus-virtual/2223/pl2223/practicas/drafts/calc2js-solution/src/complex.js");
const print = x => { console.log(x); return x; };
let $a, $b;
($a = Complex("4").add(Complex("i")), $b = Complex("2").sub(Complex("2i"))), print($a.mul($b));
2
3
4
5
6
# Análisis de las dependencias de funciones de soporte
El programa JS generado incluye el código de soporte para números complejos y sólo se añade la función print
y no otras del catálogo de funciones de soporte como factorial
, max
o min
que no son necesarias
const print = x => { console.log(x); return x; };
Nótese como se genera la importación del módulo complex.js
con la ruta en la que haya quedado la instalación del módulo
const Complex = require("/Users/casianorodriguezleon/campus-virtual/2223/pl2223/practicas/drafts/calc2js-solution/src/complex.js");
# Análisis de las variables inicializadas
Las variables que se inicializan en el fuente como a
y b
se declaran como variables globales siguiendo al código de soporte y precediendo al programa JS generado:
// ... Código de soporte
let $a, $b;
($a = Complex("4").add(Complex("i")),
$b = Complex("2").sub(Complex("2i"))
),
print($a.mul($b));
2
3
4
5
6
# Evitación de conflictos con palabras reservadas y nombres de soporte
Nótese también como las variables del programa de entrada se declaran con un prefijo $
para evitar conflictos con las variables de soporte, palabras reservadas JS, etc. en el programa JS generado. Por ejemplo, si el programa de entrada es while = 4
el potencial conflicto con el while
de JS es resuelto mediante la traducción $while = 4
.
# Código generado
El código generado deberá poder ejecutarse correctamente con NodeJS:
➜ calc2js-solution git:(develop) ✗ bin/calc2js.mjs test/data/input/test-id.calc | node -
{ re: 10, im: -6 }
2
# Declaración de Variables
Asumiremos que en nuestra calculadora, similar a como ocurre en Ruby, tan pronto una variable es inicializada se declara (en este caso en el único ámbito global).
Un programa que contenga el uso de una variable no inicializada/declarada debe dar un error de ejecución. Por ejemplo:
➜ scope-intro-solution git:(scope-analysis) cat test/data/input/test-scope1.calc
a = 4+d+i,
b = 2-2i,
print(c)
2
3
4
producirá una salida como:
➜ scope-intro-solution git:(scope-analysis) bin/calc2js.mjs test/data/input/test-scope1.calc
Not declared variable 'd'
Not declared variable 'c'
2
3
# Fases del Compilador de la Calculadora
El compilador de la calculadora se puede dividir en las siguientes fases:
module.exports = async function transpile(inputFile, outputFile) {
let input = await fs.readFile(inputFile, 'utf-8') // 1. leer la entrada
let ast;
try {
ast = p.parse(input); // 2. parsear la entrada
} catch (e) {
let m = e.message // 3. si hay error, mostrarlo
console.error(m);
return m;
}
ast = dependencies(ast); // 4. calcular que funciones de soporte se necesitan
ast = initializedVariables(ast); // 5. calcular que variables se inicializan paa su declaración
ast = usedVariables(ast); // 6. calcular que variables son utilizadas
let d = difference(ast.used, ast.symbolTable)
if (d.size > 0) { // 7. si hay variables usadas y no inicializadas, mostrar error
let m = notDeclared(d).join('');
console.error(m);
return m;
}
let output = codeGen(ast); // 8. generar el código JS
await writeCode(output, outputFile); // 9. escribir el código JS en el fichero de salida
return output;
}
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
Para las fases 4,5 y 6 deberá recorrer el AST usando visit
del módulo ast-types
para completar el código de las funciones dependencies
, initializedVariables
y usedVariables
respectivamente.
Utilice el módulo recast para la escritura de codeGen
.
# Pruebas, Covering e Integración Continua
Escriba las pruebas, haga un estudio de cubrimiento usando c8 (opens new window) y añada integración continua usando GitHub Actions.
Lea las secciones Testing with Mocha y Jest.
# Documentación
Documente
el módulo incorporando un README.md
y la documentación de la función exportada usando JsDoc.
Lea la sección Documenting the JavaScript Sources
# References
- ast-types
- recast
- More on JSCodeshift in the article Write Code to Rewrite Your Code: jscodeshift (opens new window) by Jeremy Greer
- See Tree Transformations References