fixing issue #485, unary minus following an existential operator.

This commit is contained in:
Jeremy Ashkenas 2010-07-10 11:27:43 -04:00
parent 49020208f9
commit 1c7d51a2c4
7 changed files with 32 additions and 21 deletions

View File

@ -565,7 +565,7 @@
return new OpNode('&&', $1, $3);
}), o("Expression || Expression", function() {
return new OpNode('||', $1, $3);
}), o("Expression ? Expression", function() {
}), o("Expression OP? Expression", function() {
return new OpNode('?', $1, $3);
}), o("Expression -= Expression", function() {
return new OpNode('-=', $1, $3);
@ -596,7 +596,7 @@
})
]
};
operators = [["left", '?'], ["nonassoc", 'UMINUS', 'UPLUS', '!', '!!', '~', '++', '--'], ["left", '*', '/', '%'], ["left", '+', '-'], ["left", '<<', '>>', '>>>'], ["left", '&', '|', '^'], ["left", '<=', '<', '>', '>='], ["right", 'DELETE', 'INSTANCEOF', 'TYPEOF'], ["left", '==', '!='], ["left", '&&', '||'], ["right", '-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?='], ["left", '.'], ["right", 'INDENT'], ["left", 'OUTDENT'], ["right", 'WHEN', 'LEADING_WHEN', 'IN', 'OF', 'BY', 'THROW'], ["right", 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'NEW', 'SUPER', 'CLASS'], ["left", 'EXTENDS'], ["right", 'ASSIGN', 'RETURN'], ["right", '->', '=>', 'UNLESS', 'IF', 'ELSE']];
operators = [["left", '?'], ["nonassoc", 'UMINUS', 'UPLUS', '!', '!!', '~', '++', '--'], ["left", '*', '/', '%'], ["left", '+', '-'], ["left", '<<', '>>', '>>>'], ["left", '&', '|', '^'], ["left", '<=', '<', '>', '>='], ["right", 'DELETE', 'INSTANCEOF', 'TYPEOF'], ["left", '==', '!='], ["left", '&&', '||', 'OP?'], ["right", '-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?='], ["left", '.'], ["right", 'INDENT'], ["left", 'OUTDENT'], ["right", 'WHEN', 'LEADING_WHEN', 'IN', 'OF', 'BY', 'THROW'], ["right", 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'NEW', 'SUPER', 'CLASS'], ["left", 'EXTENDS'], ["right", 'ASSIGN', 'RETURN'], ["right", '->', '=>', 'UNLESS', 'IF', 'ELSE']];
tokens = [];
_a = grammar;
for (name in _a) { if (__hasProp.call(_a, name)) {

View File

@ -306,6 +306,8 @@
}
} else if (value === ';') {
tag = 'TERMINATOR';
} else if (value === '?' && prevSpaced) {
tag = 'OP?';
} else if (include(CALLABLE, this.tag()) && !prevSpaced) {
if (value === '(') {
tag = 'CALL_START';
@ -363,6 +365,9 @@
};
Lexer.prototype.tagHalfAssignment = function(tag) {
var last;
if (tag === 'OP?') {
tag = '?';
}
last = this.tokens.pop();
this.tokens.push([("" + tag + "="), ("" + tag + "="), last[2]]);
return true;
@ -600,7 +605,7 @@
NOT_REGEX = ['NUMBER', 'REGEX', '++', '--', 'FALSE', 'NULL', 'TRUE', ']'];
CALLABLE = ['IDENTIFIER', 'SUPER', ')', ']', '}', 'STRING', '@', 'THIS', '?', '::'];
LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR'];
HALF_ASSIGNMENTS = ['-', '+', '/', '*', '%', '||', '&&', '?'];
HALF_ASSIGNMENTS = ['-', '+', '/', '*', '%', '||', '&&', '?', 'OP?'];
CONVERSIONS = {
'and': '&&',
'or': '||',

View File

@ -96,10 +96,10 @@
BaseNode.prototype.traverse = function(block) {
return this.traverseChildren(true, block);
};
BaseNode.prototype.toString = function(idt) {
var _b, _c, _d, _e, child;
BaseNode.prototype.toString = function(idt, override) {
var _b, _c, _d, _e, child, children;
idt = idt || '';
return '\n' + idt + this['class'] + (function() {
children = (function() {
_b = []; _d = this.collectChildren();
for (_c = 0, _e = _d.length; _c < _e; _c++) {
child = _d[_c];
@ -107,6 +107,7 @@
}
return _b;
}).call(this).join('');
return '\n' + idt + (override || this['class']) + children;
};
BaseNode.prototype.eachChild = function(func) {
var _b, _c, _d, _e, _f, _g, _h, attr, child;
@ -1114,6 +1115,9 @@
OpNode.prototype.isChainable = function() {
return indexOf(this.CHAINABLE, this.operator) >= 0;
};
OpNode.prototype.toString = function(idt) {
return OpNode.__superClass__.toString.call(this, idt, this['class'] + ' ' + this.operator);
};
OpNode.prototype.compileNode = function(o) {
o.operation = true;
if (this.isChainable() && this.first.unwrap() instanceof OpNode && this.first.unwrap().isChainable()) {

File diff suppressed because one or more lines are too long

View File

@ -538,7 +538,7 @@ grammar: {
o "Expression && Expression", -> new OpNode '&&', $1, $3
o "Expression || Expression", -> new OpNode '||', $1, $3
o "Expression ? Expression", -> new OpNode '?', $1, $3
o "Expression OP? Expression", -> new OpNode '?', $1, $3
o "Expression -= Expression", -> new OpNode '-=', $1, $3
o "Expression += Expression", -> new OpNode '+=', $1, $3
@ -579,7 +579,7 @@ operators: [
["left", '<=', '<', '>', '>=']
["right", 'DELETE', 'INSTANCEOF', 'TYPEOF']
["left", '==', '!=']
["left", '&&', '||']
["left", '&&', '||', 'OP?']
["right", '-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=']
["left", '.']
["right", 'INDENT']

View File

@ -98,8 +98,8 @@ exports.Lexer: class Lexer
else if include(RESERVED, id)
@identifierError id
unless forcedIdentifier
tag: id: CONVERSIONS[id] if include COFFEE_ALIASES, id
return @tagHalfAssignment tag if @prev() and @prev()[0] is 'ASSIGN' and include HALF_ASSIGNMENTS, tag
tag: id: CONVERSIONS[id] if include COFFEE_ALIASES, id
return @tagHalfAssignment tag if @prev() and @prev()[0] is 'ASSIGN' and include HALF_ASSIGNMENTS, tag
@token tag, id
@token ']', ']' if close_index
true
@ -264,6 +264,8 @@ exports.Lexer: class Lexer
@assignmentError() if include JS_FORBIDDEN, @value
else if value is ';'
tag: 'TERMINATOR'
else if value is '?' and prevSpaced
tag: 'OP?'
else if include(CALLABLE, @tag()) and not prevSpaced
if value is '('
tag: 'CALL_START'
@ -309,6 +311,7 @@ exports.Lexer: class Lexer
# Tag a half assignment.
tagHalfAssignment: (tag) ->
tag: '?' if tag is 'OP?'
last: @tokens.pop()
@tokens.push ["$tag=", "$tag=", last[2]]
true
@ -554,7 +557,7 @@ CALLABLE: ['IDENTIFIER', 'SUPER', ')', ']', '}', 'STRING', '@', 'THIS', '?', '::
LINE_BREAK: ['INDENT', 'OUTDENT', 'TERMINATOR']
# Half-assignments...
HALF_ASSIGNMENTS: ['-', '+', '/', '*', '%', '||', '&&', '?']
HALF_ASSIGNMENTS: ['-', '+', '/', '*', '%', '||', '&&', '?', 'OP?']
# Conversions from CoffeeScript operators into JavaScript ones.
CONVERSIONS: {

View File

@ -78,3 +78,8 @@ ok result
# Safely calls values off of non-existent variables.
result: nothing?.value
ok result is undefined
# Assign to the result of an exsitential operation with a minus.
x: null ? - 1
ok x is - 1