WIP
This commit is contained in:
137
lib/ast.js
137
lib/ast.js
@@ -8,6 +8,8 @@ function DEFNODE(type, props, methods, base) {
|
|||||||
for (var i = props.length; --i >= 0;) {
|
for (var i = props.length; --i >= 0;) {
|
||||||
code += "this." + props[i] + " = props." + props[i] + ";";
|
code += "this." + props[i] + " = props." + props[i] + ";";
|
||||||
}
|
}
|
||||||
|
if (methods && methods.initialize)
|
||||||
|
code += "this.initialize();"
|
||||||
code += " } }";
|
code += " } }";
|
||||||
var ctor = new Function(code)();
|
var ctor = new Function(code)();
|
||||||
if (base) {
|
if (base) {
|
||||||
@@ -29,11 +31,15 @@ var AST_Token = DEFNODE("Token", "type value line col pos endpos nlb comments_be
|
|||||||
}, null);
|
}, null);
|
||||||
|
|
||||||
var AST_Node = DEFNODE("Node", "start end", {
|
var AST_Node = DEFNODE("Node", "start end", {
|
||||||
renew: function(args) {
|
clone: function() {
|
||||||
var ctor = this.CTOR, props = ctor.props;
|
return new this.CTOR(this);
|
||||||
for (var i in props) if (!HOP(args, i)) args[i] = this[i];
|
|
||||||
return new ctor(args);
|
|
||||||
},
|
},
|
||||||
|
// 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) {
|
walk: function(w) {
|
||||||
w._visit(this);
|
w._visit(this);
|
||||||
}
|
}
|
||||||
@@ -75,41 +81,50 @@ Used for bodies of FUNCTION/TRY/CATCH/THROW/SWITCH.",
|
|||||||
|
|
||||||
/* -----[ loops ]----- */
|
/* -----[ loops ]----- */
|
||||||
|
|
||||||
var AST_LabeledStatement = DEFNODE("LabeledStatement", "label body", {
|
var AST_Statement = DEFNODE("Statement", "label body", {
|
||||||
walk: function(w) {
|
walk: function(w) {
|
||||||
w._visit(this, function(){
|
w._visit(this, function(){
|
||||||
if (this.label) this.label.walk(w);
|
if (this.label) this.label.walk(w);
|
||||||
if (this.body) {
|
if (this.body) {
|
||||||
if (this.body instanceof Array)
|
if (this.body instanceof AST_Node)
|
||||||
AST_Bracketed.prototype.walk.call(this, w);
|
|
||||||
else
|
|
||||||
this.body.walk(w);
|
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_LabeledStatement);
|
}, AST_Statement);
|
||||||
|
|
||||||
|
var AST_BlockStatement = DEFNODE("BlockStatement", null, {
|
||||||
|
|
||||||
|
}, AST_Statement);
|
||||||
|
|
||||||
|
var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
|
||||||
|
|
||||||
|
}, AST_Statement);
|
||||||
|
|
||||||
var AST_Do = DEFNODE("Do", "condition", {
|
var AST_Do = DEFNODE("Do", "condition", {
|
||||||
walk: function(w) {
|
walk: function(w) {
|
||||||
w._visit(this, function(){
|
w._visit(this, function(){
|
||||||
this.condition.walk(w);
|
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", {
|
var AST_While = DEFNODE("While", "condition", {
|
||||||
walk: function(w) {
|
walk: function(w) {
|
||||||
w._visit(this, function(){
|
w._visit(this, function(){
|
||||||
this.condition.walk(w);
|
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", {
|
var AST_For = DEFNODE("For", "init condition step", {
|
||||||
walk: function(w) {
|
walk: function(w) {
|
||||||
@@ -117,42 +132,42 @@ var AST_For = DEFNODE("For", "init condition step", {
|
|||||||
if (this.init) this.init.walk(w);
|
if (this.init) this.init.walk(w);
|
||||||
if (this.condition) this.condition.walk(w);
|
if (this.condition) this.condition.walk(w);
|
||||||
if (this.step) this.step.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", {
|
var AST_ForIn = DEFNODE("ForIn", "init name object", {
|
||||||
walk: function(w) {
|
walk: function(w) {
|
||||||
w._visit(this, function(){
|
w._visit(this, function(){
|
||||||
if (this.init) this.init.walk(w);
|
if (this.init) this.init.walk(w);
|
||||||
this.object.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) {
|
walk: function(w) {
|
||||||
w._visit(this, function(){
|
w._visit(this, function(){
|
||||||
this.expression.walk(w);
|
this.expression.walk(w);
|
||||||
AST_LabeledStatement.prototype.walk.call(this, w);
|
AST_Statement.prototype.walk.call(this, w);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}, AST_Statement);
|
||||||
|
|
||||||
/* -----[ functions ]----- */
|
/* -----[ functions ]----- */
|
||||||
|
|
||||||
var AST_Scope = DEFNODE("Scope", "identifiers body", {
|
var AST_Scope = DEFNODE("Scope", "identifiers", {
|
||||||
walk: function(w) {
|
walk: function(w) {
|
||||||
w._visit(this, function(){
|
w._visit(this, function(){
|
||||||
if (this.identifiers) this.identifiers.forEach(function(el){
|
if (this.identifiers) this.identifiers.forEach(function(el){
|
||||||
el.walk(w);
|
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, {
|
var AST_Toplevel = DEFNODE("Toplevel", null, {
|
||||||
|
|
||||||
@@ -234,25 +249,23 @@ var AST_Switch = DEFNODE("Switch", "expression", {
|
|||||||
walk: function(w) {
|
walk: function(w) {
|
||||||
w._visit(this, function(){
|
w._visit(this, function(){
|
||||||
this.expression.walk(w);
|
this.expression.walk(w);
|
||||||
AST_LabeledStatement.prototype.walk.call(this, w);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, AST_LabeledStatement);
|
|
||||||
|
|
||||||
var AST_SwitchBlock = DEFNODE("SwitchBlock", null, {
|
|
||||||
|
|
||||||
}, AST_Bracketed);
|
|
||||||
|
|
||||||
var AST_SwitchBranch = DEFNODE("SwitchBranch", "body", {
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
var AST_Default = DEFNODE("Default", null, {
|
|
||||||
walk: function(w) {
|
|
||||||
w._visit(this, function(){
|
|
||||||
AST_Statement.prototype.walk.call(this, w);
|
AST_Statement.prototype.walk.call(this, w);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}, 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, {
|
||||||
|
|
||||||
}, AST_SwitchBranch);
|
}, AST_SwitchBranch);
|
||||||
|
|
||||||
var AST_Case = DEFNODE("Case", "expression", {
|
var AST_Case = DEFNODE("Case", "expression", {
|
||||||
@@ -482,10 +495,8 @@ var AST_Number = DEFNODE("Number", "value", {
|
|||||||
}, AST_Constant);
|
}, AST_Constant);
|
||||||
|
|
||||||
var AST_RegExp = DEFNODE("Regexp", "pattern mods", {
|
var AST_RegExp = DEFNODE("Regexp", "pattern mods", {
|
||||||
getValue: function() {
|
initialize: function() {
|
||||||
return this._regexp || (
|
this.value = new RegExp(this.pattern, this.mods);
|
||||||
this._regexp = new RegExp(this.pattern, this.mods)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}, AST_Constant);
|
}, AST_Constant);
|
||||||
|
|
||||||
@@ -494,17 +505,45 @@ var AST_Atom = DEFNODE("Atom", null, {
|
|||||||
}, AST_Constant);
|
}, AST_Constant);
|
||||||
|
|
||||||
var AST_Null = DEFNODE("Null", null, {
|
var AST_Null = DEFNODE("Null", null, {
|
||||||
getValue: function() { return null }
|
value: null
|
||||||
}, AST_Atom);
|
}, AST_Atom);
|
||||||
|
|
||||||
var AST_Undefined = DEFNODE("Undefined", null, {
|
var AST_Undefined = DEFNODE("Undefined", null, {
|
||||||
getValue: function() { return (function(){}()) }
|
value: (function(){}())
|
||||||
}, AST_Atom);
|
}, AST_Atom);
|
||||||
|
|
||||||
var AST_False = DEFNODE("False", null, {
|
var AST_False = DEFNODE("False", null, {
|
||||||
getValue: function() { return false }
|
value: false
|
||||||
}, AST_Atom);
|
}, AST_Atom);
|
||||||
|
|
||||||
var AST_True = DEFNODE("True", null, {
|
var AST_True = DEFNODE("True", null, {
|
||||||
getValue: function() { return true }
|
value: true
|
||||||
}, AST_Atom);
|
}, 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -24,12 +24,10 @@
|
|||||||
console.timeEnd("parse");
|
console.timeEnd("parse");
|
||||||
|
|
||||||
console.time("walk");
|
console.time("walk");
|
||||||
ast.walk({
|
var w = new TreeWalker(function(node){
|
||||||
_visit: function(node, descend) {
|
console.log(node.TYPE + " [ start: " + node.start.line + ":" + node.start.col + ", end: " + node.end.line + ":" + node.end.col + "]");
|
||||||
//console.log(node);
|
|
||||||
if (descend) descend.call(node);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
ast.walk(w);
|
||||||
console.timeEnd("walk");
|
console.timeEnd("walk");
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ function OutputStream(options) {
|
|||||||
indent_level : 4,
|
indent_level : 4,
|
||||||
quote_keys : false,
|
quote_keys : false,
|
||||||
space_colon : false,
|
space_colon : false,
|
||||||
beautify : true,
|
|
||||||
ascii_only : false,
|
ascii_only : false,
|
||||||
inline_script : false,
|
inline_script : false,
|
||||||
width : 80
|
width : 80
|
||||||
@@ -12,6 +11,7 @@ function OutputStream(options) {
|
|||||||
|
|
||||||
var indentation = 0;
|
var indentation = 0;
|
||||||
var current_col = 0;
|
var current_col = 0;
|
||||||
|
var current_line = 0;
|
||||||
var OUTPUT = "";
|
var OUTPUT = "";
|
||||||
|
|
||||||
function to_ascii(str) {
|
function to_ascii(str) {
|
||||||
@@ -45,12 +45,9 @@ function OutputStream(options) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function print(str) {
|
function print(str) {
|
||||||
var nl = str.lastIndexOf("\n");
|
var a = str.split(/\r?\n/), n = a.length;
|
||||||
if (nl >= 0) {
|
current_line += n;
|
||||||
current_col = nl;
|
current_col += a[n - 1].length;
|
||||||
} else {
|
|
||||||
current_col += str.length;
|
|
||||||
}
|
|
||||||
OUTPUT += str;
|
OUTPUT += str;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -71,12 +68,12 @@ function OutputStream(options) {
|
|||||||
function make_indent(line) {
|
function make_indent(line) {
|
||||||
if (line == null)
|
if (line == null)
|
||||||
line = "";
|
line = "";
|
||||||
if (beautify)
|
line = repeat_string(" ", options.indent_start + indentation) + line;
|
||||||
line = repeat_string(" ", options.indent_start + indentation) + line;
|
|
||||||
return line;
|
return line;
|
||||||
};
|
};
|
||||||
|
|
||||||
function with_indent(col, cont) {
|
function with_indent(col, cont) {
|
||||||
|
if (col === true) col = next_indent();
|
||||||
var save_indentation = indentation;
|
var save_indentation = indentation;
|
||||||
indentation = col;
|
indentation = col;
|
||||||
var ret = cont();
|
var ret = cont();
|
||||||
@@ -85,34 +82,31 @@ function OutputStream(options) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function indent() {
|
function indent() {
|
||||||
if (options.beautify) print(make_indent());
|
print(make_indent());
|
||||||
};
|
};
|
||||||
|
|
||||||
function newline() {
|
function newline() {
|
||||||
if (options.beautify) {
|
print("\n");
|
||||||
print("\n");
|
|
||||||
print(make_indent());
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function next_indent() {
|
function next_indent() {
|
||||||
return indentation + options.indent_level;
|
return indentation + options.indent_level;
|
||||||
};
|
};
|
||||||
|
|
||||||
function with_block(cont) {
|
function with_block(cont, beautify) {
|
||||||
var ret;
|
var ret;
|
||||||
print("{");
|
print("{");
|
||||||
with_indent(next_indent(), function(){
|
with_indent(next_indent(), function(){
|
||||||
newline();
|
if (beautify) newline();
|
||||||
ret = cont();
|
ret = cont();
|
||||||
newline();
|
if (beautify) newline();
|
||||||
});
|
});
|
||||||
indent();
|
if (beautify) indent();
|
||||||
print("}");
|
print("}");
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
function with_parens(cont) {
|
function with_parens(cont, beautify) {
|
||||||
print("(");
|
print("(");
|
||||||
var ret = with_indent(current_col, cont);
|
var ret = with_indent(current_col, cont);
|
||||||
print(")");
|
print(")");
|
||||||
@@ -128,7 +122,10 @@ function OutputStream(options) {
|
|||||||
with_indent : with_indent,
|
with_indent : with_indent,
|
||||||
with_block : with_block,
|
with_block : with_block,
|
||||||
with_parens : with_parens,
|
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 }
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -739,13 +739,13 @@ function parse($TEXT, exigent_mode) {
|
|||||||
case "punc":
|
case "punc":
|
||||||
switch (S.token.value) {
|
switch (S.token.value) {
|
||||||
case "{":
|
case "{":
|
||||||
return new AST_Statement({ body: block_() });
|
return new AST_BlockStatement({ body: block_() });
|
||||||
case "[":
|
case "[":
|
||||||
case "(":
|
case "(":
|
||||||
return simple_statement();
|
return simple_statement();
|
||||||
case ";":
|
case ";":
|
||||||
next();
|
next();
|
||||||
return new AST_Statement();
|
return new AST_EmptyStatement();
|
||||||
default:
|
default:
|
||||||
unexpected();
|
unexpected();
|
||||||
}
|
}
|
||||||
|
|||||||
343
lib/test.js
343
lib/test.js
@@ -1,345 +1,4 @@
|
|||||||
var func = function tokenizer($TEXT) {
|
var func = function TEST () {
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user