@alu0100819786/constant-folding
v2.1.1
Published
Constant Folding javascript code
Downloads
9
Maintainers
Readme
descripcion: Super Repositorio que contiene los archivos de la Práctica 3 además de un README.md explicando todo el proceso de desarrollo. alumno: "Gabriel Melián Hernández Alu0100819786"
Constant-Folding
El proceso de creación de nuestras funciones de constant folding, lo podremos encontrar dentro del subrepo constant-folding-module-gabriel-melian-hernandez-alu0100819786
.
Partimos como base para realizar nuestro constant folding de la utilización de estraverse
y su función traverse
. Vamos a utilizar está función de manera que actuemos sobre los nodos hijos primero que sobre los padres, para poder realizar el constant folding de manera correcta, para ello usaremos leave
a la hora de aplicar las condiciones para que se llamen a las funciones.
Partimos de una base para conseguir Constant folding, en BinaryExpressions
de esta manera:
const t = espree.parse(input, { ecmaVersion: 6, loc: false });
estraverse.traverse(t, {
leave: function (n, p) {
if (n.type == "BinaryExpression" &&
n.left.type == "Literal" && n.right.type == "Literal") { replaceByLiteral(n); }
Con este código lo que hacemos es llamar a replaceByLiteral(n)
con el nodo n, cuando entremos a un nodo que sea de tipo BinaryExpression
y sus hijos izquierdo y derecho sean de tipo Literal
.
Una vez hemos accedido al nodo en el cual queremos llevar a cabo nuestro constant folding, lo realizamos de la siguiente manera:
function replaceByLiteral(n) {
n.type = "Literal";
n.value = eval(`${n.left.raw} ${n.operator} ${n.right.raw}`);
n.raw = String(n.value);
delete n.left;
delete n.right;
}
Realizamos la operación que contenía la BinaryExpression
inicial y renombramos n, para que ahora tenga type Literal
y borramos sus hijos izquierdo y derecho.
Por otra parte y respondiendo al Challenge de esta práctica se pide que el programa sea capaz de hacer constant folding no solo para BinaryExpressions
sino también para operaciones con Arrays, en CallExpressions
, para esto, seguimos el mismo procedimiento que mostramos anteriormente buscando ahora un nodo que tenga tipo CallExpression
además de un n.callee.property.name
que caze con la operación que tratará cada función, vamos a ver un par de ejemplos:
if (n.type == "CallExpression"){
if(n.callee.property.name == "join") { replaceByString(n);}
}
if (n.type == "CallExpression"){
if(n.callee.property.name == "shift") { replaceByShift(n);}
}
Tanto join
como shift
son operaciones que se realizan sobre arrays, pero aunque ambos crean nodos del tipo CallExpression
, su n.callee.property.name
es diferente y por ende la operación que van a realizar sobre el array también y es necesario tratarlas de forma separada, teniendo una función para cada una de ellas.
Por ejemplo en la función join:
function replaceByString(n){
n.type = "Literal";
var aux;
var aux2 ="";
aux = n.callee.object.elements.length;
if(n.arguments[0] != null){
for( var i = 0; i < aux ; i ++){
if ( i == aux-1){
aux2 = aux2 + (n.callee.object.elements[i].value);
}
if (i < aux-1){
aux2 = aux2 + (n.callee.object.elements[i].value) + n.arguments[0].value;
}
}
}
if(n.arguments[0] == null){
for( var i = 0; i < aux ; i ++){
if ( i == aux-1){
aux2 = aux2 + (n.callee.object.elements[i].value);
}
if (i < aux-1){
aux2 = aux2 + (n.callee.object.elements[i].value) + ',';
}
}
}
n.value = aux2;
delete n.callee;
delete n.arguments;
}
Entramos al nodo y vamos a ir accediendo a cada elemento del array y lo vamos a ir colocando uno a uno en una cadena separados por una coma por defecto, o si la función join tiene argumento, sustituiremos la coma, por dicho argumento a la hora de realizar el join final. y por último borraremos el calle del nodo y sus argumentos quedandonos unicamente con el nodo padre y realizando correctamente el constant folding.
Por otra parte en la función se shift:
function replaceByShift(n){
n.type = "Literal";
var aux;
aux = n.callee.object.elements[0].value;
n.value = aux;
delete n.callee;
}
Al igual que en el ejemplo anterior entramos al nodo seleccionado, cambios su tipo y realizamos el comportamiento que debería llevar a cabo la función shift() que es la de sacar el primer valor del array y devolverlo. Por lo que, renombramos el tipo del nodo padre a Literal
y a su value
le asignamos el valor del primer elemento del array original y luego borramos el array quedando solamente el nodo padre ahora de tipo Literal
con el value correspondiente al resultado del constant folding.
Siguiendo este razonamiento se realiza la implementación de las demás funciones para llevar a cabo todos los ejemplos requeridos en el challenge.
Publicación e Instalación.
Para llevar a cabo la publicación de nuestro npm package
lo primero que tendremos que hacer es crear un usuario y darnos de alta en npm.
Para ello vamos a acceder a su página web oficial.
Y haremos click en el botón Sign Up
. Rellenamos nuestros datos:
Tras esto nos pedirá confirmación de e-mail y además la introducción de uncódigo de contraseña de un sólo uso que nos enviarán por correo al e-mail proporcionado en el mometnod e la creación de la cuenta. Una vez verificado el e-mail, ya dispondremos de nuestra cuenta en npm y podremos publicar nuestro paquete, para ello debemos colocarnos en el repositorio que contiene nuestro paquete y abrir una terminal para primero de todo logearnos de nuevo con el comando npm login
. Introducimos nuestras credenciales de acceso y ya estaríamos logeados correctamente.
Para poder publicar nuestro paquete debemos definir primero en el archivo package.json
nuestro ámbito y esto lo haremos simplemente modificando las primeras líneas del mismo.
{
"name": "@alu0100819786/constant-folding",
"version": "0.0.1",
"author": "alu0100819786 <[email protected]>",
"description": "Constant Folding javascript code",
Lo que escribamos en el campo name
será el ámbito con el que se publicará nuestro paquete. Una vez configurado dicho fichero solo quedará el último paso que es publicar el propio package mediante el comando npm publish --access=public
. Una vez hecho esto, ya podremos venirnos a la página web de npm y veremos nuestro package publicado.
Una vez publicado ya podremos instalarlo en cualquiera de nuestro proyectos, para ello usaremos un comando que podemos encontrar en esta misma página en el lado derecho, en el apartado install y se trata del comando npm i @alu0100819786/constant-folding
.
De esta forma podemos ir a otro proyecto y ejecutar el comando. Veremos como se instala al igual que cualquier otra dependencia que tengamos en el package.json, y se incluirá dentro de la carpeta de node_modules
, tal y como vemos en la siguiente imagen.
Github Submodules:
En esta práctica se nos pide que sea realizada en tres repositorios diferentes, donde tendremos un super repo y los otros dos serán sub repos del primero. Para lograr esto nos colocamos en el super repo y una vez inicializado procedemos a usar el comando
git submodule add <repo url>
Utilizaremos, este comando dos veces, una por cada repo que tenemos y esto creará un directorio en el super repo llamado .gitmodules
que tendrá la información principal de los dos sub repos.
[submodule "constant-folding-module-gabriel-melian-hernandez-alu0100819786"]
path = constant-folding-module-gabriel-melian-hernandez-alu0100819786
url = https://github.com/ULL-ESIT-PL-2122/constant-folding-module-gabriel-melian-hernandez-alu0100819786
[submodule "constant-folding-production-test-gabriel-melian-hernandez-alu0100819786"]
path = constant-folding-production-test-gabriel-melian-hernandez-alu0100819786
url = https://github.com/ULL-ESIT-PL-2122/constant-folding-production-test-gabriel-melian-hernandez-alu0100819786
Además desde que hagamos nuestro primer commit luego de añadir los sub repos y hagamos un push, se actualizará la información de nuestro super repo y aparecerán los sub repos enlazados al principal como podemos ver en la siguiente imagen.
Coverage y Github Actions:
Para llevar a cabo nuestro coverage, al igual que en la práctica anterior haremos uso de la herramienta nyc
, que nos proporcionará información trás cada ejecución de nuestro programa. También podríamos utilizar la opción de sacar los resultados en un archivo .html y mostrarlo en github pages como en la práctica anterior.
Por otra parta para hacer uso de las Github Actions vamos a modificar el archivo nodejs.yml
donde especificaremos las acciones que queremos realizar cada vez que hagamos un push o un pull request en nuestro repositorio.
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
# Runs a single command using the runners shell
- name: Install Dependencies
run: npm i
# Runs a set of commands using the runners shell
- name: Run Program
run: npm run execute
# Runs a set of commands using the runners shell
- name: Run program with coverage
run: npm run covnyc
Uso del módulo y Pruebas de Producción.
Una vez publicado el módulo, si queremos utilizarlo en algún otro proyecto debemos añadirlo en el package.json
como cualquiier otra dependencia.
"@alu0100819786/constant-folding": "*",
Con esto conseguimos que el módulo sea accesible una vez que hayamos instalado las dependencias del proyecto con npm i
.
Ahora si queremos utilizarlo en nuestro proyecto bastará con agregar la siguietne línea de código:
const prueba = require ("@alu0100819786/constant-folding");
Y ahora ya podremos a través de prueba
acceder a todos los métodos que están presentes en nuestro módulo, comopor ejemplo el método resolverProblema
que nos realizará el constant folding de una expresión, haciendo uso de los demás métodos del módulo.
fs.readFile(inputFilename, 'utf-8', (error, datos) => {
if(error) throw error;
prueba.resolverProblema(datos);
});
Examples
Podemos ejecutar nuestro programa rápidamente con un input de prueba para comprobar el correcto funcionamiento del mismo.
const input = `
["a", "b", "c"].join();
["a", "b", "c"].join('@');
[1, 2, 3].length;
[1, 2, 3][2-1];
[1, 2, 3].shift();
[a, b, c].pop();
`;
donde tras ejecutar nuestro programa conseguimos las siguientes salidas:
'a,b,c';
'a@b@c';
3;
2;
1;
c;
Tests
Para llevar a cabo los test de está practica utilizaremos el archivo test.js
u las herramientas mocha y chai
. Y haciendo uso de los métodos definidos en la el fichero src/constant-folding.js
.
describe("Constant Folding test", () => {
for (let c of Checks) {
it(`Test ${c.text} => ${c.result}`, () =>{
let oldconstant = constantFolding.resolverProblema;
constantFolding.resolverProblema = x => x;
const t = constantFolding.resolverProblema(c.text);
const result = eval(t);
result.should.equal(c.result);
constantFolding.resolverProblema = oldconstant;
});
}
});
Con este código y las entradas de prueba:
const Checks = [
{text: `a=2*3`, result: 6},
{text: `a=2*4`, result: 8},
{text: `["a", "b", "c"].join()`, result: 'a,b,c'},
{text:`["a", "b", "c"].join('@')`, result: 'a@b@c'},
{text: `[1, 2, 3].length`, result: 3},
{text: `[1, 2, 3][2-1]`, result: 2},
{text: `[1, 2, 3].shift()`, result: 1},
{text: `[1, 2, 3].pop()`, result: 3},
];
Conseguimos que nuestros test funcionen correctamente, tal y como podemos apreciar en la resolución por ejemplo de las tareas de Github Actions.
Constant Folding test
✔ Test a=2*3 => 6
✔ Test a=2*4 => 8
✔ Test ["a", "b", "c"].join() => a,b,c
✔ Test ["a", "b", "c"].join('@') => a@b@c
✔ Test [1, 2, 3].length => 3
✔ Test [1, 2, 3][2-1] => 2
✔ Test [1, 2, 3].shift() => 1
✔ Test [1, 2, 3].pop() => 3
8 passing (7ms)