All files scope.js

100% Statements 50/50
86.36% Branches 19/22
100% Functions 7/7
100% Lines 49/49

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111                      1x   1x   4x 1x               34x 34x   221x 221x 132x   221x     34x 34x               1x 34x 34x 34x   34x   18x 18x     18x 18x 18x   18x 18x 4x     18x   18x 18x 1x   18x     49x 49x 49x 49x 49x 49x       49x     342x 342x 137x   342x       34x 23x     34x   34x 34x 1x     34x     1x        
/**
 * Universidad de La Laguna
 * Escuela Superior de Ingeniería y Tecnología
 * Grado en Ingeniería Informática
 * Procesadores de Lenguajes
 *
 * @author Juan Rodríguez Suárez
 * @since Mar 04 2024
 * @desc Contains functions to analyze the scope of the program and the dependencies of the functions.
 */
 
const astTypes = require("ast-types");
 
const support = require("./support-lib.js");
// Build regexp matching the names of the support functions /factorial|power|min|max|.../
const FUNCTION_NAMES = Object.keys(support).map(n => n.replace(/[$*.^]/, '[$&]')); // Escape regexp special characters
const patternIsSupport = new RegExp(FUNCTION_NAMES.join('|'));
 
/**
 * @brief Builds the set of function dependencies
 * @param {object} dAst - The decorated AST
 * @returns {object} The decorated AST
 */
function dependencies(dAst) {
  let usedFunctions = new Set();
  astTypes.visit(dAst.ast, {
    visitCallExpression(path) {
      const NODE = path.node;
      if (patternIsSupport.test(NODE.callee.name)) {
        usedFunctions.add(NODE.callee.name);
      }
      this.traverse(path);
    }
  });
  dAst.dependencies = usedFunctions;
  return dAst;
}
 
/**
 * @brief Analyzes the scope of the program
 * @param {object} dAst - The decorated AST
 * @returns {object} The decorated AST
 */
const scopeAnalysis = (dAst) => {
  const Scope = require("./scope-class.js");
  let scope = new Scope(null); // global scope
  let ast = dAst.ast;
 
  astTypes.visit(ast, {
    visitFunctionExpression(path) {
      let node = path.node;
      scope = new Scope(scope);
 
      // mark parameters as initialized
      const PARAMETERS = node.params;
      for (const PARAM of PARAMETERS) {
        scope.setAsInitialized(PARAM.name);
      }
      this.traverse(path);
      if (scope.length > 0) { // insert declarations at the beginning of the function
        node.body.body.unshift(scope.buildDeclaration());
      }
 
      node.scope = scope;
 
      const NOT_DECLARED_MESSAGE = scope.notDeclaredMessage();
      if (NOT_DECLARED_MESSAGE) {
        console.error('*** WARNING: ' + NOT_DECLARED_MESSAGE + ' in function scope ***\n');
      }
      scope = scope.parent;
    },
    visitAssignmentExpression(path) {
      const NODE = path.node;
      Eif (NODE.left.type === 'Identifier') {
        let name = NODE.left.name;
        Eif (name && !scope.has(name)) {
          Eif (!dAst.dependencies.has(name)) {
            scope.add(name);
          }
        }
      }
      this.traverse(path);
    },
    visitIdentifier(path) {
      const NAME = path.node.name;
      if (/^[$]/.test(NAME) && !dAst.dependencies.has(NAME)) {
        scope.setAsUsed(NAME);
      }
      this.traverse(path);
    }
  });
 
  if (scope.length > 0) { // insert declarations at the beginning of the program
    ast.body.unshift(scope.buildDeclaration());
  }
 
  ast.scope = scope;
 
  const NOT_DECLARED_MESSAGE = scope.notDeclaredMessage();
  if (NOT_DECLARED_MESSAGE) {
    console.error('*** WARNING: ' + NOT_DECLARED_MESSAGE + ' in global scope ***\n');
  }
 
  return dAst;
};
 
module.exports = {
  dependencies,
  scopeAnalysis
}