Initial commit

This commit is contained in:
benedikt willi 2014-04-01 10:04:40 +02:00
commit cb0bf1a130
7 changed files with 3155 additions and 0 deletions

18
README.md Normal file
View file

@ -0,0 +1,18 @@
#Brackets Extension Template
This is a sample README file for a Brackets extensions.
##Install from URL
1. Open the the Extension Manager from the File menu
2. Copy paste the URL of the github repo or zip file
##Install from file system
1. Download this extension using the ZIP button above and unzip it.
2. Copy it in Brackets' `/extensions/user` folder by selecting `Help > Show Extension Folder` in the menu.
3. Reload Brackets.
##Instructions

167
main.js Normal file
View file

@ -0,0 +1,167 @@
/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
/*global define, $, brackets, window */
/** {ext_name} Extension
description
*/
define(function (require, exports, module) {
'use strict';
var CommandManager = brackets.getModule("command/CommandManager");
var Menus = brackets.getModule("command/Menus");
var KeyBindingManager = brackets.getModule("command/KeyBindingManager");
var Acorn_loose = require("thirdparty/acorn/acorn_loose");
var Walker = require("thirdparty/acorn/util/walk");
var EditorManager = brackets.getModule("editor/EditorManager");
var COMMAND_ID = "annotate.annotateFromParser";
var MENU_NAME = "Annotate";
var EMPTY_MSG = "No function definition found";
/**
* Create a jsdoc annotation of the next function found (using a js parser) an insert it one line above
*/
function annotate() {
var editor = EditorManager.getCurrentFullEditor();
//Get cursor position and set it to the beginning of the line
var pos = editor.getCursorPos();
pos.ch = 0;
// Get the text from the start of the document to the current cursor position and count it's length'
var txtTo = editor._codeMirror.getRange({line:0,ch:0},pos);
var cursorPosition = txtTo.length;
// Get document text
var txtFull = editor._codeMirror.getValue();
// Parse text
var acornTxtFull = Acorn_loose.parse_dammit(txtFull, {locations: true});
// Find next function
var found = new Walker.findNodeAfter(acornTxtFull, cursorPosition, "Function");
if(found){
// There was a result, so build jsdoc
var jsdoc = {};
jsdoc.loc = found.node.loc;
jsdoc.prefix = "";
jsdoc.name = found.node.id ? found.node.id.name : null;
jsdoc.params = [];
jsdoc.returnValue = undefined;
//FIXME This is a mess.
if(!found.node.id) {
var nameHolder = new Walker.findNodeAround(acornTxtFull, found.node.start-1);
var id, i, l;
if (nameHolder.node.type === "ObjectExpression"){
// set a random id, so it can be found a
id = Math.random();
found.node.id = id;
// Find its neighbour and get the name from that
i = 0;
l = nameHolder.node.properties.length;
for (i = 0; i < l; i++) {
var prop = nameHolder.node.properties[i];
jsdoc.name = prop.key.name;
if(prop.value.id===found.node.id) break;
}
} else if (nameHolder.node.type === "VariableDeclaration") {
// set a random id, so it can be found a
id = Math.random();
found.node.id = id;
// Find its neighbour and get the name from that
i = 0;
l = nameHolder.node.declarations.length;
for (i = 0; i < l; i++) {
var dec = nameHolder.node.declarations[i];
jsdoc.name = dec.id.name;
if(dec.init.id===found.node.id) break;
}
} else {
nameHolder = new Walker.findNodeBefore(acornTxtFull, found.node.start);
jsdoc.name = nameHolder.node.property.name;
}
}
// Add parameters to the jsdoc object
found.node.params.forEach(function (param) {
jsdoc.params.push(param.name);
});
// Find and add return value
var foundReturnValue = new Walker.findNodeAfter(found.node, 0, "ReturnStatement");
jsdoc.returnValue = foundReturnValue.node ? foundReturnValue.node.argument.name : undefined;
// set prefix (find first none whitespace character)
var codeLine = editor._codeMirror.getLine(jsdoc.loc.start.line-1);
jsdoc.prefix = codeLine.substr(0, codeLine.length - codeLine.trimLeft().length).replace(/[^\s\n]/g, ' ');
// build annotation string
var jsdocString = generateString(jsdoc);
// insertJsdoc string into editor
insertJsdoc(jsdocString, jsdoc.loc);
} else {
// No function definition found
window.alert(EMPTY_MSG);
}
}
/**
* Create the string representation of the jsdoc object
* @param {object} jsdoc input
* @returns {string} representation of the jsdoc object
*/
function generateString(jsdoc){
var jsdocString = jsdoc.prefix + "/**\n";
if (jsdoc.name.charAt(0) === "_") {
jsdocString += jsdoc.prefix + " * @private \n";
}
// Add description
jsdocString += jsdoc.prefix + " * Description \n";
jsdoc.params.forEach(function (param) {
jsdocString += jsdoc.prefix + " * @param {type} " + param + " Description \n";
});
if (jsdoc.returnValue)
jsdocString += jsdoc.prefix + " * @returns {type} Description \n";
jsdocString += jsdoc.prefix + " */ \n";
return jsdocString;
}
/**
* Description
* @param {string} jsdocString
* @param {object} loc location of the function
*/
function insertJsdoc(jsdocString, loc) {
// Get editor instance
var editor = EditorManager.getCurrentFullEditor();
var pos = {
line: loc.start.line-1,
ch: 0
};
// Place jsdocString in the editor
editor._codeMirror.replaceRange(jsdocString, pos);
EditorManager.focusEditor();
}
CommandManager.register(MENU_NAME, COMMAND_ID, annotate);
KeyBindingManager.addBinding(COMMAND_ID, "Ctrl-Alt-A");
var menu = Menus.getMenu(Menus.AppMenuBar.EDIT_MENU);
menu.addMenuDivider();
menu.addMenuItem(COMMAND_ID);//"menu-edit-annotate",
});

