mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-01-14 17:27:59 -05:00
Merge branch 'master' of github.com:jashkenas/coffee-script
This commit is contained in:
@@ -1 +1 @@
|
||||
lottery.drawWinner()?.address?.zipcode
|
||||
lottery.drawWinner?().address?.zipcode
|
||||
|
||||
@@ -179,7 +179,7 @@
|
||||
return true;
|
||||
};
|
||||
Lexer.prototype.regexToken = function() {
|
||||
var match;
|
||||
var match, regex;
|
||||
if (this.chunk.charAt(0) !== '/') {
|
||||
return false;
|
||||
}
|
||||
@@ -192,8 +192,9 @@
|
||||
if (!(match = REGEX.exec(this.chunk))) {
|
||||
return false;
|
||||
}
|
||||
this.token('REGEX', match[0]);
|
||||
this.i += match[0].length;
|
||||
regex = match[0];
|
||||
this.token('REGEX', regex === '//' ? '/(?:)/' : regex);
|
||||
this.i += regex.length;
|
||||
return true;
|
||||
};
|
||||
Lexer.prototype.heregexToken = function(match) {
|
||||
@@ -610,7 +611,7 @@
|
||||
MULTI_DENT = /^(?:\n[ \t]*)+/;
|
||||
SIMPLESTR = /^'[^\\']*(?:\\.[^\\']*)*'/;
|
||||
JSTOKEN = /^`[^\\`]*(?:\\.[^\\`]*)*`/;
|
||||
REGEX = /^\/(?!\s)(?:[^[\/\n\\]+|\\[\s\S]|\[([^\]\n\\]+|\\[\s\S])*])+\/[imgy]{0,4}(?![A-Za-z])/;
|
||||
REGEX = /^\/(?!\s)[^[\/\n\\]*(?:(?:\\[\s\S]|\[[^\]\n\\]*(?:\\[\s\S][^\]\n\\]*)*])[^[\/\n\\]*)*\/[imgy]{0,4}(?![A-Za-z])/;
|
||||
HEREGEX = /^\/{3}([\s\S]+?)\/{3}([imgy]{0,4})(?![A-Za-z])/;
|
||||
HEREGEX_OMIT = /\s+(?:#.*)?/g;
|
||||
MULTILINER = /\n/g;
|
||||
|
||||
127
lib/nodes.js
127
lib/nodes.js
@@ -105,7 +105,7 @@
|
||||
}
|
||||
return _result;
|
||||
}).call(this).join('');
|
||||
klass = override || this.constructor.name + (this.soakNode || this.exist ? '?' : '');
|
||||
klass = override || this.constructor.name + (this.soakNode ? '?' : '');
|
||||
return '\n' + idt + klass + children;
|
||||
};
|
||||
Base.prototype.eachChild = function(func) {
|
||||
@@ -152,6 +152,7 @@
|
||||
Base.prototype.isPureStatement = NO;
|
||||
Base.prototype.isComplex = YES;
|
||||
Base.prototype.topSensitive = NO;
|
||||
Base.prototype.unfoldSoak = NO;
|
||||
Base.prototype.assigns = NO;
|
||||
return Base;
|
||||
})();
|
||||
@@ -391,9 +392,9 @@
|
||||
return !o.top || this.properties.length ? Value.__super__.compile.call(this, o) : this.base.compile(o);
|
||||
};
|
||||
Value.prototype.compileNode = function(o) {
|
||||
var _i, _len, code, ex, prop, props;
|
||||
if (ex = this.unfoldSoak(o)) {
|
||||
return ex.compile(o);
|
||||
var _i, _len, code, ifn, prop, props;
|
||||
if (ifn = this.unfoldSoak(o)) {
|
||||
return ifn.compile(o);
|
||||
}
|
||||
props = this.properties;
|
||||
if (this.parenthetical && !props.length) {
|
||||
@@ -411,9 +412,9 @@
|
||||
};
|
||||
Value.prototype.unfoldSoak = function(o) {
|
||||
var _len, _ref2, fst, i, ifn, prop, ref, snd;
|
||||
if (this.base.soakNode) {
|
||||
Array.prototype.push.apply(this.base.body.properties, this.properties);
|
||||
return this.base;
|
||||
if (ifn = this.base.unfoldSoak(o)) {
|
||||
Array.prototype.push.apply(ifn.body.properties, this.properties);
|
||||
return ifn;
|
||||
}
|
||||
for (i = 0, _len = (_ref2 = this.properties).length; i < _len; i++) {
|
||||
prop = _ref2[i];
|
||||
@@ -427,31 +428,15 @@
|
||||
snd.base = ref;
|
||||
}
|
||||
ifn = new If(new Existence(fst), snd, {
|
||||
operation: true
|
||||
soak: true
|
||||
});
|
||||
ifn.soakNode = true;
|
||||
return ifn;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
Value.unfoldSoak = function(o, parent, name) {
|
||||
var ifnode, node;
|
||||
node = parent[name];
|
||||
if (node instanceof If && node.soakNode) {
|
||||
ifnode = node;
|
||||
} else if (node instanceof Value) {
|
||||
ifnode = node.unfoldSoak(o);
|
||||
}
|
||||
if (!ifnode) {
|
||||
return;
|
||||
}
|
||||
parent[name] = ifnode.body;
|
||||
ifnode.body = new Value(parent);
|
||||
return ifnode;
|
||||
};
|
||||
return Value;
|
||||
}).call(this);
|
||||
})();
|
||||
exports.Comment = (function() {
|
||||
Comment = (function() {
|
||||
function Comment(_arg) {
|
||||
@@ -472,7 +457,7 @@
|
||||
exports.Call = (function() {
|
||||
Call = (function() {
|
||||
function Call(variable, _arg, _arg2) {
|
||||
this.exist = _arg2;
|
||||
this.soakNode = _arg2;
|
||||
this.args = _arg;
|
||||
Call.__super__.constructor.call(this);
|
||||
this.isNew = false;
|
||||
@@ -508,7 +493,25 @@
|
||||
return method.klass ? ("" + (method.klass) + ".__super__." + name) : ("" + name + ".__super__.constructor");
|
||||
};
|
||||
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;
|
||||
list = [];
|
||||
while (true) {
|
||||
@@ -525,51 +528,35 @@
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (_i = 0, _len = (_ref2 = list.reverse()).length; _i < _len; _i++) {
|
||||
call = _ref2[_i];
|
||||
if (node) {
|
||||
for (_i = 0, _len = (_ref3 = list.reverse()).length; _i < _len; _i++) {
|
||||
call = _ref3[_i];
|
||||
if (ifn) {
|
||||
if (call.variable instanceof Call) {
|
||||
call.variable = node;
|
||||
call.variable = ifn;
|
||||
} else {
|
||||
call.variable.base = node;
|
||||
call.variable.base = ifn;
|
||||
}
|
||||
}
|
||||
node = Value.unfoldSoak(o, call, 'variable');
|
||||
ifn = If.unfoldSoak(o, call, 'variable');
|
||||
}
|
||||
return node;
|
||||
return ifn;
|
||||
};
|
||||
Call.prototype.compileNode = function(o) {
|
||||
var _i, _j, _len, _len2, _ref2, _ref3, _ref4, _ref5, _result, arg, args, left, node, rite, val;
|
||||
if (node = this.unfoldSoak(o)) {
|
||||
return node.compile(o);
|
||||
var _i, _j, _len, _len2, _ref2, _ref3, _ref4, _result, arg, args, ifn;
|
||||
if (ifn = this.unfoldSoak(o)) {
|
||||
return ifn.compile(o);
|
||||
}
|
||||
(((_ref2 = this.variable) != null) ? (_ref2.tags.front = this.tags.front) : undefined);
|
||||
if (this.exist) {
|
||||
if (val = this.variable) {
|
||||
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];
|
||||
for (_i = 0, _len = (_ref3 = this.args).length; _i < _len; _i++) {
|
||||
arg = _ref3[_i];
|
||||
if (arg instanceof Splat) {
|
||||
return this.compileSplat(o);
|
||||
}
|
||||
}
|
||||
args = (function() {
|
||||
_result = [];
|
||||
for (_j = 0, _len2 = (_ref5 = this.args).length; _j < _len2; _j++) {
|
||||
arg = _ref5[_j];
|
||||
for (_j = 0, _len2 = (_ref4 = this.args).length; _j < _len2; _j++) {
|
||||
arg = _ref4[_j];
|
||||
_result.push((arg.parenthetical = true) && arg.compile(o));
|
||||
}
|
||||
return _result;
|
||||
@@ -1005,7 +992,7 @@
|
||||
if (this.variable.isSplice()) {
|
||||
return this.compileSplice(o);
|
||||
}
|
||||
if (ifn = Value.unfoldSoak(o, this, 'variable')) {
|
||||
if (ifn = If.unfoldSoak(o, this, 'variable')) {
|
||||
delete o.top;
|
||||
return ifn.compile(o);
|
||||
}
|
||||
@@ -1816,11 +1803,11 @@
|
||||
})();
|
||||
exports.If = (function() {
|
||||
If = (function() {
|
||||
function If(condition, _arg, _arg2) {
|
||||
this.tags = _arg2;
|
||||
function If(condition, _arg, tags) {
|
||||
this.body = _arg;
|
||||
this.tags || (this.tags = {});
|
||||
this.condition = this.tags.invert ? condition.invert() : condition;
|
||||
this.tags = tags || (tags = {});
|
||||
this.condition = tags.invert ? condition.invert() : condition;
|
||||
this.soakNode = tags.soak;
|
||||
this.elseBody = null;
|
||||
this.isChain = false;
|
||||
return this;
|
||||
@@ -1898,10 +1885,22 @@
|
||||
ifPart = this.condition.compile(o) + ' ? ' + this.bodyNode().compile(o);
|
||||
elsePart = this.elseBody ? this.elseBodyNode().compile(o) : 'undefined';
|
||||
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;
|
||||
};
|
||||
If.unfoldSoak = function(o, parent, name) {
|
||||
var ifn;
|
||||
if (!(ifn = parent[name].unfoldSoak(o))) {
|
||||
return;
|
||||
}
|
||||
parent[name] = ifn.body;
|
||||
ifn.body = new Value(parent);
|
||||
return ifn;
|
||||
};
|
||||
return If;
|
||||
})();
|
||||
}).call(this);
|
||||
Push = {
|
||||
wrap: function(name, expressions) {
|
||||
if (expressions.empty() || expressions.containsPureStatement()) {
|
||||
|
||||
@@ -189,8 +189,9 @@ exports.Lexer = class Lexer
|
||||
return @heregexToken match if match = HEREGEX.exec @chunk
|
||||
return false if include NOT_REGEX, @tag()
|
||||
return false unless match = REGEX.exec @chunk
|
||||
@token 'REGEX', match[0]
|
||||
@i += match[0].length
|
||||
[regex] = match
|
||||
@token 'REGEX', if regex is '//' then '/(?:)/' else regex
|
||||
@i += regex.length
|
||||
true
|
||||
|
||||
# Matches experimental, multiline and extended regular expression literals.
|
||||
@@ -559,11 +560,16 @@ JSTOKEN = /^`[^\\`]*(?:\\.[^\\`]*)*`/
|
||||
|
||||
# Regex-matching-regexes.
|
||||
REGEX = /// ^
|
||||
/ (?!\s) # disallow leading whitespace
|
||||
(?: [^ [ / \n \\ ]+ # every other thing
|
||||
| \\[\s\S] # anything escaped
|
||||
| \[ ( [^ \] \n \\ ]+ | \\[\s\S] )* ] # character class
|
||||
)+
|
||||
/ (?! \s ) # disallow leading whitespace
|
||||
[^ [ / \n \\ ]* # every other thing
|
||||
(?:
|
||||
(?: \\[\s\S] # anything escaped
|
||||
| \[ # character class
|
||||
[^ \] \n \\ ]*
|
||||
(?: \\[\s\S] [^ \] \n \\ ]* )*
|
||||
]
|
||||
) [^ [ / \n \\ ]*
|
||||
)*
|
||||
/ [imgy]{0,4} (?![A-Za-z])
|
||||
///
|
||||
HEREGEX = /^\/{3}([\s\S]+?)\/{3}([imgy]{0,4})(?![A-Za-z])/
|
||||
|
||||
@@ -110,7 +110,7 @@ exports.Base = class Base
|
||||
toString: (idt, override) ->
|
||||
idt or= ''
|
||||
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
|
||||
|
||||
eachChild: (func) ->
|
||||
@@ -141,6 +141,7 @@ exports.Base = class Base
|
||||
isPureStatement : NO
|
||||
isComplex : YES
|
||||
topSensitive : NO
|
||||
unfoldSoak : NO
|
||||
|
||||
# Is this node used to assign a certain variable?
|
||||
assigns: NO
|
||||
@@ -376,7 +377,7 @@ exports.Value = class Value extends Base
|
||||
# operators `?.` interspersed. Then we have to take care not to accidentally
|
||||
# evaluate a anything twice when building the soak chain.
|
||||
compileNode: (o) ->
|
||||
return ex.compile o if ex = @unfoldSoak o
|
||||
return ifn.compile o if ifn = @unfoldSoak o
|
||||
props = @properties
|
||||
@base.parenthetical = yes if @parenthetical and not props.length
|
||||
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?`
|
||||
unfoldSoak: (o) ->
|
||||
if @base.soakNode
|
||||
Array::push.apply @base.body.properties, @properties
|
||||
return @base
|
||||
if ifn = @base.unfoldSoak o
|
||||
Array::push.apply ifn.body.properties, @properties
|
||||
return ifn
|
||||
for prop, i in @properties when prop.soakNode
|
||||
prop.soakNode = off
|
||||
fst = new Value @base, @properties.slice 0, i
|
||||
@@ -397,23 +398,10 @@ exports.Value = class Value extends Base
|
||||
ref = new Literal o.scope.freeVariable 'ref'
|
||||
fst = new Parens new Assign ref, fst
|
||||
snd.base = ref
|
||||
ifn = new If new Existence(fst), snd, operation: yes
|
||||
ifn.soakNode = on
|
||||
ifn = new If new Existence(fst), snd, soak: yes
|
||||
return ifn
|
||||
null
|
||||
|
||||
# Unfold a node's child if soak, then tuck the node under created `If`
|
||||
@unfoldSoak: (o, parent, name) ->
|
||||
node = parent[name]
|
||||
if node instanceof If and node.soakNode
|
||||
ifnode = node
|
||||
else if node instanceof Value
|
||||
ifnode = node.unfoldSoak o
|
||||
return unless ifnode
|
||||
parent[name] = ifnode.body
|
||||
ifnode.body = new Value parent
|
||||
ifnode
|
||||
|
||||
#### Comment
|
||||
|
||||
# CoffeeScript passes through block comments as JavaScript block comments
|
||||
@@ -438,7 +426,7 @@ exports.Call = class Call extends Base
|
||||
|
||||
children: ['variable', 'args']
|
||||
|
||||
constructor: (variable, @args, @exist) ->
|
||||
constructor: (variable, @args, @soakNode) ->
|
||||
super()
|
||||
@isNew = false
|
||||
@isSuper = variable is 'super'
|
||||
@@ -469,6 +457,18 @@ exports.Call = class Call extends Base
|
||||
|
||||
# Soaked chained invocations unfold into if/else ternary structures.
|
||||
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
|
||||
list = []
|
||||
loop
|
||||
@@ -480,30 +480,18 @@ exports.Call = class Call extends Base
|
||||
list.push call
|
||||
break unless (call = call.variable.base) instanceof Call
|
||||
for call in list.reverse()
|
||||
if node
|
||||
if ifn
|
||||
if call.variable instanceof Call
|
||||
call.variable = node
|
||||
call.variable = ifn
|
||||
else
|
||||
call.variable.base = node
|
||||
node = Value.unfoldSoak o, call, 'variable'
|
||||
node
|
||||
call.variable.base = ifn
|
||||
ifn = If.unfoldSoak o, call, 'variable'
|
||||
ifn
|
||||
|
||||
# Compile a vanilla function call.
|
||||
compileNode: (o) ->
|
||||
return node.compile o if node = @unfoldSoak o
|
||||
return ifn.compile o if ifn = @unfoldSoak o
|
||||
@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
|
||||
return @compileSplat o
|
||||
args = ((arg.parenthetical = on) and arg.compile o for arg in @args).join ', '
|
||||
@@ -868,7 +856,7 @@ exports.Assign = class Assign extends Base
|
||||
if isValue = @isValue()
|
||||
return @compilePatternMatch(o) if @variable.isArray() or @variable.isObject()
|
||||
return @compileSplice(o) if @variable.isSplice()
|
||||
if ifn = Value.unfoldSoak o, this, 'variable'
|
||||
if ifn = If.unfoldSoak o, this, 'variable'
|
||||
delete o.top
|
||||
return ifn.compile o
|
||||
top = del o, 'top'
|
||||
@@ -1528,9 +1516,10 @@ exports.If = class If extends Base
|
||||
|
||||
topSensitive: YES
|
||||
|
||||
constructor: (condition, @body, @tags) ->
|
||||
@tags or= {}
|
||||
@condition = if @tags.invert then condition.invert() else condition
|
||||
constructor: (condition, @body, tags) ->
|
||||
@tags = tags or= {}
|
||||
@condition = if tags.invert then condition.invert() else condition
|
||||
@soakNode = tags.soak
|
||||
@elseBody = null
|
||||
@isChain = false
|
||||
|
||||
@@ -1592,7 +1581,16 @@ exports.If = class If extends Base
|
||||
ifPart = @condition.compile(o) + ' ? ' + @bodyNode().compile(o)
|
||||
elsePart = if @elseBody then @elseBodyNode().compile(o) else 'undefined'
|
||||
code = "#{ifPart} : #{elsePart}"
|
||||
if @tags.operation then "(#{code})" else code
|
||||
if @tags.operation or @soakNode then "(#{code})" else code
|
||||
|
||||
unfoldSoak: -> @soakNode and this
|
||||
|
||||
# Unfold a node's child if soak, then tuck the node under created `If`
|
||||
@unfoldSoak: (o, parent, name) ->
|
||||
return unless ifn = parent[name].unfoldSoak o
|
||||
parent[name] = ifn.body
|
||||
ifn.body = new Value parent
|
||||
ifn
|
||||
|
||||
# Faux-Nodes
|
||||
# ----------
|
||||
|
||||
@@ -113,21 +113,26 @@ obj = {
|
||||
returnThis: -> this
|
||||
}
|
||||
|
||||
ok plus1?(41) is 42
|
||||
ok (plus1? 41) is 42
|
||||
ok plus2?(41) is undefined
|
||||
ok (plus2? 41) is undefined
|
||||
ok obj.returnThis?() is obj
|
||||
ok obj.counter().counter().returnThis?() is obj
|
||||
ok count is 2
|
||||
eq plus1?(41), 42
|
||||
eq (plus1? 41), 42
|
||||
eq plus2?(41), undefined
|
||||
eq (plus2? 41), undefined
|
||||
eq obj.returnThis?(), obj
|
||||
eq obj.returnSelf?(), undefined
|
||||
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
|
||||
|
||||
ok maybe_close(plus1, 41)?() is 42
|
||||
ok (maybe_close plus1, 41)?() is 42
|
||||
ok (maybe_close 'string', 41)?() is undefined
|
||||
eq maybe_close(plus1, 41)?(), 42
|
||||
eq (maybe_close plus1, 41)?(), 42
|
||||
eq (maybe_close 'string', 41)?(), undefined
|
||||
|
||||
eq 2?(3), undefined
|
||||
eq new Number?(42) | 0, 42
|
||||
eq new Bumper?(42) | 0, 0
|
||||
|
||||
#726
|
||||
eq calendar?[Date()], undefined
|
||||
|
||||
Reference in New Issue
Block a user