npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@kiliankgr/scope-intro

v1.0.0

Published

A simple calculator written in Jison

Downloads

6

Readme

Open in Codespaces

Lab Introduction to Scope Analysis

Ejecucion

  • Para la ejecución del programa primero descargaremos las dependencias $ npm ci
  • Luego compilaremos el programa $ npm run compile
  • Tambien podemos compilar y probar los test predefinidos $ npm run test
  • Para más información consulte $ npm run help
  • Si se quisiera ejecutar con un fichero en particular $ ./bin/calc2js.mjs <nombre_test> [-o <fichero_salida>]

Analizador lexico

Debemos de modificar el analizador lexico para que pueda trabajar con números complejos, así como con palabras reservadas como print, o, identificadores.

%{
const reservedWords = ["fun", "true", "false", "i"] 
const predefinedIds = ["print","write"]  

const idOrReserved = text => {/* fill the code */
    if (reservedWords.find(w => w == text)) return text.toUpperCase();    
    if (predefinedIds.find(w => w == text)) return 'PID';
    return 'ID';
  }
%}

number (\d+(\.?\d+)?([eE][-+]?\d+)?"i"?) | "i" //numero, numero flotante, numero elevado a, numero complejo

%%
\s+            /* skip whites */;
"#".*          /* skip comments */;
\/\*(.|\n)*?\*\/ /* skip multiline comments*/;
{number}       return 'N';
[a-zA-Z_]\w*   return idOrReserved(yytext);
'**'           return '**'; //power
'=='           return '=='; //equal
'&&'           return '&&'; // logical and
'||'           return '||'; // logical or
[-=+*/!(),<>@&{}\[\]]  return yytext;

Gramatica

Modificaremos la gramatica para tratar con variables, numeros complejos, palabras reservadas ...

%left ','

%right '='
%left '=='
%left '@'
%left '&'
%left '-' '+'
%left '*' '/'
%nonassoc UMINUS
%right '**'
%left '!'
%%
es: e { return { ast: buildRoot($e) }; }
;

e: 
   /* rules for assignment, comma, print, ids */
  
  e ',' e               { $$ = buildSequenceExpression([$e1, $e2]); } 
  | ID '=' e            { $$ = buildAssignmentExpression($($1), '=', $e);}
  | e '==' e            { $$ = buildCallMemberExpression($e1, 'equals', [$e2])}


  | e '@' e             { $$ = buildMax($e1, $e2, true); }
  | e '&' e             { $$ = buildMin($e1, $e2, true); }


  | e '-' e             { $$ = buildCallMemberExpression($e1, 'sub', [$e2]); }
  | e '+' e             { $$ = buildCallMemberExpression($e1, 'add', [$e2]); }
  | e '*' e             { $$ = buildCallMemberExpression($e1, 'mul', [$e2]); }
  | e '/' e             { $$ = buildCallMemberExpression($e1, 'div', [$e2]); }
  | e '**' e            { $$ = buildCallMemberExpression($e1, 'pow', [$e2]); }
  | '(' e ')'           { $$ = $2; }
  | '-' e %prec UMINUS  { $$ = buildCallMemberExpression($e, 'neg', []); }
  | e '!'               { $$ = buildCallExpression('factorial', [$e], true); }
  | N                   { $$ = buildCallExpression('Complex',[buildLiteral($N)], true); }

  
  | PID '(' e ')'     { $$ = buildCallExpression($PID, [$3], true); }
  | ID                  { $$ = buildIdentifier($($1))} //$(algo) lo concatena con el $ delante
  
;

scope-class

Creamos una clase para tratar con el árbol ast.

class Scope {
  constructor(parent) {
    this.parent = parent;
    this.initialized = new Set();
    this.used = new Set();
    this.letDeclarations = []; //list of VariableDeclarator ASTs for let $a = 4... declarations
    this.errors = []; //contain array of errors in the program
  }
  add(name) {
    this.initialized.add(name);
    this.letDeclarations.push(buildVariableDeclarator(buildIdentifier(name)));
  }
  setAsInitialized(name) {
    this.initialized.add(name);
  }
  setAsUsed(name) {
    this.used.add(name);
  }
  has(name) {
    return this.initialized.has(name);
  }
  buildDeclaration() {
    return buildVariableDeclaration(this.letDeclarations);
  }

