Improving double-parentheses suppression. Issue #587

This commit is contained in:
Jeremy Ashkenas 2010-08-14 17:25:29 -04:00
parent 129e950c59
commit 9894eeb8e9
10 changed files with 76 additions and 66 deletions

View File

@ -37,7 +37,7 @@
return path.exists('Cakefile', function(exists) { return path.exists('Cakefile', function(exists) {
var _a, _b, _c, _d, arg, args; var _a, _b, _c, _d, arg, args;
if (!(exists)) { if (!(exists)) {
throw new Error(("Cakefile not found in " + (process.cwd()))); throw new Error("Cakefile not found in " + (process.cwd()));
} }
args = process.argv.slice(2, process.argv.length); args = process.argv.slice(2, process.argv.length);
CoffeeScript.run(fs.readFileSync('Cakefile').toString(), { CoffeeScript.run(fs.readFileSync('Cakefile').toString(), {
@ -71,14 +71,14 @@
return _b; return _b;
})().join('') : ''; })().join('') : '';
desc = task.description ? ("# " + (task.description)) : ''; desc = task.description ? ("# " + (task.description)) : '';
puts(("cake " + (name) + (spaces) + " " + (desc))); puts("cake " + (name) + (spaces) + " " + (desc));
} }
if (switches.length) { if (switches.length) {
return puts(oparse.help()); return puts(oparse.help());
} }
}; };
missingTask = function(task) { missingTask = function(task) {
puts(("No such task: \"" + (task) + "\"")); puts("No such task: \"" + (task) + "\"");
return process.exit(1); return process.exit(1);
}; };
})(); })();

View File

