{"_id":"@alu0101392561/scope_intro","name":"@alu0101392561/scope_intro","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@alu0101392561/scope_intro","version":"1.0.0","description":"A simple calculator written in Jison","type":"commonjs","bin":{"calc2js":"bin/calc2js.js"},"scripts":{"test":"npm run compile; mocha test/test.mjs","only-test":"mocha --inspect-brk only-test/test.js","cov":"c8 -r html npm test","test1":"npm run compile; ./bin/calc2js.mjs test/data/test1.calc ","compile":"jison src/grammar.jison src/lexer.l -o src/calc.js","clean":"rm -f src/calc.js; rm -fR test/data/out?.js","docs":"npx c8 --reporter=html --reporter=text --report-dir docs mocha"},"keywords":["ull","jison","espree","compiler"],"author":{"name":"Casiano Rodriguez Leon","email":"crguezl@ull.edu.es","url":"https://crguezl.github.io/"},"license":"ISC","dependencies":{"ast-types":"^0.14.2","c8":"^7.13.0","calc":"^0.0.2","chmod":"^0.2.1","commander":"^10.0.0","complex.js":"^2.1.1","jison":"^0.4.18","nyc":"^15.1.0","recast":"^0.22.0","set-operations":"^2.0.2","shelljs.exec":"^1.1.8"},"devDependencies":{"mocha":"^10.2.0","shelljs":"^0.8.5"},"main":"index.js","directories":{"doc":"docs","test":"test"},"repository":{"type":"git","url":"git+https://github.com/ULL-ESIT-PL-2223/scope-intro-jose-lozano-armas-alu0101392561.git"},"bugs":{"url":"https://github.com/ULL-ESIT-PL-2223/scope-intro-jose-lozano-armas-alu0101392561/issues"},"homepage":"https://github.com/ULL-ESIT-PL-2223/scope-intro-jose-lozano-armas-alu0101392561#readme","gitHead":"a507ba11c7ff0f74eed56df74ecd2901c63a278d","_id":"@alu0101392561/scope_intro@1.0.0","_nodeVersion":"19.6.0","_npmVersion":"9.4.0","dist":{"integrity":"sha512-DZTtlZ4foPm3aiOyDp2bW5LkKTW2uQSdNh2rGasKB9dHHyOb3G8C4HpASu3gGoIymLFygxbgUAONR4duIbMWMg==","shasum":"b5eb3bf3545fa60252408ce8425db31962e0cd7b","tarball":"https://registry.npmjs.org/@alu0101392561/scope_intro/-/scope_intro-1.0.0.tgz","fileCount":158,"unpackedSize":7963805,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDZ4c4aC6RTSbUnCCp+9kPj7QMC6b99h2dajcLX3NuwswIhANtD/+6ljNy+K5BKP6UoFkqwqurzyDQKiKozgORcNV5I"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkEizrACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrEIg//b2hsQ/zM+c8XnN7r1DgHHuQ+03zeewZFljmBPwI1tgJ0NX9M\r\nBEc2FRq9n1EEvS5La8V29GzGAPTmFgQHXJg8Q0zhbHueMZK+krFbXFPxb7TB\r\nv3G41Lj70gOM6wRrLXodn4Zz2g7oTa9M5g3sM6ax0d0aKWwnsMmJTYib2Jg4\r\nhXKlVe19vsSmnfdSF+d43Gojpcf6yoEPzjas1ZUS4HKUgftYBD/wTyUYkvFl\r\ntCKyPOMznf+ZHYBYg0bD7rM5DcwN5cSbGk1uiWcvc8QnKqxwPhhI35tVbGNi\r\nbBYdLFFY2wJHVCtU+WR2Es/1y9q3exQqgl1fKfhrxAFwfjEOP65TwTm/8UiH\r\nAb3dcHlARM1tOnIz2xFLkYr/u6SIZuHANWRaQzIgMINpVi+OPGGar+OLi5xy\r\nugfb3tbcSy9UrF38jgyBybQbajTocY1hnf0okzGaj6Wq8HTJVjpIFCcWb2HN\r\n1xDhu7+nr6bn2iAZzV8i2G10sBL9V6pCFva3nZ4ArtWCM8cBN0fZS1AxIvDU\r\n/chaDMRLiYqxgFQG53r8+RXwHaUdpAuAX2bSj4absIN99pCsBmMIYcZKfJq2\r\nlONq8rwqeI9F8Uynr8C97IUuiqGy9xLrPOuyIQZufHkzIKfdjj153tfDrRip\r\nMfZa3++j924GjPDgFsFiC/COIlrXSdG/ztM=\r\n=uOeg\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"alu0101392561","email":"alu0101392561@ull.edu.es"},"maintainers":[{"name":"alu0101392561","email":"alu0101392561@ull.edu.es"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/scope_intro_1.0.0_1678912747639_0.4557473213274452"},"_hasShrinkwrap":false}},"time":{"created":"2023-03-15T20:39:07.568Z","1.0.0":"2023-03-15T20:39:07.943Z","modified":"2023-03-15T20:39:08.188Z"},"maintainers":[{"name":"alu0101392561","email":"alu0101392561@ull.edu.es"}],"description":"A simple calculator written in Jison","homepage":"https://github.com/ULL-ESIT-PL-2223/scope-intro-jose-lozano-armas-alu0101392561#readme","keywords":["ull","jison","espree","compiler"],"repository":{"type":"git","url":"git+https://github.com/ULL-ESIT-PL-2223/scope-intro-jose-lozano-armas-alu0101392561.git"},"author":{"name":"Casiano Rodriguez Leon","email":"crguezl@ull.edu.es","url":"https://crguezl.github.io/"},"bugs":{"url":"https://github.com/ULL-ESIT-PL-2223/scope-intro-jose-lozano-armas-alu0101392561/issues"},"license":"ISC","readme":"[![Open in Codespaces](https://classroom.github.com/assets/launch-codespace-f4981d0f882b2a3f0472912d15f9806d57e124e0fc890972558857b51b24a6f9.svg)](https://classroom.github.com/open-in-codespaces?assignment_repo_id=10367296)\n# Lab Introduction to Scope Analysis\n\nVéase el lab [scope-intro](https://ull-esit-pl.netlify.app/practicas/scope-intro.html)\n\n## Introducción \nEl objetivo de esta práctica es de el ampliar la calculadora que hemos estado desarrollando hasta este momento. Así mismo en esta práctica hemos vuelto a visitar conceptos cómo son el manejo del AST, el como hacer grámaticas, testing, etc... Todo con el fin de poder añadir números complejos a nuestra calculadora\n\n## Compilación de la calculadora\nDe cara a gestionar la calculadora es necesario explicar una serie de ficheros, de los cuales me centraré en los más importantes, siendo estos:\n\n**lexer.l**\n\n%{\nconst reservedWords = [\"print\",\"i\"]  \n\nconst idOrReserved = text => {\n                            if (reservedWords.find(w => w == text)) return text;\n                            return 'ID';\n                            }\n%}\nnumber  [0-9]+(\\.[0-9]+)?([eE][+-]?[0-9]+)?\"i\"?|\"i\"\n\n%%\n\\s+            ;\n{number}       return 'N';\n[a-zA-Z_]\\w*   return idOrReserved(yytext); \n'**'           return '**';\n[-=+*/!(),@&]  return yytext;\n\nComenzando estaría el fichero lexer.l, este se ha modificado con el objetivo de poder ampliar la antes mencionada calculadora. Para ello se ha cambiado la expresión regular que gestionaba los números para que esta ahora pudiese localizar número complejos tambíen.\n\nAsí mismo se ha empleado un array llamado 'reservedWords' el cual conserva los nombres de aquellas palabras que se consideran reservadas, tales cómo la 'i' (de los números complejos), cómo de la palabra print, con la que poder imprimir. Todo ello gestionado a través de la función idOrReserverd, la cual recibe cualquier texto que caze con su exprexión regular ([a-zA-Z_]\\w*) y en caso de coincidir con la antes mencionada reservedWords es almacenado.\n\n**grammar.jison**\n```jison\n%{\nconst { \n  buildRoot, \n  buildBinaryExpression, \n  buildLiteral, \n  buildCallExpression, \n  buildIdentifier,\n  buildAssignmentExpression,\n  buildSequenceExpression,\n  buildCallMemberExpression,\n  buildMax,\n  buildMin,\n} = require('./ast-build');\nconst {$} = require('./utils.js')\n%}\n\n%left ','\n%right '='\n%left '@'\n%left '&'\n%left '-' '+'\n%left '*' '/'\n%nonassoc UMINUS\n%right '**'\n%left '!'\n%%\nes: e { return { ast: buildRoot($e) }; }\n;\n\ne: \n    e ',' e             { $$ = buildSequenceExpression([$e1, $e2]) }\n  | ID '=' e            { $$ = buildAssignmentExpression($($ID), '=', $e); }\n  | e '@' e             { $$ = buildMax($e1, $e2, true); }\n  | e '&' e             { $$ = buildMin($e1, $e2, true); }\n\n  | e '-' e             { $$ = buildCallMemberExpression($e1, 'sub', [$e2]); }\n  | e '+' e             { $$ = buildCallMemberExpression($e1, 'add', [$e2]); }\n  | e '*' e             { $$ = buildCallMemberExpression($e1, 'mul', [$e2]); }\n  | e '/' e             { $$ = buildCallMemberExpression($e1, 'div', [$e2]); }\n  | e '**' e            { $$ = buildCallMemberExpression($e1, 'pow', [$e2]); }\n  | '(' e ')'           { $$ = $2; }\n  | '-' e %prec UMINUS  { $$ = buildCallMemberExpression($e, 'neg', []); }\n  | e '!'               { $$ = buildCallExpression('factorial', [$e], true); }\n  | N                   { $$ = buildCallExpression('Complex',[buildLiteral($N)], true); }\n  \n  | print '(' e ')'     { $$ = buildCallExpression('print', [$e], true)}\n  | ID                  { $$ = buildIdentifier($($1));}\n;\n```\nEl fichero que se puede ver en este apartado es en el que queda reflejada la grámatica de la calculadora. Este es igual al de la práctica anterior, con la diferencia de que ahora se tiene en cuenta el ID del identificador que estamos recibiendo y se cuenta con una línea exclusiva para usar el método print:\n\n```jison\n  | print '(' e ')'     { $$ = buildCallExpression('print', [$e], true)}\n  | ID                  { $$ = buildIdentifier($($1));}\n```\n\nPor otro lado las funciones que se nos pedía desarrollar en este apartado eran principalmente las de buildRoot y de build literal, las cuales serán explicadas a continuación.\n\n**ast-build.js**\n\nDe cara al siguiente fichero me gustaría destacar solo las funciones que se nos pidió realizar y que mencionamos anteriormente. Siendo estas las siguientes:\n\n```javascript\nfunction buildRoot(child) {\n  return {\n    type: \"Program\",\n    body: [\n      {\n        type: \"ExpressionStatement\",\n        expression: child,\n      },\n    ],\n    sourceType: \"script\",\n  };\n}\n\nfunction buildLiteral(value) {\n  return {\n    type: \"Literal\",\n    value: String(value),\n    raw: `\"${value}\"`, \n  };\n}\n```\n\nDichas funciones son efectivamente buildRoot y buildLiteral respectivamente. BuildRoot es la que gestiona de donde comienza la grámatica. Lo que hace es recibir el nodo de arranque y retornar que se trata de un programa el cual será también ExpressionStatement y que estará definida por el nodo pasado por parámetros.\n\nPor otro lado estaría buildLiteral, la cual recibe un valor y devuelve su valor codificado asignadole el tipo de Literal y guardando su valor convertido a string.\n\n**transpile.js**\n```javascript\n#!/usr/bin/env node\nconst {deb} = require('./deb.js');\nconst { difference, notDeclared } = require('./utils.js');\nconst p = require('./calc').parser;\nconst fs = require('fs/promises');\nconst { initializedVariables, dependencies, usedVariables } = require('./scope.js');\nconst codeGen = require('./code-generation.js')\nconst writeCode = require('./write-code.js');\n\nmodule.exports = async function transpile(inputFile, outputFile) {\n  let input = await fs.readFile(inputFile, 'utf-8')\n  let ast;\n  try {\n    ast = p.parse(input);\n  } catch (e) {\n    let m = e.message\n    console.error(m);\n    return m;\n  }\n  ast = dependencies(ast);\n  ast = initializedVariables(ast);\n  ast = usedVariables(ast);\n  let d = difference(ast.used, ast.symbolTable)\n  if (d.size > 0) { \n    let m = notDeclared(d).join('');\n    console.error(m);\n    return m;\n  }\n  let output = codeGen(ast);\n  debugger;\n  await writeCode(output, outputFile);\n  return output;\n}\n```\nUna vez explicado y aplicado todo lo anterior podemos proceder a explicar lo el fichero reflejado en este apartado. Este es el que gestiona el cómo se debe traducir la información del fichero que se le introduzca, y debe encargarse de devolver un versión traducida del mismo.\n\nLo que se hace es leer un fichero el cual se pasa a formato 'utf-8' y se crea una variable ast. Acto seguido se pasa por un bloque try catch, el cual en caso de acertar pasará la información al parser, en caso contrario se retornará un mensaje de error.\n\nMás adelante se hacen llamadas a las funciones de dependencies, initializedVariables y usedVariables, las cuales serán explicadas en el siguiente apartado. Estas lo que harán serán guardar en el ast la información relacionada acerca de las dependencias del árbol, así como de las variables usadas e inicializadas. Para luego calcular la diferencia entra lo usado y lo declarado en su tabla de símbolos y en caso de haber, se imprimirá un error.\n\nFinalmente se creará una variable output la cual guardará el ast que se habrá generado apoyado en el uso codeGen, el cual llamará a distintas funciones con el fin de gestionar la creación del código junto con sus dependencias. Para luego finalmente imprimir el código en el fichero de salida.\n\n**scope.js**\n\nPara concluir en el siguiente fichero me gustaría centrarme en las 3 funciones principales del mismo, las cuales fueron anteriormente mencionadas. Siendo las siguientes:\n\n```javascript\nfunction dependencies(dAst) {\n  dAst.dependencies = new Set([]); \n  astTypes.visit(dAst.ast, { \n    visitCallExpression(path) { \n      const node = path.node;\n      let name = node.callee.name;\n      if (patternIsSupport.test(name)) {\n        dAst.dependencies.add(name); \n      }\n      this.traverse(path);\n    }\n  });\n  return dAst;\n}\n\nconst initializedVariables = (dAst) => {\n  let initialized = new Set(); \n  let letDeclarations = [];\n  astTypes.visit(dAst.ast, {\n    visitAssignmentExpression(path) {\n      const node = path.node;\n      if (node.left.type === \"Identifier\") { \n        let name = node.left.name;\n        if (name !== \"undefined\" && !initialized.has(name)) { \n          if (!dAst.dependencies.has(name)) { \n            initialized.add(name);\n            letDeclarations.push(buildVariableDeclarator(buildIdentifier(name))); \n          }\n        }\n      }\n      this.traverse(path);\n    }\n  });\n  if (letDeclarations.length > 0) {\n    dAst.ast.body.unshift(buildVariableDeclaration(letDeclarations));\n  }\n  dAst.symbolTable = initialized;\n  return dAst;\n};\n\nconst usedVariables = (dAst) => {\n  let usedVars = new Set();\n  astTypes.visit(dAst.ast, {\n    visitIdentifier(path) {\n      let name = path.node.name;\n      if (/^[$]/.test(name) && !dAst.dependencies.has(name)) {\n        usedVars.add(name);\n      }\n      this.traverse(path); \n    }\n  });\n  dAst.used = usedVars;\n  return dAst;\n};\n```\n*función dependencies*\n\nEsta función se encarga de gestionar las posibles dependencias que pueda tener nuestro árbol. Para ello lo que se se hace es crear un objeto Set, el cual almacenará dichas dependencias, y luego invocará al objeto astTypes para que active su función visit. Está recibe nuestro árbol y un objeto, siendo en este caso el formato por la función visitCallExpression.\n\nEstá función recibe un path y guarda su nombre así como también su atributo callee (debido al tipo de función con la que estamos tratando). Para luego invocar a la función patternIsSupport que se encarga de generar una expresión regular, que verificará si el callee de node es soportado por nuestro programa. Y en caso de serlo se añade a las dependencias del árbol.\n\nFinalmente se llama a la función traverse, con la cual nos introducimos en el árbol para mirar todo esto de manera recursiva y por último retornar el objeto dAST invocante modificado.\n\n*función initializedVariables*\n\nSimilar al caso anterior se comienza creando un Set llamada initialized, que guardará todas las variables inicializadas. Así cómo tambíen una variable letDeclarations que guardará un array con las declaraciones de las mismas.\n\nApartir de aquí el proceso es el mismo. Se invoca a astTypes con su función visit y lo que se va a mirar en esta ocasión son las expresiones de asignación. Se guarda el camino del nodo y cómo se trata de una variable de asignación, al lado izquierdo debería haber un Identifier. Si este mismo esta definido, no se encuentra en initialized y no está ya en las dependencias del dAst, se añade a initialized y se le construye una declaración, la cual se guardará en letDeclarations.\n\nFinalmente se vuelve a llamar la función traverse para mirar todo de manera recursiva. Y acto seguido se comprueba que en caso de haber variables en letDeclarations, estas se incluyan en el body de dAst. Para luego actualizar la tabla de símbolos de initialized y retornar el árbol modificado.\n\n*función usedVariables*\n\nPara finalizar con este apartado toca mencionar la función usedVariables, esta sigue la misma lógica que las anteriores, con la diferencia de se buscan identificadores visitados. Para ello se guarda todo en usedVars, para luego volver a aplicar el mencionado método visit.\n\nActo seguido se usa la función visitIdentifiers para comprobar el path. Guardando el nombre del mismo y comprobando que el nombre coincida con lo esperado y no esté incluido ya en las dependencias. Si esto se cumple se añade a usedVars y usando traverse se mira todo de manera recursiva. Para luego finalmente actualizar el atributo used de nuestro dAst con el set de usedVars, y luego retornarlo.\n\n## Testing y uso de parámetros de versión y ayuda\n![captura_commander](info/captura_v_y_h.png)\n\nDe cara a este apartado solo resaltar que se puede hacer uso del parámetro -V y -h tal y cómo en las prácticas anteriores, y cómo se puede ver arriba. A continuación se mostrará un ejemplo de ejecución de los tests.\n\n![captura_test](info/tests.png)\n\n## Documentación\n\nA continuación adjunto una captura del coverage resultante en github pages:\n\n![captura_coverage](info/captura_coverage.png)\n\n","readmeFilename":"README.md"}