12
package.json Normal file
View file

@ -0,0 +1,12 @@
{
"name": "hopiu.annotate",
"title": "JSDoc Annotate",
"description": "Add JSDoc annotations to sourcecode",
"homepage": "https://github.com/hopiu/annotate", // URL for your project
"version": "0.0.1",
"author": "Benedikt Willi",
"license": "MIT",
"engines": {
"brackets": ">=0.20.0"
}
}

64
test_dummy.js Normal file
View file

@ -0,0 +1,64 @@
/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
/*global define, $, brackets, window */
/** This file is only used to be tested against annotate */
define(function (require, exports, module) {
'use strict';
var greetings = "Hello world";
function declaration(input) {
var content = "stuff";
}
var expression = function(p1, p2) {
var content = "stuff";
};
function noParams() {
return null;
}
var _privateStuff = function (p1, p2) {
var content = "I start with an underscore";
};
var myObject = {};
myObject.myFunction = function (param1, param2, param3) {
};
myObject.prototype.myFunction = function (param1, param2) {
};
var a = {
doA: function(param1, param2){
var content = "stuff";
return content;
},
doB: function(param1, param2){
var content = "stuff";
return content;
}
};
});

1784
thirdparty/acorn/acorn.js vendored Executable file

File diff suppressed because it is too large Load diff

780
thirdparty/acorn/acorn_loose.js vendored Executable file
View file

@ -0,0 +1,780 @@
// Acorn: Loose parser
//
// This module provides an alternative parser (`parse_dammit`) that
// exposes that same interface as `parse`, but will try to parse
// anything as JavaScript, repairing syntax error the best it can.
// There are circumstances in which it will raise an error and give
// up, but they are very rare. The resulting AST will be a mostly
// valid JavaScript AST (as per the [Mozilla parser API][api], except
// that:
//
// - Return outside functions is allowed
//
// - Label consistency (no conflicts, break only to existing labels)
// is not enforced.
//
// - Bogus Identifier nodes with a name of `"✖"` are inserted whenever
// the parser got too confused to return anything meaningful.
//
// [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API
//
// The expected use for this is to *first* try `acorn.parse`, and only
// if that fails switch to `parse_dammit`. The loose parser might
// parse badly indented code incorrectly, so **don't** use it as
// your default parser.
//
// Quite a lot of acorn.js is duplicated here. The alternative was to
// add a *lot* of extra cruft to that file, making it less readable
// and slower. Copying and editing the code allowed me to make
// invasive changes and simplifications without creating a complicated
// tangle.
(function(root, mod) {
if (typeof exports == "object" && typeof module == "object") return mod(exports, require("./acorn")); // CommonJS
if (typeof define == "function" && define.amd) return define(["exports", "./acorn"], mod); // AMD
mod(root.acorn || (root.acorn = {}), root.acorn); // Plain browser env
})(this, function(exports, acorn) {
"use strict";
var tt = acorn.tokTypes;
var options, input, fetchToken, context;
exports.parse_dammit = function(inpt, opts) {
if (!opts) opts = {};
input = String(inpt);
options = opts;
if (!opts.tabSize) opts.tabSize = 4;
fetchToken = acorn.tokenize(inpt, opts);
sourceFile = options.sourceFile || null;
context = [];
nextLineStart = 0;
ahead.length = 0;
next();
return parseTopLevel();
};
var lastEnd, token = {start: 0, end: 0}, ahead = [];
var curLineStart, nextLineStart, curIndent, lastEndLoc, sourceFile;
function next() {
lastEnd = token.end;
if (options.locations)
lastEndLoc = token.endLoc;
if (ahead.length)
token = ahead.shift();
else
token = readToken();
if (token.start >= nextLineStart) {
while (token.start >= nextLineStart) {
curLineStart = nextLineStart;
nextLineStart = lineEnd(curLineStart) + 1;
}
curIndent = indentationAfter(curLineStart);
}
}
function readToken() {
for (;;) {
try {
return fetchToken();
} catch(e) {
if (!(e instanceof SyntaxError)) throw e;
// Try to skip some text, based on the error message, and then continue
var msg = e.message, pos = e.raisedAt, replace = true;
if (/unterminated/i.test(msg)) {
pos = lineEnd(e.pos);
if (/string/.test(msg)) {
replace = {start: e.pos, end: pos, type: tt.string, value: input.slice(e.pos + 1, pos)};
} else if (/regular expr/i.test(msg)) {
var re = input.slice(e.pos, pos);
try { re = new RegExp(re); } catch(e) {}
replace = {start: e.pos, end: pos, type: tt.regexp, value: re};
} else {
replace = false;
}
} else if (/invalid (unicode|regexp|number)|expecting unicode|octal literal|is reserved|directly after number/i.test(msg)) {
while (pos < input.length && !isSpace(input.charCodeAt(pos))) ++pos;
} else if (/character escape|expected hexadecimal/i.test(msg)) {
while (pos < input.length) {
var ch = input.charCodeAt(pos++);
if (ch === 34 || ch === 39 || isNewline(ch)) break;
}
} else if (/unexpected character/i.test(msg)) {
pos++;
replace = false;
} else if (/regular expression/i.test(msg)) {
replace = true;
} else {
throw e;
}
resetTo(pos);
if (replace === true) replace = {start: pos, end: pos, type: tt.name, value: "✖"};
if (replace) {
if (options.locations) {
replace.startLoc = acorn.getLineInfo(input, replace.start);
replace.endLoc = acorn.getLineInfo(input, replace.end);
}
return replace;
}
}
}
}
function resetTo(pos) {
var ch = input.charAt(pos - 1);
var reAllowed = !ch || /[\[\{\(,;:?\/*=+\-~!|&%^<>]/.test(ch) ||
/[enwfd]/.test(ch) && /\b(keywords|case|else|return|throw|new|in|(instance|type)of|delete|void)$/.test(input.slice(pos - 10, pos));
fetchToken.jumpTo(pos, reAllowed);
}
function copyToken(token) {
var copy = {start: token.start, end: token.end, type: token.type, value: token.value};
if (options.locations) {
copy.startLoc = token.startLoc;
copy.endLoc = token.endLoc;
}
return copy;
}
function lookAhead(n) {
// Copy token objects, because fetchToken will overwrite the one
// it returns, and in this case we still need it
if (!ahead.length)
token = copyToken(token);
while (n > ahead.length)
ahead.push(copyToken(readToken()));
return ahead[n-1];
}
var newline = /[\n\r\u2028\u2029]/;
function isNewline(ch) {
return ch === 10 || ch === 13 || ch === 8232 || ch === 8329;
}
function isSpace(ch) {
return (ch < 14 && ch > 8) || ch === 32 || ch === 160 || isNewline(ch);
}
function pushCx() {
context.push(curIndent);
}
function popCx() {
curIndent = context.pop();
}
function lineEnd(pos) {
while (pos < input.length && !isNewline(input.charCodeAt(pos))) ++pos;
return pos;
}
function indentationAfter(pos) {
for (var count = 0;; ++pos) {
var ch = input.charCodeAt(pos);
if (ch === 32) ++count;
else if (ch === 9) count += options.tabSize;
else return count;
}
}
function closes(closeTok, indent, line, blockHeuristic) {
if (token.type === closeTok || token.type === tt.eof) return true;
if (line != curLineStart && curIndent < indent && tokenStartsLine() &&
(!blockHeuristic || nextLineStart >= input.length ||
indentationAfter(nextLineStart) < indent)) return true;
return false;
}
function tokenStartsLine() {
for (var p = token.start - 1; p >= curLineStart; --p) {
var ch = input.charCodeAt(p);
if (ch !== 9 && ch !== 32) return false;
}
return true;
}
function Node(start) {
this.type = null;
this.start = start;
this.end = null;
}
Node.prototype = acorn.Node.prototype;
function SourceLocation(start) {
this.start = start || token.startLoc || {line: 1, column: 0};
this.end = null;
if (sourceFile !== null) this.source = sourceFile;
}
function startNode() {
var node = new Node(token.start);
if (options.locations)
node.loc = new SourceLocation();
if (options.directSourceFile)
node.sourceFile = options.directSourceFile;
return node;
}
function startNodeFrom(other) {
var node = new Node(other.start);
if (options.locations)
node.loc = new SourceLocation(other.loc.start);
return node;
}
function finishNode(node, type) {
node.type = type;
node.end = lastEnd;
if (options.locations)
node.loc.end = lastEndLoc;
return node;
}
function getDummyLoc() {
if (options.locations) {
var loc = new SourceLocation();
loc.end = loc.start;
return loc;
}
};
function dummyIdent() {
var dummy = new Node(token.start);
dummy.type = "Identifier";
dummy.end = token.start;
dummy.name = "✖";
dummy.loc = getDummyLoc();
return dummy;
}
function isDummy(node) { return node.name == "✖"; }
function eat(type) {
if (token.type === type) {
next();
return true;
}
}
function canInsertSemicolon() {
return (token.type === tt.eof || token.type === tt.braceR || newline.test(input.slice(lastEnd, token.start)));
}
function semicolon() {
eat(tt.semi);
}
function expect(type) {
if (eat(type)) return true;
if (lookAhead(1).type == type) {
next(); next();
return true;
}
if (lookAhead(2).type == type) {
next(); next(); next();
return true;
}
}
function checkLVal(expr) {
if (expr.type === "Identifier" || expr.type === "MemberExpression") return expr;
return dummyIdent();
}
function parseTopLevel() {
var node = startNode();
node.body = [];
while (token.type !== tt.eof) node.body.push(parseStatement());
return finishNode(node, "Program");
}
function parseStatement() {
var starttype = token.type, node = startNode();
switch (starttype) {
case tt._break: case tt._continue:
next();
var isBreak = starttype === tt._break;
node.label = token.type === tt.name ? parseIdent() : null;
semicolon();
return finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement");
case tt._debugger:
next();
semicolon();
return finishNode(node, "DebuggerStatement");
case tt._do:
next();
node.body = parseStatement();
node.test = eat(tt._while) ? parseParenExpression() : dummyIdent();
semicolon();
return finishNode(node, "DoWhileStatement");
case tt._for:
next();
pushCx();
expect(tt.parenL);
if (token.type === tt.semi) return parseFor(node, null);
if (token.type === tt._var) {
var init = startNode();
next();
parseVar(init, true);
if (init.declarations.length === 1 && eat(tt._in))
return parseForIn(node, init);
return parseFor(node, init);
}
var init = parseExpression(false, true);
if (eat(tt._in)) {return parseForIn(node, checkLVal(init));}
return parseFor(node, init);
case tt._function:
next();
return parseFunction(node, true);
case tt._if:
next();
node.test = parseParenExpression();
node.consequent = parseStatement();
node.alternate = eat(tt._else) ? parseStatement() : null;
return finishNode(node, "IfStatement");
case tt._return:
next();
if (eat(tt.semi) || canInsertSemicolon()) node.argument = null;
else { node.argument = parseExpression(); semicolon(); }
return finishNode(node, "ReturnStatement");
case tt._switch:
var blockIndent = curIndent, line = curLineStart;
next();
node.discriminant = parseParenExpression();
node.cases = [];
pushCx();
expect(tt.braceL);
for (var cur; !closes(tt.braceR, blockIndent, line, true);) {
if (token.type === tt._case || token.type === tt._default) {
var isCase = token.type === tt._case;
if (cur) finishNode(cur, "SwitchCase");
node.cases.push(cur = startNode());
cur.consequent = [];
next();
if (isCase) cur.test = parseExpression();
else cur.test = null;
expect(tt.colon);
} else {
if (!cur) {
node.cases.push(cur = startNode());
cur.consequent = [];
cur.test = null;
}
cur.consequent.push(parseStatement());
}
}
if (cur) finishNode(cur, "SwitchCase");
popCx();
eat(tt.braceR);
return finishNode(node, "SwitchStatement");
case tt._throw:
next();
node.argument = parseExpression();
semicolon();
return finishNode(node, "ThrowStatement");
case tt._try:
next();
node.block = parseBlock();
node.handler = null;
if (token.type === tt._catch) {
var clause = startNode();
next();
expect(tt.parenL);
clause.param = parseIdent();
expect(tt.parenR);
clause.guard = null;
clause.body = parseBlock();
node.handler = finishNode(clause, "CatchClause");
}
node.finalizer = eat(tt._finally) ? parseBlock() : null;
if (!node.handler && !node.finalizer) return node.block;
return finishNode(node, "TryStatement");
case tt._var:
next();
node = parseVar(node);
semicolon();
return node;
case tt._while:
next();
node.test = parseParenExpression();
node.body = parseStatement();
return finishNode(node, "WhileStatement");
case tt._with:
next();
node.object = parseParenExpression();
node.body = parseStatement();
return finishNode(node, "WithStatement");
case tt.braceL:
return parseBlock();
case tt.semi:
next();
return finishNode(node, "EmptyStatement");
default:
var expr = parseExpression();
if (isDummy(expr)) {
next();
if (token.type === tt.eof) return finishNode(node, "EmptyStatement");
return parseStatement();
} else if (starttype === tt.name && expr.type === "Identifier" && eat(tt.colon)) {
node.body = parseStatement();
node.label = expr;
return finishNode(node, "LabeledStatement");
} else {
node.expression = expr;
semicolon();
return finishNode(node, "ExpressionStatement");
}
}
}
function parseBlock() {
var node = startNode();
pushCx();
expect(tt.braceL);
var blockIndent = curIndent, line = curLineStart;
node.body = [];
while (!closes(tt.braceR, blockIndent, line, true))
node.body.push(parseStatement());
popCx();
eat(tt.braceR);
return finishNode(node, "BlockStatement");
}
function parseFor(node, init) {
node.init = init;
node.test = node.update = null;
if (eat(tt.semi) && token.type !== tt.semi) node.test = parseExpression();
if (eat(tt.semi) && token.type !== tt.parenR) node.update = parseExpression();
popCx();
expect(tt.parenR);
node.body = parseStatement();
return finishNode(node, "ForStatement");
}
function parseForIn(node, init) {
node.left = init;
node.right = parseExpression();
popCx();
expect(tt.parenR);
node.body = parseStatement();
return finishNode(node, "ForInStatement");
}
function parseVar(node, noIn) {
node.declarations = [];
node.kind = "var";
while (token.type === tt.name) {
var decl = startNode();
decl.id = parseIdent();
decl.init = eat(tt.eq) ? parseExpression(true, noIn) : null;
node.declarations.push(finishNode(decl, "VariableDeclarator"));
if (!eat(tt.comma)) break;
}
if (!node.declarations.length) {
var decl = startNode();
decl.id = dummyIdent();
node.declarations.push(finishNode(decl, "VariableDeclarator"));
}
return finishNode(node, "VariableDeclaration");
}
function parseExpression(noComma, noIn) {
var expr = parseMaybeAssign(noIn);
if (!noComma && token.type === tt.comma) {
var node = startNodeFrom(expr);
node.expressions = [expr];
while (eat(tt.comma)) node.expressions.push(parseMaybeAssign(noIn));
return finishNode(node, "SequenceExpression");
}
return expr;
}
function parseParenExpression() {
pushCx();
expect(tt.parenL);
var val = parseExpression();
popCx();
expect(tt.parenR);
return val;
}
function parseMaybeAssign(noIn) {
var left = parseMaybeConditional(noIn);
if (token.type.isAssign) {
var node = startNodeFrom(left);
node.operator = token.value;
node.left = checkLVal(left);
next();
node.right = parseMaybeAssign(noIn);
return finishNode(node, "AssignmentExpression");
}
return left;
}
function parseMaybeConditional(noIn) {
var expr = parseExprOps(noIn);
if (eat(tt.question)) {
var node = startNodeFrom(expr);
node.test = expr;
node.consequent = parseExpression(true);
node.alternate = expect(tt.colon) ? parseExpression(true, noIn) : dummyIdent();
return finishNode(node, "ConditionalExpression");
}
return expr;
}
function parseExprOps(noIn) {
var indent = curIndent, line = curLineStart;
return parseExprOp(parseMaybeUnary(noIn), -1, noIn, indent, line);
}
function parseExprOp(left, minPrec, noIn, indent, line) {
if (curLineStart != line && curIndent < indent && tokenStartsLine()) return left;
var prec = token.type.binop;
if (prec != null && (!noIn || token.type !== tt._in)) {
if (prec > minPrec) {
var node = startNodeFrom(left);
node.left = left;
node.operator = token.value;
next();
if (curLineStart != line && curIndent < indent && tokenStartsLine())
node.right = dummyIdent();
else
node.right = parseExprOp(parseMaybeUnary(noIn), prec, noIn, indent, line);
var node = finishNode(node, /&&|\|\|/.test(node.operator) ? "LogicalExpression" : "BinaryExpression");
return parseExprOp(node, minPrec, noIn, indent, line);
}
}
return left;
}
function parseMaybeUnary(noIn) {
if (token.type.prefix) {
var node = startNode(), update = token.type.isUpdate;
node.operator = token.value;
node.prefix = true;
next();
node.argument = parseMaybeUnary(noIn);
if (update) node.argument = checkLVal(node.argument);
return finishNode(node, update ? "UpdateExpression" : "UnaryExpression");
}
var expr = parseExprSubscripts();
while (token.type.postfix && !canInsertSemicolon()) {
var node = startNodeFrom(expr);
node.operator = token.value;
node.prefix = false;
node.argument = checkLVal(expr);
next();
expr = finishNode(node, "UpdateExpression");
}
return expr;
}
function parseExprSubscripts() {
return parseSubscripts(parseExprAtom(), false, curIndent, curLineStart);
}
function parseSubscripts(base, noCalls, startIndent, line) {
for (;;) {
if (curLineStart != line && curIndent <= startIndent && tokenStartsLine()) {
if (token.type == tt.dot && curIndent == startIndent)
--startIndent;
else
return base;
}
if (eat(tt.dot)) {
var node = startNodeFrom(base);
node.object = base;
if (curLineStart != line && curIndent <= startIndent && tokenStartsLine())
node.property = dummyIdent();
else
node.property = parsePropertyName() || dummyIdent();
node.computed = false;
base = finishNode(node, "MemberExpression");
} else if (token.type == tt.bracketL) {
pushCx();
next();
var node = startNodeFrom(base);
node.object = base;
node.property = parseExpression();
node.computed = true;
popCx();
expect(tt.bracketR);
base = finishNode(node, "MemberExpression");
} else if (!noCalls && token.type == tt.parenL) {
pushCx();
var node = startNodeFrom(base);
node.callee = base;
node.arguments = parseExprList(tt.parenR);
base = finishNode(node, "CallExpression");
} else {
return base;
}
}
}
function parseExprAtom() {
switch (token.type) {
case tt._this:
var node = startNode();
next();
return finishNode(node, "ThisExpression");
case tt.name:
return parseIdent();
case tt.num: case tt.string: case tt.regexp:
var node = startNode();
node.value = token.value;
node.raw = input.slice(token.start, token.end);
next();
return finishNode(node, "Literal");
case tt._null: case tt._true: case tt._false:
var node = startNode();
node.value = token.type.atomValue;
node.raw = token.type.keyword;
next();
return finishNode(node, "Literal");
case tt.parenL:
var tokStart1 = token.start;
next();
var val = parseExpression();
val.start = tokStart1;
val.end = token.end;
expect(tt.parenR);
return val;
case tt.bracketL:
var node = startNode();
pushCx();
node.elements = parseExprList(tt.bracketR);
return finishNode(node, "ArrayExpression");
case tt.braceL:
return parseObj();
case tt._function:
var node = startNode();
next();
return parseFunction(node, false);
case tt._new:
return parseNew();
default:
return dummyIdent();
}
}
function parseNew() {
var node = startNode(), startIndent = curIndent, line = curLineStart;
next();
node.callee = parseSubscripts(parseExprAtom(), true, startIndent, line);
if (token.type == tt.parenL) {
pushCx();
node.arguments = parseExprList(tt.parenR);
} else {
node.arguments = [];
}
return finishNode(node, "NewExpression");
}
function parseObj() {
var node = startNode();
node.properties = [];
pushCx();
next();
var propIndent = curIndent, line = curLineStart;
while (!closes(tt.braceR, propIndent, line)) {
var name = parsePropertyName();
if (!name) { if (isDummy(parseExpression(true))) next(); eat(tt.comma); continue; }
var prop = {key: name}, isGetSet = false, kind;
if (eat(tt.colon)) {
prop.value = parseExpression(true);
kind = prop.kind = "init";
} else if (options.ecmaVersion >= 5 && prop.key.type === "Identifier" &&
(prop.key.name === "get" || prop.key.name === "set")) {
isGetSet = true;
kind = prop.kind = prop.key.name;
prop.key = parsePropertyName() || dummyIdent();
prop.value = parseFunction(startNode(), false);
} else {
next();
eat(tt.comma);
continue;
}
node.properties.push(prop);
eat(tt.comma);
}
popCx();
eat(tt.braceR);
return finishNode(node, "ObjectExpression");
}
function parsePropertyName() {
if (token.type === tt.num || token.type === tt.string) return parseExprAtom();
if (token.type === tt.name || token.type.keyword) return parseIdent();
}
function parseIdent() {
var node = startNode();
node.name = token.type === tt.name ? token.value : token.type.keyword;
next();
return finishNode(node, "Identifier");
}
function parseFunction(node, isStatement) {
if (token.type === tt.name) node.id = parseIdent();
else if (isStatement) node.id = dummyIdent();
else node.id = null;
node.params = [];
pushCx();
expect(tt.parenL);
while (token.type == tt.name) {
node.params.push(parseIdent());
eat(tt.comma);
}
popCx();
eat(tt.parenR);
node.body = parseBlock();
return finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression");
}
function parseExprList(close) {
var indent = curIndent, line = curLineStart, elts = [], continuedLine = nextLineStart;
next(); // Opening bracket
if (curLineStart > continuedLine) continuedLine = curLineStart;
while (!closes(close, indent + (curLineStart <= continuedLine ? 1 : 0), line)) {
var elt = parseExpression(true);
if (isDummy(elt)) {
if (closes(close, indent, line)) break;
next();
} else {
elts.push(elt);
}
while (eat(tt.comma)) {}
}
popCx();
eat(close);
return elts;
}
});

330
thirdparty/acorn/util/walk.js vendored Executable file
View file

@ -0,0 +1,330 @@
// AST walker module for Mozilla Parser API compatible trees
(function(mod) {
if (typeof exports == "object" && typeof module == "object") return mod(exports); // CommonJS
if (typeof define == "function" && define.amd) return define(["exports"], mod); // AMD
mod((this.acorn || (this.acorn = {})).walk = {}); // Plain browser env
})(function(exports) {
"use strict";
// A simple walk is one where you simply specify callbacks to be
// called on specific nodes. The last two arguments are optional. A
// simple use would be
//
// walk.simple(myTree, {
// Expression: function(node) { ... }
// });
//
// to do something with all expressions. All Parser API node types
// can be used to identify node types, as well as Expression,
// Statement, and ScopeBody, which denote categories of nodes.
//
// The base argument can be used to pass a custom (recursive)
// walker, and state can be used to give this walked an initial
// state.
exports.simple = function(node, visitors, base, state) {
if (!base) base = exports.base;
function c(node, st, override) {
var type = override || node.type, found = visitors[type];
base[type](node, st, c);
if (found) found(node, st);
}
c(node, state);
};
// An ancestor walk builds up an array of ancestor nodes (including
// the current node) and passes them to the callback as the state parameter.
exports.ancestor = function(node, visitors, base, state) {
if (!base) base = exports.base;
if (!state) state = [];
function c(node, st, override) {
var type = override || node.type, found = visitors[type];
if (node != st[st.length - 1]) {
st = st.slice();
st.push(node);
}
base[type](node, st, c);
if (found) found(node, st);
}
c(node, state);
};
// A recursive walk is one where your functions override the default
// walkers. They can modify and replace the state parameter that's
// threaded through the walk, and can opt how and whether to walk
// their child nodes (by calling their third argument on these
// nodes).
exports.recursive = function(node, state, funcs, base) {
var visitor = funcs ? exports.make(funcs, base) : base;
function c(node, st, override) {
visitor[override || node.type](node, st, c);
}
c(node, state);
};
function makeTest(test) {
if (typeof test == "string")
return function(type) { return type == test; };
else if (!test)
return function() { return true; };
else
return test;
}
function Found(node, state) { this.node = node; this.state = state; }
// Find a node with a given start, end, and type (all are optional,
// null can be used as wildcard). Returns a {node, state} object, or
// undefined when it doesn't find a matching node.
exports.findNodeAt = function(node, start, end, test, base, state) {
test = makeTest(test);
try {
if (!base) base = exports.base;
var c = function(node, st, override) {
var type = override || node.type;
if ((start == null || node.start <= start) &&
(end == null || node.end >= end))
base[type](node, st, c);
if (test(type, node) &&
(start == null || node.start == start) &&
(end == null || node.end == end))
throw new Found(node, st);
};
c(node, state);
} catch (e) {
if (e instanceof Found) return e;
throw e;
}
};
// Find the innermost node of a given type that contains the given
// position. Interface similar to findNodeAt.
exports.findNodeAround = function(node, pos, test, base, state) {
test = makeTest(test);
try {
if (!base) base = exports.base;
var c = function(node, st, override) {
var type = override || node.type;
if (node.start > pos || node.end < pos) return;
base[type](node, st, c);
if (test(type, node)) throw new Found(node, st);
};
c(node, state);
} catch (e) {
if (e instanceof Found) return e;
throw e;
}
};
// Find the outermost matching node after a given position.
exports.findNodeAfter = function(node, pos, test, base, state) {
test = makeTest(test);
try {
if (!base) base = exports.base;
var c = function(node, st, override) {
if (node.end < pos) return;
var type = override || node.type;
if (node.start >= pos && test(type, node)) throw new Found(node, st);
base[type](node, st, c);
};
c(node, state);
} catch (e) {
if (e instanceof Found) return e;
throw e;
}
};
// Find the outermost matching node before a given position.
exports.findNodeBefore = function(node, pos, test, base, state) {
test = makeTest(test);
if (!base) base = exports.base;
var max;
var c = function(node, st, override) {
if (node.start > pos) return;
var type = override || node.type;
if (node.end <= pos && (!max || max.node.end < node.end) && test(type, node))
max = new Found(node, st);
base[type](node, st, c);
};
c(node, state);
return max;
};
// Used to create a custom walker. Will fill in all missing node
// type properties with the defaults.
exports.make = function(funcs, base) {
if (!base) base = exports.base;
var visitor = {};
for (var type in base) visitor[type] = base[type];
for (var type in funcs) visitor[type] = funcs[type];
return visitor;
};
function skipThrough(node, st, c) { c(node, st); }
function ignore(_node, _st, _c) {}
// Node walkers.
var base = exports.base = {};
base.Program = base.BlockStatement = function(node, st, c) {
for (var i = 0; i < node.body.length; ++i)
c(node.body[i], st, "Statement");
};
base.Statement = skipThrough;
base.EmptyStatement = ignore;
base.ExpressionStatement = function(node, st, c) {
c(node.expression, st, "Expression");
};
base.IfStatement = function(node, st, c) {
c(node.test, st, "Expression");
c(node.consequent, st, "Statement");
if (node.alternate) c(node.alternate, st, "Statement");
};
base.LabeledStatement = function(node, st, c) {
c(node.body, st, "Statement");
};
base.BreakStatement = base.ContinueStatement = ignore;
base.WithStatement = function(node, st, c) {
c(node.object, st, "Expression");
c(node.body, st, "Statement");
};
base.SwitchStatement = function(node, st, c) {
c(node.discriminant, st, "Expression");
for (var i = 0; i < node.cases.length; ++i) {
var cs = node.cases[i];
if (cs.test) c(cs.test, st, "Expression");
for (var j = 0; j < cs.consequent.length; ++j)
c(cs.consequent[j], st, "Statement");
}
};
base.ReturnStatement = function(node, st, c) {
if (node.argument) c(node.argument, st, "Expression");
};
base.ThrowStatement = function(node, st, c) {
c(node.argument, st, "Expression");
};
base.TryStatement = function(node, st, c) {
c(node.block, st, "Statement");
if (node.handler) c(node.handler.body, st, "ScopeBody");
if (node.finalizer) c(node.finalizer, st, "Statement");
};
base.WhileStatement = function(node, st, c) {
c(node.test, st, "Expression");
c(node.body, st, "Statement");
};
base.DoWhileStatement = base.WhileStatement;
base.ForStatement = function(node, st, c) {
if (node.init) c(node.init, st, "ForInit");
if (node.test) c(node.test, st, "Expression");
if (node.update) c(node.update, st, "Expression");
c(node.body, st, "Statement");
};
base.ForInStatement = function(node, st, c) {
c(node.left, st, "ForInit");
c(node.right, st, "Expression");
c(node.body, st, "Statement");
};
base.ForInit = function(node, st, c) {
if (node.type == "VariableDeclaration") c(node, st);
else c(node, st, "Expression");
};
base.DebuggerStatement = ignore;
base.FunctionDeclaration = function(node, st, c) {
c(node, st, "Function");
};
base.VariableDeclaration = function(node, st, c) {
for (var i = 0; i < node.declarations.length; ++i) {
var decl = node.declarations[i];
if (decl.init) c(decl.init, st, "Expression");
}
};
base.Function = function(node, st, c) {
c(node.body, st, "ScopeBody");
};
base.ScopeBody = function(node, st, c) {
c(node, st, "Statement");
};
base.Expression = skipThrough;
base.ThisExpression = ignore;
base.ArrayExpression = function(node, st, c) {
for (var i = 0; i < node.elements.length; ++i) {
var elt = node.elements[i];
if (elt) c(elt, st, "Expression");
}
};
base.ObjectExpression = function(node, st, c) {
for (var i = 0; i < node.properties.length; ++i)
c(node.properties[i].value, st, "Expression");
};
base.FunctionExpression = base.FunctionDeclaration;
base.SequenceExpression = function(node, st, c) {
for (var i = 0; i < node.expressions.length; ++i)
c(node.expressions[i], st, "Expression");
};
base.UnaryExpression = base.UpdateExpression = function(node, st, c) {
c(node.argument, st, "Expression");
};
base.BinaryExpression = base.AssignmentExpression = base.LogicalExpression = function(node, st, c) {
c(node.left, st, "Expression");
c(node.right, st, "Expression");
};
base.ConditionalExpression = function(node, st, c) {
c(node.test, st, "Expression");
c(node.consequent, st, "Expression");
c(node.alternate, st, "Expression");
};
base.NewExpression = base.CallExpression = function(node, st, c) {
c(node.callee, st, "Expression");
if (node.arguments) for (var i = 0; i < node.arguments.length; ++i)
c(node.arguments[i], st, "Expression");
};
base.MemberExpression = function(node, st, c) {
c(node.object, st, "Expression");
if (node.computed) c(node.property, st, "Expression");
};
base.Identifier = base.Literal = ignore;
// A custom walker that keeps track of the scope chain and the
// variables defined in it.
function makeScope(prev, isCatch) {
return {vars: Object.create(null), prev: prev, isCatch: isCatch};
}
function normalScope(scope) {
while (scope.isCatch) scope = scope.prev;
return scope;
}
exports.scopeVisitor = exports.make({
Function: function(node, scope, c) {
var inner = makeScope(scope);
for (var i = 0; i < node.params.length; ++i)
inner.vars[node.params[i].name] = {type: "argument", node: node.params[i]};
if (node.id) {
var decl = node.type == "FunctionDeclaration";
(decl ? normalScope(scope) : inner).vars[node.id.name] =
{type: decl ? "function" : "function name", node: node.id};
}
c(node.body, inner, "ScopeBody");
},
TryStatement: function(node, scope, c) {
c(node.block, scope, "Statement");
if (node.handler) {
var inner = makeScope(scope, true);
inner.vars[node.handler.param.name] = {type: "catch clause", node: node.handler.param};
c(node.handler.body, inner, "ScopeBody");
}
if (node.finalizer) c(node.finalizer, scope, "Statement");
},
VariableDeclaration: function(node, scope, c) {
var target = normalScope(scope);
for (var i = 0; i < node.declarations.length; ++i) {
var decl = node.declarations[i];
target.vars[decl.id.name] = {type: "var", node: decl.id};
if (decl.init) c(decl.init, scope, "Expression");
}
}
});
});