  notDeclared() {    
    return difference(this.used, this.initialized)
  }
  notDeclaredMessage(){
    let d = this.notDeclared();
    //console.log("Comproabando si alguna variable no a sido declarada: ", d);
    if ( d.size > 0 ) {
        return Array.from(d).
            map(x => x.replace(/^[$]/, '')).
            map(x => `Not declared variable '${x}'`).join('\n')
    }
    return null;
  }

  get length() {
    return this.letDeclarations.length;
  }
}

Scope

Por ultimo deberemos de rellenar las funciones del fichero scope.js para así transformar el arbol AST generado por la libreria recast a nuestro antojo mediante la librería ast-types


/**
 *  Detect what support functions are used: dependency analysis
 * recoge el arbolmediante Ast-types y busca las dependecias que soporte
 * @function
 * @param dAst - Arbol AST
 * @returns dAst modificado
 */
function dependencies(dAst) {
  const functionNames = Object.keys(require("./support-lib.js")); 
  dAst.dependencies = new Set([]);
  visit(dAst.ast, {
    visitCallExpression(path) {
      const node = path.node;
      let name = node.callee.name;
      if (functionNames.includes(name)) {
        dAst.dependencies.add(name);
      }
      this.traverse(path);
    }
  });    
  //console.log("Dast from dependencias: ", dAst.dependencies);
  return dAst;
}

/**
 * Builds the set of variables that are initialized in the program and
 * Detects which variables are used in the program
 * @function
 * @param dAst - Arbol AST
 * @returns dAst modificado
 */
const scopeAnalysis = (dAst) => {
  const Scope = require('./scope-class.js');
  let scope = new Scope(null); // global scope
  dAst.errors = ""; // conjunto de errores controlados
  let ast = dAst.ast;
  visit(ast, {
    visitFunctionExpression(path) {
      let node =path.node;
      scope = new Scope(scope);

      //mark parameters as initialized
      let params = node.params;
      for (let param of params) {
        scope.setAsInitializated(param.name);
      }
      this.traverse(path);
      if (scope.length > 0) { //inset declarations at the beginning of the function
        node.body.body.unshift(scope.buildDeclaration())
      }
      node.scope = scope;
      let d = scope.notDeclaredMessage();
      dAst.errors = d;
      if (d) console.error(d+ ' used in function scope');

      scope = scope.parent;
    },
    visitAssignmentExpression(path) {
      const node = path.node;
      if (node.left.type === "Identifier") {
        let name = node.left.name;
        if (name && !scope.has(name)) {
          if (!dAst.dependencies.has(name)) {
            scope.add(name);
          }
        }
      }
      this.traverse(path);
    },
    visitIdentifier(path) {
      let name = path.node.name;
      
      if(/^[$]/.test(name) && !dAst.dependencies.has(name)) {
        

        scope.setAsUsed(name);
      }

      this.traverse(path);
    }
  });

  if (scope.length > 0) {
    ast.body.unshift(scope.buildDeclaration());
  }

  ast.scope = scope;

  let d = scope.notDeclaredMessage();
  

  if (d) dAst.errors = d+"\n";//console.error(d)

  
  return dAst;
}

Functions

dependencies(dAst) ⇒

Detect what support functions are used: dependency analysis recoge el arbolmediante Ast-types y busca las dependecias que soporte

Kind: global function
Returns: dAst modificado

| Param | Description | | --- | --- | | dAst | Arbol AST |

scopeAnalysis(dAst) ⇒

Builds the set of variables that are initialized in the program and Detects which variables are used in the program

Kind: global function
Returns: dAst modificado

| Param | Description | | --- | --- | | dAst | Arbol AST |

GitHub Actions

Creamos un fichero .yml donde se implementaran las Github Actions

name: Node.js CI

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:
  build:

    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [14.x, 16.x, 18.x]
        # See supported Node.js release schedule at https://nodejs.org/en/about/releases/

    steps:
    - uses: actions/checkout@v3 #clona el repo
    - name: Use Node.js ${{ matrix.node-version }} 
      uses: actions/setup-node@v3 #instalacion de node
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'npm'
    - run: npm ci
    - run: npm run build --if-present
    - run: npm test

Coverage

Inicialmente hemos tratado de realizar las pruebas mediante nyc, pero al paracer tiene problemas al reconocer algunos de nuestros archivos y por tanto los test salen erroneos.

Por lo tanto hemos recurrido al uso de la libreria c8 similar a nyc

Documentación (modificar)

Para la documentación del código se han utilizado comentarios del estilo JSDoc. Y se ha utilizado la herramienta jsdoc-to-markdown para la creación de un readme automático que se encuentra en la carpeta /docs.

Véase el lab scope-intro