compound assignments are now represented as Assign nodes (rather than Op) and have the same precedence as `=`

This commit is contained in:
satyr 2010-10-20 09:42:12 +09:00
parent 15cfe8ebf1
commit 90a13bd791
15 changed files with 111 additions and 112 deletions

File diff suppressed because one or more lines are too long

View File

@ -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);

View File

@ -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;

View File

@ -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;
}
}

View File

@ -143,7 +143,7 @@
quote: quote,
indent: null
});
if (quote === '"' && (0 <= doc.indexOf('#{'))) {
if (quote === '"' && 0 <= doc.indexOf('#{')) {
this.interpolateString(doc, {
heredoc: true
});

View File

@ -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()) {

View File

@ -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

View File

@ -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;
};

View File

@ -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']
]

View File

@ -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'

View File

@ -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) ->

View File

@ -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

View File

@ -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

View File

@ -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