mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
fixing issue 196, better handling of soak/existence chains
This commit is contained in:
parent
1f870911c9
commit
3df7bd98f4
3 changed files with 40 additions and 35 deletions
38
lib/nodes.js
38
lib/nodes.js
|
@ -115,6 +115,14 @@
|
|||
o.shared_scope = o.scope;
|
||||
return ClosureNode.wrap(this).compile(o);
|
||||
};
|
||||
// If the code generation wishes to use the result of a complex expression
|
||||
// in multiple places, ensure that the expression is only ever evaluated once.
|
||||
Node.prototype.compile_reference = function compile_reference(o) {
|
||||
var compiled, reference;
|
||||
reference = new LiteralNode(o.scope.free_variable());
|
||||
compiled = new AssignNode(reference, this);
|
||||
return [compiled, reference];
|
||||
};
|
||||
// Quick short method for the current indentation level, plus tabbing in.
|
||||
Node.prototype.idt = function idt(tabs) {
|
||||
var _a, _b, _c, _d, i, idt;
|
||||
|
@ -357,7 +365,7 @@
|
|||
return this.base.is_statement && this.base.is_statement() && !this.has_properties();
|
||||
},
|
||||
compile_node: function compile_node(o) {
|
||||
var _a, _b, baseline, code, only, op, part, parts, prop, props, soaked, temp;
|
||||
var _a, _b, baseline, complete, only, op, part, prop, props, soaked, temp;
|
||||
soaked = false;
|
||||
only = del(o, 'only_first');
|
||||
op = del(o, 'operation');
|
||||
|
@ -366,28 +374,27 @@
|
|||
if (this.base instanceof ObjectNode && this.has_properties()) {
|
||||
baseline = '(' + baseline + ')';
|
||||
}
|
||||
parts = [baseline];
|
||||
complete = (this.last = baseline);
|
||||
_a = props;
|
||||
for (_b = 0; _b < _a.length; _b++) {
|
||||
prop = _a[_b];
|
||||
this.source = baseline;
|
||||
if (prop instanceof AccessorNode && prop.soak) {
|
||||
soaked = true;
|
||||
if (this.base instanceof CallNode && prop === props[0]) {
|
||||
temp = o.scope.free_variable();
|
||||
parts[parts.length - 1] = '(' + temp + ' = ' + baseline + ')' + this.SOAK + ((baseline = temp + prop.compile(o)));
|
||||
complete = '(' + temp + ' = ' + complete + ')' + this.SOAK + ((baseline = temp + prop.compile(o)));
|
||||
} else {
|
||||
parts[parts.length - 1] += (this.SOAK + (baseline += prop.compile(o)));
|
||||
complete = complete + this.SOAK + (baseline += prop.compile(o));
|
||||
}
|
||||
} else {
|
||||
part = prop.compile(o);
|
||||
baseline += part;
|
||||
parts.push(part);
|
||||
complete += part;
|
||||
this.last = part;
|
||||
}
|
||||
}
|
||||
this.last = parts[parts.length - 1];
|
||||
this.source = parts.length > 1 ? parts.slice(0, (parts.length - 1)).join('') : null;
|
||||
code = parts.join('').replace(/\)\(\)\)/, '()))');
|
||||
return op && soaked ? '(' + code + ')' : code;
|
||||
return op && soaked ? '(' + complete + ')' : complete;
|
||||
}
|
||||
}));
|
||||
// Pass through CoffeeScript comments into JavaScript comments at the
|
||||
|
@ -466,14 +473,6 @@
|
|||
return _a;
|
||||
}).call(this);
|
||||
return this.prefix + meth + '.apply(' + obj + ', ' + args.join('') + ')';
|
||||
},
|
||||
// If the code generation wished to use the result of a function call
|
||||
// in multiple places, ensure that the function is only ever called once.
|
||||
compile_reference: function compile_reference(o) {
|
||||
var call, reference;
|
||||
reference = new LiteralNode(o.scope.free_variable());
|
||||
call = new ParentheticalNode(new AssignNode(reference, this));
|
||||
return [call, reference];
|
||||
}
|
||||
}));
|
||||
// Node to extend an object's prototype with an ancestor object.
|
||||
|
@ -875,7 +874,8 @@
|
|||
return this;
|
||||
},
|
||||
compile_node: function compile_node(o) {
|
||||
return (typeof this.index !== "undefined" && this.index !== null) ? this.compile_param(o) : this.name.compile(o);
|
||||
var _a;
|
||||
return (typeof (_a = this.index) !== "undefined" && _a !== null) ? this.compile_param(o) : this.name.compile(o);
|
||||
},
|
||||
compile_param: function compile_param(o) {
|
||||
var name;
|
||||
|
@ -1056,7 +1056,7 @@
|
|||
_a = [variable, variable];
|
||||
first = _a[0];
|
||||
second = _a[1];
|
||||
if (variable instanceof CallNode) {
|
||||
if (variable instanceof CallNode || (variable instanceof ValueNode && variable.has_properties())) {
|
||||
_b = variable.compile_reference(o);
|
||||
first = _b[0];
|
||||
second = _b[1];
|
||||
|
|
|
@ -77,6 +77,13 @@ Node::compile_closure: (o) ->
|
|||
o.shared_scope: o.scope
|
||||
ClosureNode.wrap(this).compile(o)
|
||||
|
||||
# If the code generation wishes to use the result of a complex expression
|
||||
# in multiple places, ensure that the expression is only ever evaluated once.
|
||||
Node::compile_reference: (o) ->
|
||||
reference: new LiteralNode(o.scope.free_variable())
|
||||
compiled: new AssignNode(reference, this)
|
||||
[compiled, reference]
|
||||
|
||||
# Quick short method for the current indentation level, plus tabbing in.
|
||||
Node::idt: (tabs) ->
|
||||
idt: (@indent || '')
|
||||
|
@ -272,25 +279,24 @@ ValueNode: exports.ValueNode: inherit Node, {
|
|||
props: if only then @properties[0...@properties.length - 1] else @properties
|
||||
baseline: @base.compile o
|
||||
baseline: '(' + baseline + ')' if @base instanceof ObjectNode and @has_properties()
|
||||
parts: [baseline]
|
||||
complete: @last: baseline
|
||||
|
||||
for prop in props
|
||||
@source: baseline
|
||||
if prop instanceof AccessorNode and prop.soak
|
||||
soaked: true
|
||||
if @base instanceof CallNode and prop is props[0]
|
||||
temp: o.scope.free_variable()
|
||||
parts[parts.length - 1]: '(' + temp + ' = ' + baseline + ')' + @SOAK + (baseline: temp + prop.compile(o))
|
||||
complete: '(' + temp + ' = ' + complete + ')' + @SOAK + (baseline: temp + prop.compile(o))
|
||||
else
|
||||
parts[parts.length - 1] += (@SOAK + (baseline += prop.compile(o)))
|
||||
complete: complete + @SOAK + (baseline += prop.compile(o))
|
||||
else
|
||||
part: prop.compile(o)
|
||||
baseline += part
|
||||
parts.push(part)
|
||||
complete += part
|
||||
@last: part
|
||||
|
||||
@last: parts[parts.length - 1]
|
||||
@source: if parts.length > 1 then parts[0...(parts.length - 1)].join('') else null
|
||||
code: parts.join('').replace(/\)\(\)\)/, '()))')
|
||||
if op and soaked then '(' + code + ')' else code
|
||||
if op and soaked then '(' + complete + ')' else complete
|
||||
|
||||
}
|
||||
|
||||
|
@ -356,13 +362,6 @@ CallNode: exports.CallNode: inherit Node, {
|
|||
if i is 0 then code else '.concat(' + code + ')'
|
||||
@prefix + meth + '.apply(' + obj + ', ' + args.join('') + ')'
|
||||
|
||||
# If the code generation wished to use the result of a function call
|
||||
# in multiple places, ensure that the function is only ever called once.
|
||||
compile_reference: (o) ->
|
||||
reference: new LiteralNode(o.scope.free_variable())
|
||||
call: new ParentheticalNode(new AssignNode(reference, this))
|
||||
[call, reference]
|
||||
|
||||
}
|
||||
|
||||
# Node to extend an object's prototype with an ancestor object.
|
||||
|
@ -845,7 +844,8 @@ ExistenceNode: exports.ExistenceNode: inherit Node, {
|
|||
|
||||
ExistenceNode.compile_test: (o, variable) ->
|
||||
[first, second]: [variable, variable]
|
||||
[first, second]: variable.compile_reference(o) if variable instanceof CallNode
|
||||
if variable instanceof CallNode or (variable instanceof ValueNode and variable.has_properties())
|
||||
[first, second]: variable.compile_reference(o)
|
||||
'(typeof ' + first.compile(o) + ' !== "undefined" && ' + second.compile(o) + ' !== null)'
|
||||
|
||||
# An extra set of parentheses, specified explicitly in the source.
|
||||
|
|
|
@ -68,3 +68,8 @@ result: value?.toString().toLowerCase()
|
|||
|
||||
ok result is '10'
|
||||
|
||||
|
||||
# Safely existence test on soaks.
|
||||
result: not value?.property?
|
||||
ok result
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue