UNPKG

panino

Version:

API documentation generator with a strict grammar and testing tools

250 lines (192 loc) 6.96 kB
var _ = require('underscore'); // Transforms Esprima AST into string var to_s = exports.to_s = function(ast) { switch (ast["type"]) { case "Program": return _.map(ast["body"], function(s) { return to_s(s); }).join(); // Statements case "BlockStatement": return "{" + _.map(ast["body"], function(s) { to_s(s) }).join() + "}"; case "BreakStatement": return "break" + (ast["label"] ? " " + to_s(ast["label"]) : "") + ";"; case "ContinueStatement": return "continue" + (ast["label"] ? " " + to_s(ast["label"]) : "") + ";"; case "DoWhileStatement": return "do " + to_s(ast["body"]) + " while (" + to_s(ast["test"]) + ");"; case "DebuggerStatement": return "debugger;"; case "EmptyStatement": return ";"; case "ExpressionStatement": return to_s(ast["expression"]) + ";"; case "ForStatement": var init = ast["init"] ? to_s(ast["init"]).replace(/;$/, "") : "" var test = ast["test"] ? to_s(ast["test"]) : ""; var update = ast["update"] ? to_s(ast["update"]) : ""; return "for (" + init + "; " + test + "; " + update + ") " + to_s(ast["body"]); case "ForInStatement": var left = to_s(ast["left"]).replace(/;$/, ""); var right = to_s(ast["right"]); return "for (" + left + " in " + right + ") " + to_s(ast["body"]); case "IfStatement": var alternate = ast["alternate"] ? " else " + to_s(ast["alternate"]) : ""; return "if (" + to_s(ast["test"]) + ") " + to_s(ast["consequent"]) + alternate; case "LabeledStatement": return to_s(ast["label"]) + ": " + to_s(ast["body"]); case "ReturnStatement": var arg = ast["argument"] ? to_s(ast["argument"]) : ""; return "return " + arg + ";"; case "SwitchStatement": return "switch (" + to_s(ast["discriminant"]) + ") {" + _.map(ast["cases"], function(c) { to_s(c) }).join() + "}"; case "SwitchCase": var test = ast["test"] ? "case " + to_s(ast["test"]) : "default"; return test + ": " + _.map(ast["consequent"], function(c) { to_s(c) }).join(); case "ThrowStatement": return "throw " + to_s(ast["argument"]) + ";"; case "TryStatement": var handlers = _.map(ast["handlers"], function(h) { to_s(h) }).join(); var finalizer = ast["finalizer"] ? " finally " + to_s(ast["finalizer"]) : ""; return "try " + to_s(ast["block"]) + handlers + finalizer; case "CatchClause": var param = ast["param"] ? to_s(ast["param"]) : ""; return " catch (" + param + ") " + to_s(ast["body"]); case "WhileStatement": return "while (" + to_s(ast["test"]) + ") " + to_s(ast["body"]); case "WithStatement": return "with (" + to_s(ast["object"]) + ") " + to_s(ast["body"]); // Declarations case "FunctionDeclaration": return func(ast); case "VariableDeclaration": return ast["kind"] + " " + list(ast["declarations"]) + ";"; case "VariableDeclarator": if (ast["init"]) return to_s(ast["id"]) + " = " + to_s(ast["init"]); else return to_s(ast["id"]); // Expressions case "AssignmentExpression": return parens(ast, ast["left"]) + " " + ast["operator"] + " " + to_s(ast["right"]); case "ArrayExpression": return "[" + list(ast["elements"]) + "]"; case "BinaryExpression": return binary(ast); case "CallExpression": return serializeCall(ast); case "ConditionalExpression": return parens(ast, ast["test"]) + " ? " + to_s(ast["consequent"]) + " : " + to_s(ast["alternate"]); case "FunctionExpression": return func(ast); case "LogicalExpression": return binary(ast); case "MemberExpression": if (ast["computed"]) return parens(ast, ast["object"]) + "[" + to_s(ast["property"]) + "]"; else return parens(ast, ast["object"]) + "." + to_s(ast["property"]); case "NewExpression": return "new " + serializeCall(ast); case "ObjectExpression": return "{" + list(ast["properties"]) + "}"; case "Property": return to_s(ast["key"]) + ": " + to_s(ast["value"]); case "SequenceExpression": return list(ast["expressions"]); case "ThisExpression": return "this"; case "UnaryExpression": return ast["operator"] + parens(ast, ast["argument"]); case "UpdateExpression": if (ast["prefix"]) return ast["operator"] + parens(ast, ast["argument"]); else return parens(ast, ast["argument"]) + ast["operator"]; // Basics case "Identifier": return ast["name"]; case "Literal": return ast["raw"]; default: console.trace(); throw new Error("Unknown node type: ", ast["type"]); } } // serializes function declaration or expression function func(ast) { var params = list(ast["params"]) var id = ast["id"] ? to_s(ast["id"]) : "" return "function " + id + "(" + params + ") " + to_s(ast["body"]) } // serializes list of comma-separated items function list(array) { return _.map(array, function(x) { to_s(x) }).join(", "); } // serializes call- and new-expression function serializeCall(ast) { return parens(ast, ast["callee"]) + "(" + list(ast["arguments"]) + ")"; } // Handles both binary- and logical-expression function binary(ast) { return parens(ast, ast["left"]) + " " + ast["operator"] + " " + parens(ast, ast["right"]); } // serializes child node and wraps it inside parenthesis if the // precedence rules compared to parent node would require so. function parens(parent, child) { if (precedence(parent) >= precedence(child)) return to_s(child); else return "(" + to_s(child) + ")"; } // Returns the precedence of operator represented by given AST node function precedence(ast) { var p = PRECEDENCE[ast["type"]]; if ( _.isNumber(p) ) // represents Fixnum? I'm so sorry. return p; else if ( p && p.constructor === Object ) // p is a {} object return p[ast["operator"]]; else return 0; } // Precedence rules of JavaScript operators. // // Taken from: https://developer.mozilla.org/en/JavaScript/Reference/Operators/Operator_Precedence // var PRECEDENCE = { "SequenceExpression" : 17, "AssignmentExpression" : 16, "ConditionalExpression" : 15, "LogicalExpression" : { "||" : 14, "&&" : 13, }, "BinaryExpression" : { "|" : 12, "^" : 11, "&" : 10, "==" : 9, "!=" : 9, "===" : 9, "!==" : 9, "<" : 8, "<=" : 8, ">" : 8, ">=" : 8, "in" : 8, "instanceof" : 8, "<<" : 7, ">>" : 7, ">>>" : 7, "+" : 6, "-" : 6, "*" : 5, "/" : 5, "%" : 5, }, "UnaryExpression" : 4, "UpdateExpression" : 3, "CallExpression" : 2, "MemberExpression" : 1, "NewExpression" : 1, }