aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/ast.js123
-rwxr-xr-xlib/node.js8
-rw-r--r--lib/output.js37
-rw-r--r--lib/parse.js4
-rw-r--r--lib/test.js345
5 files changed, 105 insertions, 412 deletions
diff --git a/lib/ast.js b/lib/ast.js
index 683f602f..b8b6b072 100644
--- a/lib/ast.js
+++ b/lib/ast.js
@@ -8,6 +8,8 @@ function DEFNODE(type, props, methods, base) {
for (var i = props.length; --i >= 0;) {
code += "this." + props[i] + " = props." + props[i] + ";";
}
+ if (methods && methods.initialize)
+ code += "this.initialize();"
code += " } }";
var ctor = new Function(code)();
if (base) {
@@ -29,11 +31,15 @@ var AST_Token = DEFNODE("Token", "type value line col pos endpos nlb comments_be
}, null);
var AST_Node = DEFNODE("Node", "start end", {
- renew: function(args) {
- var ctor = this.CTOR, props = ctor.props;
- for (var i in props) if (!HOP(args, i)) args[i] = this[i];
- return new ctor(args);
+ clone: function() {
+ return new this.CTOR(this);
},
+ // XXX: what was this for?
+ // renew: function(args) {
+ // var ctor = this.CTOR, props = ctor.props;
+ // for (var i in props) if (!HOP(args, i)) args[i] = this[i];
+ // return new ctor(args);
+ // },
walk: function(w) {
w._visit(this);
}
@@ -75,41 +81,50 @@ Used for bodies of FUNCTION/TRY/CATCH/THROW/SWITCH.",
/* -----[ loops ]----- */
-var AST_LabeledStatement = DEFNODE("LabeledStatement", "label body", {
+var AST_Statement = DEFNODE("Statement", "label body", {
walk: function(w) {
w._visit(this, function(){
if (this.label) this.label.walk(w);
if (this.body) {
- if (this.body instanceof Array)
- AST_Bracketed.prototype.walk.call(this, w);
- else
+ if (this.body instanceof AST_Node)
this.body.walk(w);
+ else
+ this.walk_array(w);
}
});
- }
+ },
+ walk_array: AST_Bracketed.prototype.walk
});
-var AST_Statement = DEFNODE("Statement", null, {
+var AST_SimpleStatement = DEFNODE("SimpleStatement", null, {
+
+}, AST_Statement);
-}, AST_LabeledStatement);
+var AST_BlockStatement = DEFNODE("BlockStatement", null, {
+
+}, AST_Statement);
+
+var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
+
+}, AST_Statement);
var AST_Do = DEFNODE("Do", "condition", {
walk: function(w) {
w._visit(this, function(){
this.condition.walk(w);
- AST_LabeledStatement.prototype.walk.call(this, w);
+ AST_Statement.prototype.walk.call(this, w);
});
}
-}, AST_LabeledStatement);
+}, AST_Statement);
var AST_While = DEFNODE("While", "condition", {
walk: function(w) {
w._visit(this, function(){
this.condition.walk(w);
- AST_LabeledStatement.prototype.walk.call(this, w);
+ AST_Statement.prototype.walk.call(this, w);
});
}
-}, AST_LabeledStatement);
+}, AST_Statement);
var AST_For = DEFNODE("For", "init condition step", {
walk: function(w) {
@@ -117,42 +132,42 @@ var AST_For = DEFNODE("For", "init condition step", {
if (this.init) this.init.walk(w);
if (this.condition) this.condition.walk(w);
if (this.step) this.step.walk(w);
- AST_LabeledStatement.prototype.walk.call(this, w);
+ AST_Statement.prototype.walk.call(this, w);
});
}
-}, AST_LabeledStatement);
+}, AST_Statement);
var AST_ForIn = DEFNODE("ForIn", "init name object", {
walk: function(w) {
w._visit(this, function(){
if (this.init) this.init.walk(w);
this.object.walk(w);
- AST_LabeledStatement.prototype.walk.call(this, w);
+ AST_Statement.prototype.walk.call(this, w);
});
}
-}, AST_LabeledStatement);
+}, AST_Statement);
-var AST_With = DEFNODE("With", "expression body", {
+var AST_With = DEFNODE("With", "expression", {
walk: function(w) {
w._visit(this, function(){
this.expression.walk(w);
- AST_LabeledStatement.prototype.walk.call(this, w);
+ AST_Statement.prototype.walk.call(this, w);
});
}
-});
+}, AST_Statement);
/* -----[ functions ]----- */
-var AST_Scope = DEFNODE("Scope", "identifiers body", {
+var AST_Scope = DEFNODE("Scope", "identifiers", {
walk: function(w) {
w._visit(this, function(){
if (this.identifiers) this.identifiers.forEach(function(el){
el.walk(w);
});
- AST_LabeledStatement.prototype.walk.call(this, w);
+ AST_Statement.prototype.walk.call(this, w);
});
}
-});
+}, AST_Statement);
var AST_Toplevel = DEFNODE("Toplevel", null, {
@@ -234,25 +249,23 @@ var AST_Switch = DEFNODE("Switch", "expression", {
walk: function(w) {
w._visit(this, function(){
this.expression.walk(w);
- AST_LabeledStatement.prototype.walk.call(this, w);
+ AST_Statement.prototype.walk.call(this, w);
});
}
-}, AST_LabeledStatement);
+}, AST_Statement);
var AST_SwitchBlock = DEFNODE("SwitchBlock", null, {
-
+ walk : AST_Statement.prototype.walk,
+ walk_array : AST_Bracketed.prototype.walk
}, AST_Bracketed);
var AST_SwitchBranch = DEFNODE("SwitchBranch", "body", {
-
+ walk : AST_Statement.prototype.walk,
+ walk_array : AST_Bracketed.prototype.walk
});
var AST_Default = DEFNODE("Default", null, {
- walk: function(w) {
- w._visit(this, function(){
- AST_Statement.prototype.walk.call(this, w);
- });
- }
+
}, AST_SwitchBranch);
var AST_Case = DEFNODE("Case", "expression", {
@@ -482,10 +495,8 @@ var AST_Number = DEFNODE("Number", "value", {
}, AST_Constant);
var AST_RegExp = DEFNODE("Regexp", "pattern mods", {
- getValue: function() {
- return this._regexp || (
- this._regexp = new RegExp(this.pattern, this.mods)
- );
+ initialize: function() {
+ this.value = new RegExp(this.pattern, this.mods);
}
}, AST_Constant);
@@ -494,17 +505,45 @@ var AST_Atom = DEFNODE("Atom", null, {
}, AST_Constant);
var AST_Null = DEFNODE("Null", null, {
- getValue: function() { return null }
+ value: null
}, AST_Atom);
var AST_Undefined = DEFNODE("Undefined", null, {
- getValue: function() { return (function(){}()) }
+ value: (function(){}())
}, AST_Atom);
var AST_False = DEFNODE("False", null, {
- getValue: function() { return false }
+ value: false
}, AST_Atom);
var AST_True = DEFNODE("True", null, {
- getValue: function() { return true }
+ value: true
}, AST_Atom);
+
+/* -----[ Walker ]----- */
+
+function TreeWalker(visitor) {
+ this.stack = [];
+ if (visitor) this.visit = visitor;
+};
+
+TreeWalker.prototype = {
+ visit: function(node){},
+ parent: function(n) {
+ if (n == null) n = 1;
+ return this.stack[this.stack.length - n];
+ },
+ find_parent: function(type) {
+ for (var a = this.stack, i = a.length; --i >= 0;)
+ if (a[i] instanceof type) return a[i];
+ return null;
+ },
+ _visit: function(node, descend) {
+ this.visit(node);
+ if (descend) {
+ this.stack.push(node);
+ descend.call(node);
+ this.stack.pop();
+ }
+ }
+};
diff --git a/lib/node.js b/lib/node.js
index 9089a5fe..571cdd80 100755
--- a/lib/node.js
+++ b/lib/node.js
@@ -24,12 +24,10 @@
console.timeEnd("parse");
console.time("walk");
- ast.walk({
- _visit: function(node, descend) {
- //console.log(node);
- if (descend) descend.call(node);
- }
+ var w = new TreeWalker(function(node){
+ console.log(node.TYPE + " [ start: " + node.start.line + ":" + node.start.col + ", end: " + node.end.line + ":" + node.end.col + "]");
});
+ ast.walk(w);
console.timeEnd("walk");
})();
diff --git a/lib/output.js b/lib/output.js
index 2c4c6fdc..cf17c3e6 100644
--- a/lib/output.js
+++ b/lib/output.js
@@ -4,7 +4,6 @@ function OutputStream(options) {
indent_level : 4,
quote_keys : false,
space_colon : false,
- beautify : true,
ascii_only : false,
inline_script : false,
width : 80
@@ -12,6 +11,7 @@ function OutputStream(options) {
var indentation = 0;
var current_col = 0;
+ var current_line = 0;
var OUTPUT = "";
function to_ascii(str) {
@@ -45,12 +45,9 @@ function OutputStream(options) {
};
function print(str) {
- var nl = str.lastIndexOf("\n");
- if (nl >= 0) {
- current_col = nl;
- } else {
- current_col += str.length;
- }
+ var a = str.split(/\r?\n/), n = a.length;
+ current_line += n;
+ current_col += a[n - 1].length;
OUTPUT += str;
};
@@ -71,12 +68,12 @@ function OutputStream(options) {
function make_indent(line) {
if (line == null)
line = "";
- if (beautify)
- line = repeat_string(" ", options.indent_start + indentation) + line;
+ line = repeat_string(" ", options.indent_start + indentation) + line;
return line;
};
function with_indent(col, cont) {
+ if (col === true) col = next_indent();
var save_indentation = indentation;
indentation = col;
var ret = cont();
@@ -85,34 +82,31 @@ function OutputStream(options) {
};
function indent() {
- if (options.beautify) print(make_indent());
+ print(make_indent());
};
function newline() {
- if (options.beautify) {
- print("\n");
- print(make_indent());
- }
+ print("\n");
};
function next_indent() {
return indentation + options.indent_level;
};
- function with_block(cont) {
+ function with_block(cont, beautify) {
var ret;
print("{");
with_indent(next_indent(), function(){
- newline();
+ if (beautify) newline();
ret = cont();
- newline();
+ if (beautify) newline();
});
- indent();
+ if (beautify) indent();
print("}");
return ret;
};
- function with_parens(cont) {
+ function with_parens(cont, beautify) {
print("(");
var ret = with_indent(current_col, cont);
print(")");
@@ -128,7 +122,10 @@ function OutputStream(options) {
with_indent : with_indent,
with_block : with_block,
with_parens : with_parens,
- options : function() { return options }
+ options : function() { return options },
+ line : function() { return current_line },
+ col : function() { return current_col },
+ pos : function() { return OUTPUT.length }
};
};
diff --git a/lib/parse.js b/lib/parse.js
index 9dcfa635..2cd160fa 100644
--- a/lib/parse.js
+++ b/lib/parse.js
@@ -739,13 +739,13 @@ function parse($TEXT, exigent_mode) {
case "punc":
switch (S.token.value) {
case "{":
- return new AST_Statement({ body: block_() });
+ return new AST_BlockStatement({ body: block_() });
case "[":
case "(":
return simple_statement();
case ";":
next();
- return new AST_Statement();
+ return new AST_EmptyStatement();
default:
unexpected();
}
diff --git a/lib/test.js b/lib/test.js
index b765132f..7972d346 100644
--- a/lib/test.js
+++ b/lib/test.js
@@ -1,346 +1,5 @@
-var func = function tokenizer($TEXT) {
-
- var S = {
- text : $TEXT.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, ''),
- pos : 0,
- tokpos : 0,
- line : 0,
- tokline : 0,
- col : 0,
- tokcol : 0,
- newline_before : false,
- regex_allowed : false,
- comments_before : []
- };
-
- function peek() { return S.text.charAt(S.pos); };
-
- function next(signal_eof, in_string) {
- var ch = S.text.charAt(S.pos++);
- if (signal_eof && !ch)
- throw EX_EOF;
- if (ch == "\n") {
- S.newline_before = S.newline_before || !in_string;
- ++S.line;
- S.col = 0;
- } else {
- ++S.col;
- }
- return ch;
- };
-
- function eof() {
- return !S.peek();
- };
-
- function find(what, signal_eof) {
- var pos = S.text.indexOf(what, S.pos);
- if (signal_eof && pos == -1) throw EX_EOF;
- return pos;
- };
-
- function start_token() {
- S.tokline = S.line;
- S.tokcol = S.col;
- S.tokpos = S.pos;
- };
-
- function token(type, value, is_comment) {
- S.regex_allowed = ((type == "operator" && !HOP(UNARY_POSTFIX, value)) ||
- (type == "keyword" && HOP(KEYWORDS_BEFORE_EXPRESSION, value)) ||
- (type == "punc" && HOP(PUNC_BEFORE_EXPRESSION, value)));
- var ret = {
- type : type,
- value : value,
- line : S.tokline,
- col : S.tokcol,
- pos : S.tokpos,
- endpos : S.pos,
- nlb : S.newline_before
- };
- if (!is_comment) {
- ret.comments_before = S.comments_before;
- S.comments_before = [];
- // make note of any newlines in the comments that came before
- for (var i = 0, len = ret.comments_before.length; i < len; i++) {
- ret.nlb = ret.nlb || ret.comments_before[i].nlb;
- }
- }
- S.newline_before = false;
- return new AST_Token(ret);
- };
-
- function skip_whitespace() {
- while (HOP(WHITESPACE_CHARS, peek()))
- next();
- };
-
- function read_while(pred) {
- var ret = "", ch = peek(), i = 0;
- while (ch && pred(ch, i++)) {
- ret += next();
- ch = peek();
- }
- return ret;
- };
-
- function parse_error(err) {
- js_error(err, S.tokline, S.tokcol, S.tokpos);
- };
-
- function read_num(prefix) {
- var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".";
- var num = read_while(function(ch, i){
- if (ch == "x" || ch == "X") {
- if (has_x) return false;
- return has_x = true;
- }
- if (!has_x && (ch == "E" || ch == "e")) {
- if (has_e) return false;
- return has_e = after_e = true;
- }
- if (ch == "-") {
- if (after_e || (i == 0 && !prefix)) return true;
- return false;
- }
- if (ch == "+") return after_e;
- after_e = false;
- if (ch == ".") {
- if (!has_dot && !has_x && !has_e)
- return has_dot = true;
- return false;
- }
- return is_alphanumeric_char(ch);
- });
- if (prefix)
- num = prefix + num;
- var valid = parse_js_number(num);
- if (!isNaN(valid)) {
- return token("num", valid);
- } else {
- parse_error("Invalid syntax: " + num);
- }
- };
-
- function read_escaped_char(in_string) {
- var ch = next(true, in_string);
- switch (ch) {
- case "n" : return "\n";
- case "r" : return "\r";
- case "t" : return "\t";
- case "b" : return "\b";
- case "v" : return "\u000b";
- case "f" : return "\f";
- case "0" : return "\0";
- case "x" : return String.fromCharCode(hex_bytes(2));
- case "u" : return String.fromCharCode(hex_bytes(4));
- case "\n": return "";
- default : return ch;
- }
- };
-
- function hex_bytes(n) {
- var num = 0;
- for (; n > 0; --n) {
- var digit = parseInt(next(true), 16);
- if (isNaN(digit))
- parse_error("Invalid hex-character pattern in string");
- num = (num << 4) | digit;
- }
- return num;
- };
-
- function read_string() {
- return with_eof_error("Unterminated string constant", function(){
- var quote = next(), ret = "";
- for (;;) {
- var ch = next(true);
- if (ch == "\\") {
- // read OctalEscapeSequence (XXX: deprecated if "strict mode")
- // https://github.com/mishoo/UglifyJS/issues/178
- var octal_len = 0, first = null;
- ch = read_while(function(ch){
- if (ch >= "0" && ch <= "7") {
- if (!first) {
- first = ch;
- return ++octal_len;
- }
- else if (first <= "3" && octal_len <= 2) return ++octal_len;
- else if (first >= "4" && octal_len <= 1) return ++octal_len;
- }
- return false;
- });
- if (octal_len > 0) ch = String.fromCharCode(parseInt(ch, 8));
- else ch = read_escaped_char(true);
- }
- else if (ch == quote) break;
- ret += ch;
- }
- return token("string", ret);
- });
- };
-
- function read_line_comment() {
- next();
- var i = find("\n"), ret;
- if (i == -1) {
- ret = S.text.substr(S.pos);
- S.pos = S.text.length;
- } else {
- ret = S.text.substring(S.pos, i);
- S.pos = i;
- }
- return token("comment1", ret, true);
- };
-
- function read_multiline_comment() {
- next();
- return with_eof_error("Unterminated multiline comment", function(){
- var i = find("*/", true),
- text = S.text.substring(S.pos, i);
- S.pos = i + 2;
- S.line += text.split("\n").length - 1;
- S.newline_before = S.newline_before || text.indexOf("\n") >= 0;
-
- // https://github.com/mishoo/UglifyJS/issues/#issue/100
- if (/^@cc_on/i.test(text)) {
- warn("WARNING: at line " + S.line);
- warn("*** Found \"conditional comment\": " + text);
- warn("*** UglifyJS DISCARDS ALL COMMENTS. This means your code might no longer work properly in Internet Explorer.");
- }
-
- return token("comment2", text, true);
- });
- };
-
- function read_name() {
- var backslash = false, name = "", ch, escaped = false, hex;
- while ((ch = peek()) != null) {
- if (!backslash) {
- if (ch == "\\") escaped = backslash = true, next();
- else if (is_identifier_char(ch)) name += next();
- else break;
- }
- else {
- if (ch != "u") parse_error("Expecting UnicodeEscapeSequence -- uXXXX");
- ch = read_escaped_char();
- if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier");
- name += ch;
- backslash = false;
- }
- }
- if (HOP(KEYWORDS, name) && escaped) {
- hex = name.charCodeAt(0).toString(16).toUpperCase();
- name = "\\u" + "0000".substr(hex.length) + hex + name.slice(1);
- }
- return name;
- };
-
- function read_regexp(regexp) {
- return with_eof_error("Unterminated regular expression", function(){
- var prev_backslash = false, ch, in_class = false;
- while ((ch = next(true))) if (prev_backslash) {
- regexp += "\\" + ch;
- prev_backslash = false;
- } else if (ch == "[") {
- in_class = true;
- regexp += ch;
- } else if (ch == "]" && in_class) {
- in_class = false;
- regexp += ch;
- } else if (ch == "/" && !in_class) {
- break;
- } else if (ch == "\\") {
- prev_backslash = true;
- } else {
- regexp += ch;
- }
- var mods = read_name();
- return token("regexp", [ regexp, mods ]);
- });
- };
-
- function read_operator(prefix) {
- function grow(op) {
- if (!peek()) return op;
- var bigger = op + peek();
- if (HOP(OPERATORS, bigger)) {
- next();
- return grow(bigger);
- } else {
- return op;
- }
- };
- return token("operator", grow(prefix || next()));
- };
-
- function handle_slash() {
- next();
- var regex_allowed = S.regex_allowed;
- switch (peek()) {
- case "/":
- S.comments_before.push(read_line_comment());
- S.regex_allowed = regex_allowed;
- return next_token();
- case "*":
- S.comments_before.push(read_multiline_comment());
- S.regex_allowed = regex_allowed;
- return next_token();
- }
- return S.regex_allowed ? read_regexp("") : read_operator("/");
- };
-
- function handle_dot() {
- next();
- return is_digit(peek())
- ? read_num(".")
- : token("punc", ".");
- };
-
- function read_word() {
- var word = read_name();
- return HOP(KEYWORDS_ATOM, word)
- ? token("atom", word)
- : !HOP(KEYWORDS, word)
- ? token("name", word)
- : HOP(OPERATORS, word)
- ? token("operator", word)
- : token("keyword", word);
- };
-
- function with_eof_error(eof_error, cont) {
- try {
- return cont();
- } catch(ex) {
- if (ex === EX_EOF) parse_error(eof_error);
- else throw ex;
- }
- };
-
- function next_token(force_regexp) {
- if (force_regexp != null)
- return read_regexp(force_regexp);
- skip_whitespace();
- start_token();
- var ch = peek();
- if (!ch) return token("eof");
- if (is_digit(ch)) return read_num();
- if (ch == '"' || ch == "'") return read_string();
- if (HOP(PUNC_CHARS, ch)) return token("punc", next());
- if (ch == ".") return handle_dot();
- if (ch == "/") return handle_slash();
- if (HOP(OPERATOR_CHARS, ch)) return read_operator();
- if (ch == "\\" || is_identifier_start(ch)) return read_word();
- parse_error("Unexpected character '" + ch + "'");
- };
-
- next_token.context = function(nc) {
- if (nc) S = nc;
- return S;
- };
-
- return next_token;
-
+var func = function TEST () {
+
};
console.time("parse");