lexer now distinguishes between IN/OF and FORIN/FOROF to help grammar, fixing #737

This commit is contained in:
satyr
2010-10-06 04:46:17 +09:00
parent 380bee97dd
commit 2e6b50335f
8 changed files with 101 additions and 90 deletions

View File

@@ -460,38 +460,38 @@
}) })
], ],
ForSource: [ ForSource: [
o("IN Expression", function() { o("FORIN Expression", function() {
return { return {
source: $2 source: $2
}; };
}), o("OF Expression", function() { }), o("FOROF Expression", function() {
return { return {
source: $2, source: $2,
object: true object: true
}; };
}), o("IN Expression WHEN Expression", function() { }), o("FORIN Expression WHEN Expression", function() {
return { return {
source: $2, source: $2,
guard: $4 guard: $4
}; };
}), o("OF Expression WHEN Expression", function() { }), o("FOROF Expression WHEN Expression", function() {
return { return {
source: $2, source: $2,
guard: $4, guard: $4,
object: true object: true
}; };
}), o("IN Expression BY Expression", function() { }), o("FORIN Expression BY Expression", function() {
return { return {
source: $2, source: $2,
step: $4 step: $4
}; };
}), o("IN Expression WHEN Expression BY Expression", function() { }), o("FORIN Expression WHEN Expression BY Expression", function() {
return { return {
source: $2, source: $2,
guard: $4, guard: $4,
step: $6 step: $6
}; };
}), o("IN Expression BY Expression WHEN Expression", function() { }), o("FORIN Expression BY Expression WHEN Expression", function() {
return { return {
source: $2, source: $2,
step: $4, step: $4,
@@ -597,18 +597,12 @@
return new OpNode($2, $1, $3); return new OpNode($2, $1, $3);
}), o("Value COMPOUND_ASSIGN INDENT Expression OUTDENT", function() { }), o("Value COMPOUND_ASSIGN INDENT Expression OUTDENT", function() {
return new OpNode($2, $1, $4); return new OpNode($2, $1, $4);
}), o("Expression IN Expression", function() { }), o("Expression RELATION Expression", function() {
return new InNode($1, $3); return $2.charAt(0) === '!' ? ($2 === '!in' ? new OpNode('!', new InNode($1, $3)) : new OpNode('!', new ParentheticalNode(new OpNode($2.slice(1), $1, $3)))) : ($2 === 'in' ? new InNode($1, $3) : new OpNode($2, $1, $3));
}), o("Expression OF Expression", function() {
return new OpNode($2, $1, $3);
}), o("Expression INSTANCEOF Expression", function() {
return new OpNode($2, $1, $3);
}), o("Expression NOT_RELATED Expression", function() {
return $2 === 'in' ? new OpNode('!', new InNode($1, $3)) : new OpNode('!', new ParentheticalNode(new OpNode($2, $1, $3)));
}) })
] ]
}; };
operators = [["right", '?'], ["left", 'CALL_START', 'CALL_END'], ["nonassoc", '++', '--'], ["right", 'UNARY'], ["left", 'MATH'], ["left", '+', '-'], ["left", 'SHIFT'], ["left", 'COMPARE'], ["left", 'INSTANCEOF', 'NOT_RELATED'], ["left", '==', '!='], ["left", 'LOGIC'], ["right", 'COMPOUND_ASSIGN'], ["left", '.'], ["nonassoc", 'INDENT', 'OUTDENT'], ["right", 'WHEN', 'LEADING_WHEN', 'IN', 'OF', 'BY', 'THROW'], ["right", 'IF', 'UNLESS', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS', 'EXTENDS'], ["right", '=', ':', 'RETURN'], ["right", '->', '=>', 'UNLESS', 'POST_IF', 'POST_UNLESS']]; operators = [["right", '?'], ["left", 'CALL_START', 'CALL_END'], ["nonassoc", '++', '--'], ["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']];
tokens = []; tokens = [];
for (name in grammar) { for (name in grammar) {
if (!__hasProp.call(grammar, name)) continue; if (!__hasProp.call(grammar, name)) continue;

View File

@@ -1,5 +1,5 @@
(function() { (function() {
var ASSIGNED, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, CONVERSIONS, HEREDOC, HEREDOC_INDENT, HEREGEX, HEREGEX_OMIT, IDENTIFIER, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LEADING_SPACES, LINE_BREAK, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NEXT_CHARACTER, NOT_REGEX, NO_NEWLINE, NUMBER, OPERATOR, REGEX, RELATION, RESERVED, Rewriter, SHIFT, SIMPLESTR, TRAILING_SPACES, UNARY, WHITESPACE, _ref, compact, count, include, last, starts; var ASSIGNED, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HEREDOC, HEREDOC_INDENT, HEREGEX, HEREGEX_OMIT, IDENTIFIER, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LEADING_SPACES, LINE_BREAK, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NEXT_CHARACTER, NOT_REGEX, NO_NEWLINE, NUMBER, OPERATOR, REGEX, RELATION, RESERVED, Rewriter, SHIFT, SIMPLESTR, TRAILING_SPACES, UNARY, WHITESPACE, _ref, compact, count, include, last, op, starts;
Rewriter = require('./rewriter').Rewriter; Rewriter = require('./rewriter').Rewriter;
_ref = require('./helpers'), include = _ref.include, count = _ref.count, starts = _ref.starts, compact = _ref.compact, last = _ref.last; _ref = require('./helpers'), include = _ref.include, count = _ref.count, starts = _ref.starts, compact = _ref.compact, last = _ref.last;
exports.Lexer = (function() { exports.Lexer = (function() {
@@ -16,6 +16,7 @@
this.indent = 0; this.indent = 0;
this.indebt = 0; this.indebt = 0;
this.outdebt = 0; this.outdebt = 0;
this.seenFor = false;
this.indents = []; this.indents = [];
this.tokens = []; this.tokens = [];
while ((this.chunk = code.slice(this.i))) { while ((this.chunk = code.slice(this.i))) {
@@ -28,7 +29,7 @@
return (new Rewriter).rewrite(this.tokens); return (new Rewriter).rewrite(this.tokens);
}; };
Lexer.prototype.identifierToken = function() { Lexer.prototype.identifierToken = function() {
var _ref2, closeIndex, forcedIdentifier, id, match, tag; var closeIndex, forcedIdentifier, id, match, tag;
if (!(match = IDENTIFIER.exec(this.chunk))) { if (!(match = IDENTIFIER.exec(this.chunk))) {
return false; return false;
} }
@@ -44,11 +45,21 @@
tag = id.toUpperCase(); tag = id.toUpperCase();
if (tag === 'WHEN' && include(LINE_BREAK, this.tag())) { if (tag === 'WHEN' && include(LINE_BREAK, this.tag())) {
tag = 'LEADING_WHEN'; tag = 'LEADING_WHEN';
} else if (tag === 'FOR') {
this.seenFor = true;
} else if (include(UNARY, tag)) { } else if (include(UNARY, tag)) {
tag = 'UNARY'; tag = 'UNARY';
} else if (('not' === (_ref2 = include(RELATION, tag) && this.value()) || '!' === _ref2)) { } else if (include(RELATION, tag)) {
this.tokens.pop(); if (tag !== 'INSTANCEOF' && this.seenFor) {
tag = 'NOT_RELATED'; this.seenFor = false;
tag = 'FOR' + tag;
} else {
tag = 'RELATION';
if (this.value() === '!') {
this.tokens.pop();
id = '!' + id;
}
}
} }
} }
if (include(JS_FORBIDDEN, id)) { if (include(JS_FORBIDDEN, id)) {
@@ -67,8 +78,8 @@
} }
} }
if (!(forcedIdentifier)) { if (!(forcedIdentifier)) {
if (include(COFFEE_ALIASES, id)) { if (COFFEE_ALIASES.hasOwnProperty(id)) {
tag = (id = CONVERSIONS[id]); tag = (id = COFFEE_ALIASES[id]);
} }
if (id === '!') { if (id === '!') {
tag = 'UNARY'; tag = 'UNARY';
@@ -330,7 +341,7 @@
if (('or' === pval || 'and' === pval)) { if (('or' === pval || 'and' === pval)) {
prev = last(this.tokens); prev = last(this.tokens);
prev[0] = 'COMPOUND_ASSIGN'; prev[0] = 'COMPOUND_ASSIGN';
prev[1] = CONVERSIONS[pval] + '='; prev[1] = COFFEE_ALIASES[pval] + '=';
return true; return true;
} }
} }
@@ -575,8 +586,18 @@
return Lexer; return Lexer;
})(); })();
JS_KEYWORDS = ['if', 'else', 'true', 'false', 'new', 'return', 'try', 'catch', 'finally', 'throw', 'break', 'continue', 'for', 'in', 'while', 'delete', 'instanceof', 'typeof', 'switch', 'super', 'extends', 'class', 'this', 'null', 'debugger']; JS_KEYWORDS = ['if', 'else', 'true', 'false', 'new', 'return', 'try', 'catch', 'finally', 'throw', 'break', 'continue', 'for', 'in', 'while', 'delete', 'instanceof', 'typeof', 'switch', 'super', 'extends', 'class', 'this', 'null', 'debugger'];
COFFEE_ALIASES = ['and', 'or', 'is', 'isnt', 'not']; COFFEE_KEYWORDS = ['then', 'unless', 'until', 'loop', 'yes', 'no', 'on', 'off', 'of', 'by', 'when'];
COFFEE_KEYWORDS = COFFEE_ALIASES.concat(['then', 'unless', 'until', 'loop', 'yes', 'no', 'on', 'off', 'of', 'by', 'when']); COFFEE_ALIASES = {
and: '&&',
or: '||',
is: '==',
isnt: '!=',
not: '!'
};
for (op in COFFEE_ALIASES) {
COFFEE_KEYWORDS.push(op);
}
COFFEE_ALIASES['==='] = '==';
RESERVED = ['case', 'default', 'do', 'function', 'var', 'void', 'with', 'const', 'let', 'enum', 'export', 'import', 'native', '__hasProp', '__extends', '__slice']; RESERVED = ['case', 'default', 'do', 'function', 'var', 'void', 'with', 'const', 'let', 'enum', 'export', 'import', 'native', '__hasProp', '__extends', '__slice'];
JS_FORBIDDEN = JS_KEYWORDS.concat(RESERVED); JS_FORBIDDEN = JS_KEYWORDS.concat(RESERVED);
IDENTIFIER = /^[a-zA-Z_$][\w$]*/; IDENTIFIER = /^[a-zA-Z_$][\w$]*/;
@@ -609,12 +630,4 @@
NOT_REGEX = ['NUMBER', 'REGEX', '++', '--', 'FALSE', 'NULL', 'TRUE', ']']; NOT_REGEX = ['NUMBER', 'REGEX', '++', '--', 'FALSE', 'NULL', 'TRUE', ']'];
CALLABLE = ['IDENTIFIER', 'SUPER', ')', ']', '}', 'STRING', '@', 'THIS', '?', '::']; CALLABLE = ['IDENTIFIER', 'SUPER', ')', ']', '}', 'STRING', '@', 'THIS', '?', '::'];
LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR']; LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR'];
CONVERSIONS = {
'and': '&&',
'or': '||',
'is': '==',
'isnt': '!=',
'not': '!',
'===': '=='
};
}).call(this); }).call(this);

File diff suppressed because one or more lines are too long

View File

@@ -103,7 +103,7 @@
var action, condition; var action, condition;
condition = function(token, i) { condition = function(token, i) {
var _ref; var _ref;
return ((')' === (_ref = token[0]) || 'CALL_END' === _ref)) || token[0] === 'OUTDENT' && this.tag(i - 1) === ')'; return (')' === (_ref = token[0]) || 'CALL_END' === _ref) || token[0] === 'OUTDENT' && this.tag(i - 1) === ')';
}; };
action = function(token, i) { action = function(token, i) {
return (this.tokens[token[0] === 'OUTDENT' ? i - 1 : i][0] = 'CALL_END'); return (this.tokens[token[0] === 'OUTDENT' ? i - 1 : i][0] = 'CALL_END');
@@ -141,7 +141,7 @@
} }
_ref = this.tokens.slice(i + 1, i + 4), one = _ref[0], two = _ref[1], three = _ref[2]; _ref = this.tokens.slice(i + 1, i + 4), one = _ref[0], two = _ref[1], three = _ref[2];
tag = token[0]; tag = token[0];
return (('TERMINATOR' === tag || 'OUTDENT' === tag)) && !(((two != null) ? two[0] === ':' : undefined) || ((one != null) ? one[0] === '@' : undefined) && ((three != null) ? three[0] === ':' : undefined)) || tag === ',' && (!('IDENTIFIER' === (_ref = ((one != null) ? one[0] : undefined)) || 'STRING' === _ref || '@' === _ref || 'TERMINATOR' === _ref || 'OUTDENT' === _ref)); return ('TERMINATOR' === tag || 'OUTDENT' === tag) && !(((two != null) ? two[0] === ':' : undefined) || ((one != null) ? one[0] === '@' : undefined) && ((three != null) ? three[0] === ':' : undefined)) || tag === ',' && !('IDENTIFIER' === (_ref = ((one != null) ? one[0] : undefined)) || 'STRING' === _ref || '@' === _ref || 'TERMINATOR' === _ref || 'OUTDENT' === _ref);
}; };
action = function(token, i) { action = function(token, i) {
return this.tokens.splice(i, 0, ['}', '}', token[2]]); return this.tokens.splice(i, 0, ['}', '}', token[2]]);
@@ -198,7 +198,7 @@
if (prev && !prev.spaced && tag === '?') { if (prev && !prev.spaced && tag === '?') {
token.call = true; token.call = true;
} }
if (callObject || prev && prev.spaced && (prev.call || include(IMPLICIT_FUNC, prev[0])) && include(IMPLICIT_CALL, tag) && !(tag === 'UNARY' && (('IN' === (_ref = this.tag(i + 1)) || 'OF' === _ref || 'INSTANCEOF' === _ref)))) { if (callObject || prev && prev.spaced && (prev.call || include(IMPLICIT_FUNC, prev[0])) && include(IMPLICIT_CALL, tag) && !(tag === 'UNARY' && ('IN' === (_ref = this.tag(i + 1)) || 'OF' === _ref || 'INSTANCEOF' === _ref))) {
tokens.splice(i, 0, ['CALL_START', '(', token[2]]); tokens.splice(i, 0, ['CALL_START', '(', token[2]]);
condition = function(token, i) { condition = function(token, i) {
var post; var post;
@@ -231,7 +231,7 @@
tokens.splice.apply(tokens, [i, 0].concat(this.indentation(token))); tokens.splice.apply(tokens, [i, 0].concat(this.indentation(token)));
return 2; return 2;
} }
if (tag === 'CATCH' && (('TERMINATOR' === (_ref = this.tag(i + 2)) || 'FINALLY' === _ref))) { if (tag === 'CATCH' && ('TERMINATOR' === (_ref = this.tag(i + 2)) || 'FINALLY' === _ref)) {
tokens.splice.apply(tokens, [i + 2, 0].concat(this.indentation(token))); tokens.splice.apply(tokens, [i + 2, 0].concat(this.indentation(token)));
return 4; return 4;
} }

View File

@@ -476,13 +476,13 @@ grammar =
# clause. If it's an array comprehension, you can also choose to step through # clause. If it's an array comprehension, you can also choose to step through
# in fixed-size increments. # in fixed-size increments.
ForSource: [ ForSource: [
o "IN Expression", -> source: $2 o "FORIN Expression", -> source: $2
o "OF Expression", -> source: $2, object: true o "FOROF Expression", -> source: $2, object: true
o "IN Expression WHEN Expression", -> source: $2, guard: $4 o "FORIN Expression WHEN Expression", -> source: $2, guard: $4
o "OF Expression WHEN Expression", -> source: $2, guard: $4, object: true o "FOROF Expression WHEN Expression", -> source: $2, guard: $4, object: true
o "IN Expression BY Expression", -> source: $2, step: $4 o "FORIN Expression BY Expression", -> source: $2, step: $4
o "IN Expression WHEN Expression BY Expression", -> source: $2, guard: $4, step: $6 o "FORIN Expression WHEN Expression BY Expression", -> source: $2, guard: $4, step: $6
o "IN Expression BY Expression WHEN Expression", -> source: $2, step: $4, guard: $6 o "FORIN Expression BY Expression WHEN Expression", -> source: $2, step: $4, guard: $6
] ]
Switch: [ Switch: [
@@ -552,14 +552,14 @@ grammar =
o "Value COMPOUND_ASSIGN Expression", -> new OpNode $2, $1, $3 o "Value COMPOUND_ASSIGN Expression", -> new OpNode $2, $1, $3
o "Value COMPOUND_ASSIGN INDENT Expression OUTDENT", -> new OpNode $2, $1, $4 o "Value COMPOUND_ASSIGN INDENT Expression OUTDENT", -> new OpNode $2, $1, $4
o "Expression IN Expression", -> new InNode $1, $3 o "Expression RELATION Expression", ->
o "Expression OF Expression", -> new OpNode $2, $1, $3 if $2.charAt(0) is '!'
o "Expression INSTANCEOF Expression", -> new OpNode $2, $1, $3 if $2 is '!in'
o "Expression NOT_RELATED Expression", -> new OpNode '!', new InNode $1, $3
if $2 is 'in' else
new OpNode '!', new InNode $1, $3 new OpNode '!', new ParentheticalNode new OpNode $2[1..], $1, $3
else else
new OpNode '!', new ParentheticalNode new OpNode $2, $1, $3 if $2 is 'in' then new InNode $1, $3 else new OpNode $2, $1, $3
] ]
@@ -583,13 +583,13 @@ operators = [
["left", '+', '-'] ["left", '+', '-']
["left", 'SHIFT'] ["left", 'SHIFT']
["left", 'COMPARE'] ["left", 'COMPARE']
["left", 'INSTANCEOF', 'NOT_RELATED'] ["left", 'RELATION']
["left", '==', '!='] ["left", '==', '!=']
["left", 'LOGIC'] ["left", 'LOGIC']
["right", 'COMPOUND_ASSIGN'] ["right", 'COMPOUND_ASSIGN']
["left", '.'] ["left", '.']
["nonassoc", 'INDENT', 'OUTDENT'] ["nonassoc", 'INDENT', 'OUTDENT']
["right", 'WHEN', 'LEADING_WHEN', 'IN', 'OF', 'BY', 'THROW'] ["right", 'WHEN', 'LEADING_WHEN', 'FORIN', 'FOROF', 'BY', 'THROW']
["right", 'IF', 'UNLESS', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS', 'EXTENDS'] ["right", 'IF', 'UNLESS', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS', 'EXTENDS']
["right", '=', ':', 'RETURN'] ["right", '=', ':', 'RETURN']
["right", '->', '=>', 'UNLESS', 'POST_IF', 'POST_UNLESS'] ["right", '->', '=>', 'UNLESS', 'POST_IF', 'POST_UNLESS']

View File

@@ -41,6 +41,7 @@ exports.Lexer = class Lexer
@indent = 0 # The current indentation level. @indent = 0 # The current indentation level.
@indebt = 0 # The over-indentation at the current level. @indebt = 0 # The over-indentation at the current level.
@outdebt = 0 # The under-outdentation at the current level. @outdebt = 0 # The under-outdentation at the current level.
@seenFor = no # The flag for distinguishing FORIN/FOROF from IN/OF.
@indents = [] # The stack of all current indentation levels. @indents = [] # The stack of all current indentation levels.
@tokens = [] # Stream of parsed tokens in the form ['TYPE', value, line] @tokens = [] # Stream of parsed tokens in the form ['TYPE', value, line]
# At every position, run through this list of attempted matches, # At every position, run through this list of attempted matches,
@@ -84,11 +85,19 @@ exports.Lexer = class Lexer
tag = id.toUpperCase() tag = id.toUpperCase()
if tag is 'WHEN' and include LINE_BREAK, @tag() if tag is 'WHEN' and include LINE_BREAK, @tag()
tag = 'LEADING_WHEN' tag = 'LEADING_WHEN'
else if tag is 'FOR'
@seenFor = yes
else if include UNARY, tag else if include UNARY, tag
tag = 'UNARY' tag = 'UNARY'
else if include(RELATION, tag) and @value() in ['not', '!'] else if include RELATION, tag
@tokens.pop() if tag isnt 'INSTANCEOF' and @seenFor
tag = 'NOT_RELATED' @seenFor = no
tag = 'FOR' + tag
else
tag = 'RELATION'
if @value() is '!'
@tokens.pop()
id = '!' + id
if include JS_FORBIDDEN, id if include JS_FORBIDDEN, id
if forcedIdentifier if forcedIdentifier
tag = 'STRING' tag = 'STRING'
@@ -100,7 +109,7 @@ exports.Lexer = class Lexer
else if include(RESERVED, id) else if include(RESERVED, id)
@identifierError id @identifierError id
unless forcedIdentifier unless forcedIdentifier
tag = id = CONVERSIONS[id] if include COFFEE_ALIASES, id tag = id = COFFEE_ALIASES[id] if COFFEE_ALIASES.hasOwnProperty id
if id is '!' if id is '!'
tag = 'UNARY' tag = 'UNARY'
else if include LOGIC, id else if include LOGIC, id
@@ -307,7 +316,7 @@ exports.Lexer = class Lexer
if pval in ['or', 'and'] if pval in ['or', 'and']
prev = last @tokens prev = last @tokens
prev[0] = 'COMPOUND_ASSIGN' prev[0] = 'COMPOUND_ASSIGN'
prev[1] = CONVERSIONS[pval] + '=' prev[1] = COFFEE_ALIASES[pval] + '='
return true return true
if ';' is value then tag = 'TERMINATOR' if ';' is value then tag = 'TERMINATOR'
else if include LOGIC , value then tag = 'LOGIC' else if include LOGIC , value then tag = 'LOGIC'
@@ -511,14 +520,20 @@ JS_KEYWORDS = [
'this', 'null', 'debugger' 'this', 'null', 'debugger'
] ]
# CoffeeScript-only keywords, which we're more relaxed about allowing. They can't # CoffeeScript-only keywords.
# be used standalone, but you can reference them as an attached property. COFFEE_KEYWORDS = [
COFFEE_ALIASES = ['and', 'or', 'is', 'isnt', 'not']
COFFEE_KEYWORDS = COFFEE_ALIASES.concat [
'then', 'unless', 'until', 'loop' 'then', 'unless', 'until', 'loop'
'yes', 'no', 'on', 'off' 'yes', 'no', 'on', 'off'
'of', 'by', 'when' 'of', 'by', 'when'
] ]
COFFEE_ALIASES =
and : '&&'
or : '||'
is : '=='
isnt : '!='
not : '!'
COFFEE_KEYWORDS.push op for all op of COFFEE_ALIASES
COFFEE_ALIASES['==='] = '=='
# The list of keywords that are reserved by JavaScript, but not used, or are # The list of keywords that are reserved by JavaScript, but not used, or are
# used by CoffeeScript internally. We throw an error when these are encountered, # used by CoffeeScript internally. We throw an error when these are encountered,
@@ -611,12 +626,3 @@ CALLABLE = ['IDENTIFIER', 'SUPER', ')', ']', '}', 'STRING', '@', 'THIS', '?', ':
# occurs at the start of a line. We disambiguate these from trailing whens to # occurs at the start of a line. We disambiguate these from trailing whens to
# avoid an ambiguity in the grammar. # avoid an ambiguity in the grammar.
LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR'] LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR']
# Conversions from CoffeeScript operators into JavaScript ones.
CONVERSIONS =
'and': '&&'
'or': '||'
'is': '=='
'isnt': '!='
'not': '!'
'===': '=='

View File

@@ -102,8 +102,8 @@ class exports.Rewriter
# calls that close on the same line, just before their outdent. # calls that close on the same line, just before their outdent.
closeOpenCalls: -> closeOpenCalls: ->
condition = (token, i) -> condition = (token, i) ->
(token[0] in [')', 'CALL_END']) or token[0] in [')', 'CALL_END'] or
token[0] is 'OUTDENT' and @tag(i - 1) is ')' token[0] is 'OUTDENT' and @tag(i - 1) is ')'
action = (token, i) -> action = (token, i) ->
@tokens[if token[0] is 'OUTDENT' then i - 1 else i][0] = 'CALL_END' @tokens[if token[0] is 'OUTDENT' then i - 1 else i][0] = 'CALL_END'
@scanTokens (token, i) -> @scanTokens (token, i) ->
@@ -127,8 +127,8 @@ class exports.Rewriter
return false if 'HERECOMMENT' in [@tag(i + 1), @tag(i - 1)] return false if 'HERECOMMENT' in [@tag(i + 1), @tag(i - 1)]
[one, two, three] = @tokens.slice i + 1, i + 4 [one, two, three] = @tokens.slice i + 1, i + 4
[tag] = token [tag] = token
(tag in ['TERMINATOR', 'OUTDENT']) and not (two?[0] is ':' or one?[0] is '@' and three?[0] is ':') or tag in ['TERMINATOR', 'OUTDENT'] and not (two?[0] is ':' or one?[0] is '@' and three?[0] is ':') or
tag is ',' and (one?[0] not in ['IDENTIFIER', 'STRING', '@', 'TERMINATOR', 'OUTDENT']) tag is ',' and one?[0] not in ['IDENTIFIER', 'STRING', '@', 'TERMINATOR', 'OUTDENT']
action = (token, i) -> @tokens.splice i, 0, ['}', '}', token[2]] action = (token, i) -> @tokens.splice i, 0, ['}', '}', token[2]]
@scanTokens (token, i, tokens) -> @scanTokens (token, i, tokens) ->
if include EXPRESSION_START, tag = token[0] if include EXPRESSION_START, tag = token[0]
@@ -170,7 +170,7 @@ class exports.Rewriter
token.call = yes if prev and not prev.spaced and tag is '?' token.call = yes if prev and not prev.spaced and tag is '?'
if callObject or if callObject or
prev and prev.spaced and (prev.call or include(IMPLICIT_FUNC, prev[0])) and include(IMPLICIT_CALL, tag) and prev and prev.spaced and (prev.call or include(IMPLICIT_FUNC, prev[0])) and include(IMPLICIT_CALL, tag) and
not (tag is 'UNARY' and (@tag(i + 1) in ['IN', 'OF', 'INSTANCEOF'])) not (tag is 'UNARY' and @tag(i + 1) in ['IN', 'OF', 'INSTANCEOF'])
tokens.splice i, 0, ['CALL_START', '(', token[2]] tokens.splice i, 0, ['CALL_START', '(', token[2]]
condition = (token, i) -> condition = (token, i) ->
return yes if not seenSingle and token.fromThen return yes if not seenSingle and token.fromThen
@@ -196,7 +196,7 @@ class exports.Rewriter
if tag is 'ELSE' and @tag(i - 1) isnt 'OUTDENT' if tag is 'ELSE' and @tag(i - 1) isnt 'OUTDENT'
tokens.splice i, 0, @indentation(token)... tokens.splice i, 0, @indentation(token)...
return 2 return 2
if tag is 'CATCH' and (@tag(i + 2) in ['TERMINATOR', 'FINALLY']) if tag is 'CATCH' and @tag(i + 2) in ['TERMINATOR', 'FINALLY']
tokens.splice i + 2, 0, @indentation(token)... tokens.splice i + 2, 0, @indentation(token)...
return 4 return 4
if include(SINGLE_LINERS, tag) and @tag(i + 1) isnt 'INDENT' and if include(SINGLE_LINERS, tag) and @tag(i + 1) isnt 'INDENT' and

View File

@@ -141,3 +141,7 @@ ok c is 3
# Instanceof. # Instanceof.
ok new String instanceof String ok new String instanceof String
ok new Number not instanceof String ok new Number not instanceof String
#737: `in` should have higher precedence than logical operators
eq 1, 1 in [1] and 1