@ -64,7 +64,7 @@
compile = function(source, topLevel) { compile = function(source, topLevel) {
return path.exists(source, function(exists) { return path.exists(source, function(exists) {
if (!(exists)) { if (!(exists)) {
throw new Error(("File not found: " + (source))); throw new Error("File not found: " + (source));
} }
return fs.stat(source, function(err, stats) { return fs.stat(source, function(err, stats) {
if (stats.isDirectory()) { if (stats.isDirectory()) {
@ -179,12 +179,12 @@
} }
return fs.writeFile(jsPath, js, function(err) { return fs.writeFile(jsPath, js, function(err) {
if (options.compile && options.watch) { if (options.compile && options.watch) {
return puts(("Compiled " + (source))); return puts("Compiled " + (source));
} }
}); });
}; };
return path.exists(dir, function(exists) { return path.exists(dir, function(exists) {
return exists ? compile() : exec(("mkdir -p " + (dir)), compile); return exists ? compile() : exec("mkdir -p " + (dir), compile);
}); });
}; };
lint = function(js) { lint = function(js) {
@ -238,7 +238,7 @@
return process.exit(0); return process.exit(0);
}; };
version = function() { version = function() {
puts(("CoffeeScript version " + (CoffeeScript.VERSION))); puts("CoffeeScript version " + (CoffeeScript.VERSION));
return process.exit(0); return process.exit(0);
}; };
})(); })();

View File

@ -539,13 +539,13 @@
Operation: [ Operation: [
o("UNARY Expression", function() { o("UNARY Expression", function() {
return new OpNode($1, $2); return new OpNode($1, $2);
}), o("- Expression", (function() { }), o("- Expression", function() {
return new OpNode('-', $2); return new OpNode('-', $2);
}), { }, {
prec: 'UNARY' prec: 'UNARY'
}), o("+ Expression", (function() { }), o("+ Expression", function() {
return new OpNode('+', $2); return new OpNode('+', $2);
}), { }, {
prec: 'UNARY' prec: 'UNARY'
}), o("-- Expression", function() { }), o("-- Expression", function() {
return new OpNode('--', $2); return new OpNode('--', $2);

View File

@ -1,6 +1,6 @@
(function() { (function() {
var compact, count, del, ends, extend, flatten, helpers, include, indexOf, merge, starts; var compact, count, del, ends, extend, flatten, helpers, include, indexOf, merge, starts;
if (!((typeof process !== "undefined" && process !== null))) { if (!(typeof process !== "undefined" && process !== null)) {
this.exports = this; this.exports = this;
} }
helpers = (exports.helpers = {}); helpers = (exports.helpers = {});
@ -72,7 +72,7 @@
_a = []; _b = properties; _a = []; _b = properties;
for (key in _b) { for (key in _b) {
val = _b[key]; val = _b[key];
_a.push((object[key] = val)); _a.push(object[key] = val);
} }
return _a; return _a;
}); });

View File

@ -216,7 +216,7 @@
return '\\' + escaped; return '\\' + escaped;
}); });
this.tokens = this.tokens.concat([['(', '('], ['NEW', 'new'], ['IDENTIFIER', 'RegExp'], ['CALL_START', '(']]); this.tokens = this.tokens.concat([['(', '('], ['NEW', 'new'], ['IDENTIFIER', 'RegExp'], ['CALL_START', '(']]);
this.interpolateString(("\"" + (str) + "\""), { this.interpolateString("\"" + (str) + "\"", {
escapeQuotes: true escapeQuotes: true
}); });
if (flags) { if (flags) {
@ -400,7 +400,7 @@
if (options.herecomment) { if (options.herecomment) {
return doc; return doc;
} }
return doc.replace(MULTILINER, "\\n").replace(new RegExp(options.quote, 'g'), ("\\" + (options.quote))); return doc.replace(MULTILINER, "\\n").replace(new RegExp(options.quote, 'g'), "\\" + (options.quote));
}; };
Lexer.prototype.tagParameters = function() { Lexer.prototype.tagParameters = function() {
var _d, i, tok; var _d, i, tok;
@ -428,10 +428,10 @@
return this.outdentToken(this.indent); return this.outdentToken(this.indent);
}; };
Lexer.prototype.identifierError = function(word) { Lexer.prototype.identifierError = function(word) {
throw new Error(("SyntaxError: Reserved word \"" + (word) + "\" on line " + (this.line + 1))); throw new Error("SyntaxError: Reserved word \"" + (word) + "\" on line " + (this.line + 1));
}; };
Lexer.prototype.assignmentError = function() { Lexer.prototype.assignmentError = function() {
throw new Error(("SyntaxError: Reserved word \"" + (this.value()) + "\" on line " + (this.line + 1) + " can't be assigned")); throw new Error("SyntaxError: Reserved word \"" + (this.value()) + "\" on line " + (this.line + 1) + " can't be assigned");
}; };
Lexer.prototype.balancedString = function(str, delimited, options) { Lexer.prototype.balancedString = function(str, delimited, options) {
var _d, _e, _f, _g, close, i, levels, open, pair, slash; var _d, _e, _f, _g, close, i, levels, open, pair, slash;
@ -472,7 +472,7 @@
if (slash) { if (slash) {
return false; return false;
} }
throw new Error(("SyntaxError: Unterminated " + (levels.pop()[0]) + " starting on line " + (this.line + 1))); throw new Error("SyntaxError: Unterminated " + (levels.pop()[0]) + " starting on line " + (this.line + 1));
} }
return !i ? false : str.substring(0, i); return !i ? false : str.substring(0, i);
}; };
@ -500,7 +500,7 @@
if (options.heredoc) { if (options.heredoc) {
inner = inner.replace(new RegExp('\\\\' + quote, 'g'), quote); inner = inner.replace(new RegExp('\\\\' + quote, 'g'), quote);
} }
nested = lexer.tokenize(("(" + (inner) + ")"), { nested = lexer.tokenize("(" + (inner) + ")", {
line: this.line line: this.line
}); });
_e = nested; _e = nested;
@ -540,7 +540,7 @@
this.tokens = this.tokens.concat(value); this.tokens = this.tokens.concat(value);
} else if (tag === 'STRING' && options.escapeQuotes) { } else if (tag === 'STRING' && options.escapeQuotes) {
escaped = value.substring(1, value.length - 1).replace(/"/g, '\\"'); escaped = value.substring(1, value.length - 1).replace(/"/g, '\\"');
this.token(tag, ("\"" + (escaped) + "\"")); this.token(tag, "\"" + (escaped) + "\"");
} else { } else {
this.token(tag, value); this.token(tag, value);
} }

View File

@ -383,6 +383,9 @@
op = del(o, 'operation'); op = del(o, 'operation');
props = only ? this.properties.slice(0, this.properties.length - 1) : this.properties; props = only ? this.properties.slice(0, this.properties.length - 1) : this.properties;
o.chainRoot = o.chainRoot || this; o.chainRoot = o.chainRoot || this;
if (this.parenthetical && !props.length) {
this.base.parenthetical = true;
}
baseline = this.base.compile(o); baseline = this.base.compile(o);
if (this.hasProperties() && (this.base instanceof ObjectNode || this.isNumber())) { if (this.hasProperties() && (this.base instanceof ObjectNode || this.isNumber())) {
baseline = ("(" + (baseline) + ")"); baseline = ("(" + (baseline) + ")");
@ -483,18 +486,21 @@
compilation = this.compileSplat(o); compilation = this.compileSplat(o);
} }
} }
if (!(compilation)) { if (!compilation) {
args = (function() { args = (function() {
_e = []; _g = this.args; _e = []; _g = this.args;
for (_f = 0, _h = _g.length; _f < _h; _f++) { for (_f = 0, _h = _g.length; _f < _h; _f++) {
arg = _g[_f]; arg = _g[_f];
_e.push(arg.compile(o)); _e.push((function() {
arg.parenthetical = true;
return arg.compile(o);
})());
} }
return _e; return _e;
}).call(this).join(', '); }).call(this);
compilation = this.isSuper ? this.compileSuper(args, o) : ("" + (this.prefix()) + (this.variable.compile(o)) + "(" + (args) + ")"); compilation = this.isSuper ? this.compileSuper(args.join(', '), o) : ("" + (this.prefix()) + (this.variable.compile(o)) + "(" + (args.join(', ')) + ")");
} }
return o.operation && this.wrapped ? ("(" + (compilation) + ")") : compilation; return compilation;
}; };
CallNode.prototype.compileSuper = function(args, o) { CallNode.prototype.compileSuper = function(args, o) {
return "" + (this.superReference(o)) + ".call(this" + (args.length ? ', ' : '') + (args) + ")"; return "" + (this.superReference(o)) + ".call(this" + (args.length ? ', ' : '') + (args) + ")";
@ -759,11 +765,11 @@
if (obj instanceof SplatNode) { if (obj instanceof SplatNode) {
return this.compileSplatLiteral(o); return this.compileSplatLiteral(o);
} else if (obj instanceof CommentNode) { } else if (obj instanceof CommentNode) {
objects.push(("\n" + (code) + "\n" + (o.indent))); objects.push("\n" + (code) + "\n" + (o.indent));
} else if (i === this.objects.length - 1) { } else if (i === this.objects.length - 1) {
objects.push(code); objects.push(code);
} else { } else {
objects.push(("" + (code) + ", ")); objects.push("" + (code) + ", ");
} }
} }
objects = objects.join(''); objects = objects.join('');
@ -832,7 +838,7 @@
if (constructor.body.empty()) { if (constructor.body.empty()) {
constructor.body.push(new ReturnNode(literal('this'))); constructor.body.push(new ReturnNode(literal('this')));
} }
constructor.body.unshift(literal(("this." + (pname) + " = function(){ return " + (className) + ".prototype." + (pname) + ".apply(" + (me) + ", arguments); }"))); constructor.body.unshift(literal("this." + (pname) + " = function(){ return " + (className) + ".prototype." + (pname) + ".apply(" + (me) + ", arguments); }"));
} }
if (pvar) { if (pvar) {
access = prop.context === 'this' ? pvar.base.properties[0] : new AccessorNode(pvar, 'prototype'); access = prop.context === 'this' ? pvar.base.properties[0] : new AccessorNode(pvar, 'prototype');
@ -842,7 +848,7 @@
props.push(prop); props.push(prop);
} }
if (me) { if (me) {
constructor.body.unshift(literal(("" + (me) + " = this"))); constructor.body.unshift(literal("" + (me) + " = this"));
} }
construct = this.idt() + (new AssignNode(this.variable, constructor)).compile(merge(o, { construct = this.idt() + (new AssignNode(this.variable, constructor)).compile(merge(o, {
sharedScope: constScope sharedScope: constScope
@ -915,7 +921,7 @@
if (stmt) { if (stmt) {
return ("" + (this.tab) + (val) + ";"); return ("" + (this.tab) + (val) + ";");
} }
return top ? val : ("(" + (val) + ")"); return top || this.parenthetical ? val : ("(" + (val) + ")");
}; };
AssignNode.prototype.compilePatternMatch = function(o) { AssignNode.prototype.compilePatternMatch = function(o) {
var _b, _c, _d, accessClass, assigns, code, i, idx, isString, obj, oindex, olength, splat, val, valVar, value; var _b, _c, _d, accessClass, assigns, code, i, idx, isString, obj, oindex, olength, splat, val, valVar, value;
@ -944,7 +950,7 @@
isString = idx.value && idx.value.match(IS_STRING); isString = idx.value && idx.value.match(IS_STRING);
accessClass = isString || this.variable.isArray() ? IndexNode : AccessorNode; accessClass = isString || this.variable.isArray() ? IndexNode : AccessorNode;
if (obj instanceof SplatNode && !splat) { if (obj instanceof SplatNode && !splat) {
val = literal(obj.compileValue(o, valVar, (oindex = indexOf(this.variable.base.objects, obj)), (olength = this.variable.base.objects.length) - oindex - 1)); val = literal(obj.compileValue(o, valVar, oindex = indexOf(this.variable.base.objects, obj), (olength = this.variable.base.objects.length) - oindex - 1));
splat = true; splat = true;
} else { } else {
if (typeof idx !== 'object') { if (typeof idx !== 'object') {
@ -1125,7 +1131,7 @@
assign.value = trailing; assign.value = trailing;
} }
pos = this.trailings.length - idx; pos = this.trailings.length - idx;
o.scope.assign(trailing.compile(o), ("arguments[" + (variadic) + " ? " + (len) + " - " + (pos) + " : " + (this.index + idx) + "]")); o.scope.assign(trailing.compile(o), "arguments[" + (variadic) + " ? " + (len) + " - " + (pos) + " : " + (this.index + idx) + "]");
} }
} }
return "" + (name) + " = " + (utility('slice')) + ".call(arguments, " + (this.index) + (end) + ")"; return "" + (name) + " = " + (utility('slice')) + ".call(arguments, " + (this.index) + (end) + ")";
@ -1354,7 +1360,7 @@
_b = []; _c = this.array.base.objects; _b = []; _c = this.array.base.objects;
for (i = 0, _d = _c.length; i < _d; i++) { for (i = 0, _d = _c.length; i < _d; i++) {
item = _c[i]; item = _c[i];
_b.push(("" + (item.compile(o)) + " === " + (i ? this.obj2 : this.obj1))); _b.push("" + (item.compile(o)) + " === " + (i ? this.obj2 : this.obj1));
} }
return _b; return _b;
}).call(this); }).call(this);
@ -1438,7 +1444,9 @@
ExistenceNode.prototype["class"] = 'ExistenceNode'; ExistenceNode.prototype["class"] = 'ExistenceNode';
ExistenceNode.prototype.children = ['expression']; ExistenceNode.prototype.children = ['expression'];
ExistenceNode.prototype.compileNode = function(o) { ExistenceNode.prototype.compileNode = function(o) {
return ExistenceNode.compileTest(o, this.expression)[0]; var test;
test = ExistenceNode.compileTest(o, this.expression)[0];
return this.parenthetical ? test.substring(1, test.length - 1) : test;
}; };
ExistenceNode.compileTest = function(o, variable) { ExistenceNode.compileTest = function(o, variable) {
var _b, first, second; var _b, first, second;
@ -1469,17 +1477,14 @@
return true; return true;
}; };
ParentheticalNode.prototype.compileNode = function(o) { ParentheticalNode.prototype.compileNode = function(o) {
var code, l, top; var code, top;
top = del(o, 'top'); top = del(o, 'top');
this.expression.parenthetical = true;
code = this.expression.compile(o); code = this.expression.compile(o);
if (this.isStatement(o)) { if (this.parenthetical || this.isStatement(o)) {
return (top ? this.tab + code + ';' : code); return top ? this.tab + code + ';' : code;
} }
l = code.length; return "(" + (code) + ")";
if (code.substr(l - 1, 1) === ';') {
code = code.substr(o, l - 1);
}
return this.expression instanceof AssignNode ? code : ("(" + (code) + ")");
}; };
return ParentheticalNode; return ParentheticalNode;
})(); })();
@ -1571,7 +1576,7 @@
svar = scope.freeVariable(); svar = scope.freeVariable();
sourcePart = ("" + (svar) + " = " + (this.source.compile(o)) + ";"); sourcePart = ("" + (svar) + " = " + (this.source.compile(o)) + ";");
if (this.pattern) { if (this.pattern) {
namePart = new AssignNode(this.name, literal(("" + (svar) + "[" + (ivar) + "]"))).compile(merge(o, { namePart = new AssignNode(this.name, literal("" + (svar) + "[" + (ivar) + "]")).compile(merge(o, {
indent: this.idt(1), indent: this.idt(1),
top: true top: true
})) + '\n'; })) + '\n';
@ -1597,13 +1602,13 @@
} }
if (codeInBody) { if (codeInBody) {
if (range) { if (range) {
body.unshift(literal(("var " + (name) + " = " + (ivar)))); body.unshift(literal("var " + (name) + " = " + (ivar)));
} }
if (namePart) { if (namePart) {
body.unshift(literal(("var " + (namePart)))); body.unshift(literal("var " + (namePart)));
} }
if (index) { if (index) {
body.unshift(literal(("var " + (index) + " = " + (ivar)))); body.unshift(literal("var " + (index) + " = " + (ivar)));
} }
body = ClosureNode.wrap(body, true); body = ClosureNode.wrap(body, true);
} else { } else {
@ -1660,7 +1665,7 @@
IfNode.prototype.rewriteSwitch = function(o) { IfNode.prototype.rewriteSwitch = function(o) {
var _b, _c, _d, cond, i, variable; var _b, _c, _d, cond, i, variable;
this.assigner = this.switchSubject; this.assigner = this.switchSubject;
if (!((this.switchSubject.unwrap() instanceof LiteralNode))) { if (!(this.switchSubject.unwrap() instanceof LiteralNode)) {
variable = literal(o.scope.freeVariable()); variable = literal(o.scope.freeVariable());
this.assigner = new AssignNode(variable, this.switchSubject); this.assigner = new AssignNode(variable, this.switchSubject);
this.switchSubject = variable; this.switchSubject = variable;
@ -1673,7 +1678,7 @@
if (cond instanceof OpNode) { if (cond instanceof OpNode) {
cond = new ParentheticalNode(cond); cond = new ParentheticalNode(cond);
} }
return new OpNode('==', (i === 0 ? this.assigner : this.switchSubject), cond); return new OpNode('==', i === 0 ? this.assigner : this.switchSubject, cond);
}).call(this)); }).call(this));
} }
return _b; return _b;

