code generator finally seems to work properly

This commit is contained in:
Mihai Bazon
2012-08-17 15:59:42 +03:00
parent c7c163b82e
commit 13f7b119bb
4 changed files with 375 additions and 143 deletions

View File

@@ -9,7 +9,7 @@ function DEFNODE(type, props, methods, base) {
code += "this." + props[i] + " = props." + props[i] + ";"; code += "this." + props[i] + " = props." + props[i] + ";";
} }
if (methods && methods.initialize) if (methods && methods.initialize)
code += "this.initialize();" code += "this.initialize();";
code += " } }"; code += " } }";
var ctor = new Function(code)(); var ctor = new Function(code)();
if (base) { if (base) {
@@ -36,7 +36,7 @@ var AST_Token = DEFNODE("Token", "type value line col pos endpos nlb comments_be
var AST_Node = DEFNODE("Node", "start end", { var AST_Node = DEFNODE("Node", "start end", {
clone: function() { clone: function() {
return new this.CTOR(this); return new this.CTOR(this);
}, }
}, null); }, null);
var AST_Directive = DEFNODE("Directive", "value", { var AST_Directive = DEFNODE("Directive", "value", {
@@ -47,16 +47,6 @@ var AST_Debugger = DEFNODE("Debugger", null, {
}); });
var AST_Parenthesized = DEFNODE("Parenthesized", "expression", {
$documentation: "Represents an expression which is always parenthesized. Used for the \
conditions in IF/WHILE/DO and expression in SWITCH/WITH.",
});
var AST_Bracketed = DEFNODE("Bracketed", "body", {
$documentation: "Represents a block of statements that are always included in brackets. \
Used for bodies of FUNCTION/TRY/CATCH/THROW/SWITCH.",
});
/* -----[ loops ]----- */ /* -----[ loops ]----- */
var AST_LabeledStatement = DEFNODE("LabeledStatement", "label statement", { var AST_LabeledStatement = DEFNODE("LabeledStatement", "label statement", {
@@ -78,11 +68,14 @@ var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
}, AST_Statement); }, AST_Statement);
var AST_Do = DEFNODE("Do", "condition", { var AST_DWLoop = DEFNODE("DWLoop", "condition", {
}, AST_Statement); }, AST_Statement);
var AST_While = DEFNODE("While", "condition", { var AST_Do = DEFNODE("Do", null, {
}, AST_Statement); }, AST_DWLoop);
var AST_While = DEFNODE("While", null, {
}, AST_DWLoop);
var AST_For = DEFNODE("For", "init condition step", { var AST_For = DEFNODE("For", "init condition step", {
}, AST_Statement); }, AST_Statement);
@@ -152,7 +145,7 @@ var AST_Switch = DEFNODE("Switch", "expression", {
}, AST_Statement); }, AST_Statement);
var AST_SwitchBlock = DEFNODE("SwitchBlock", null, { var AST_SwitchBlock = DEFNODE("SwitchBlock", null, {
}, AST_Bracketed); }, AST_BlockStatement);
var AST_SwitchBranch = DEFNODE("SwitchBranch", "body", { var AST_SwitchBranch = DEFNODE("SwitchBranch", "body", {
}); });
@@ -177,7 +170,7 @@ var AST_Finally = DEFNODE("Finally", "body", {
/* -----[ VAR/CONST ]----- */ /* -----[ VAR/CONST ]----- */
var AST_Definitions = DEFNODE("Definitions", "definitions inline", { var AST_Definitions = DEFNODE("Definitions", "definitions", {
}); });
var AST_Var = DEFNODE("Var", null, { var AST_Var = DEFNODE("Var", null, {

View File

@@ -19,13 +19,15 @@
/// ///
var filename = process.argv[2]; var filename = process.argv[2];
console.time("parse"); //console.time("parse");
var ast = parse(fs.readFileSync(filename, "utf8")); var ast = parse(fs.readFileSync(filename, "utf8"));
console.timeEnd("parse"); //console.timeEnd("parse");
//console.time("generate");
var stream = OutputStream({ beautify: true }); var stream = OutputStream({ beautify: true });
ast.print(stream); ast.print(stream);
console.log(stream.get()); //console.timeEnd("generate");
sys.puts(stream.get());
// console.time("walk"); // console.time("walk");
// var w = new TreeWalker(function(node){ // var w = new TreeWalker(function(node){

View File

@@ -8,7 +8,8 @@ function OutputStream(options) {
ascii_only : false, ascii_only : false,
inline_script : false, inline_script : false,
width : 80, width : 80,
beautify : true beautify : true,
scope_style : "negate"
}); });
function noop() {}; function noop() {};
@@ -63,11 +64,8 @@ function OutputStream(options) {
return name; return name;
}; };
function make_indent(line) { function make_indent(back) {
if (line == null) return repeat_string(" ", options.indent_start + indentation - back * options.indent_level);
line = "";
line = repeat_string(" ", options.indent_start + indentation) + line;
return line;
}; };
function last_char() { function last_char() {
@@ -77,11 +75,14 @@ function OutputStream(options) {
/* -----[ beautification/minification ]----- */ /* -----[ beautification/minification ]----- */
var might_need_space = false; var might_need_space = false;
var might_need_semicolon = false;
var last = null;
function print(str) { function print(str) {
last = str;
str = String(str); str = String(str);
var ch = str.charAt(0);
if (might_need_space) { if (might_need_space) {
var ch = str.charAt(0);
if ((is_identifier_char(last_char()) if ((is_identifier_char(last_char())
&& (is_identifier_char(ch) || ch == "\\")) && (is_identifier_char(ch) || ch == "\\"))
|| ||
@@ -91,8 +92,16 @@ function OutputStream(options) {
current_col++; current_col++;
current_pos++; current_pos++;
} }
might_need_space = false;
}
if (might_need_semicolon) {
if (";{}".indexOf(ch) < 0 && !/[;]$/.test(OUTPUT)) {
OUTPUT += ";";
current_col++;
current_pos++;
}
might_need_semicolon = false;
} }
might_need_space = false;
var a = str.split(/\r?\n/), n = a.length; var a = str.split(/\r?\n/), n = a.length;
current_line += n; current_line += n;
if (n == 1) { if (n == 1) {
@@ -110,9 +119,9 @@ function OutputStream(options) {
might_need_space = true; might_need_space = true;
}; };
var indent = options.beautify ? function() { var indent = options.beautify ? function(half) {
if (options.beautify) { if (options.beautify) {
print(make_indent()); print(make_indent(half ? 0.5 : 0));
} }
} : noop; } : noop;
@@ -129,6 +138,12 @@ function OutputStream(options) {
print("\n"); print("\n");
} : noop; } : noop;
var semicolon = options.beautify ? function() {
print(";");
} : function() {
might_need_semicolon = true;
};
function next_indent() { function next_indent() {
return indentation + options.indent_level; return indentation + options.indent_level;
}; };
@@ -156,15 +171,12 @@ function OutputStream(options) {
function with_square(cont) { function with_square(cont) {
print("["); print("[");
var ret = with_indent(current_col, cont); //var ret = with_indent(current_col, cont);
var ret = cont();
print("]"); print("]");
return ret; return ret;
}; };
function semicolon() {
print(";");
};
function comma() { function comma() {
print(","); print(",");
space(); space();
@@ -184,6 +196,7 @@ function OutputStream(options) {
space : space, space : space,
comma : comma, comma : comma,
colon : colon, colon : colon,
last : function() { return last },
semicolon : semicolon, semicolon : semicolon,
print_name : function(name) { print(make_name(name)) }, print_name : function(name) { print(make_name(name)) },
print_string : function(str) { print(encode_string(str)) }, print_string : function(str) { print(encode_string(str)) },
@@ -191,57 +204,170 @@ function OutputStream(options) {
with_block : with_block, with_block : with_block,
with_parens : with_parens, with_parens : with_parens,
with_square : with_square, with_square : with_square,
options : function() { return options }, options : function(opt) { return options[opt] },
line : function() { return current_line }, line : function() { return current_line },
col : function() { return current_col }, col : function() { return current_col },
pos : function() { return current_pos }, pos : function() { return current_pos },
push_node : function(node) { stack.push(node) }, push_node : function(node) { stack.push(node) },
pop_node : function() { return stack.pop() }, pop_node : function() { return stack.pop() },
stack : function() { return stack }, stack : function() { return stack },
parent : function() { return stack[stack.length - 2] } parent : function(n) {
return stack[stack.length - 2 - (n || 0)];
}
}; };
}; };
/* -----[ code generators ]----- */ /* -----[ code generators ]----- */
(function(DEFPRINT){ (function(){
/* -----[ utils ]----- */
function DEFPRINT(nodetype, generator) {
nodetype.DEFMETHOD("print", function(stream){
var self = this;
stream.push_node(self);
if (self.needs_parens(stream)) {
stream.with_parens(function(){
generator(self, stream);
});
} else {
generator(self, stream);
}
stream.pop_node();
});
};
function PARENS(nodetype, func) {
nodetype.DEFMETHOD("needs_parens", func);
};
/* -----[ PARENTHESES ]----- */
PARENS(AST_Node, function(){
return false;
});
// a function expression needs parens around it when it's provably
// the first token to appear in a statement.
PARENS(AST_Lambda, function(output){
return first_in_statement(output);
});
// same goes for an object literal, because otherwise it would be
// interpreted as a block of code.
PARENS(AST_Object, function(output){
return first_in_statement(output);
});
// Defun inherits from Lambda, but we don't want parens here.
PARENS(AST_Defun, function(){
return false;
});
PARENS(AST_Seq, function(output){
var p = output.parent();
return p instanceof AST_Call
|| p instanceof AST_Binary
|| p instanceof AST_VarDef
|| p instanceof AST_Dot
|| p instanceof AST_Array
|| p instanceof AST_ObjectProperty
|| p instanceof AST_Conditional;
});
PARENS(AST_Binary, function(output){
var p = output.parent();
if (p instanceof AST_Call && p.expression === this)
return true;
if (p instanceof AST_Unary)
return true;
if (p instanceof AST_PropAccess && p.expression === this)
return true;
if (p instanceof AST_Binary) {
var po = p.operator, pp = PRECEDENCE[po];
var so = this.operator, sp = PRECEDENCE[so];
if (pp > sp
|| (pp == sp
&& this === p.right
&& !(so == po &&
(so == "*" ||
so == "&&" ||
so == "||")))) {
return true;
}
}
if (this.operator == "in") {
// the “NoIn” stuff :-\
// UglifyJS 1.3.3 misses this one.
if ((p instanceof AST_For || p instanceof AST_ForIn) && p.init === this)
return true;
if (p instanceof AST_VarDef) {
var v = output.parent(1), p2 = output.parent(2);
if ((p2 instanceof AST_For || p2 instanceof AST_ForIn) && p2.init === v)
return true;
}
}
});
PARENS(AST_New, function(output){
var p = output.parent();
if (p instanceof AST_Dot && no_constructor_parens(this, output))
return true;
});
function assign_and_conditional_paren_rules(output) {
var p = output.parent();
if (p instanceof AST_Unary)
return true;
if (p instanceof AST_Binary && !(p instanceof AST_Assign))
return true;
if (p instanceof AST_Call && p.expression === this)
return true;
if (p instanceof AST_Conditional && p.condition === this)
return true;
if (p instanceof AST_PropAccess && p.expression === this)
return true;
};
PARENS(AST_Conditional, assign_and_conditional_paren_rules);
PARENS(AST_Assign, assign_and_conditional_paren_rules);
/* -----[ PRINTERS ]----- */
DEFPRINT(AST_Directive, function(self, output){ DEFPRINT(AST_Directive, function(self, output){
output.print_string(self.value); output.print_string(self.value);
}); });
DEFPRINT(AST_Debugger, function(self, output){ DEFPRINT(AST_Debugger, function(self, output){
output.print_string("debugger"); output.print("debugger");
output.semicolon();
}); });
DEFPRINT(AST_Parenthesized, function(self, output){
output.with_parens(function(){ /* -----[ statements ]----- */
self.expression.print(output);
}); function display_body(body, is_toplevel, output) {
}); body.forEach(function(stmt){
DEFPRINT(AST_Bracketed, function(self, output){ if (!(stmt instanceof AST_EmptyStatement)) {
if (self.body.length > 0) output.with_block(function(){
self.body.forEach(function(stmt){
output.indent(); output.indent();
stmt.print(output); stmt.print(output);
output.newline(); output.newline();
}); if (is_toplevel) output.newline();
}
}); });
else output.print("{}"); };
});
/* -----[ statements ]----- */
DEFPRINT(AST_Statement, function(self, output){ DEFPRINT(AST_Statement, function(self, output){
if (self.body instanceof AST_Node) { if (self.body instanceof AST_Node) {
self.body.print(output); self.body.print(output);
output.semicolon(); output.semicolon();
} else { } else {
self.body.forEach(function(stmt){ display_body(self.body, self instanceof AST_Toplevel, output);
stmt.print(output);
output.newline();
});
} }
}); });
DEFPRINT(AST_LabeledStatement, function(self, output){ DEFPRINT(AST_LabeledStatement, function(self, output){
output.print(self.label + ":"); self.label.print(output);
output.space(); output.colon();
self.statement.print(output); self.statement.print(output);
}); });
DEFPRINT(AST_SimpleStatement, function(self, output){ DEFPRINT(AST_SimpleStatement, function(self, output){
@@ -249,7 +375,10 @@ function OutputStream(options) {
output.semicolon(); output.semicolon();
}); });
DEFPRINT(AST_BlockStatement, function(self, output){ DEFPRINT(AST_BlockStatement, function(self, output){
AST_Bracketed.prototype.print.call(self, output); if (self.body.length > 0) output.with_block(function(){
display_body(self.body, false, output);
});
else output.print("{}");
}); });
DEFPRINT(AST_EmptyStatement, function(self, output){ DEFPRINT(AST_EmptyStatement, function(self, output){
output.semicolon(); output.semicolon();
@@ -260,8 +389,20 @@ function OutputStream(options) {
self.body.print(output); self.body.print(output);
output.space(); output.space();
output.print("while"); output.print("while");
self.condition.print(output); output.space();
self.semicolon(); output.with_parens(function(){
self.condition.print(output);
});
output.semicolon();
});
DEFPRINT(AST_While, function(self, output){
output.print("while");
output.space();
output.with_parens(function(){
self.condition.print(output);
});
output.space();
self.body.print(output);
}); });
DEFPRINT(AST_For, function(self, output){ DEFPRINT(AST_For, function(self, output){
output.print("for"); output.print("for");
@@ -269,17 +410,17 @@ function OutputStream(options) {
output.with_parens(function(){ output.with_parens(function(){
if (self.init) { if (self.init) {
self.init.print(output); self.init.print(output);
output.semicolon(); output.print(";");
output.space(); output.space();
} else { } else {
output.semicolon(); output.print(";");
} }
if (self.condition) { if (self.condition) {
self.condition.print(output); self.condition.print(output);
output.semicolon(); output.print(";");
output.space(); output.space();
} else { } else {
output.semicolon(); output.print(";");
} }
if (self.step) { if (self.step) {
self.step.print(output); self.step.print(output);
@@ -312,11 +453,15 @@ function OutputStream(options) {
output.space(); output.space();
self.body.print(output); self.body.print(output);
}); });
/* -----[ functions ]----- */ /* -----[ functions ]----- */
DEFPRINT(AST_Lambda, function(self, output){ AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword){
output.print("function"); var self = this;
output.space(); if (!nokeyword) {
output.print("function");
}
if (self.name) { if (self.name) {
output.space();
self.name.print(output); self.name.print(output);
} }
output.with_parens(function(){ output.with_parens(function(){
@@ -328,11 +473,17 @@ function OutputStream(options) {
output.space(); output.space();
self.body.print(output); self.body.print(output);
}); });
DEFPRINT(AST_Lambda, function(self, output){
self._do_print(output);
});
/* -----[ exits ]----- */ /* -----[ exits ]----- */
AST_Exit.DEFMETHOD("_do_print", function(output, kind){ AST_Exit.DEFMETHOD("_do_print", function(output, kind){
output.print(kind); output.print(kind);
output.space(); if (this.value) {
this.value.print(output); output.space();
this.value.print(output);
}
output.semicolon(); output.semicolon();
}); });
DEFPRINT(AST_Return, function(self, output){ DEFPRINT(AST_Return, function(self, output){
@@ -341,6 +492,7 @@ function OutputStream(options) {
DEFPRINT(AST_Throw, function(self, output){ DEFPRINT(AST_Throw, function(self, output){
self._do_print(output, "throw"); self._do_print(output, "throw");
}); });
/* -----[ loop control ]----- */ /* -----[ loop control ]----- */
AST_LoopControl.DEFMETHOD("_do_print", function(output, kind){ AST_LoopControl.DEFMETHOD("_do_print", function(output, kind){
output.print(kind); output.print(kind);
@@ -356,6 +508,7 @@ function OutputStream(options) {
DEFPRINT(AST_Continue, function(self, output){ DEFPRINT(AST_Continue, function(self, output){
self._do_print(output, "continue"); self._do_print(output, "continue");
}); });
/* -----[ if ]----- */ /* -----[ if ]----- */
DEFPRINT(AST_If, function(self, output){ DEFPRINT(AST_If, function(self, output){
output.print("if"); output.print("if");
@@ -366,10 +519,13 @@ function OutputStream(options) {
output.space(); output.space();
self.consequent.print(output); self.consequent.print(output);
if (self.alternative) { if (self.alternative) {
output.space();
output.print("else");
output.space(); output.space();
self.alternative.print(output); self.alternative.print(output);
} }
}); });
/* -----[ switch ]----- */ /* -----[ switch ]----- */
DEFPRINT(AST_Switch, function(self, output){ DEFPRINT(AST_Switch, function(self, output){
output.print("switch"); output.print("switch");
@@ -380,16 +536,28 @@ function OutputStream(options) {
output.space(); output.space();
self.body.print(output); self.body.print(output);
}); });
AST_SwitchBranch.DEFMETHOD("_do_print_body", function(output){ DEFPRINT(AST_SwitchBlock, function(self, output){
this.body.forEach(function(stmt){ if (self.body.length > 0) output.with_block(function(){
output.indent(); self.body.forEach(function(stmt, i){
stmt.print(output); if (i) output.newline();
output.newline(); output.indent(true);
stmt.print(output);
});
}); });
else output.print("{}");
});
AST_SwitchBranch.DEFMETHOD("_do_print_body", function(output){
if (this.body.length > 0) {
output.newline();
this.body.forEach(function(stmt){
output.indent();
stmt.print(output);
output.newline();
});
}
}); });
DEFPRINT(AST_Default, function(self, output){ DEFPRINT(AST_Default, function(self, output){
output.print("default:"); output.print("default:");
output.newline();
self._do_print_body(output); self._do_print_body(output);
}); });
DEFPRINT(AST_Case, function(self, output){ DEFPRINT(AST_Case, function(self, output){
@@ -397,9 +565,9 @@ function OutputStream(options) {
output.space(); output.space();
self.expression.print(output); self.expression.print(output);
output.print(":"); output.print(":");
output.newline();
self._do_print_body(output); self._do_print_body(output);
}); });
/* -----[ exceptions ]----- */ /* -----[ exceptions ]----- */
DEFPRINT(AST_Try, function(self, output){ DEFPRINT(AST_Try, function(self, output){
output.print("try"); output.print("try");
@@ -417,6 +585,10 @@ function OutputStream(options) {
DEFPRINT(AST_Catch, function(self, output){ DEFPRINT(AST_Catch, function(self, output){
output.print("catch"); output.print("catch");
output.space(); output.space();
output.with_parens(function(){
self.argname.print(output);
});
output.space();
self.body.print(output); self.body.print(output);
}); });
DEFPRINT(AST_Finally, function(self, output){ DEFPRINT(AST_Finally, function(self, output){
@@ -424,6 +596,7 @@ function OutputStream(options) {
output.space(); output.space();
self.body.print(output); self.body.print(output);
}); });
/* -----[ var/const ]----- */ /* -----[ var/const ]----- */
AST_Definitions.DEFMETHOD("_do_print", function(output, kind){ AST_Definitions.DEFMETHOD("_do_print", function(output, kind){
output.print(kind); output.print(kind);
@@ -432,7 +605,11 @@ function OutputStream(options) {
if (i) output.comma(); if (i) output.comma();
def.print(output); def.print(output);
}); });
if (!this.inline) output.semicolon(); var p = output.parent();
var in_for = p instanceof AST_For || p instanceof AST_ForIn;
var avoid_semicolon = in_for && p.init === this;
if (!avoid_semicolon)
output.semicolon();
}); });
DEFPRINT(AST_Var, function(self, output){ DEFPRINT(AST_Var, function(self, output){
self._do_print(output, "var"); self._do_print(output, "var");
@@ -449,16 +626,24 @@ function OutputStream(options) {
self.value.print(output); self.value.print(output);
} }
}); });
/* -----[ other expressions ]----- */ /* -----[ other expressions ]----- */
DEFPRINT(AST_Call, function(self, output){ DEFPRINT(AST_Call, function(self, output){
self.expression.print(output); self.expression.print(output);
if (self instanceof AST_New && no_constructor_parens(self, output))
return;
output.with_parens(function(){ output.with_parens(function(){
self.args.forEach(function(arg, i){ self.args.forEach(function(expr, i){
if (i) output.comma(); if (i) output.comma();
arg.print(output); expr.print(output);
}); });
}); });
}); });
function no_constructor_parens(self, output) {
return (self.args.length == 0
// && !output.options("beautify")
);
};
DEFPRINT(AST_New, function(self, output){ DEFPRINT(AST_New, function(self, output){
output.print("new"); output.print("new");
output.space(); output.space();
@@ -470,7 +655,12 @@ function OutputStream(options) {
self.second.print(output); self.second.print(output);
}); });
DEFPRINT(AST_Dot, function(self, output){ DEFPRINT(AST_Dot, function(self, output){
self.expression.print(output); var expr = self.expression;
expr.print(output);
if (expr instanceof AST_Number) {
if (!/[xa-f.]/i.test(output.last()))
output.print(".");
}
output.print("."); output.print(".");
output.print_name(self.property); output.print_name(self.property);
}); });
@@ -488,43 +678,12 @@ function OutputStream(options) {
self.expression.print(output); self.expression.print(output);
output.print(self.operator); output.print(self.operator);
}); });
AST_Binary.DEFMETHOD("_do_print", function(output){
this.left.print(output);
output.space();
output.print(this.operator);
output.space();
this.right.print(output);
});
DEFPRINT(AST_Binary, function(self, output){ DEFPRINT(AST_Binary, function(self, output){
var p = output.parent(); self.left.print(output);
if (p instanceof AST_Binary) { output.space();
var po = p.operator, pp = PRECEDENCE[po]; output.print(self.operator);
var so = self.operator, sp = PRECEDENCE[so]; output.space();
if (pp > sp self.right.print(output);
|| (pp == sp
&& self === p.right
&& !(so == po &&
(so == "*" ||
so == "&&" ||
so == "||")))) {
output.with_parens(function(){
self._do_print(output);
});
return;
}
}
self._do_print(output);
});
// XXX: this is quite similar as for AST_Binary, except for the parens.
DEFPRINT(AST_Assign, function(self, output){
var p = output.parent();
if (p instanceof AST_Binary) {
output.with_parens(function(){
self._do_print(output);
});
return;
}
self._do_print(output);
}); });
DEFPRINT(AST_Conditional, function(self, output){ DEFPRINT(AST_Conditional, function(self, output){
self.condition.print(output); self.condition.print(output);
@@ -532,9 +691,11 @@ function OutputStream(options) {
output.print("?"); output.print("?");
output.space(); output.space();
self.consequent.print(output); self.consequent.print(output);
output.space();
output.colon(); output.colon();
self.alternative.print(output); self.alternative.print(output);
}); });
/* -----[ literals ]----- */ /* -----[ literals ]----- */
DEFPRINT(AST_Array, function(self, output){ DEFPRINT(AST_Array, function(self, output){
output.with_square(function(){ output.with_square(function(){
@@ -547,24 +708,43 @@ function OutputStream(options) {
DEFPRINT(AST_Object, function(self, output){ DEFPRINT(AST_Object, function(self, output){
if (self.properties.length > 0) output.with_block(function(){ if (self.properties.length > 0) output.with_block(function(){
self.properties.forEach(function(prop, i){ self.properties.forEach(function(prop, i){
if (i) output.comma(); if (i) {
output.comma();
output.newline();
}
output.indent(); output.indent();
prop.print(output); prop.print(output);
output.newline();
}); });
output.newline();
}); });
else output.print("{}"); else output.print("{}");
}); });
DEFPRINT(AST_ObjectKeyVal, function(self, output){ DEFPRINT(AST_ObjectKeyVal, function(self, output){
output.print_name(self.key); var key = self.key;
if (output.options("quote_keys")) {
output.print_string(key);
} else if ((typeof key == "number"
|| !output.options("beautify")
&& +key + "" == key)
&& parseFloat(key) >= 0) {
output.print(make_num(key));
} else if (!is_identifier(key)) {
output.print_string(key);
} else {
output.print_name(key);
}
output.colon(); output.colon();
self.value.print(output); self.value.print(output);
}); });
DEFPRINT(AST_ObjectSetter, function(self, output){ DEFPRINT(AST_ObjectSetter, function(self, output){
throw "not yet done"; output.print("set");
output.space();
self.func._do_print(output, true);
}); });
DEFPRINT(AST_ObjectGetter, function(self, output){ DEFPRINT(AST_ObjectGetter, function(self, output){
throw "not yet done"; output.print("get");
output.space();
self.func._do_print(output, true);
}); });
DEFPRINT(AST_Symbol, function(self, output){ DEFPRINT(AST_Symbol, function(self, output){
output.print_name(self.name); output.print_name(self.name);
@@ -581,16 +761,69 @@ function OutputStream(options) {
DEFPRINT(AST_String, function(self, output){ DEFPRINT(AST_String, function(self, output){
output.print_string(self.getValue()); output.print_string(self.getValue());
}); });
DEFPRINT(AST_Number, function(self, output){
output.print(make_num(self.getValue()));
});
DEFPRINT(AST_RegExp, function(self, output){ DEFPRINT(AST_RegExp, function(self, output){
output.print("/"); output.print("/");
output.print(self.pattern); output.print(self.pattern);
output.print("/"); output.print("/");
if (self.mods) output.print(self.mods); if (self.mods) output.print(self.mods);
}); });
})(function DEF(nodetype, generator) {
nodetype.DEFMETHOD("print", function(stream){ // return true if the node at the top of the stack (that means the
stream.push_node(this); // innermost node in the current output) is lexically the first in
generator(this, stream); // a statement.
stream.pop_node(); function first_in_statement(output) {
}); var a = output.stack(), i = a.length, node = a[--i], p = a[--i];
}); while (i > 0) {
if (p instanceof AST_Statement) return true;
if ((p instanceof AST_Seq && p.first === node) ||
(p instanceof AST_Call && p.expression === node) ||
(p instanceof AST_Dot && p.expression === node) ||
(p instanceof AST_Sub && p.expression === node) ||
(p instanceof AST_Conditional && p.condition === node) ||
(p instanceof AST_Binary && p.first === node) ||
(p instanceof AST_Assign && p.first === node) ||
(p instanceof AST_UnaryPostfix && p.expression === node))
{
node = p;
p = a[--i];
} else {
return false;
}
}
};
function best_of(a) {
var best = a[0], len = best.length;
for (var i = 1; i < a.length; ++i) {
if (a[i].length < len) {
best = a[i];
len = best.length;
}
}
return best;
};
function make_num(num) {
var str = num.toString(10), a = [ str.replace(/^0\./, ".").replace('e+', 'e') ], m;
if (Math.floor(num) === num) {
if (num >= 0) {
a.push("0x" + num.toString(16).toLowerCase(), // probably pointless
"0" + num.toString(8)); // same.
} else {
a.push("-0x" + (-num).toString(16).toLowerCase(), // probably pointless
"-0" + (-num).toString(8)); // same.
}
if ((m = /^(.*?)(0+)$/.exec(num))) {
a.push(m[1] + "e" + m[2].length);
}
} else if ((m = /^0?\.(0+)(.*)$/.exec(num))) {
a.push(m[2] + "e-" + (m[1].length + m[2].length),
str.substr(str.indexOf(".")));
}
return best_of(a);
};
})();

View File

@@ -166,6 +166,14 @@ function is_unicode_connector_punctuation(ch) {
return UNICODE.connector_punctuation.test(ch); return UNICODE.connector_punctuation.test(ch);
}; };
function is_identifier(name) {
return /^[a-z_$][a-z0-9_$]*$/i.test(name)
&& name != "this"
&& !HOP(KEYWORDS_ATOM, name)
&& !HOP(RESERVED_WORDS, name)
&& !HOP(KEYWORDS, name);
};
function is_identifier_start(ch) { function is_identifier_start(ch) {
return ch == "$" || ch == "_" || is_letter(ch); return ch == "$" || ch == "_" || is_letter(ch);
}; };
@@ -695,11 +703,10 @@ function parse($TEXT, exigent_mode) {
}; };
function parenthesised() { function parenthesised() {
return new AST_Parenthesized({ expect("(");
start : expect("("), var exp = expression();
expression : expression(), expect(")");
end : expect(")") return exp;
});
}; };
function embed_tokens(parser) { function embed_tokens(parser) {
@@ -834,7 +841,7 @@ function parse($TEXT, exigent_mode) {
S.labels.push(label); S.labels.push(label);
var start = S.token, stat = statement(); var start = S.token, stat = statement();
S.labels.pop(); S.labels.pop();
return new AST_LabeledStatement({ statement: stat }); return new AST_LabeledStatement({ statement: stat, label: label });
}; };
function simple_statement() { function simple_statement() {
@@ -866,9 +873,6 @@ function parse($TEXT, exigent_mode) {
init = is("keyword", "var") init = is("keyword", "var")
? (next(), var_(true)) ? (next(), var_(true))
: expression(true, true); : expression(true, true);
if (init instanceof AST_Var) {
init.inline = true;
}
if (is("operator", "in")) { if (is("operator", "in")) {
if (init instanceof AST_Var && init.definitions.length > 1) if (init instanceof AST_Var && init.definitions.length > 1)
croak("Only one variable declaration allowed in for..in loop"); croak("Only one variable declaration allowed in for..in loop");
@@ -932,7 +936,7 @@ function parse($TEXT, exigent_mode) {
--S.in_function; --S.in_function;
S.in_loop = loop; S.in_loop = loop;
S.labels = labels; S.labels = labels;
return new AST_Bracketed({ body: a }); return new AST_BlockStatement({ body: a });
})() })()
}); });
}; };
@@ -997,7 +1001,7 @@ function parse($TEXT, exigent_mode) {
})); }));
function try_() { function try_() {
var body = new AST_Bracketed({ var body = new AST_BlockStatement({
start : S.token, start : S.token,
body : block_(), body : block_(),
end : prev() end : prev()
@@ -1011,7 +1015,7 @@ function parse($TEXT, exigent_mode) {
bcatch = new AST_Catch({ bcatch = new AST_Catch({
start : start, start : start,
argname : name, argname : name,
body : new AST_Bracketed({ body : new AST_BlockStatement({
start : S.token, start : S.token,
body : block_(), body : block_(),
end : prev() end : prev()
@@ -1024,7 +1028,7 @@ function parse($TEXT, exigent_mode) {
next(); next();
bfinally = new AST_Finally({ bfinally = new AST_Finally({
start : start, start : start,
body : new AST_Bracketed({ body : new AST_BlockStatement({
start : S.token, start : S.token,
body : block_(), body : block_(),
end : prev() end : prev()
@@ -1191,7 +1195,7 @@ function parse($TEXT, exigent_mode) {
var type = start.type; var type = start.type;
var name = as_property_name(); var name = as_property_name();
if (type == "name" && !is("punc", ":")) { if (type == "name" && !is("punc", ":")) {
if (name.name == "get") { if (name == "get") {
a.push(new AST_ObjectGetter({ a.push(new AST_ObjectGetter({
start : start, start : start,
name : name, name : name,
@@ -1200,7 +1204,7 @@ function parse($TEXT, exigent_mode) {
})); }));
continue; continue;
} }
if (name.name == "set") { if (name == "set") {
a.push(new AST_ObjectSetter({ a.push(new AST_ObjectSetter({
start : start, start : start,
name : name, name : name,