etnbrd/flx-compiler

View on GitHub
prototypes/blender/lib/dependency.js

Summary

Maintainability
A
25 mins
Test Coverage
var errors = require("./errors");
var cons = require("./constructors");
var h = require("./helpers");

module.exports = start;

var _types = {};

function start(ast) {
  return _next(ast.program, new cons.DepBuilder());
}

function _next(n, c) {
  if (!n.type)
    throw errors.missingType(n);

  if (!_types[n.type])
    throw errors.missingHandler(n);

  return _types[n.type](n, c);
}

/******************************************************************************************
 * TYPES                                                                                  *
 ******************************************************************************************


Programs
  program(body[, loc])

Statements
  emptyStatement([loc])
  blockStatement(body[, loc])
  expressionStatement(expr[, loc])
  labeledStatement(label, body[, loc])
  ifStatement(test, cons, alt[, loc])
  switchStatement(disc, cases, isLexical[, loc])
  whileStatement(test, body[, loc])
  doWhileStatement(body, test[, loc])
  forStatement(init, test, update, body[, loc])
  forInStatement(left, right, body, isForEach[, loc])
  breakStatement(label[, loc])
  continueStatement(label[, loc])
  withStatement(obj, body[, loc])
  returnStatement(arg[, loc])
  tryStatement(body, handlers, fin[, loc])
  throwStatement(arg[, loc])
  debuggerStatement([loc])
  letStatement(head, body[, loc])

Declarations
  functionDeclaration(name, args, body, isGenerator, isExpression[, loc])
  variableDeclaration(kind, dtors[, loc])
  variableDeclarator(patt, init[, loc])

Expressions
  sequenceExpression(exprs[, loc])
  conditionalExpression(test, cons, alt[, loc])
  unaryExpression(op, arg, isPrefix[, loc])
  binaryExpression(op, left, right[, loc])
  assignmentExpression(op, left, right[, loc])
  logicalExpression(op, left, right[, loc])
  updateExpression(op, arg, isPrefix[, loc])
  newExpression(callee, args[, loc])
  callExpression(callee, args[, loc])
  memberExpression(obj, prop, isComputed[, loc])
  functionExpression(name, args, body, isGenerator, isExpression[, loc])
  arrayExpression(elts[, loc])
  objectExpression(props[, loc])
  thisExpression([loc])
  graphExpression(index, expr[, loc])
  graphIndexExpression(index[, loc])
  comprehensionExpression(body, blocks, filter[, loc])
  generatorExpression(body, blocks, filter[, loc])
  yieldExpression(arg[, loc])
  letExpression(head, body[, loc])

Patterns
  arrayPattern(elts[, loc])
  objectPattern(props[, loc])
  propertyPattern(key, patt[, loc])

Clauses
  switchCase(test, cons[, loc])
  catchClause(arg, guard, body[, loc])
  comprehensionBlock(left, right, isForEach[, loc])

Miscellaneous
  identifier(name[, loc])
  literal(val[, loc])
  property(kind, key, val[, loc])

*/

_types._block = function(n, c) {
  return n.body.reduce(_types._before(c), undefined);
}

_types._before = function(c) {
  return function(previous, current) {
    node = _next(current, c);
    if (previous)
      c.pushDep(new cons.Dep(previous.id, node.id, "Before"));
    return node;
  }
}


////////////////////////////////////////////////////////////////////////////////
// Programs                                                                   //
////////////////////////////////////////////////////////////////////////////////

//   program(body[, loc])
_types.Program = function(n, c) {
  var id = h.hash(n);
  c.pushContext(new cons.Context(n.type, id));
  _types._block(n, c);
  return c.popContext();
}

////////////////////////////////////////////////////////////////////////////////
// Statements                                                                 //
////////////////////////////////////////////////////////////////////////////////

//   emptyStatement([loc])
// TODO


//   blockStatement(body[, loc])
_types.BlockStatement = function(n, c) {
  return _types._block(n, c);
}

//   expressionStatement(expr[, loc])
_types.ExpressionStatement = function(n, c) {
  return _next(n.expression, c);
}

//   labeledStatement(label, body[, loc])
// TODO

//   ifStatement(test, cons, alt[, loc])
// TODO

