Improving soaks to avoid uncessesary parentheses.

This commit is contained in:
Jeremy Ashkenas 2010-02-20 20:00:07 -05:00
parent a93229b14d
commit 0f2a2ee11e
3 changed files with 39 additions and 12 deletions

View File

@ -99,6 +99,9 @@
var closure, top;
this.options = merge(o || {});
this.indent = o.indent;
if (!(this.operation_sensitive())) {
del(this.options, 'operation');
}
top = this.top_sensitive() ? this.options.top : del(this.options, 'top');
closure = this.is_statement() && !this.is_statement_only() && !top && !this.options.returns && !(this instanceof CommentNode) && !this.contains(function(node) {
return node.is_statement_only();
@ -164,6 +167,9 @@
Node.prototype.top_sensitive = function top_sensitive() {
return false;
};
Node.prototype.operation_sensitive = function operation_sensitive() {
return false;
};
// A collection of nodes, each one representing an expression.
Expressions = (exports.Expressions = inherit(Node, {
type: 'Expressions',
@ -326,6 +332,9 @@
this.children.push(prop);
return this;
},
operation_sensitive: function operation_sensitive() {
return true;
},
has_properties: function has_properties() {
return !!this.properties.length;
},
@ -349,9 +358,10 @@
return this.base.is_statement && this.base.is_statement() && !this.has_properties();
},
compile_node: function compile_node(o) {
var _a, _b, baseline, code, only, part, parts, prop, props, soaked, temp;
var _a, _b, baseline, code, only, op, part, parts, prop, props, soaked, temp;
soaked = false;
only = del(o, 'only_first');
op = del(o, 'operation');
props = only ? this.properties.slice(0, this.properties.length - 1) : this.properties;
baseline = this.base.compile(o);
if (this.base instanceof ObjectNode && this.has_properties()) {
@ -378,10 +388,7 @@
this.last = parts[parts.length - 1];
this.source = parts.length > 1 ? parts.slice(0, (parts.length - 1)).join('') : null;
code = parts.join('').replace(/\)\(\)\)/, '()))');
if (!(soaked)) {
return code;
}
return '(' + code + ')';
return op && soaked ? '(' + code + ')' : code;
}
}));
// Pass through CoffeeScript comments into JavaScript comments at the
@ -943,6 +950,7 @@
return this.CHAINABLE.indexOf(this.operator) >= 0;
},
compile_node: function compile_node(o) {
o.operation = true;
if (this.is_chainable() && this.first.unwrap() instanceof OpNode && this.first.unwrap().is_chainable()) {
return this.compile_chain(o);
}

View File

@ -63,6 +63,7 @@ Node: exports.Node: ->
Node::compile: (o) ->
@options: merge o or {}
@indent: o.indent
del @options, 'operation' unless @operation_sensitive()
top: if @top_sensitive() then @options.top else del @options, 'top'
closure: @is_statement() and not @is_statement_only() and not top and
not @options.returns and not (this instanceof CommentNode) and
@ -95,11 +96,12 @@ Node::toString: (idt) ->
'\n' + idt + @type + (child.toString(idt + TAB) for child in @children).join('')
# Default implementations of the common node methods.
Node::unwrap: -> this
Node::children: []
Node::is_statement: -> false
Node::is_statement_only: -> false
Node::top_sensitive: -> false
Node::unwrap: -> this
Node::children: []
Node::is_statement: -> false
Node::is_statement_only: -> false
Node::top_sensitive: -> false
Node::operation_sensitive: -> false
# A collection of nodes, each one representing an expression.
Expressions: exports.Expressions: inherit Node, {
@ -239,6 +241,9 @@ ValueNode: exports.ValueNode: inherit Node, {
@children.push(prop)
this
operation_sensitive: ->
true
has_properties: ->
!!@properties.length
@ -264,6 +269,7 @@ ValueNode: exports.ValueNode: inherit Node, {
compile_node: (o) ->
soaked: false
only: del(o, 'only_first')
op: del(o, 'operation')
props: if only then @properties[0...@properties.length - 1] else @properties
baseline: @base.compile o
baseline: '(' + baseline + ')' if @base instanceof ObjectNode and @has_properties()
@ -285,8 +291,7 @@ ValueNode: exports.ValueNode: inherit Node, {
@last: parts[parts.length - 1]
@source: if parts.length > 1 then parts[0...(parts.length - 1)].join('') else null
code: parts.join('').replace(/\)\(\)\)/, '()))')
return code unless soaked
'(' + code + ')'
if op and soaked then '(' + code + ')' else code
}
@ -757,6 +762,7 @@ OpNode: exports.OpNode: inherit Node, {
@CHAINABLE.indexOf(@operator) >= 0
compile_node: (o) ->
o.operation: true
return @compile_chain(o) if @is_chainable() and @first.unwrap() instanceof OpNode and @first.unwrap().is_chainable()
return @compile_assignment(o) if @ASSIGNMENT.indexOf(@operator) >= 0
return @compile_unary(o) if @is_unary()

View File

@ -55,3 +55,16 @@ ok arr.pop()?.length is 2
ok arr.pop()?.length is undefined
ok arr[0]?.length is undefined
ok arr.pop()?.length?.non?.existent()?.property is undefined
# Soaks method calls safely.
value: undefined
result: value?.toString().toLowerCase()
ok result is undefined
value: 10
result: value?.toString().toLowerCase()
ok result is '10'