compound assignments are now represented as Assign nodes (rather than Op) and have the same precedence as `=`
This commit is contained in:
parent
15cfe8ebf1
commit
90a13bd791
File diff suppressed because one or more lines are too long
|
@ -133,7 +133,7 @@
|
|||
code = '';
|
||||
stdin = process.openStdin();
|
||||
stdin.on('data', function(buffer) {
|
||||
return buffer ? code += buffer.toString() : undefined;
|
||||
return buffer ? (code += buffer.toString()) : undefined;
|
||||
});
|
||||
return stdin.on('end', function() {
|
||||
return compileScript('stdio', code);
|
||||
|
|
|
@ -2,14 +2,16 @@
|
|||
var Parser, _i, _j, _len, _len2, _ref, _result, alt, alternatives, grammar, name, o, operators, token, tokens, unwrap;
|
||||
var __hasProp = Object.prototype.hasOwnProperty;
|
||||
Parser = require('jison').Parser;
|
||||
unwrap = /function\s*\(\)\s*\{\s*return\s*([\s\S]*);\s*\}/;
|
||||
unwrap = /^function\s*\(\)\s*\{\s*return\s*([\s\S]*);\s*\}/;
|
||||
o = function(patternString, action, options) {
|
||||
var match;
|
||||
patternString = patternString.replace(/\s{2,}/g, ' ');
|
||||
if (!action) {
|
||||
return [patternString, '$$ = $1;', options];
|
||||
}
|
||||
action = (match = (action + '').match(unwrap)) ? match[1] : ("(" + action + "())");
|
||||
action = action.replace(/\bnew (\w+)\b/g, 'new yy.$1').replace(/Expressions\.wrap/g, 'yy.Expressions.wrap');
|
||||
action = (match = unwrap.exec(action)) ? match[1] : ("(" + action + "())");
|
||||
action = action.replace(/\bnew /g, '$&yy.');
|
||||
action = action.replace(/\bExpressions\.wrap\b/g, 'yy.$&');
|
||||
return [patternString, ("$$ = " + action + ";"), options];
|
||||
};
|
||||
grammar = {
|
||||
|
@ -78,11 +80,11 @@
|
|||
AssignObj: [
|
||||
o("Identifier", function() {
|
||||
return new Value($1);
|
||||
}), o("AlphaNumeric"), o("ThisProperty"), o("Identifier : Expression", function() {
|
||||
}), o("AlphaNumeric"), o("ThisProperty"), o("Identifier : Expression", function() {
|
||||
return new Assign(new Value($1), $3, 'object');
|
||||
}), o("AlphaNumeric : Expression", function() {
|
||||
return new Assign(new Value($1), $3, 'object');
|
||||
}), o("Identifier : INDENT Expression OUTDENT", function() {
|
||||
}), o("Identifier : INDENT Expression OUTDENT", function() {
|
||||
return new Assign(new Value($1), $4, 'object');
|
||||
}), o("AlphaNumeric : INDENT Expression OUTDENT", function() {
|
||||
return new Assign(new Value($1), $4, 'object');
|
||||
|
@ -580,15 +582,15 @@
|
|||
}), o("Expression LOGIC Expression", function() {
|
||||
return new Op($2, $1, $3);
|
||||
}), o("SimpleAssignable COMPOUND_ASSIGN Expression", function() {
|
||||
return new Op($2, $1, $3);
|
||||
return new Assign($1, $3, $2);
|
||||
}), o("SimpleAssignable COMPOUND_ASSIGN INDENT Expression OUTDENT", function() {
|
||||
return new Op($2, $1, $4);
|
||||
return new Assign($1, $4, $2);
|
||||
}), o("Expression RELATION Expression", function() {
|
||||
return $2.charAt(0) === '!' ? ($2 === '!in' ? new Op('!', new In($1, $3)) : new Op('!', new Parens(new Op($2.slice(1), $1, $3)))) : ($2 === 'in' ? new In($1, $3) : new Op($2, $1, $3));
|
||||
})
|
||||
]
|
||||
};
|
||||
operators = [["left", 'CALL_START', 'CALL_END'], ["nonassoc", '++', '--'], ["left", '?'], ["right", 'UNARY'], ["left", 'MATH'], ["left", '+', '-'], ["left", 'SHIFT'], ["left", 'COMPARE'], ["left", 'RELATION'], ["left", '==', '!='], ["left", 'LOGIC'], ["right", 'COMPOUND_ASSIGN'], ["left", '.'], ["nonassoc", 'INDENT', 'OUTDENT'], ["right", 'WHEN', 'LEADING_WHEN', 'FORIN', 'FOROF', 'BY', 'THROW'], ["right", 'IF', 'UNLESS', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS', 'EXTENDS'], ["right", '=', ':', 'RETURN'], ["right", '->', '=>', 'UNLESS', 'POST_IF', 'POST_UNLESS']];
|
||||
operators = [["left", 'CALL_START', 'CALL_END'], ["nonassoc", '++', '--'], ["left", '?'], ["right", 'UNARY'], ["left", 'MATH'], ["left", '+', '-'], ["left", 'SHIFT'], ["left", 'COMPARE'], ["left", 'RELATION'], ["left", '==', '!='], ["left", 'LOGIC'], ["left", '.'], ["nonassoc", 'INDENT', 'OUTDENT'], ["right", 'WHEN', 'LEADING_WHEN', 'FORIN', 'FOROF', 'BY', 'THROW'], ["right", 'IF', 'UNLESS', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS', 'EXTENDS'], ["right", '=', ':', 'COMPOUND_ASSIGN', 'RETURN'], ["right", '->', '=>', 'UNLESS', 'POST_IF', 'POST_UNLESS']];
|
||||
tokens = [];
|
||||
for (name in grammar) {
|
||||
if (!__hasProp.call(grammar, name)) continue;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
var _len, index, other;
|
||||
for (index = 0, _len = array.length; index < _len; index++) {
|
||||
other = array[index];
|
||||
if (other === item && (!from || (from <= index))) {
|
||||
if (other === item && (!from || from <= index)) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -143,7 +143,7 @@
|
|||
quote: quote,
|
||||
indent: null
|
||||
});
|
||||
if (quote === '"' && (0 <= doc.indexOf('#{'))) {
|
||||
if (quote === '"' && 0 <= doc.indexOf('#{')) {
|
||||
this.interpolateString(doc, {
|
||||
heredoc: true
|
||||
});
|
||||
|
|
72
lib/nodes.js
72
lib/nodes.js
|
@ -62,13 +62,7 @@
|
|||
return pair;
|
||||
};
|
||||
Base.prototype.idt = function(tabs) {
|
||||
var idt, num;
|
||||
idt = this.tab || '';
|
||||
num = (tabs || 0) + 1;
|
||||
while (num -= 1) {
|
||||
idt += TAB;
|
||||
}
|
||||
return idt;
|
||||
return (this.tab || '') + Array((tabs || 0) + 1).join(TAB);
|
||||
};
|
||||
Base.prototype.makeReturn = function() {
|
||||
return new Return(this);
|
||||
|
@ -154,6 +148,7 @@
|
|||
Base.prototype.isStatement = NO;
|
||||
Base.prototype.isPureStatement = NO;
|
||||
Base.prototype.isComplex = YES;
|
||||
Base.prototype.isChainable = NO;
|
||||
Base.prototype.topSensitive = NO;
|
||||
Base.prototype.unfoldSoak = NO;
|
||||
Base.prototype.assigns = NO;
|
||||
|
@ -189,7 +184,7 @@
|
|||
var end, idx;
|
||||
end = this.expressions[(idx = this.expressions.length - 1)];
|
||||
if (end instanceof Comment) {
|
||||
end = this.expressions[idx -= 1];
|
||||
end = this.expressions[(idx -= 1)];
|
||||
}
|
||||
if (end && !(end instanceof Return)) {
|
||||
this.expressions[idx] = end.makeReturn();
|
||||
|
@ -718,7 +713,7 @@
|
|||
};
|
||||
Range.prototype.compileArray = function(o) {
|
||||
var _i, _ref2, _ref3, _result, body, clause, i, idt, post, pre, range, result, vars;
|
||||
if (this.fromNum && this.toNum && (Math.abs(this.fromNum - this.toNum) <= 20)) {
|
||||
if (this.fromNum && this.toNum && Math.abs(this.fromNum - this.toNum) <= 20) {
|
||||
range = (function() {
|
||||
_result = [];
|
||||
for (var _i = _ref2 = +this.fromNum, _ref3 = +this.toNum; _ref2 <= _ref3 ? _i <= _ref3 : _i >= _ref3; _ref2 <= _ref3 ? _i += 1 : _i -= 1){ _result.push(_i); }
|
||||
|
@ -761,7 +756,7 @@
|
|||
var from, to;
|
||||
from = this.range.from ? this.range.from.compile(o) : '0';
|
||||
to = this.range.to ? this.range.to.compile(o) : '';
|
||||
to += (!to || this.range.exclusive ? '' : ' + 1');
|
||||
to += !to || this.range.exclusive ? '' : ' + 1';
|
||||
if (to) {
|
||||
to = ', ' + to;
|
||||
}
|
||||
|
@ -894,7 +889,7 @@
|
|||
return this;
|
||||
};
|
||||
Class.prototype.compileNode = function(o) {
|
||||
var _i, _len, _ref2, _ref3, _ref4, access, applied, apply, className, constScope, construct, constructor, extension, func, me, pname, prop, props, pvar, ref, returns, val, variable;
|
||||
var _i, _len, _ref2, _ref3, access, applied, apply, className, constScope, construct, constructor, extension, func, me, pname, prop, props, pvar, ref, returns, val, variable;
|
||||
variable = this.variable;
|
||||
if (variable.value === '__temp__') {
|
||||
variable = new Literal(o.scope.freeVariable('ctor'));
|
||||
|
@ -913,10 +908,10 @@
|
|||
}
|
||||
for (_i = 0, _len = (_ref2 = this.properties).length; _i < _len; _i++) {
|
||||
prop = _ref2[_i];
|
||||
_ref3 = [prop.variable, prop.value], pvar = _ref3[0], func = _ref3[1];
|
||||
pvar = prop.variable, func = prop.value;
|
||||
if (pvar && pvar.base.value === 'constructor') {
|
||||
if (!(func instanceof Code)) {
|
||||
_ref4 = func.compileReference(o), func = _ref4[0], ref = _ref4[1];
|
||||
_ref3 = func.compileReference(o), func = _ref3[0], ref = _ref3[1];
|
||||
if (func !== ref) {
|
||||
props.push(func);
|
||||
}
|
||||
|
@ -931,7 +926,7 @@
|
|||
variable = new Value(variable);
|
||||
variable.namespaced = 0 < className.indexOf('.');
|
||||
constructor = func;
|
||||
if (props.expressions[props.expressions.length - 1] instanceof Comment) {
|
||||
if (last(props.expressions) instanceof Comment) {
|
||||
constructor.comment = props.expressions.pop();
|
||||
}
|
||||
continue;
|
||||
|
@ -984,14 +979,12 @@
|
|||
})();
|
||||
__extends(Assign, Base);
|
||||
Assign.prototype.METHOD_DEF = /^(?:(\S+)\.prototype\.)?([$A-Za-z_][$\w]*)$/;
|
||||
Assign.prototype.CONDITIONAL = ['||=', '&&=', '?='];
|
||||
Assign.prototype.children = ['variable', 'value'];
|
||||
Assign.prototype.topSensitive = YES;
|
||||
Assign.prototype.isValue = function() {
|
||||
return this.variable instanceof Value;
|
||||
};
|
||||
Assign.prototype.compileNode = function(o) {
|
||||
var ifn, isValue, match, name, stmt, top, val;
|
||||
if (isValue = this.isValue()) {
|
||||
if (isValue = this.variable instanceof Value) {
|
||||
if (this.variable.isArray() || this.variable.isObject()) {
|
||||
return this.compilePatternMatch(o);
|
||||
}
|
||||
|
@ -1002,6 +995,9 @@
|
|||
delete o.top;
|
||||
return ifn.compile(o);
|
||||
}
|
||||
if (include(this.CONDITIONAL, this.context)) {
|
||||
return this.compileConditional(o);
|
||||
}
|
||||
}
|
||||
top = del(o, 'top');
|
||||
stmt = del(o, 'asStatement');
|
||||
|
@ -1017,7 +1013,7 @@
|
|||
if (!(isValue && (this.variable.hasProperties() || this.variable.namespaced))) {
|
||||
o.scope.find(name);
|
||||
}
|
||||
val = ("" + name + " = " + val);
|
||||
val = name + (" " + (this.context || '=') + " ") + val;
|
||||
if (stmt) {
|
||||
return ("" + (this.tab) + val + ";");
|
||||
}
|
||||
|
@ -1099,6 +1095,11 @@
|
|||
val = this.value.compile(o);
|
||||
return "([].splice.apply(" + name + ", [" + from + ", " + to + "].concat(" + ref + " = " + val + ")), " + ref + ")";
|
||||
};
|
||||
Assign.prototype.compileConditional = function(o) {
|
||||
var _ref2, left, rite;
|
||||
_ref2 = this.variable.cacheReference(o), left = _ref2[0], rite = _ref2[1];
|
||||
return new Op(this.context.slice(0, -1), left, new Assign(rite, this.value)).compile(o);
|
||||
};
|
||||
Assign.prototype.assigns = function(name) {
|
||||
return this[this.context === 'object' ? 'value' : 'variable'].assigns(name);
|
||||
};
|
||||
|
@ -1373,15 +1374,15 @@
|
|||
Op.prototype.CONVERSIONS = {
|
||||
'==': '===',
|
||||
'!=': '!==',
|
||||
of: 'in'
|
||||
'of': 'in'
|
||||
};
|
||||
Op.prototype.INVERSIONS = {
|
||||
'!==': '===',
|
||||
'===': '!=='
|
||||
};
|
||||
Op.prototype.CHAINABLE = ['<', '>', '>=', '<=', '===', '!=='];
|
||||
Op.prototype.ASSIGNMENT = ['||=', '&&=', '?='];
|
||||
Op.prototype.PREFIX_OPERATORS = ['new', 'typeof', 'delete'];
|
||||
Op.prototype.MUTATORS = ['++', '--', 'delete'];
|
||||
Op.prototype.children = ['first', 'second'];
|
||||
Op.prototype.isUnary = function() {
|
||||
return !this.second;
|
||||
|
@ -1389,10 +1390,6 @@
|
|||
Op.prototype.isComplex = function() {
|
||||
return this.operator !== '!' || this.first.isComplex();
|
||||
};
|
||||
Op.prototype.isMutator = function() {
|
||||
var _ref2;
|
||||
return ends(this.operator, '=') && !((_ref2 = this.operator) === '===' || _ref2 === '!==');
|
||||
};
|
||||
Op.prototype.isChainable = function() {
|
||||
return include(this.CHAINABLE, this.operator);
|
||||
};
|
||||
|
@ -1407,27 +1404,20 @@
|
|||
return Op.__super__.toString.call(this, idt, this.constructor.name + ' ' + this.operator);
|
||||
};
|
||||
Op.prototype.compileNode = function(o) {
|
||||
if (this.second) {
|
||||
this.first.tags.front = this.tags.front;
|
||||
}
|
||||
if (this.isChainable() && this.first.unwrap() instanceof Op && this.first.unwrap().isChainable()) {
|
||||
var ifn;
|
||||
if (this.isChainable() && this.first.unwrap().isChainable()) {
|
||||
return this.compileChain(o);
|
||||
}
|
||||
if (include(this.ASSIGNMENT, this.operator)) {
|
||||
return this.compileAssignment(o);
|
||||
}
|
||||
if (this.isUnary()) {
|
||||
if (include(this.MUTATORS, this.operator) && (ifn = If.unfoldSoak(o, this, 'first'))) {
|
||||
return ifn.compile(o);
|
||||
}
|
||||
return this.compileUnary(o);
|
||||
}
|
||||
if (this.operator === '?') {
|
||||
return this.compileExistence(o);
|
||||
}
|
||||
if (this.first instanceof Op && this.first.isMutator()) {
|
||||
this.first = new Parens(this.first);
|
||||
}
|
||||
if (this.second instanceof Op && this.second.isMutator()) {
|
||||
this.second = new Parens(this.second);
|
||||
}
|
||||
this.first.tags.front = this.tags.front;
|
||||
return [this.first.compile(o), this.operator, this.second.compile(o)].join(' ');
|
||||
};
|
||||
Op.prototype.compileChain = function(o) {
|
||||
|
@ -1437,12 +1427,6 @@
|
|||
_ref3 = [this.first.compile(o), this.second.compile(o), shared.compile(o)], first = _ref3[0], second = _ref3[1], shared = _ref3[2];
|
||||
return "(" + first + ") && (" + shared + " " + (this.operator) + " " + second + ")";
|
||||
};
|
||||
Op.prototype.compileAssignment = function(o) {
|
||||
var _ref2, left, rite;
|
||||
_ref2 = this.first.cacheReference(o), left = _ref2[0], rite = _ref2[1];
|
||||
rite = new Assign(rite, this.second);
|
||||
return new Op(this.operator.slice(0, -1), left, rite).compile(o);
|
||||
};
|
||||
Op.prototype.compileExistence = function(o) {
|
||||
var fst, ref;
|
||||
if (this.first.isComplex()) {
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
for (_i = 0, _len2 = (_ref = this.rules).length; _i < _len2; _i++) {
|
||||
rule = _ref[_i];
|
||||
if (rule.shortFlag === arg || rule.longFlag === arg) {
|
||||
value = rule.hasArgument ? args[i += 1] : true;
|
||||
value = rule.hasArgument ? args[(i += 1)] : true;
|
||||
options[rule.name] = rule.isList ? (options[rule.name] || []).concat(value) : value;
|
||||
matchedRule = true;
|
||||
break;
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -27,7 +27,7 @@
|
|||
tokens = this.tokens;
|
||||
i = 0;
|
||||
while (token = tokens[i]) {
|
||||
i += block.call(this, token, i, tokens);
|
||||
(i += block.call(this, token, i, tokens));
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
|
|
@ -23,7 +23,7 @@ Parser = require('jison').Parser
|
|||
# Since we're going to be wrapped in a function by Jison in any case, if our
|
||||
# action immediately returns a value, we can optimize by removing the function
|
||||
# wrapper and just returning the value directly.
|
||||
unwrap = /function\s*\(\)\s*\{\s*return\s*([\s\S]*);\s*\}/
|
||||
unwrap = /^function\s*\(\)\s*\{\s*return\s*([\s\S]*);\s*\}/
|
||||
|
||||
# Our handy DSL for Jison grammar generation, thanks to
|
||||
# [Tim Caswell](http://github.com/creationix). For every rule in the grammar,
|
||||
|
@ -31,9 +31,11 @@ unwrap = /function\s*\(\)\s*\{\s*return\s*([\s\S]*);\s*\}/
|
|||
# optionally. If no action is specified, we simply pass the value of the
|
||||
# previous nonterminal.
|
||||
o = (patternString, action, options) ->
|
||||
patternString = patternString.replace /\s{2,}/g, ' '
|
||||
return [patternString, '$$ = $1;', options] unless action
|
||||
action = if match = (action + '').match(unwrap) then match[1] else "(#{action}())"
|
||||
action = action.replace(/\bnew (\w+)\b/g, 'new yy.$1').replace(/Expressions\.wrap/g, 'yy.Expressions.wrap');
|
||||
action = if match = unwrap.exec action then match[1] else "(#{action}())"
|
||||
action = action.replace /\bnew /g, '$&yy.'
|
||||
action = action.replace /\bExpressions\.wrap\b/g, 'yy.$&'
|
||||
[patternString, "$$ = #{action};", options]
|
||||
|
||||
# Grammatical Rules
|
||||
|
@ -145,9 +147,9 @@ grammar =
|
|||
o "Identifier", -> new Value $1
|
||||
o "AlphaNumeric"
|
||||
o "ThisProperty"
|
||||
o "Identifier : Expression", -> new Assign new Value($1), $3, 'object'
|
||||
o "AlphaNumeric : Expression", -> new Assign new Value($1), $3, 'object'
|
||||
o "Identifier : INDENT Expression OUTDENT", -> new Assign new Value($1), $4, 'object'
|
||||
o "Identifier : Expression", -> new Assign new Value($1), $3, 'object'
|
||||
o "AlphaNumeric : Expression", -> new Assign new Value($1), $3, 'object'
|
||||
o "Identifier : INDENT Expression OUTDENT", -> new Assign new Value($1), $4, 'object'
|
||||
o "AlphaNumeric : INDENT Expression OUTDENT", -> new Assign new Value($1), $4, 'object'
|
||||
o "Comment"
|
||||
]
|
||||
|
@ -283,8 +285,8 @@ grammar =
|
|||
|
||||
# Assignments that can happen directly inside a class declaration.
|
||||
ClassAssign: [
|
||||
o "AssignObj", -> $1
|
||||
o "ThisProperty : Expression", -> new Assign new Value($1), $3, 'this'
|
||||
o "AssignObj", -> $1
|
||||
o "ThisProperty : Expression", -> new Assign new Value($1), $3, 'this'
|
||||
o "ThisProperty : INDENT Expression OUTDENT", -> new Assign new Value($1), $4, 'this'
|
||||
]
|
||||
|
||||
|
@ -541,8 +543,10 @@ grammar =
|
|||
o "Expression SHIFT Expression", -> new Op $2, $1, $3
|
||||
o "Expression COMPARE Expression", -> new Op $2, $1, $3
|
||||
o "Expression LOGIC Expression", -> new Op $2, $1, $3
|
||||
o "SimpleAssignable COMPOUND_ASSIGN Expression", -> new Op $2, $1, $3
|
||||
o "SimpleAssignable COMPOUND_ASSIGN INDENT Expression OUTDENT", -> new Op $2, $1, $4
|
||||
o "SimpleAssignable COMPOUND_ASSIGN Expression",
|
||||
-> new Assign $1, $3, $2
|
||||
o "SimpleAssignable COMPOUND_ASSIGN INDENT Expression OUTDENT",
|
||||
-> new Assign $1, $4, $2
|
||||
|
||||
o "Expression RELATION Expression", ->
|
||||
if $2.charAt(0) is '!'
|
||||
|
@ -578,12 +582,11 @@ operators = [
|
|||
["left", 'RELATION']
|
||||
["left", '==', '!=']
|
||||
["left", 'LOGIC']
|
||||
["right", 'COMPOUND_ASSIGN']
|
||||
["left", '.']
|
||||
["nonassoc", 'INDENT', 'OUTDENT']
|
||||
["right", 'WHEN', 'LEADING_WHEN', 'FORIN', 'FOROF', 'BY', 'THROW']
|
||||
["right", 'IF', 'UNLESS', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS', 'EXTENDS']
|
||||
["right", '=', ':', 'RETURN']
|
||||
["right", '=', ':', 'COMPOUND_ASSIGN', 'RETURN']
|
||||
["right", '->', '=>', 'UNLESS', 'POST_IF', 'POST_UNLESS']
|
||||
]
|
||||
|
||||
|
|
|
@ -70,10 +70,7 @@ exports.Base = class Base
|
|||
|
||||
# Convenience method to grab the current indentation level, plus tabbing in.
|
||||
idt: (tabs) ->
|
||||
idt = @tab or ''
|
||||
num = (tabs or 0) + 1
|
||||
idt += TAB while num -= 1
|
||||
idt
|
||||
(@tab or '') + Array((tabs or 0) + 1).join TAB
|
||||
|
||||
# Construct a node that returns the current node's result.
|
||||
# Note that this is overridden for smarter behavior for
|
||||
|
@ -140,6 +137,7 @@ exports.Base = class Base
|
|||
isStatement : NO
|
||||
isPureStatement : NO
|
||||
isComplex : YES
|
||||
isChainable : NO
|
||||
topSensitive : NO
|
||||
unfoldSoak : NO
|
||||
|
||||
|
@ -791,7 +789,7 @@ exports.Class = class Class extends Base
|
|||
constructor = new Code [], new Expressions [new Return new Literal 'this']
|
||||
|
||||
for prop in @properties
|
||||
[pvar, func] = [prop.variable, prop.value]
|
||||
{variable: pvar, value: func} = prop
|
||||
if pvar and pvar.base.value is 'constructor'
|
||||
if func not instanceof Code
|
||||
[func, ref] = func.compileReference o
|
||||
|
@ -804,7 +802,7 @@ exports.Class = class Class extends Base
|
|||
variable = new Value variable
|
||||
variable.namespaced = 0 < className.indexOf '.'
|
||||
constructor = func
|
||||
constructor.comment = props.expressions.pop() if props.expressions[props.expressions.length - 1] instanceof Comment
|
||||
constructor.comment = props.expressions.pop() if last(props.expressions) instanceof Comment
|
||||
continue
|
||||
if func instanceof Code and func.bound
|
||||
if prop.context is 'this'
|
||||
|
@ -839,6 +837,8 @@ exports.Assign = class Assign extends Base
|
|||
# Matchers for detecting class/method names
|
||||
METHOD_DEF: /^(?:(\S+)\.prototype\.)?([$A-Za-z_][$\w]*)$/
|
||||
|
||||
CONDITIONAL: ['||=', '&&=', '?=']
|
||||
|
||||
children: ['variable', 'value']
|
||||
|
||||
constructor: (@variable, @value, @context) ->
|
||||
|
@ -846,30 +846,28 @@ exports.Assign = class Assign extends Base
|
|||
|
||||
topSensitive: YES
|
||||
|
||||
isValue: ->
|
||||
@variable instanceof Value
|
||||
|
||||
# Compile an assignment, delegating to `compilePatternMatch` or
|
||||
# `compileSplice` if appropriate. Keep track of the name of the base object
|
||||
# we've been assigned to, for correct internal references. If the variable
|
||||
# has not been seen yet within the current scope, declare it.
|
||||
compileNode: (o) ->
|
||||
if isValue = @isValue()
|
||||
if isValue = @variable instanceof Value
|
||||
return @compilePatternMatch(o) if @variable.isArray() or @variable.isObject()
|
||||
return @compileSplice(o) if @variable.isSplice()
|
||||
if ifn = If.unfoldSoak o, this, 'variable'
|
||||
delete o.top
|
||||
return ifn.compile o
|
||||
top = del o, 'top'
|
||||
stmt = del o, 'asStatement'
|
||||
name = @variable.compile(o)
|
||||
return @compileConditional o if include @CONDITIONAL, @context
|
||||
top = del o, 'top'
|
||||
stmt = del o, 'asStatement'
|
||||
name = @variable.compile o
|
||||
if @value instanceof Code and match = @METHOD_DEF.exec name
|
||||
@value.name = match[2]
|
||||
@value.klass = match[1]
|
||||
val = @value.compile o
|
||||
return "#{name}: #{val}" if @context is 'object'
|
||||
o.scope.find name unless isValue and (@variable.hasProperties() or @variable.namespaced)
|
||||
val = "#{name} = #{val}"
|
||||
val = name + " #{ @context or '=' } " + val
|
||||
return "#{@tab}#{val};" if stmt
|
||||
if top or @parenthetical then val else "(#{val})"
|
||||
|
||||
|
@ -938,6 +936,13 @@ exports.Assign = class Assign extends Base
|
|||
val = @value.compile(o)
|
||||
"([].splice.apply(#{name}, [#{from}, #{to}].concat(#{ref} = #{val})), #{ref})"
|
||||
|
||||
# When compiling a conditional assignment, take care to ensure that the
|
||||
# operands are only evaluated once, even though we have to reference them
|
||||
# more than once.
|
||||
compileConditional: (o) ->
|
||||
[left, rite] = @variable.cacheReference o
|
||||
return new Op(@context.slice(0, -1), left, new Assign(rite, @value)).compile o
|
||||
|
||||
assigns: (name) ->
|
||||
@[if @context is 'object' then 'value' else 'variable'].assigns name
|
||||
|
||||
|
@ -1152,7 +1157,7 @@ exports.Op = class Op extends Base
|
|||
CONVERSIONS:
|
||||
'==': '==='
|
||||
'!=': '!=='
|
||||
of: 'in'
|
||||
'of': 'in'
|
||||
|
||||
# The map of invertible operators.
|
||||
INVERSIONS:
|
||||
|
@ -1163,12 +1168,12 @@ exports.Op = class Op extends Base
|
|||
# [Python-style comparison chaining](http://docs.python.org/reference/expressions.html#notin).
|
||||
CHAINABLE: ['<', '>', '>=', '<=', '===', '!==']
|
||||
|
||||
# Our assignment operators that have no JavaScript equivalent.
|
||||
ASSIGNMENT: ['||=', '&&=', '?=']
|
||||
|
||||
# Operators must come before their operands with a space.
|
||||
PREFIX_OPERATORS: ['new', 'typeof', 'delete']
|
||||
|
||||
# Operators that modify a reference.
|
||||
MUTATORS: ['++', '--', 'delete']
|
||||
|
||||
children: ['first', 'second']
|
||||
|
||||
constructor: (op, first, second, flip) ->
|
||||
|
@ -1186,9 +1191,6 @@ exports.Op = class Op extends Base
|
|||
|
||||
isComplex: -> @operator isnt '!' or @first.isComplex()
|
||||
|
||||
isMutator: ->
|
||||
ends(@operator, '=') and @operator not in ['===', '!==']
|
||||
|
||||
isChainable: ->
|
||||
include(@CHAINABLE, @operator)
|
||||
|
||||
|
@ -1205,13 +1207,12 @@ exports.Op = class Op extends Base
|
|||
super(idt, @constructor.name + ' ' + @operator)
|
||||
|
||||
compileNode: (o) ->
|
||||
@first.tags.front = @tags.front if @second
|
||||
return @compileChain(o) if @isChainable() and @first.unwrap() instanceof Op and @first.unwrap().isChainable()
|
||||
return @compileAssignment(o) if include @ASSIGNMENT, @operator
|
||||
return @compileUnary(o) if @isUnary()
|
||||
return @compileExistence(o) if @operator is '?'
|
||||
@first = new Parens @first if @first instanceof Op and @first.isMutator()
|
||||
@second = new Parens @second if @second instanceof Op and @second.isMutator()
|
||||
return @compileChain o if @isChainable() and @first.unwrap().isChainable()
|
||||
if @isUnary()
|
||||
return ifn.compile o if include(@MUTATORS, @operator) and ifn = If.unfoldSoak o, this, 'first'
|
||||
return @compileUnary o
|
||||
return @compileExistence o if @operator is '?'
|
||||
@first.tags.front = @tags.front
|
||||
[@first.compile(o), @operator, @second.compile(o)].join ' '
|
||||
|
||||
# Mimic Python's chained comparisons when multiple comparison operators are
|
||||
|
@ -1225,14 +1226,6 @@ exports.Op = class Op extends Base
|
|||
[first, second, shared] = [@first.compile(o), @second.compile(o), shared.compile(o)]
|
||||
"(#{first}) && (#{shared} #{@operator} #{second})"
|
||||
|
||||
# When compiling a conditional assignment, take care to ensure that the
|
||||
# operands are only evaluated once, even though we have to reference them
|
||||
# more than once.
|
||||
compileAssignment: (o) ->
|
||||
[left, rite] = @first.cacheReference o
|
||||
rite = new Assign rite, @second
|
||||
return new Op(@operator.slice(0, -1), left, rite).compile o
|
||||
|
||||
compileExistence: (o) ->
|
||||
if @first.isComplex()
|
||||
ref = o.scope.freeVariable 'ref'
|
||||
|
|
|
@ -42,7 +42,7 @@ class exports.Rewriter
|
|||
scanTokens: (block) ->
|
||||
{tokens} = this
|
||||
i = 0
|
||||
i += block.call this, token, i, tokens while token = tokens[i]
|
||||
(i += block.call this, token, i, tokens) while token = tokens[i]
|
||||
true
|
||||
|
||||
detectEnd: (i, condition, action) ->
|
||||
|
|
|
@ -19,7 +19,7 @@ ok(area(
|
|||
|
||||
sumOfArgs = ->
|
||||
sum = 0
|
||||
sum += val for val in arguments
|
||||
(sum += val) for val in arguments
|
||||
sum
|
||||
|
||||
ok sumOfArgs(1, 2, 3, 4, 5) is 15
|
||||
|
|
|
@ -134,12 +134,29 @@ eq 2?(3), undefined
|
|||
eq new Number?(42) | 0, 42
|
||||
eq new Bumper?(42) | 0, 0
|
||||
|
||||
|
||||
#726
|
||||
eq calendar?[Date()], undefined
|
||||
|
||||
|
||||
#733
|
||||
a = b: {c: null}
|
||||
eq a.b?.c?(), undefined
|
||||
|
||||
a.b?.c or= (it) -> it
|
||||
eq a.b?.c?(1), 1
|
||||
eq a.b?.c?([2, 3]...), 2
|
||||
|
||||
|
||||
#756
|
||||
a = null
|
||||
ok isNaN a?.b.c + 1
|
||||
eq undefined, a?.b.c += 1
|
||||
eq undefined, ++a?.b.c
|
||||
eq undefined, delete a?.b.c
|
||||
|
||||
a = b: {c: 0}
|
||||
eq 1, a?.b.c + 1
|
||||
eq 1, a?.b.c += 1
|
||||
eq 2, ++a?.b.c
|
||||
eq yes, delete a?.b.c
|
||||
|
|
|
@ -117,7 +117,7 @@ ok del() is 5
|
|||
|
||||
# Ensure that functions can have a trailing comma in their argument list
|
||||
mult = (x, mids..., y) ->
|
||||
x *= n for n in mids
|
||||
(x *= n) for n in mids
|
||||
x *= y
|
||||
|
||||
ok mult(1, 2,) is 2
|
||||
|
|
Loading…
Reference in New Issue