Existence checks on functions now supported.

This commit is contained in:
Timothy Jones
2010-08-26 07:39:30 +12:00
parent 9598b11c77
commit 6224edd6ce
5 changed files with 165 additions and 134 deletions

View File

@@ -265,17 +265,22 @@
}) })
], ],
Invocation: [ Invocation: [
o("Value Arguments", function() { o("Value OptFuncExist Arguments", function() {
return new CallNode($1, $2); return new CallNode($1, $3, $2);
}), o("Invocation Arguments", function() { }), o("Invocation OptFuncExist Arguments", function() {
return new CallNode($1, $2); return new CallNode($1, $3, $2);
})
],
OptFuncExist: [
o("", function() {
return false;
}), o("FUNC_EXIST", function() {
return true;
}) })
], ],
Arguments: [ Arguments: [
o("CALL_START ArgList OptComma CALL_END", function() { o("CALL_START ArgList OptComma CALL_END", function() {
return $2; return $2;
}), o("FUNC_EXIST CALL_START ArgList OptComma CALL_END", function() {
return $3;
}) })
], ],
Super: [ Super: [

View File

@@ -443,13 +443,15 @@
return CommentNode; return CommentNode;
})(); })();
exports.CallNode = (function() { exports.CallNode = (function() {
CallNode = function(variable, _b) { CallNode = function(variable, _b, _c) {
this.exist = _c;
this.args = _b; this.args = _b;
CallNode.__super__.constructor.call(this); CallNode.__super__.constructor.call(this);
this.isNew = false; this.isNew = false;
this.isSuper = variable === 'super'; this.isSuper = variable === 'super';
this.variable = this.isSuper ? null : variable; this.variable = this.isSuper ? null : variable;
this.args || (this.args = []); this.args || (this.args = []);
this.first = (this.last = '');
this.compileSplatArguments = function(o) { this.compileSplatArguments = function(o) {
return SplatNode.compileSplattedArray.call(this, this.args, o); return SplatNode.compileSplattedArray.call(this, this.args, o);
}; };
@@ -479,30 +481,41 @@
})()); })());
}; };
CallNode.prototype.compileNode = function(o) { CallNode.prototype.compileNode = function(o) {
var _b, _c, _d, _e, _f, _g, _h, arg, args, compilation; var _b, _c, _d, _e, _f, _g, _h, _i, arg, args, compilation;
if (!(o.chainRoot)) { if (!(o.chainRoot)) {
o.chainRoot = this; o.chainRoot = this;
} }
_c = this.args; if (this.exist) {
for (_b = 0, _d = _c.length; _b < _d; _b++) { _b = this.variable.compileReference(o, {
arg = _c[_b]; precompile: true
});
this.first = _b[0];
this.meth = _b[1];
this.first = ("typeof " + (this.first) + " === \"function\" ? ");
this.last = " : null";
} else if (this.variable) {
this.meth = this.variable.compile(o);
}
_d = this.args;
for (_c = 0, _e = _d.length; _c < _e; _c++) {
arg = _d[_c];
if (arg instanceof SplatNode) { if (arg instanceof SplatNode) {
compilation = this.compileSplat(o); compilation = this.compileSplat(o);
} }
} }
if (!compilation) { if (!compilation) {
args = (function() { args = (function() {
_e = []; _g = this.args; _f = []; _h = this.args;
for (_f = 0, _h = _g.length; _f < _h; _f++) { for (_g = 0, _i = _h.length; _g < _i; _g++) {
arg = _g[_f]; arg = _h[_g];
_e.push((function() { _f.push((function() {
arg.parenthetical = true; arg.parenthetical = true;
return arg.compile(o); return arg.compile(o);
})()); })());
} }
return _e; return _f;
}).call(this); }).call(this);
compilation = this.isSuper ? this.compileSuper(args.join(', '), o) : ("" + (this.prefix()) + (this.variable.compile(o)) + "(" + (args.join(', ')) + ")"); compilation = this.isSuper ? this.compileSuper(args.join(', '), o) : ("" + (this.first) + (this.prefix()) + (this.meth) + "(" + (args.join(', ')) + ")" + (this.last));
} }
return compilation; return compilation;
}; };
@@ -511,7 +524,7 @@
}; };
CallNode.prototype.compileSplat = function(o) { CallNode.prototype.compileSplat = function(o) {
var meth, obj, temp; var meth, obj, temp;
meth = this.variable ? this.variable.compile(o) : this.superReference(o); meth = this.meth || this.superReference(o);
obj = this.variable && this.variable.source || 'this'; obj = this.variable && this.variable.source || 'this';
if (obj.match(/\(/)) { if (obj.match(/\(/)) {
temp = o.scope.freeVariable(); temp = o.scope.freeVariable();
@@ -520,9 +533,9 @@
} }
if (this.isNew) { if (this.isNew) {
utility('extends'); utility('extends');
return "(function() {\n" + (this.idt(1)) + "var ctor = function(){};\n" + (this.idt(1)) + "__extends(ctor, " + (meth) + ");\n" + (this.idt(1)) + "return " + (meth) + ".apply(new ctor, " + (this.compileSplatArguments(o)) + ");\n" + (this.tab) + "}).call(this)"; return "" + (this.first) + "(function() {\n" + (this.idt(1)) + "var ctor = function(){};\n" + (this.idt(1)) + "__extends(ctor, " + (meth) + ");\n" + (this.idt(1)) + "return " + (meth) + ".apply(new ctor, " + (this.compileSplatArguments(o)) + ");\n" + (this.tab) + "}).call(this)" + (this.last);
} else { } else {
return "" + (this.prefix()) + (meth) + ".apply(" + (obj) + ", " + (this.compileSplatArguments(o)) + ")"; return "" + (this.first) + (this.prefix()) + (meth) + ".apply(" + (obj) + ", " + (this.compileSplatArguments(o)) + ")" + (this.last);
} }
}; };
return CallNode; return CallNode;

File diff suppressed because one or more lines are too long

View File

@@ -312,14 +312,19 @@ grammar =
# Ordinary function invocation, or a chained series of calls. # Ordinary function invocation, or a chained series of calls.
Invocation: [ Invocation: [
o "Value Arguments", -> new CallNode $1, $2 o "Value OptFuncExist Arguments", -> new CallNode $1, $3, $2
o "Invocation Arguments", -> new CallNode $1, $2 o "Invocation OptFuncExist Arguments", -> new CallNode $1, $3, $2
]
# An optional existence check on a function.
OptFuncExist: [
o "", -> no
o "FUNC_EXIST", -> yes
] ]
# The list of arguments to a function call. # The list of arguments to a function call.
Arguments: [ Arguments: [
o "CALL_START ArgList OptComma CALL_END", -> $2 o "CALL_START ArgList OptComma CALL_END", -> $2
o "FUNC_EXIST CALL_START ArgList OptComma CALL_END", -> $3
] ]
# Calling super. # Calling super.

View File

@@ -410,12 +410,13 @@ exports.CallNode = class CallNode extends BaseNode
class: 'CallNode' class: 'CallNode'
children: ['variable', 'args'] children: ['variable', 'args']
constructor: (variable, @args) -> constructor: (variable, @args, @exist) ->
super() super()
@isNew = false @isNew = false
@isSuper = variable is 'super' @isSuper = variable is 'super'
@variable = if @isSuper then null else variable @variable = if @isSuper then null else variable
@args or= [] @args or= []
@first = @last = ''
@compileSplatArguments = (o) -> @compileSplatArguments = (o) ->
SplatNode.compileSplattedArray.call(this, @args, o) SplatNode.compileSplattedArray.call(this, @args, o)
@@ -439,6 +440,11 @@ exports.CallNode = class CallNode extends BaseNode
# Compile a vanilla function call. # Compile a vanilla function call.
compileNode: (o) -> compileNode: (o) ->
o.chainRoot = this unless o.chainRoot o.chainRoot = this unless o.chainRoot
if @exist
[@first, @meth] = @variable.compileReference o, precompile: yes
@first = "typeof #{@first} === \"function\" ? "
@last = " : null"
else if @variable then @meth = @variable.compile o
for arg in @args when arg instanceof SplatNode for arg in @args when arg instanceof SplatNode
compilation = @compileSplat(o) compilation = @compileSplat(o)
if not compilation if not compilation
@@ -448,7 +454,7 @@ exports.CallNode = class CallNode extends BaseNode
compilation = if @isSuper compilation = if @isSuper
@compileSuper(args.join(', '), o) @compileSuper(args.join(', '), o)
else else
"#{@prefix()}#{@variable.compile(o)}(#{ args.join(', ') })" "#{@first}#{@prefix()}#{@meth}(#{ args.join(', ') })#{@last}"
compilation compilation
# `super()` is converted into a call against the superclass's implementation # `super()` is converted into a call against the superclass's implementation
@@ -461,23 +467,23 @@ exports.CallNode = class CallNode extends BaseNode
# If it's a constructor, then things get real tricky. We have to inject an # If it's a constructor, then things get real tricky. We have to inject an
# inner constructor in order to be able to pass the varargs. # inner constructor in order to be able to pass the varargs.
compileSplat: (o) -> compileSplat: (o) ->
meth = if @variable then @variable.compile(o) else @superReference(o) meth = @meth or @superReference(o)
obj = @variable and @variable.source or 'this' obj = @variable and @variable.source or 'this'
if obj.match(/\(/) if obj.match(/\(/)
temp = o.scope.freeVariable() temp = o.scope.freeVariable()
obj = temp obj = temp
meth = "(#{temp} = #{ @variable.source })#{ @variable.last }" meth = "(#{temp} = #{ @variable.source })#{ @variable.last }"
if @isNew if @isNew
utility 'extends' utility 'extends'
""" """
(function() { #{@first}(function() {
#{@idt(1)}var ctor = function(){}; #{@idt(1)}var ctor = function(){};
#{@idt(1)}__extends(ctor, #{meth}); #{@idt(1)}__extends(ctor, #{meth});
#{@idt(1)}return #{meth}.apply(new ctor, #{ @compileSplatArguments(o) }); #{@idt(1)}return #{meth}.apply(new ctor, #{ @compileSplatArguments(o) });
#{@tab}}).call(this) #{@tab}}).call(this)#{@last}
""" """
else else
"#{@prefix()}#{meth}.apply(#{obj}, #{ @compileSplatArguments(o) })" "#{@first}#{@prefix()}#{meth}.apply(#{obj}, #{ @compileSplatArguments(o) })#{@last}"
#### ExtendsNode #### ExtendsNode