//   switchStatement(disc, cases, isLexical[, loc])
// TODO

//   whileStatement(test, body[, loc])
// TODO

//   doWhileStatement(body, test[, loc])
// TODO

//   forStatement(init, test, update, body[, loc])
// TODO

//   forInStatement(left, right, body, isForEach[, loc])
// TODO

//   breakStatement(label[, loc])
// TODO

//   continueStatement(label[, loc])
// TODO

//   withStatement(obj, body[, loc])
// TODO

//   returnStatement(arg[, loc])
_types.ReturnStatement = function(n, c) {
  var hash = h.hash(n);
  c.pushDep(new cons.Dep(_next(n.argument, c).id, hash, "return"));
  return c.pushNode(new cons.Node(n, 'return', hash, "ReturnStatement"));
}

//   tryStatement(body, handlers, fin[, loc])
// TODO

//   throwStatement(arg[, loc])
// TODO

//   debuggerStatement([loc])
// TODO

//   letStatement(head, body[, loc])
// TODO


////////////////////////////////////////////////////////////////////////////////
// Declarations                                                               //
////////////////////////////////////////////////////////////////////////////////

//   functionDeclaration(name, args, body, isGenerator, isExpression[, loc])
_types.Param = function(n, c) {
  return new cons.Node(n, _next(n, c), h.hash(n), 'Param');
}

_types.FunctionDeclaration = function(n, c) {
  var next = _next(n.id, c);
  var node = c.pushNode(new cons.Node(n, next.name, h.hash(n), 'FunctionDeclaration'));
  var params = n.params.map(function(param) {
    return _types.Param(param, c);
  })

  c.pushContext(new cons.Context(node.name, node.id, params));

  params.forEach(function(param) {
    c.pushNode(param);
  })
  _next(n.body, c);

  c.popContext();
  return node;
}

//   variableDeclaration(kind, dtors[, loc])
_types.VariableDeclaration = function(n, c) {
  return n.declarations.reduce(_types._before(c), undefined);;
}

//   variableDeclarator(patt, init[, loc])
_types.VariableDeclarator = function(n, c) {
  var node = c.pushNode(new cons.Node(n, _next(n.id, c), null, 'VariableDeclarator', {scope: c[0].id}));
  var dep = c.pushDep(new cons.Dep(_next(n.init, c).id, node.id, 'VariableDeclarator'));
  return node;
}

////////////////////////////////////////////////////////////////////////////////
// Expressions                                                                //
////////////////////////////////////////////////////////////////////////////////

//   sequenceExpression(exprs[, loc])
// TODO

//   conditionalExpression(test, cons, alt[, loc])
// TODO

//   unaryExpression(op, arg, isPrefix[, loc])
// TODO


//   binaryExpression(op, left, right[, loc])
_types.BinaryExpression = function(n, c) {
  var name = n.operator;
  var node = new cons.Node(n, name, h.hash(n), "BinaryExpression", {operator: n.operator});

  ['right', 'left'].forEach(function(op) {    
    var _node = _next(n[op], c);

    if (_node.kind === "Identifier") {
      c.pushNode(_node);
    }

    c.pushDep(new cons.Dep(_node.id, node.id, op, {side: op}));
  })

  return c.pushNode(node);
}

//   assignmentExpression(op, left, right[, loc])
_types.AssignmentExpression = function(n, c) {
  var name = n.operator;

  var ops = ['right', 'left'].map(function(op, index) {    
    var node = _next(n[op], c);

    if (node.kind === "Identifier")
      c.pushNode(node);

    if (op === "left" && node.kind === "VariableDeclarator") {
      return c.pushNode(new cons.Node(n[op], node.name, h.hash(n), "VariableDeclarator", {version: node.version + 1 || 1}));
    }

    return node;
  })

  c.pushDep(new cons.Dep(ops[0].id, ops[1].id, "Assignment", {operator: n.operator}));

  return ops[1];
}

//   logicalExpression(op, left, right[, loc])
_types.LogicalExpression = function(n, c) {
  var name = n.operator;
  var node = new cons.Node(n, n.operator, h.hash(n), "LogicalExpression");

  ['left', 'right'].forEach(function(op) {    
    var _node = _next(n[op], c);
    c.pushDep(new cons.Dep(_node.id, node.id, n[op]));

    if (_node.kind === "Identifier")
      c.pushNode(_node);
  })

  return node;
}

//   updateExpression(op, arg, isPrefix[, loc])
// TODO

//   newExpression(callee, args[, loc])
// TODO

//   callExpression(callee, args[, loc])
_types.Argument = function(n, c) {
  var node = _next(n, c);
  return new cons.Node(n, node.name, node.id, 'Param');
}

_types.CallExpression = function(n, c) {
  var salt = h.salt();
  var next = _next(n.callee, c);
  var node = c.pushNode(new cons.Node(n, next.name, h.hash(n, salt), "CallExpression"));

  if (next.kind === "Identifier")
    c.pushNode(next);

  c.pushDep(new cons.Dep(next.id, node.id, "Callee"));

  n.arguments.forEach(function(argument, index) {
    var arg = _types.Argument(argument, c);
    c.pushDep(new cons.Dep(arg.id, node.id, "Argument", {index: index}));
  });

  return node;
}

//   memberExpression(obj, prop, isComputed[, loc])
_types.MemberExpression = function(n, c) {
  var hash = h.hash(n);
  var options = ["object", "property"].reduce(function(options, op) {
    var _node = _next(n[op], c);
    options[op] = _node;

    if (_node.kind === "Identifier")
      c.pushNode(_node);

    c.pushDep(new cons.Dep(_node.id, hash, op, {computed: n.computed}));

    return options;
  }, {})

  options.name = (n.computed) ? (options.object.name + "[" + options.property.name + "]") : (options.object.name + "." + options.property.name);

  return c.pushNode(new cons.Node(n, "__name__", hash, "MemberExpression", options));
}

//   functionExpression(name, args, body, isGenerator, isExpression[, loc])
_types.FunctionExpression = _types.FunctionDeclaration;

//   arrayExpression(elts[, loc])
// TODO

//   objectExpression(props[, loc])
_types.ObjectExpression = function(n, c) {

  n.properties.forEach(function(property) {
    _next(n.property, c);
  })

  // TODO
  throw "TODO"

  return "ObjectLiteral";
}

//   thisExpression([loc])
// TODO

//   graphExpression(index, expr[, loc])
// TODO

//   graphIndexExpression(index[, loc])
// TODO

//   comprehensionExpression(body, blocks, filter[, loc])
// TODO

//   generatorExpression(body, blocks, filter[, loc])
// TODO

//   yieldExpression(arg[, loc])
// TODO

//   letExpression(head, body[, loc])
// TODO


////////////////////////////////////////////////////////////////////////////////
// Patterns                                                                   //
////////////////////////////////////////////////////////////////////////////////

//   arrayPattern(elts[, loc])
// TODO

//   objectPattern(props[, loc])
// TODO

//   propertyPattern(key, patt[, loc])
// TODO


////////////////////////////////////////////////////////////////////////////////
// Clauses                                                                    //
////////////////////////////////////////////////////////////////////////////////

//   switchCase(test, cons[, loc])
// TODO

//   catchClause(arg, guard, body[, loc])
// TODO

//   comprehensionBlock(left, right, isForEach[, loc])
// TODO


////////////////////////////////////////////////////////////////////////////////
// Miscellaneous                                                              //
////////////////////////////////////////////////////////////////////////////////

//   identifier(name[, loc])
_types.Identifier = function(n, c) {

  var ids = c.reduce(function(ids, _c) {
    return ids.concat(_c.nodes.filter(function(id) {
      return id.name === n.name
    }))
  }, []);

  if (ids.length === 1) {
    return ids[0];
  } else if (ids.length === 0) {
    return new cons.Node(n, n.name, h.hash(n), "Identifier");
  } else if (ids.every(function(id) {
        return id.kind === "VariableDeclarator"
      })) {

      return ids.reduce(function(maxId, id) {
        return (maxId.version < id.version) ? id : maxId;
      })
  } else {
    throw errors.identifierConflict(ids);
  }
}

//   literal(val[, loc])
_types.Literal = function(n, c) {
  return c.pushNode(new cons.Node(n, n.value, h.hash(n), "Literal"));
}

//   property(kind, key, val[, loc])
// TODO