View File

@ -28,7 +28,7 @@
} }
} }
if (isOption && !matchedRule) { if (isOption && !matchedRule) {
throw new Error(("unrecognized option: " + (arg))); throw new Error("unrecognized option: " + (arg));
} }
if (!isOption) { if (!isOption) {
options.arguments = args.slice(i, args.length); options.arguments = args.slice(i, args.length);
@ -41,7 +41,7 @@
var _a, _b, _c, _d, i, letPart, lines, rule, spaces; var _a, _b, _c, _d, i, letPart, lines, rule, spaces;
lines = ['Available options:']; lines = ['Available options:'];
if (this.banner) { if (this.banner) {
lines.unshift(("" + (this.banner) + "\n")); lines.unshift("" + (this.banner) + "\n");
} }
_b = this.rules; _b = this.rules;
for (_a = 0, _c = _b.length; _a < _c; _a++) { for (_a = 0, _c = _b.length; _a < _c; _a++) {

View File

@ -275,7 +275,7 @@
levels[open] -= 1; levels[open] -= 1;
} }
if (levels[open] < 0) { if (levels[open] < 0) {
throw new Error(("too many " + (token[1]) + " on line " + (token[2] + 1))); throw new Error("too many " + (token[1]) + " on line " + (token[2] + 1));
} }
} }
return 1; return 1;
@ -294,7 +294,7 @@
if (unclosed.length) { if (unclosed.length) {
open = unclosed[0]; open = unclosed[0];
line = openLine[open] + 1; line = openLine[open] + 1;
throw new Error(("unclosed " + (open) + " on line " + (line))); throw new Error("unclosed " + (open) + " on line " + (line));
} }
}; };
Rewriter.prototype.rewriteClosingParens = function() { Rewriter.prototype.rewriteClosingParens = function() {

View File

@ -1,7 +1,7 @@
(function() { (function() {
var Scope; var Scope;
var __hasProp = Object.prototype.hasOwnProperty; var __hasProp = Object.prototype.hasOwnProperty;
if (!((typeof process !== "undefined" && process !== null))) { if (!(typeof process !== "undefined" && process !== null)) {
this.exports = this; this.exports = this;
} }
exports.Scope = (function() { exports.Scope = (function() {
@ -95,7 +95,7 @@
if (!__hasProp.call(_b, key)) continue; if (!__hasProp.call(_b, key)) continue;
val = _b[key]; val = _b[key];
if (val.assigned) { if (val.assigned) {
_a.push(("" + (key) + " = " + (val.value))); _a.push("" + (key) + " = " + (val.value));
} }
} }
return _a; return _a;

View File

@ -354,6 +354,7 @@ exports.ValueNode = class ValueNode extends BaseNode
op = del o, 'operation' op = del o, 'operation'
props = if only then @properties[0...@properties.length - 1] else @properties props = if only then @properties[0...@properties.length - 1] else @properties
o.chainRoot or= this o.chainRoot or= this
@base.parenthetical = yes if @parenthetical and not props.length
baseline = @base.compile o baseline = @base.compile o
baseline = "(#{baseline})" if @hasProperties() and (@base instanceof ObjectNode or @isNumber()) baseline = "(#{baseline})" if @hasProperties() and (@base instanceof ObjectNode or @isNumber())
complete = @last = baseline complete = @last = baseline
@ -434,11 +435,15 @@ exports.CallNode = class CallNode extends BaseNode
o.chainRoot = this unless o.chainRoot o.chainRoot = this unless o.chainRoot
for arg in @args when arg instanceof SplatNode for arg in @args when arg instanceof SplatNode
compilation = @compileSplat(o) compilation = @compileSplat(o)
unless compilation if not compilation
args = (arg.compile(o) for arg in @args).join(', ') args = for arg in @args
compilation = if @isSuper then @compileSuper(args, o) arg.parenthetical = true
else "#{@prefix()}#{@variable.compile(o)}(#{args})" arg.compile o
if o.operation and @wrapped then "(#{compilation})" else compilation compilation = if @isSuper
@compileSuper(args.join(', '), o)
else
"#{@prefix()}#{@variable.compile(o)}(#{ args.join(', ') })"
compilation
# `super()` is converted into a call against the superclass's implementation # `super()` is converted into a call against the superclass's implementation
# of the current function. # of the current function.
@ -790,7 +795,7 @@ exports.AssignNode = class AssignNode extends BaseNode
o.scope.find name unless @isValue() and (@variable.hasProperties() or @variable.namespaced) o.scope.find name unless @isValue() and (@variable.hasProperties() or @variable.namespaced)
val = "#{name} = #{val}" val = "#{name} = #{val}"
return "#{@tab}#{val};" if stmt return "#{@tab}#{val};" if stmt
if top then val else "(#{val})" if top or @parenthetical then val else "(#{val})"
# Brief implementation of recursive pattern matching, when assigning array or # Brief implementation of recursive pattern matching, when assigning array or
# object literals to a value. Peeks at their properties to assign inner names. # object literals to a value. Peeks at their properties to assign inner names.
@ -1214,7 +1219,8 @@ exports.ExistenceNode = class ExistenceNode extends BaseNode
constructor: (@expression) -> constructor: (@expression) ->
compileNode: (o) -> compileNode: (o) ->
ExistenceNode.compileTest(o, @expression)[0] test = ExistenceNode.compileTest(o, @expression)[0]
if @parenthetical then test.substring(1, test.length - 1) else test
# The meat of the **ExistenceNode** is in this static `compileTest` method # The meat of the **ExistenceNode** is in this static `compileTest` method
# because other nodes like to check the existence of their variables as well. # because other nodes like to check the existence of their variables as well.
@ -1248,12 +1254,11 @@ exports.ParentheticalNode = class ParentheticalNode extends BaseNode
compileNode: (o) -> compileNode: (o) ->
top = del o, 'top' top = del o, 'top'
@expression.parenthetical = true
code = @expression.compile(o) code = @expression.compile(o)
if @isStatement(o) if @parenthetical or @isStatement o
return (if top then @tab + code + ';' else code) return if top then @tab + code + ';' else code
l = code.length "(#{code})"
code = code.substr(o, l-1) if code.substr(l-1, 1) is ';'
if @expression instanceof AssignNode then code else "(#{code})"
#### ForNode #### ForNode