1
0
Fork 0
mirror of https://github.com/jashkenas/coffeescript.git synced 2022-11-09 12:23:24 -05:00

nodes: soaking Call now converts to If using the same logic as soaking Accessor

This commit is contained in:
satyr 2010-10-17 13:19:51 +09:00
parent 84dcd6fe2f
commit 55794d9534
4 changed files with 109 additions and 107 deletions

View file

@ -1 +1 @@
lottery.drawWinner()?.address?.zipcode lottery.drawWinner?().address?.zipcode

View file

@ -105,7 +105,7 @@
} }
return _result; return _result;
}).call(this).join(''); }).call(this).join('');
klass = override || this.constructor.name + (this.soakNode || this.exist ? '?' : ''); klass = override || this.constructor.name + (this.soakNode ? '?' : '');
return '\n' + idt + klass + children; return '\n' + idt + klass + children;
}; };
Base.prototype.eachChild = function(func) { Base.prototype.eachChild = function(func) {
@ -152,6 +152,7 @@
Base.prototype.isPureStatement = NO; Base.prototype.isPureStatement = NO;
Base.prototype.isComplex = YES; Base.prototype.isComplex = YES;
Base.prototype.topSensitive = NO; Base.prototype.topSensitive = NO;
Base.prototype.unfoldSoak = NO;
Base.prototype.assigns = NO; Base.prototype.assigns = NO;
return Base; return Base;
})(); })();
@ -391,9 +392,9 @@
return !o.top || this.properties.length ? Value.__super__.compile.call(this, o) : this.base.compile(o); return !o.top || this.properties.length ? Value.__super__.compile.call(this, o) : this.base.compile(o);
}; };
Value.prototype.compileNode = function(o) { Value.prototype.compileNode = function(o) {
var _i, _len, code, ex, prop, props; var _i, _len, code, ifn, prop, props;
if (ex = this.unfoldSoak(o)) { if (ifn = this.unfoldSoak(o)) {
return ex.compile(o); return ifn.compile(o);
} }
props = this.properties; props = this.properties;
if (this.parenthetical && !props.length) { if (this.parenthetical && !props.length) {
@ -411,9 +412,9 @@
}; };
Value.prototype.unfoldSoak = function(o) { Value.prototype.unfoldSoak = function(o) {
var _len, _ref2, fst, i, ifn, prop, ref, snd; var _len, _ref2, fst, i, ifn, prop, ref, snd;
if (this.base.soakNode) { if (ifn = this.base.unfoldSoak(o)) {
Array.prototype.push.apply(this.base.body.properties, this.properties); Array.prototype.push.apply(ifn.body.properties, this.properties);
return this.base; return ifn;
} }
for (i = 0, _len = (_ref2 = this.properties).length; i < _len; i++) { for (i = 0, _len = (_ref2 = this.properties).length; i < _len; i++) {
prop = _ref2[i]; prop = _ref2[i];
@ -427,28 +428,21 @@
snd.base = ref; snd.base = ref;
} }
ifn = new If(new Existence(fst), snd, { ifn = new If(new Existence(fst), snd, {
operation: true soak: true
}); });
ifn.soakNode = true;
return ifn; return ifn;
} }
} }
return null; return null;
}; };
Value.unfoldSoak = function(o, parent, name) { Value.unfoldSoak = function(o, parent, name) {
var ifnode, node; var ifn;
node = parent[name]; if (!(ifn = parent[name].unfoldSoak(o))) {
if (node instanceof If && node.soakNode) {
ifnode = node;
} else if (node instanceof Value) {
ifnode = node.unfoldSoak(o);
}
if (!ifnode) {
return; return;
} }
parent[name] = ifnode.body; parent[name] = ifn.body;
ifnode.body = new Value(parent); ifn.body = new Value(parent);
return ifnode; return ifn;
}; };
return Value; return Value;
}).call(this); }).call(this);
@ -472,7 +466,7 @@
exports.Call = (function() { exports.Call = (function() {
Call = (function() { Call = (function() {
function Call(variable, _arg, _arg2) { function Call(variable, _arg, _arg2) {
this.exist = _arg2; this.soakNode = _arg2;
this.args = _arg; this.args = _arg;
Call.__super__.constructor.call(this); Call.__super__.constructor.call(this);
this.isNew = false; this.isNew = false;
@ -508,7 +502,25 @@
return method.klass ? ("" + (method.klass) + ".__super__." + name) : ("" + name + ".__super__.constructor"); return method.klass ? ("" + (method.klass) + ".__super__." + name) : ("" + name + ".__super__.constructor");
}; };
Call.prototype.unfoldSoak = function(o) { Call.prototype.unfoldSoak = function(o) {
var _i, _len, _ref2, call, list, node; var _i, _len, _ref2, _ref3, call, ifn, left, list, rite, val;
if (this.soakNode) {
if (val = this.variable) {
if (!(val instanceof Value)) {
val = new Value(val);
}
_ref2 = val.cacheReference(o), left = _ref2[0], rite = _ref2[1];
} else {
left = new Literal(this.superReference(o));
rite = new Value(left);
}
rite = new Call(rite, this.args);
rite.isNew = this.isNew;
left = new Literal("typeof " + (left.compile(o)) + " === \"function\"");
ifn = new If(left, new Value(rite), {
soak: true
});
return ifn;
}
call = this; call = this;
list = []; list = [];
while (true) { while (true) {
@ -525,51 +537,35 @@
break; break;
} }
} }
for (_i = 0, _len = (_ref2 = list.reverse()).length; _i < _len; _i++) { for (_i = 0, _len = (_ref3 = list.reverse()).length; _i < _len; _i++) {
call = _ref2[_i]; call = _ref3[_i];
if (node) { if (ifn) {
if (call.variable instanceof Call) { if (call.variable instanceof Call) {
call.variable = node; call.variable = ifn;
} else { } else {
call.variable.base = node; call.variable.base = ifn;
} }
} }
node = Value.unfoldSoak(o, call, 'variable'); ifn = Value.unfoldSoak(o, call, 'variable');
} }
return node; return ifn;
}; };
Call.prototype.compileNode = function(o) { Call.prototype.compileNode = function(o) {
var _i, _j, _len, _len2, _ref2, _ref3, _ref4, _ref5, _result, arg, args, left, node, rite, val; var _i, _j, _len, _len2, _ref2, _ref3, _ref4, _result, arg, args, ifn;
if (node = this.unfoldSoak(o)) { if (ifn = this.unfoldSoak(o)) {
return node.compile(o); return ifn.compile(o);
} }
(((_ref2 = this.variable) != null) ? (_ref2.tags.front = this.tags.front) : undefined); (((_ref2 = this.variable) != null) ? (_ref2.tags.front = this.tags.front) : undefined);
if (this.exist) { for (_i = 0, _len = (_ref3 = this.args).length; _i < _len; _i++) {
if (val = this.variable) { arg = _ref3[_i];
if (!(val instanceof Value)) {
val = new Value(val);
}
_ref3 = val.cacheReference(o), left = _ref3[0], rite = _ref3[1];
rite = new Call(rite, this.args);
} else {
left = new Literal(this.superReference(o));
rite = new Call(new Value(left), this.args);
rite.isNew = this.isNew;
}
left = ("typeof " + (left.compile(o)) + " !== \"function\"");
rite = rite.compile(o);
return ("(" + left + " ? undefined : " + rite + ")");
}
for (_i = 0, _len = (_ref4 = this.args).length; _i < _len; _i++) {
arg = _ref4[_i];
if (arg instanceof Splat) { if (arg instanceof Splat) {
return this.compileSplat(o); return this.compileSplat(o);
} }
} }
args = (function() { args = (function() {
_result = []; _result = [];
for (_j = 0, _len2 = (_ref5 = this.args).length; _j < _len2; _j++) { for (_j = 0, _len2 = (_ref4 = this.args).length; _j < _len2; _j++) {
arg = _ref5[_j]; arg = _ref4[_j];
_result.push((arg.parenthetical = true) && arg.compile(o)); _result.push((arg.parenthetical = true) && arg.compile(o));
} }
return _result; return _result;
@ -1816,11 +1812,11 @@
})(); })();
exports.If = (function() { exports.If = (function() {
If = (function() { If = (function() {
function If(condition, _arg, _arg2) { function If(condition, _arg, tags) {
this.tags = _arg2;
this.body = _arg; this.body = _arg;
this.tags || (this.tags = {}); this.tags = tags || (tags = {});
this.condition = this.tags.invert ? condition.invert() : condition; this.condition = tags.invert ? condition.invert() : condition;
this.soakNode = tags.soak;
this.elseBody = null; this.elseBody = null;
this.isChain = false; this.isChain = false;
return this; return this;
@ -1898,7 +1894,10 @@
ifPart = this.condition.compile(o) + ' ? ' + this.bodyNode().compile(o); ifPart = this.condition.compile(o) + ' ? ' + this.bodyNode().compile(o);
elsePart = this.elseBody ? this.elseBodyNode().compile(o) : 'undefined'; elsePart = this.elseBody ? this.elseBodyNode().compile(o) : 'undefined';
code = ("" + ifPart + " : " + elsePart); code = ("" + ifPart + " : " + elsePart);
return this.tags.operation ? ("(" + code + ")") : code; return this.tags.operation || this.soakNode ? ("(" + code + ")") : code;
};
If.prototype.unfoldSoak = function() {
return this.soakNode && this;
}; };
return If; return If;
})(); })();

View file

@ -110,7 +110,7 @@ exports.Base = class Base
toString: (idt, override) -> toString: (idt, override) ->
idt or= '' idt or= ''
children = (child.toString idt + TAB for child in @collectChildren()).join('') children = (child.toString idt + TAB for child in @collectChildren()).join('')
klass = override or @constructor.name + if @soakNode or @exist then '?' else '' klass = override or @constructor.name + if @soakNode then '?' else ''
'\n' + idt + klass + children '\n' + idt + klass + children
eachChild: (func) -> eachChild: (func) ->
@ -141,6 +141,7 @@ exports.Base = class Base
isPureStatement : NO isPureStatement : NO
isComplex : YES isComplex : YES
topSensitive : NO topSensitive : NO
unfoldSoak : NO
# Is this node used to assign a certain variable? # Is this node used to assign a certain variable?
assigns: NO assigns: NO
@ -376,7 +377,7 @@ exports.Value = class Value extends Base
# operators `?.` interspersed. Then we have to take care not to accidentally # operators `?.` interspersed. Then we have to take care not to accidentally
# evaluate a anything twice when building the soak chain. # evaluate a anything twice when building the soak chain.
compileNode: (o) -> compileNode: (o) ->
return ex.compile o if ex = @unfoldSoak o return ifn.compile o if ifn = @unfoldSoak o
props = @properties props = @properties
@base.parenthetical = yes if @parenthetical and not props.length @base.parenthetical = yes if @parenthetical and not props.length
code = @base.compile o code = @base.compile o
@ -386,9 +387,9 @@ exports.Value = class Value extends Base
# Unfold a soak into an `If`: `a?.b` -> `a.b if a?` # Unfold a soak into an `If`: `a?.b` -> `a.b if a?`
unfoldSoak: (o) -> unfoldSoak: (o) ->
if @base.soakNode if ifn = @base.unfoldSoak o
Array::push.apply @base.body.properties, @properties Array::push.apply ifn.body.properties, @properties
return @base return ifn
for prop, i in @properties when prop.soakNode for prop, i in @properties when prop.soakNode
prop.soakNode = off prop.soakNode = off
fst = new Value @base, @properties.slice 0, i fst = new Value @base, @properties.slice 0, i
@ -397,22 +398,16 @@ exports.Value = class Value extends Base
ref = new Literal o.scope.freeVariable 'ref' ref = new Literal o.scope.freeVariable 'ref'
fst = new Parens new Assign ref, fst fst = new Parens new Assign ref, fst
snd.base = ref snd.base = ref
ifn = new If new Existence(fst), snd, operation: yes ifn = new If new Existence(fst), snd, soak: yes
ifn.soakNode = on
return ifn return ifn
null null
# Unfold a node's child if soak, then tuck the node under created `If` # Unfold a node's child if soak, then tuck the node under created `If`
@unfoldSoak: (o, parent, name) -> @unfoldSoak: (o, parent, name) ->
node = parent[name] return unless ifn = parent[name].unfoldSoak o
if node instanceof If and node.soakNode parent[name] = ifn.body
ifnode = node ifn.body = new Value parent
else if node instanceof Value ifn
ifnode = node.unfoldSoak o
return unless ifnode
parent[name] = ifnode.body
ifnode.body = new Value parent
ifnode
#### Comment #### Comment
@ -438,7 +433,7 @@ exports.Call = class Call extends Base
children: ['variable', 'args'] children: ['variable', 'args']
constructor: (variable, @args, @exist) -> constructor: (variable, @args, @soakNode) ->
super() super()
@isNew = false @isNew = false
@isSuper = variable is 'super' @isSuper = variable is 'super'
@ -469,6 +464,18 @@ exports.Call = class Call extends Base
# Soaked chained invocations unfold into if/else ternary structures. # Soaked chained invocations unfold into if/else ternary structures.
unfoldSoak: (o) -> unfoldSoak: (o) ->
if @soakNode
if val = @variable
val = new Value val unless val instanceof Value
[left, rite] = val.cacheReference o
else
left = new Literal @superReference o
rite = new Value left
rite = new Call rite, @args
rite.isNew = @isNew
left = new Literal "typeof #{ left.compile o } === \"function\""
ifn = new If left, new Value(rite), soak: yes
return ifn
call = this call = this
list = [] list = []
loop loop
@ -480,30 +487,18 @@ exports.Call = class Call extends Base
list.push call list.push call
break unless (call = call.variable.base) instanceof Call break unless (call = call.variable.base) instanceof Call
for call in list.reverse() for call in list.reverse()
if node if ifn
if call.variable instanceof Call if call.variable instanceof Call
call.variable = node call.variable = ifn
else else
call.variable.base = node call.variable.base = ifn
node = Value.unfoldSoak o, call, 'variable' ifn = Value.unfoldSoak o, call, 'variable'
node ifn
# Compile a vanilla function call. # Compile a vanilla function call.
compileNode: (o) -> compileNode: (o) ->
return node.compile o if node = @unfoldSoak o return ifn.compile o if ifn = @unfoldSoak o
@variable?.tags.front = @tags.front @variable?.tags.front = @tags.front
if @exist
if val = @variable
val = new Value val unless val instanceof Value
[left, rite] = val.cacheReference o
rite = new Call rite, @args
else
left = new Literal @superReference o
rite = new Call new Value(left), @args
rite.isNew = @isNew
left = "typeof #{ left.compile o } !== \"function\""
rite = rite.compile o
return "(#{left} ? undefined : #{rite})"
for arg in @args when arg instanceof Splat for arg in @args when arg instanceof Splat
return @compileSplat o return @compileSplat o
args = ((arg.parenthetical = on) and arg.compile o for arg in @args).join ', ' args = ((arg.parenthetical = on) and arg.compile o for arg in @args).join ', '
@ -1528,9 +1523,10 @@ exports.If = class If extends Base
topSensitive: YES topSensitive: YES
constructor: (condition, @body, @tags) -> constructor: (condition, @body, tags) ->
@tags or= {} @tags = tags or= {}
@condition = if @tags.invert then condition.invert() else condition @condition = if tags.invert then condition.invert() else condition
@soakNode = tags.soak
@elseBody = null @elseBody = null
@isChain = false @isChain = false
@ -1592,7 +1588,9 @@ exports.If = class If extends Base
ifPart = @condition.compile(o) + ' ? ' + @bodyNode().compile(o) ifPart = @condition.compile(o) + ' ? ' + @bodyNode().compile(o)
elsePart = if @elseBody then @elseBodyNode().compile(o) else 'undefined' elsePart = if @elseBody then @elseBodyNode().compile(o) else 'undefined'
code = "#{ifPart} : #{elsePart}" code = "#{ifPart} : #{elsePart}"
if @tags.operation then "(#{code})" else code if @tags.operation or @soakNode then "(#{code})" else code
unfoldSoak: -> @soakNode and this
# Faux-Nodes # Faux-Nodes
# ---------- # ----------

View file

@ -113,21 +113,26 @@ obj = {
returnThis: -> this returnThis: -> this
} }
ok plus1?(41) is 42 eq plus1?(41), 42
ok (plus1? 41) is 42 eq (plus1? 41), 42
ok plus2?(41) is undefined eq plus2?(41), undefined
ok (plus2? 41) is undefined eq (plus2? 41), undefined
ok obj.returnThis?() is obj eq obj.returnThis?(), obj
ok obj.counter().counter().returnThis?() is obj eq obj.returnSelf?(), undefined
ok count is 2 eq obj.returnThis?().flag = on, on
eq obj.returnSelf?().flag = on, undefined
eq obj.counter().counter().returnThis?(), obj
eq count, 2
maybe_close = (f, arg) -> if typeof f is 'function' then () -> f(arg) else -1 maybe_close = (f, arg) -> if typeof f is 'function' then () -> f(arg) else -1
ok maybe_close(plus1, 41)?() is 42 eq maybe_close(plus1, 41)?(), 42
ok (maybe_close plus1, 41)?() is 42 eq (maybe_close plus1, 41)?(), 42
ok (maybe_close 'string', 41)?() is undefined eq (maybe_close 'string', 41)?(), undefined
eq 2?(3), undefined eq 2?(3), undefined
eq new Number?(42) | 0, 42
eq new Bumper?(42) | 0, 0
#726 #726
eq calendar?[Date()], undefined eq calendar?[Date()], undefined