mirror of
https://github.com/Hopiu/annotate-extension.git
synced 2026-03-17 00:10:23 +00:00
create the annotation string with acorn instead of regexps
This commit is contained in:
parent
d68e2cadca
commit
e2fe6d009c
20 changed files with 52823 additions and 140 deletions
270
main.js
270
main.js
|
|
@ -26,160 +26,150 @@
|
|||
|
||||
/** extension to generate JSDoc annotations for functions */
|
||||
define(function (require, exports, module) {
|
||||
|
||||
'use strict';
|
||||
|
||||
|
||||
var CommandManager = brackets.getModule("command/CommandManager"),
|
||||
EditorManager = brackets.getModule("editor/EditorManager"),
|
||||
KeyBindingManager = brackets.getModule("command/KeyBindingManager"),
|
||||
Menus = brackets.getModule("command/Menus");
|
||||
var AppInit = brackets.getModule("utils/AppInit"),
|
||||
CommandManager = brackets.getModule("command/CommandManager"),
|
||||
EditorManager = brackets.getModule("editor/EditorManager"),
|
||||
KeyBindingManager = brackets.getModule("command/KeyBindingManager"),
|
||||
Menus = brackets.getModule("command/Menus"),
|
||||
Acorn_loose = require("thirdparty/acorn/acorn_loose"),
|
||||
Walker = require("thirdparty/acorn/util/walk");
|
||||
|
||||
var EMPTY_MSG = "No function found";
|
||||
var COMMAND_ID = "annotate.annotate";
|
||||
var MENU_NAME = "Annotate function";
|
||||
|
||||
var EMPTY_MSG = "No function found";
|
||||
var COMMAND_ID = "annotate.annotate";
|
||||
var MENU_NAME = "Annotate function";
|
||||
// Global editor instance
|
||||
var _editor = {};
|
||||
var _output = {};
|
||||
|
||||
var REGEX_PATTERNS = {
|
||||
comment: '\\/\\*.*\\*\\/',
|
||||
jsVariable: '[$A-Za-z_][0-9A-Za-z_$]*'
|
||||
|
||||
/**
|
||||
* Create a jsdoc annotation and prepend it in the active document
|
||||
*/
|
||||
var annotate = function () {
|
||||
// Get current active editor
|
||||
_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 full txt
|
||||
var fullTxt = _editor._codeMirror.getValue();
|
||||
|
||||
// Parse text
|
||||
var acornTxtFull = Acorn_loose.parse_dammit(fullTxt, {
|
||||
locations: true
|
||||
});
|
||||
|
||||
// Find next function
|
||||
var found = new Walker.findNodeAfter(acornTxtFull, cursorPosition, "Function");
|
||||
|
||||
if (found) {
|
||||
// There was a result, so build jsdoc
|
||||
_output = {};
|
||||
_output.loc = found.node.loc;
|
||||
_output.prefix = "";
|
||||
_output.name = found.node.id ? found.node.id.name : null;
|
||||
_output.params = [];
|
||||
_output.returnValue = undefined;
|
||||
|
||||
// Add parameters to the _output object
|
||||
found.node.params.forEach(function (param) {
|
||||
_output.params.push(param.name);
|
||||
});
|
||||
|
||||
// Find and add return value
|
||||
var foundReturnValue = new Walker.findNodeAfter(found.node, 0, "ReturnStatement");
|
||||
_output.returnValue = foundReturnValue.node ? foundReturnValue.node.argument.name : undefined;
|
||||
|
||||
// set prefix (find first none whitespace character)
|
||||
var codeLine = _editor._codeMirror.getLine(_output.loc.start.line - 1);
|
||||
_output.prefix = codeLine.substr(0, codeLine.length - codeLine.trimLeft().length).replace(/[^\s\n]/g, ' ');
|
||||
|
||||
// build annotation string
|
||||
var _outputString = _getJSDocString(_output);
|
||||
|
||||
// insertJsdoc string into editor
|
||||
_insertJSDocString(_outputString, _output.loc);
|
||||
} else {
|
||||
// No function definition found
|
||||
window.alert(EMPTY_MSG);
|
||||
}
|
||||
};
|
||||
|
||||
function insert(input) {
|
||||
|
||||
var editor = EditorManager.getCurrentFullEditor();
|
||||
var pos = editor.getCursorPos();
|
||||
pos.ch = 0;
|
||||
|
||||
editor._codeMirror.replaceRange(input, pos);
|
||||
/**
|
||||
* Get a functions name
|
||||
*/
|
||||
var _getName = function () {
|
||||
//Todo
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a functions return value
|
||||
*/
|
||||
var _getReturnValue = function () {
|
||||
//Todo
|
||||
};
|
||||
|
||||
/**
|
||||
* Build the string representation of the
|
||||
* @param {object} jsdoc object containing jsdoc properties
|
||||
* @returns {string} annotation as a string
|
||||
*/
|
||||
var _getJSDocString = function (jsdoc) {
|
||||
var jsdocString = jsdoc.prefix + "/**\n";
|
||||
|
||||
if (jsdoc.name && 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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Insert the JSDoc annotation string to the document
|
||||
* @param {string} jSDocString The JSDoc annotation string
|
||||
* @param {location} loc location of the function found
|
||||
*/
|
||||
var _insertJSDocString = function (jSDocString, loc) {
|
||||
var pos = {
|
||||
line: loc.start.line - 1,
|
||||
ch: 0
|
||||
};
|
||||
|
||||
// Place jsdocString in the editor
|
||||
_editor._codeMirror.replaceRange(jSDocString, pos);
|
||||
|
||||
EditorManager.focusEditor();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* get the whitespace characters from line start to beginning of function def
|
||||
* @param string input lines from start of the function definition
|
||||
* @param string match function definition start
|
||||
*/
|
||||
function getPrefix(input, match) {
|
||||
|
||||
var indexOf = input.indexOf(match),
|
||||
prefix = "";
|
||||
if (indexOf !== -1) {
|
||||
prefix = input.substr(0, indexOf).replace(/[^\s\n]/g, ' ');
|
||||
}
|
||||
|
||||
return prefix;
|
||||
|
||||
}
|
||||
|
||||
function getTarget() {
|
||||
|
||||
var editor = EditorManager.getCurrentFullEditor(),
|
||||
pos = editor.getCursorPos(),
|
||||
functionDeclarationRegex = new RegExp('^[a-z0-9]*\\s*\\n*\\bfunction\\b\\s*' + REGEX_PATTERNS.jsVariable + '\\s*\\(\\s*(' +
|
||||
REGEX_PATTERNS.jsVariable + '\\s*,?)*\\s*\\)','g'),
|
||||
|
||||
functionExpresionRegex = new RegExp('^[a-z0-9]*\\s*\\n*(var|(' + REGEX_PATTERNS.jsVariable + '.)*(' + REGEX_PATTERNS.jsVariable + ')?)?\\s*'+ REGEX_PATTERNS.jsVariable + '\\s*(=|:)\\s*function\\s*\\(\\s*(' +
|
||||
REGEX_PATTERNS.jsVariable + '\\s*(,\\s*)?)*\\s*\\)\\s*','g');
|
||||
|
||||
pos.ch = 0;
|
||||
|
||||
// Take the text of the document, starting with the current cursor line
|
||||
var txtFrom = editor._codeMirror.getRange(pos, {line: editor._codeMirror.lineCount() });
|
||||
|
||||
//checks if there is a return value
|
||||
var returnsValue = txtFrom.substr( txtFrom.indexOf('{'), txtFrom.indexOf('}')).search('return') !== -1;
|
||||
|
||||
txtFrom = txtFrom.substr(0, txtFrom.indexOf("{"));
|
||||
|
||||
//take any comment off
|
||||
txtFrom = txtFrom.replace(new RegExp(REGEX_PATTERNS.comment,'g'), '');
|
||||
|
||||
var results = txtFrom.match(new RegExp(REGEX_PATTERNS.jsVariable,'g'));
|
||||
switch(true) {
|
||||
case functionExpresionRegex.test(txtFrom):
|
||||
return {
|
||||
//check for 'var'
|
||||
name:results[results.indexOf('function')-1],
|
||||
params:results.slice(results.indexOf('function')+1),
|
||||
prefix: getPrefix(txtFrom, results[0]),
|
||||
returnsValue:returnsValue
|
||||
};
|
||||
case functionDeclarationRegex.test(txtFrom):
|
||||
//console.log(results[1]);
|
||||
return {
|
||||
name:results[1],
|
||||
params:results.slice(2),
|
||||
prefix: getPrefix(txtFrom, results[0]),
|
||||
returnsValue:returnsValue
|
||||
};
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate comment block
|
||||
* @param string fname function name
|
||||
* @param string params function parameters
|
||||
* @param string prefix whitespace prefix for comment block lines
|
||||
*/
|
||||
function generateComment(fname, params,returnsValue, prefix) {
|
||||
|
||||
var output = [];
|
||||
output.push("/**");
|
||||
|
||||
// Assume function is private if it starts with an underscore
|
||||
if (fname.charAt(0) === "_") {
|
||||
output.push(" * @private");
|
||||
}
|
||||
|
||||
// Add description
|
||||
output.push(" * Description");
|
||||
|
||||
// Add parameters
|
||||
if (params.length > 0) {
|
||||
var i;
|
||||
for (i = 0; i < params.length; i++) {
|
||||
var param = params[i];
|
||||
output.push(" * @param {type} " + param + " Description");
|
||||
}
|
||||
}
|
||||
|
||||
if (returnsValue) output.push(" * @returns {type} Description");
|
||||
|
||||
// TODO use if 'return' is found in the function body?
|
||||
//output += " * @return {type} ???\n";
|
||||
output.push(" */");
|
||||
|
||||
return prefix + output.join("\n" + prefix) + "\n";
|
||||
}
|
||||
|
||||
|
||||
|
||||
function annotate() {
|
||||
|
||||
var target = getTarget();
|
||||
|
||||
if (target === null) {
|
||||
window.alert(EMPTY_MSG);
|
||||
return;
|
||||
}
|
||||
|
||||
var comment = generateComment(target.name, target.params, target.returnsValue, target.prefix);
|
||||
|
||||
insert(comment);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Register stuff when brackets finished loading
|
||||
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",
|
||||
|
||||
menu.addMenuItem(COMMAND_ID); //"menu-edit-annotate",
|
||||
});
|
||||
23
thirdparty/acorn/LICENSE
vendored
Executable file
23
thirdparty/acorn/LICENSE
vendored
Executable file
|
|
@ -0,0 +1,23 @@
|
|||
Copyright (C) 2012-2014 by Marijn Haverbeke <marijnh@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
Please note that some subdirectories of the CodeMirror distribution
|
||||
include their own LICENSE files, and are released under different
|
||||
licences.
|
||||
218
thirdparty/acorn/README.md
vendored
Executable file
218
thirdparty/acorn/README.md
vendored
Executable file
|
|
@ -0,0 +1,218 @@
|
|||
# Acorn
|
||||
|
||||
A tiny, fast JavaScript parser, written completely in JavaScript.
|
||||
|
||||
## Installation
|
||||
|
||||
The easiest way to install acorn is with [`npm`][npm].
|
||||
|
||||
[npm]: http://npmjs.org
|
||||
|
||||
```sh
|
||||
npm install acorn
|
||||
```
|
||||
|
||||
Alternately, download the source.
|
||||
|
||||
```sh
|
||||
git clone https://github.com/marijnh/acorn.git
|
||||
```
|
||||
|
||||
## Components
|
||||
|
||||
When run in a CommonJS (node.js) or AMD environment, exported values
|
||||
appear in the interfaces exposed by the individual files, as usual.
|
||||
When loaded in the browser without any kind of module management, a
|
||||
single global object `acorn` will be defined, and all the exported
|
||||
properties will be added to that.
|
||||
|
||||
### acorn.js
|
||||
|
||||
This file contains the actual parser (and is what you get when you
|
||||
`require("acorn")` in node.js).
|
||||
|
||||
**parse**`(input, options)` is used to parse a JavaScript program.
|
||||
The `input` parameter is a string, `options` can be undefined or an
|
||||
object setting some of the options listed below. The return value will
|
||||
be an abstract syntax tree object as specified by the
|
||||
[Mozilla Parser API][mozapi].
|
||||
|
||||
When encountering a syntax error, the parser will raise a
|
||||
`SyntaxError` object with a meaningful message. The error object will
|
||||
have a `pos` property that indicates the character offset at which the
|
||||
error occurred, and a `loc` object that contains a `{line, column}`
|
||||
object referring to that same position.
|
||||
|
||||
[mozapi]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API
|
||||
|
||||
- **ecmaVersion**: Indicates the ECMAScript version to parse. Must be
|
||||
either 3 or 5. This influences support for strict mode, the set of
|
||||
reserved words, and support for getters and setter. Default is 5.
|
||||
|
||||
- **strictSemicolons**: If `true`, prevents the parser from doing
|
||||
automatic semicolon insertion, and statements that do not end with
|
||||
a semicolon will generate an error. Defaults to `false`.
|
||||
|
||||
- **allowTrailingCommas**: If `false`, the parser will not allow
|
||||
trailing commas in array and object literals. Default is `true`.
|
||||
|
||||
- **forbidReserved**: If `true`, using a reserved word will generate
|
||||
an error. Defaults to `false`. When given the value `"everywhere"`,
|
||||
reserved words and keywords can also not be used as property names
|
||||
(as in Internet Explorer's old parser).
|
||||
|
||||
- **allowReturnOutsideFunction**: By default, a return statement at
|
||||
the top level raises an error. Set this to `true` to accept such
|
||||
code.
|
||||
|
||||
- **locations**: When `true`, each node has a `loc` object attached
|
||||
with `start` and `end` subobjects, each of which contains the
|
||||
one-based line and zero-based column numbers in `{line, column}`
|
||||
form. Default is `false`.
|
||||
|
||||
- **onComment**: If a function is passed for this option, whenever a
|
||||
comment is encountered the function will be called with the
|
||||
following parameters:
|
||||
|
||||
- `block`: `true` if the comment is a block comment, false if it
|
||||
is a line comment.
|
||||
- `text`: The content of the comment.
|
||||
- `start`: Character offset of the start of the comment.
|
||||
- `end`: Character offset of the end of the comment.
|
||||
|
||||
When the `locations` options is on, the `{line, column}` locations
|
||||
of the comment’s start and end are passed as two additional
|
||||
parameters.
|
||||
|
||||
Note that you are not allowed to call the parser from the
|
||||
callback—that will corrupt its internal state.
|
||||
|
||||
- **ranges**: Nodes have their start and end characters offsets
|
||||
recorded in `start` and `end` properties (directly on the node,
|
||||
rather than the `loc` object, which holds line/column data. To also
|
||||
add a [semi-standardized][range] "range" property holding a
|
||||
`[start, end]` array with the same numbers, set the `ranges` option
|
||||
to `true`.
|
||||
|
||||
- **program**: It is possible to parse multiple files into a single
|
||||
AST by passing the tree produced by parsing the first file as the
|
||||
`program` option in subsequent parses. This will add the toplevel
|
||||
forms of the parsed file to the "Program" (top) node of an existing
|
||||
parse tree.
|
||||
|
||||
- **sourceFile**: When the `locations` option is `true`, you can pass
|
||||
this option to add a `sourceFile` attribute in every node’s `loc`
|
||||
object. Note that the contents of this option are not examined or
|
||||
processed in any way; you are free to use whatever format you
|
||||
choose.
|
||||
|
||||
- **directSourceFile**: Like `sourceFile`, but the property will be
|
||||
added directly to the nodes, rather than to a `loc` object.
|
||||
|
||||
[range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678
|
||||
|
||||
**getLineInfo**`(input, offset)` can be used to get a `{line,
|
||||
column}` object for a given program string and character offset.
|
||||
|
||||
**tokenize**`(input, options)` exports a primitive interface to
|
||||
Acorn's tokenizer. The function takes an input string and options
|
||||
similar to `parse` (though only some options are meaningful here), and
|
||||
returns a function that can be called repeatedly to read a single
|
||||
token, and returns a `{start, end, type, value}` object (with added
|
||||
`startLoc` and `endLoc` properties when the `locations` option is
|
||||
enabled). This object will be reused (updated) for each token, so you
|
||||
can't count on it staying stable.
|
||||
|
||||
**tokTypes** holds an object mapping names to the token type objects
|
||||
that end up in the `type` properties of tokens.
|
||||
|
||||
### acorn_loose.js ###
|
||||
|
||||
This file implements an error-tolerant parser. It exposes a single
|
||||
function.
|
||||
|
||||
**parse_dammit**`(input, options)` takes the same arguments and
|
||||
returns the same syntax tree as the `parse` function in `acorn.js`,
|
||||
but never raises an error, and will do its best to parse syntactically
|
||||
invalid code in as meaningful a way as it can. It'll insert identifier
|
||||
nodes with name `"✖"` as placeholders in places where it can't make
|
||||
sense of the input. Depends on `acorn.js`, because it uses the same
|
||||
tokenizer.
|
||||
|
||||
### util/walk.js ###
|
||||
|
||||
Implements an abstract syntax tree walker. Will store its interface in
|
||||
`acorn.walk` when used without a module system.
|
||||
|
||||
**simple**`(node, visitors, base, state)` does a 'simple' walk over
|
||||
a tree. `node` should be the AST node to walk, and `visitors` an
|
||||
object with properties whose names correspond to node types in the
|
||||
[Mozilla Parser API][mozapi]. The properties should contain functions
|
||||
that will be called with the node object and, if applicable the state
|
||||
at that point. The last two arguments are optional. `base` is a walker
|
||||
algorithm, and `state` is a start state. The default walker will
|
||||
simply visit all statements and expressions and not produce a
|
||||
meaningful state. (An example of a use of state it to track scope at
|
||||
each point in the tree.)
|
||||
|
||||
**ancestor**`(node, visitors, base, state)` does a 'simple' walk over
|
||||
a tree, building up an array of ancestor nodes (including the current node)
|
||||
and passing the array to callbacks in the `state` parameter.
|
||||
|
||||
**recursive**`(node, state, functions, base)` does a 'recursive'
|
||||
walk, where the walker functions are responsible for continuing the
|
||||
walk on the child nodes of their target node. `state` is the start
|
||||
state, and `functions` should contain an object that maps node types
|
||||
to walker functions. Such functions are called with `(node, state, c)`
|
||||
arguments, and can cause the walk to continue on a sub-node by calling
|
||||
the `c` argument on it with `(node, state)` arguments. The optional
|
||||
`base` argument provides the fallback walker functions for node types
|
||||
that aren't handled in the `functions` object. If not given, the
|
||||
default walkers will be used.
|
||||
|
||||
**make**`(functions, base)` builds a new walker object by using the
|
||||
walker functions in `functions` and filling in the missing ones by
|
||||
taking defaults from `base`.
|
||||
|
||||
**findNodeAt**`(node, start, end, test, base, state)` tries to
|
||||
locate a node in a tree at the given start and/or end offsets, which
|
||||
satisfies the predicate `test`. `start` end `end` can be either `null`
|
||||
(as wildcard) or a number. `test` may be a string (indicating a node
|
||||
type) or a function that takes `(nodeType, node)` arguments and
|
||||
returns a boolean indicating whether this node is interesting. `base`
|
||||
and `state` are optional, and can be used to specify a custom walker.
|
||||
Nodes are tested from inner to outer, so if two nodes match the
|
||||
boundaries, the inner one will be preferred.
|
||||
|
||||
**findNodeAround**`(node, pos, test, base, state)` is a lot like
|
||||
`findNodeAt`, but will match any node that exists 'around' (spanning)
|
||||
the given position.
|
||||
|
||||
**findNodeAfter**`(node, pos, test, base, state)` is similar to
|
||||
`findNodeAround`, but will match all nodes *after* the given position
|
||||
(testing outer nodes before inner nodes).
|
||||
|
||||
## Command line interface
|
||||
|
||||
The `bin/acorn` utility can be used to parse a file from the command
|
||||
line. It accepts as arguments its input file and the following
|
||||
options:
|
||||
|
||||
- `--ecma3|--ecma5`: Sets the ECMAScript version to parse. Default is
|
||||
version 5.
|
||||
|
||||
- `--strictSemicolons`: Prevents the parser from doing automatic
|
||||
semicolon insertion. Statements that do not end in semicolons will
|
||||
generate an error.
|
||||
|
||||
- `--locations`: Attaches a "loc" object to each node with "start" and
|
||||
"end" subobjects, each of which contains the one-based line and
|
||||
zero-based column numbers in `{line, column}` form.
|
||||
|
||||
- `--compact`: No whitespace is used in the AST output.
|
||||
|
||||
- `--silent`: Do not output the AST, just return the exit status.
|
||||
|
||||
- `--help`: Print the usage information and quit.
|
||||
|
||||
The utility spits out the syntax tree as JSON data.
|
||||
1784
thirdparty/acorn/acorn.js
vendored
Executable file
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
780
thirdparty/acorn/acorn_loose.js
vendored
Executable 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;
|
||||
}
|
||||
});
|
||||
39
thirdparty/acorn/bin/acorn
vendored
Executable file
39
thirdparty/acorn/bin/acorn
vendored
Executable file
|
|
@ -0,0 +1,39 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
var path = require("path");
|
||||
var fs = require("fs");
|
||||
var acorn = require("../acorn.js");
|
||||
|
||||
var infile, parsed, options = {}, silent = false, compact = false;
|
||||
|
||||
function help(status) {
|
||||
console.log("usage: " + path.basename(process.argv[1]) + " infile [--ecma3|--ecma5] [--strictSemicolons]");
|
||||
console.log(" [--locations] [--compact] [--silent] [--help]");
|
||||
process.exit(status);
|
||||
}
|
||||
|
||||
for (var i = 2; i < process.argv.length; ++i) {
|
||||
var arg = process.argv[i];
|
||||
if (arg == "--ecma3") options.ecmaVersion = 3;
|
||||
else if (arg == "--ecma5") options.ecmaVersion = 5;
|
||||
else if (arg == "--strictSemicolons") options.strictSemicolons = true;
|
||||
else if (arg == "--locations") options.locations = true;
|
||||
else if (arg == "--silent") silent = true;
|
||||
else if (arg == "--compact") compact = true;
|
||||
else if (arg == "--help") help(0);
|
||||
else if (arg[0] == "-") help(1);
|
||||
else infile = arg;
|
||||
}
|
||||
|
||||
if (!infile) help(1);
|
||||
|
||||
try {
|
||||
var code = fs.readFileSync(infile, "utf8");
|
||||
parsed = acorn.parse(code, options);
|
||||
} catch(e) {
|
||||
console.log(e.message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!silent)
|
||||
console.log(JSON.stringify(parsed, null, compact ? null : 2));
|
||||
192
thirdparty/acorn/docco.css
vendored
Executable file
192
thirdparty/acorn/docco.css
vendored
Executable file
|
|
@ -0,0 +1,192 @@
|
|||
/*--------------------- Layout and Typography ----------------------------*/
|
||||
body {
|
||||
font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif;
|
||||
font-size: 15px;
|
||||
line-height: 22px;
|
||||
color: #252519;
|
||||
margin: 0; padding: 0;
|
||||
}
|
||||
a {
|
||||
color: #261a3b;
|
||||
}
|
||||
a:visited {
|
||||
color: #261a3b;
|
||||
}
|
||||
p {
|
||||
margin: 0 0 15px 0;
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin: 0px 0 15px 0;
|
||||
}
|
||||
h1 {
|
||||
margin-top: 40px;
|
||||
}
|
||||
hr {
|
||||
border: 0 none;
|
||||
border-top: 1px solid #e5e5ee;
|
||||
height: 1px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
#container {
|
||||
position: relative;
|
||||
}
|
||||
#background {
|
||||
position: fixed;
|
||||
top: 0; left: 525px; right: 0; bottom: 0;
|
||||
background: #f5f5ff;
|
||||
border-left: 1px solid #e5e5ee;
|
||||
z-index: -1;
|
||||
}
|
||||
#jump_to, #jump_page {
|
||||
background: white;
|
||||
-webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777;
|
||||
-webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px;
|
||||
font: 10px Arial;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
text-align: right;
|
||||
}
|
||||
#jump_to, #jump_wrapper {
|
||||
position: fixed;
|
||||
right: 0; top: 0;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
#jump_wrapper {
|
||||
padding: 0;
|
||||
display: none;
|
||||
}
|
||||
#jump_to:hover #jump_wrapper {
|
||||
display: block;
|
||||
}
|
||||
#jump_page {
|
||||
padding: 5px 0 3px;
|
||||
margin: 0 0 25px 25px;
|
||||
}
|
||||
#jump_page .source {
|
||||
display: block;
|
||||
padding: 5px 10px;
|
||||
text-decoration: none;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
#jump_page .source:hover {
|
||||
background: #f5f5ff;
|
||||
}
|
||||
#jump_page .source:first-child {
|
||||
}
|
||||
table td {
|
||||
border: 0;
|
||||
outline: 0;
|
||||
}
|
||||
td.docs, th.docs {
|
||||
max-width: 450px;
|
||||
min-width: 450px;
|
||||
min-height: 5px;
|
||||
padding: 10px 25px 1px 50px;
|
||||
overflow-x: hidden;
|
||||
vertical-align: top;
|
||||
text-align: left;
|
||||
}
|
||||
.docs pre {
|
||||
margin: 15px 0 15px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
.docs p tt, .docs p code {
|
||||
background: #f8f8ff;
|
||||
border: 1px solid #dedede;
|
||||
font-size: 12px;
|
||||
padding: 0 0.2em;
|
||||
}
|
||||
.pilwrap {
|
||||
position: relative;
|
||||
}
|
||||
.pilcrow {
|
||||
font: 12px Arial;
|
||||
text-decoration: none;
|
||||
color: #454545;
|
||||
position: absolute;
|
||||
top: 3px; left: -20px;
|
||||
padding: 1px 2px;
|
||||
opacity: 0;
|
||||
-webkit-transition: opacity 0.2s linear;
|
||||
}
|
||||
td.docs:hover .pilcrow {
|
||||
opacity: 1;
|
||||
}
|
||||
td.code, th.code {
|
||||
padding: 14px 15px 16px 25px;
|
||||
width: 100%;
|
||||
vertical-align: top;
|
||||
background: #f5f5ff;
|
||||
border-left: 1px solid #e5e5ee;
|
||||
}
|
||||
pre, tt, code {
|
||||
font-size: 12px; line-height: 18px;
|
||||
font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace;
|
||||
margin: 0; padding: 0;
|
||||
}
|
||||
|
||||
|
||||
/*---------------------- Syntax Highlighting -----------------------------*/
|
||||
td.linenos { background-color: #f0f0f0; padding-right: 10px; }
|
||||
span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }
|
||||
body .hll { background-color: #ffffcc }
|
||||
body .c { color: #408080; font-style: italic } /* Comment */
|
||||
body .err { border: 1px solid #FF0000 } /* Error */
|
||||
body .k { color: #954121 } /* Keyword */
|
||||
body .o { color: #666666 } /* Operator */
|
||||
body .cm { color: #408080; font-style: italic } /* Comment.Multiline */
|
||||
body .cp { color: #BC7A00 } /* Comment.Preproc */
|
||||
body .c1 { color: #408080; font-style: italic } /* Comment.Single */
|
||||
body .cs { color: #408080; font-style: italic } /* Comment.Special */
|
||||
body .gd { color: #A00000 } /* Generic.Deleted */
|
||||
body .ge { font-style: italic } /* Generic.Emph */
|
||||
body .gr { color: #FF0000 } /* Generic.Error */
|
||||
body .gh { color: #000080; font-weight: bold } /* Generic.Heading */
|
||||
body .gi { color: #00A000 } /* Generic.Inserted */
|
||||
body .go { color: #808080 } /* Generic.Output */
|
||||
body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
|
||||
body .gs { font-weight: bold } /* Generic.Strong */
|
||||
body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
|
||||
body .gt { color: #0040D0 } /* Generic.Traceback */
|
||||
body .kc { color: #954121 } /* Keyword.Constant */
|
||||
body .kd { color: #954121; font-weight: bold } /* Keyword.Declaration */
|
||||
body .kn { color: #954121; font-weight: bold } /* Keyword.Namespace */
|
||||
body .kp { color: #954121 } /* Keyword.Pseudo */
|
||||
body .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */
|
||||
body .kt { color: #B00040 } /* Keyword.Type */
|
||||
body .m { color: #666666 } /* Literal.Number */
|
||||
body .s { color: #219161 } /* Literal.String */
|
||||
body .na { color: #7D9029 } /* Name.Attribute */
|
||||
body .nb { color: #954121 } /* Name.Builtin */
|
||||
body .nc { color: #0000FF; font-weight: bold } /* Name.Class */
|
||||
body .no { color: #880000 } /* Name.Constant */
|
||||
body .nd { color: #AA22FF } /* Name.Decorator */
|
||||
body .ni { color: #999999; font-weight: bold } /* Name.Entity */
|
||||
body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
|
||||
body .nf { color: #0000FF } /* Name.Function */
|
||||
body .nl { color: #A0A000 } /* Name.Label */
|
||||
body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
|
||||
body .nt { color: #954121; font-weight: bold } /* Name.Tag */
|
||||
body .nv { color: #19469D } /* Name.Variable */
|
||||
body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
|
||||
body .w { color: #bbbbbb } /* Text.Whitespace */
|
||||
body .mf { color: #666666 } /* Literal.Number.Float */
|
||||
body .mh { color: #666666 } /* Literal.Number.Hex */
|
||||
body .mi { color: #666666 } /* Literal.Number.Integer */
|
||||
body .mo { color: #666666 } /* Literal.Number.Oct */
|
||||
body .sb { color: #219161 } /* Literal.String.Backtick */
|
||||
body .sc { color: #219161 } /* Literal.String.Char */
|
||||
body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */
|
||||
body .s2 { color: #219161 } /* Literal.String.Double */
|
||||
body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
|
||||
body .sh { color: #219161 } /* Literal.String.Heredoc */
|
||||
body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
|
||||
body .sx { color: #954121 } /* Literal.String.Other */
|
||||
body .sr { color: #BB6688 } /* Literal.String.Regex */
|
||||
body .s1 { color: #219161 } /* Literal.String.Single */
|
||||
body .ss { color: #19469D } /* Literal.String.Symbol */
|
||||
body .bp { color: #954121 } /* Name.Builtin.Pseudo */
|
||||
body .vc { color: #19469D } /* Name.Variable.Class */
|
||||
body .vg { color: #19469D } /* Name.Variable.Global */
|
||||
body .vi { color: #19469D } /* Name.Variable.Instance */
|
||||
body .il { color: #666666 } /* Literal.Number.Integer.Long */
|
||||
1346
thirdparty/acorn/index.html
vendored
Executable file
1346
thirdparty/acorn/index.html
vendored
Executable file
File diff suppressed because it is too large
Load diff
17
thirdparty/acorn/package.json
vendored
Executable file
17
thirdparty/acorn/package.json
vendored
Executable file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "acorn",
|
||||
"description": "ECMAScript parser",
|
||||
"homepage": "http://marijnhaverbeke.nl/acorn/",
|
||||
"main": "acorn.js",
|
||||
"version": "0.5.1",
|
||||
"engines": {"node": ">=0.4.0"},
|
||||
"maintainers": [{"name": "Marijn Haverbeke",
|
||||
"email": "marijnh@gmail.com",
|
||||
"web": "http://marijnhaverbeke.nl"}],
|
||||
"repository": {"type": "git",
|
||||
"url": "http://marijnhaverbeke.nl/git/acorn"},
|
||||
"licenses": [{"type": "MIT",
|
||||
"url": "http://marijnhaverbeke.nl/acorn/LICENSE"}],
|
||||
"scripts": {"test": "node test/run.js"},
|
||||
"bin": {"acorn": "./bin/acorn"}
|
||||
}
|
||||
90
thirdparty/acorn/test/bench.html
vendored
Executable file
90
thirdparty/acorn/test/bench.html
vendored
Executable file
|
|
@ -0,0 +1,90 @@
|
|||
<!doctype html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Acorn benchmark</title>
|
||||
<script src="../acorn.js"></script>
|
||||
<script src="compare/esprima.js"></script>
|
||||
<script src="compare/uglifyjs2.js"></script>
|
||||
<script src="jquery-string.js"></script>
|
||||
<script src="codemirror-string.js"></script>
|
||||
<style>
|
||||
td { text-align: right; padding-right: 20px; }
|
||||
th { text-align: left; padding-right: 40px; }
|
||||
body { max-width: 50em; padding: 1em 2em; }
|
||||
h1 { font-size: 150%; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<h1>Acorn/Esprima/UglifyJS2 speed comparison</h1>
|
||||
|
||||
<p>This will run each of the three parsers on the source code of
|
||||
jQuery 1.6.4 and CodeMirror 3.0b1 for two seconds, and show a table
|
||||
indicating the number of lines parsed per second. Note that UglifyJS
|
||||
always stores location data, and is thus not fairly compared by the
|
||||
benchmark <em>without</em> location data.<p>
|
||||
|
||||
<p>Also note that having the developer tools open in Chrome, or
|
||||
Firebug in Firefox <em>heavily</em> influences the numbers you get. In
|
||||
Chrome, the effect even lingers (in the tab) after you close the
|
||||
developer tools. Load in a fresh tab to get (halfway) stable
|
||||
numbers.</p>
|
||||
|
||||
<button onclick="run(false)">Compare <strong>without</strong> location data</button>
|
||||
<button onclick="run(true)">Compare <strong>with</strong> location data</button>
|
||||
<button onclick="run(false, true)">Run only Acorn</button>
|
||||
<span id="running"></span>
|
||||
|
||||
<script>
|
||||
function runAcorn(code, locations) {
|
||||
acorn.parse(code, {locations: locations});
|
||||
}
|
||||
function runEsprima(code, locations) {
|
||||
esprima.parse(code, {loc: locations});
|
||||
}
|
||||
function runUglifyJS(code) {
|
||||
uglifyjs.parse(code);
|
||||
}
|
||||
|
||||
var totalLines = codemirror30.split("\n").length + jquery164.split("\n").length;
|
||||
|
||||
function benchmark(runner, locations) {
|
||||
// Give it a chance to warm up (first runs are usually outliers)
|
||||
runner(jquery164, locations);
|
||||
runner(codemirror30, locations);
|
||||
var t0 = +new Date, t1, lines = 0;
|
||||
for (;;) {
|
||||
runner(jquery164, locations);
|
||||
runner(codemirror30, locations);
|
||||
lines += totalLines;
|
||||
t1 = +new Date;
|
||||
if (t1 - t0 > 2000) break;
|
||||
}
|
||||
return lines / ((t1 - t0) / 1000);
|
||||
}
|
||||
|
||||
function showOutput(values) {
|
||||
var html = "<hr><table>";
|
||||
for (var i = 0; i < values.length; ++i)
|
||||
html += "<tr><th>" + values[i].name + "</td><td>" + Math.round(values[i].score) + " lines per second</td><td>" +
|
||||
Math.round(values[i].score * 100 / values[0].score) + "%</td></tr>";
|
||||
document.body.appendChild(document.createElement("div")).innerHTML = html;
|
||||
}
|
||||
|
||||
function run(locations, acornOnly) {
|
||||
var running = document.getElementById("running");
|
||||
running.innerHTML = "Running benchmark...";
|
||||
var data = [{name: "Acorn", runner: runAcorn},
|
||||
{name: "Esprima", runner: runEsprima},
|
||||
{name: "UglifyJS2", runner: runUglifyJS}];
|
||||
if (acornOnly) data.length = 1;
|
||||
var pos = 0;
|
||||
function next() {
|
||||
data[pos].score = benchmark(data[pos].runner, locations);
|
||||
if (++pos == data.length) {
|
||||
running.innerHTML = "";
|
||||
showOutput(data);
|
||||
} else setTimeout(next, 100);
|
||||
}
|
||||
setTimeout(next, 50);
|
||||
}
|
||||
</script>
|
||||
3995
thirdparty/acorn/test/codemirror-string.js
vendored
Executable file
3995
thirdparty/acorn/test/codemirror-string.js
vendored
Executable file
File diff suppressed because it is too large
Load diff
3838
thirdparty/acorn/test/compare/esprima.js
vendored
Executable file
3838
thirdparty/acorn/test/compare/esprima.js
vendored
Executable file
File diff suppressed because one or more lines are too long
1372
thirdparty/acorn/test/compare/uglifyjs.js
vendored
Executable file
1372
thirdparty/acorn/test/compare/uglifyjs.js
vendored
Executable file
File diff suppressed because it is too large
Load diff
2494
thirdparty/acorn/test/compare/uglifyjs2.js
vendored
Executable file
2494
thirdparty/acorn/test/compare/uglifyjs2.js
vendored
Executable file
File diff suppressed because it is too large
Load diff
92
thirdparty/acorn/test/driver.js
vendored
Executable file
92
thirdparty/acorn/test/driver.js
vendored
Executable file
|
|
@ -0,0 +1,92 @@
|
|||
(function(exports) {
|
||||
var tests = [];
|
||||
var acorn = typeof require == "undefined" ? window.acorn : require("../acorn.js");
|
||||
|
||||
exports.test = function(code, ast, options) {
|
||||
tests.push({code: code, ast: ast, options: options});
|
||||
};
|
||||
exports.testFail = function(code, message, options) {
|
||||
tests.push({code: code, error: message, options: options});
|
||||
};
|
||||
exports.testAssert = function(code, assert, options) {
|
||||
tests.push({code: code, assert: assert, options: options});
|
||||
};
|
||||
|
||||
exports.runTests = function(callback) {
|
||||
var opts = {locations: true};
|
||||
for (var i = 0; i < tests.length; ++i) {
|
||||
var test = tests[i];
|
||||
try {
|
||||
var ast = acorn.parse(test.code, test.options || opts);
|
||||
if (test.error) callback("fail", test.code,
|
||||
"Expected error message: " + test.error + "\nBut parsing succeeded.");
|
||||
else if (test.assert) {
|
||||
var error = test.assert(ast);
|
||||
if (error) callback("fail", test.code,
|
||||
"\n Assertion failed:\n " + error);
|
||||
else callback("ok", test.code);
|
||||
} else {
|
||||
var mis = misMatch(test.ast, ast);
|
||||
if (!mis) callback("ok", test.code);
|
||||
else callback("fail", test.code, mis);
|
||||
}
|
||||
} catch(e) {
|
||||
if (test.error && e instanceof SyntaxError) {
|
||||
if (e.message == test.error) callback("ok", test.code);
|
||||
else callback("fail", test.code,
|
||||
"Expected error message: " + test.error + "\nGot error message: " + e.message);
|
||||
} else {
|
||||
callback("error", test.code, e.message || e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function ppJSON(v) { return JSON.stringify(v, null, 2); }
|
||||
function addPath(str, pt) {
|
||||
if (str.charAt(str.length-1) == ")")
|
||||
return str.slice(0, str.length-1) + "/" + pt + ")";
|
||||
return str + " (" + pt + ")";
|
||||
}
|
||||
|
||||
function misMatch(exp, act) {
|
||||
if (!exp || !act || (typeof exp != "object") || (typeof act != "object")) {
|
||||
if (exp !== act) return ppJSON(exp) + " !== " + ppJSON(act);
|
||||
} else if (exp.splice) {
|
||||
if (!act.slice) return ppJSON(exp) + " != " + ppJSON(act);
|
||||
if (act.length != exp.length) return "array length mismatch " + exp.length + " != " + act.length;
|
||||
for (var i = 0; i < act.length; ++i) {
|
||||
var mis = misMatch(exp[i], act[i]);
|
||||
if (mis) return addPath(mis, i);
|
||||
}
|
||||
} else {
|
||||
for (var prop in exp) {
|
||||
var mis = misMatch(exp[prop], act[prop]);
|
||||
if (mis) return addPath(mis, prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function mangle(ast) {
|
||||
if (typeof ast != "object" || !ast) return;
|
||||
if (ast.slice) {
|
||||
for (var i = 0; i < ast.length; ++i) mangle(ast[i]);
|
||||
} else {
|
||||
var loc = ast.start && ast.end && {start: ast.start, end: ast.end};
|
||||
if (loc) { delete ast.start; delete ast.end; }
|
||||
for (var name in ast) if (ast.hasOwnProperty(name)) mangle(ast[name]);
|
||||
if (loc) ast.loc = loc;
|
||||
}
|
||||
}
|
||||
|
||||
exports.printTests = function() {
|
||||
var out = "";
|
||||
for (var i = 0; i < tests.length; ++i) {
|
||||
if (tests[i].error) continue;
|
||||
mangle(tests[i].ast);
|
||||
out += "test(" + JSON.stringify(tests[i].code) + ", " + JSON.stringify(tests[i].ast, null, 2) + ");\n\n";
|
||||
}
|
||||
document.body.innerHTML = "";
|
||||
document.body.appendChild(document.createElement("pre")).appendChild(document.createTextNode(out));
|
||||
};
|
||||
})(typeof exports == "undefined" ? window : exports);
|
||||
24
thirdparty/acorn/test/index.html
vendored
Executable file
24
thirdparty/acorn/test/index.html
vendored
Executable file
|
|
@ -0,0 +1,24 @@
|
|||
<!doctype html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Acorn test suite</title>
|
||||
<script src="../acorn.js"></script>
|
||||
<script src="driver.js"></script>
|
||||
<script src="tests.js" charset="utf-8"></script>
|
||||
</head>
|
||||
|
||||
<script>
|
||||
var testsRun = 0, failed = 0;
|
||||
function report(state, code, message) {
|
||||
if (state != "ok") {++failed; console.log(code, message);}
|
||||
++testsRun;
|
||||
}
|
||||
window.onload = function(){
|
||||
var t0 = +new Date;
|
||||
runTests(report);
|
||||
var out = testsRun + " tests run in " + (+new Date - t0) + "ms\n";
|
||||
if (failed) out += failed + " failures.\n";
|
||||
else out += "All passed.\n";
|
||||
document.body.appendChild(document.createElement("pre")).appendChild(document.createTextNode(out));
|
||||
};
|
||||
</script>
|
||||
9047
thirdparty/acorn/test/jquery-string.js
vendored
Executable file
9047
thirdparty/acorn/test/jquery-string.js
vendored
Executable file
File diff suppressed because it is too large
Load diff
14
thirdparty/acorn/test/run.js
vendored
Executable file
14
thirdparty/acorn/test/run.js
vendored
Executable file
|
|
@ -0,0 +1,14 @@
|
|||
var driver = require("./driver.js");
|
||||
require("./tests.js");
|
||||
|
||||
var testsRun = 0, failed = 0;
|
||||
function report(state, code, message) {
|
||||
if (state != "ok") {++failed; console.log(code, message);}
|
||||
++testsRun;
|
||||
}
|
||||
|
||||
var t0 = +new Date;
|
||||
driver.runTests(report);
|
||||
console.log(testsRun + " tests run in " + (+new Date - t0) + "ms");
|
||||
if (failed) console.log(failed + " failures.");
|
||||
else console.log("All passed.");
|
||||
26998
thirdparty/acorn/test/tests.js
vendored
Executable file
26998
thirdparty/acorn/test/tests.js
vendored
Executable file
File diff suppressed because it is too large
Load diff
330
thirdparty/acorn/util/walk.js
vendored
Executable file
330
thirdparty/acorn/util/walk.js
vendored
Executable 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");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
Loading…
Reference in a new issue