1
0
Fork 0
mirror of https://github.com/jashkenas/coffeescript.git synced 2022-11-09 12:23:24 -05:00

Reduced the time it takes to build the grammar from 80 seconds to 15, by consolidating Operation terminals by precedence level.

This commit is contained in:
Jeremy Ashkenas 2010-08-11 22:24:43 -04:00
parent d704afa0e9
commit 8c45aa480b
7 changed files with 136 additions and 237 deletions

View file

@ -537,114 +537,56 @@
})
],
Operation: [
o("! Expression", function() {
return new OpNode('!', $2);
}), o("!! Expression", function() {
return new OpNode('!!', $2);
o("UNARY Expression", function() {
return new OpNode($1, $2);
}), o("- Expression", (function() {
return new OpNode('-', $2);
}), {
prec: 'UMINUS'
prec: 'UNARY'
}), o("+ Expression", (function() {
return new OpNode('+', $2);
}), {
prec: 'UPLUS'
}), o("~ Expression", function() {
return new OpNode('~', $2);
prec: 'UNARY'
}), o("-- Expression", function() {
return new OpNode('--', $2);
}), o("++ Expression", function() {
return new OpNode('++', $2);
}), o("DELETE Expression", function() {
return new OpNode('delete', $2);
}), o("TYPEOF Expression", function() {
return new OpNode('typeof', $2);
}), o("Expression --", function() {
return new OpNode('--', $1, null, true);
}), o("Expression ++", function() {
return new OpNode('++', $1, null, true);
}), o("Expression * Expression", function() {
return new OpNode('*', $1, $3);
}), o("Expression / Expression", function() {
return new OpNode('/', $1, $3);
}), o("Expression % Expression", function() {
return new OpNode('%', $1, $3);
}), o("Expression + Expression", function() {
return new OpNode('+', $1, $3);
}), o("Expression - Expression", function() {
return new OpNode('-', $1, $3);
}), o("Expression << Expression", function() {
return new OpNode('<<', $1, $3);
}), o("Expression >> Expression", function() {
return new OpNode('>>', $1, $3);
}), o("Expression >>> Expression", function() {
return new OpNode('>>>', $1, $3);
}), o("Expression & Expression", function() {
return new OpNode('&', $1, $3);
}), o("Expression | Expression", function() {
return new OpNode('|', $1, $3);
}), o("Expression ^ Expression", function() {
return new OpNode('^', $1, $3);
}), o("Expression <<= Expression", function() {
return new OpNode('<<=', $1, $3);
}), o("Expression >>= Expression", function() {
return new OpNode('>>=', $1, $3);
}), o("Expression >>>= Expression", function() {
return new OpNode('>>>=', $1, $3);
}), o("Expression &= Expression", function() {
return new OpNode('&=', $1, $3);
}), o("Expression |= Expression", function() {
return new OpNode('|=', $1, $3);
}), o("Expression ^= Expression", function() {
return new OpNode('^=', $1, $3);
}), o("Expression <= Expression", function() {
return new OpNode('<=', $1, $3);
}), o("Expression < Expression", function() {
return new OpNode('<', $1, $3);
}), o("Expression > Expression", function() {
return new OpNode('>', $1, $3);
}), o("Expression >= Expression", function() {
return new OpNode('>=', $1, $3);
}), o("Expression == Expression", function() {
return new OpNode('==', $1, $3);
}), o("Expression != Expression", function() {
return new OpNode('!=', $1, $3);
}), o("Expression && Expression", function() {
return new OpNode('&&', $1, $3);
}), o("Expression || Expression", function() {
return new OpNode('||', $1, $3);
}), o("Expression OP? Expression", function() {
return new OpNode('?', $1, $3);
}), o("Expression -= Expression", function() {
return new OpNode('-=', $1, $3);
}), o("Expression += Expression", function() {
return new OpNode('+=', $1, $3);
}), o("Expression /= Expression", function() {
return new OpNode('/=', $1, $3);
}), o("Expression *= Expression", function() {
return new OpNode('*=', $1, $3);
}), o("Expression %= Expression", function() {
return new OpNode('%=', $1, $3);
}), o("Expression ||= Expression", function() {
return new OpNode('||=', $1, $3);
}), o("Expression &&= Expression", function() {
return new OpNode('&&=', $1, $3);
}), o("Expression ?= Expression", function() {
return new OpNode('?=', $1, $3);
}), o("Expression MATH Expression", function() {
return new OpNode($2, $1, $3);
}), o("Expression SHIFT Expression", function() {
return new OpNode($2, $1, $3);
}), o("Expression COMPARE Expression", function() {
return new OpNode($2, $1, $3);
}), o("Expression LOGIC Expression", function() {
return new OpNode($2, $1, $3);
}), o("Expression COMPOUND_ASSIGN Expression", function() {
return new OpNode($2, $1, $3);
}), o("Expression INSTANCEOF Expression", function() {
return new OpNode('instanceof', $1, $3);
}), o("Expression IN Expression", function() {
return new InNode($1, $3);
}), o("Expression OF Expression", function() {
return new OpNode('in', $1, $3);
}), o("Expression ! IN Expression", function() {
return new OpNode('!', new InNode($1, $4));
}), o("Expression ! OF Expression", function() {
return new OpNode('!', new ParentheticalNode(new OpNode('in', $1, $4)));
}), o("Expression UNARY IN Expression", function() {
return new OpNode($2, new InNode($1, $4));
}), o("Expression UNARY OF Expression", function() {
return new OpNode($2, new ParentheticalNode(new OpNode('in', $1, $4)));
})
]
};
operators = [["left", '?'], ["nonassoc", 'UMINUS', 'UPLUS', '!', '!!', '~', '++', '--'], ["left", '*', '/', '%'], ["left", '+', '-'], ["left", '<<', '>>', '>>>'], ["left", '<=', '<', '>', '>='], ["right", 'DELETE', 'INSTANCEOF', 'TYPEOF'], ["left", '==', '!='], ["left", '&', '|', '^'], ["left", '&&', '||', 'OP?'], ["right", '-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|='], ["left", '.'], ["right", 'INDENT'], ["left", 'OUTDENT'], ["right", 'WHEN', 'LEADING_WHEN', 'IN', 'OF', 'BY', 'THROW'], ["right", 'IF', 'UNLESS', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'NEW', 'SUPER', 'CLASS'], ["left", 'EXTENDS'], ["right", '=', ':', 'RETURN'], ["right", '->', '=>', 'UNLESS', 'POST_IF', 'POST_UNLESS']];
operators = [["left", '?'], ["nonassoc", '++', '--'], ["right", 'UNARY'], ["left", 'MATH'], ["left", '+', '-'], ["left", 'SHIFT'], ["left", 'COMPARE'], ["left", 'INSTANCEOF'], ["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', 'NEW', 'SUPER', 'CLASS', 'EXTENDS'], ["right", '=', ':', 'RETURN'], ["right", '->', '=>', 'UNLESS', 'POST_IF', 'POST_UNLESS']];
tokens = [];
_a = grammar;
for (name in _a) {

View file

@ -1,5 +1,5 @@
(function() {
var ASSIGNED, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_KEYWORDS, COMMENT, CONVERSIONS, HEREDOC, HEREDOC_INDENT, IDENTIFIER, JS_CLEANER, JS_FORBIDDEN, JS_KEYWORDS, LAST_DENT, LAST_DENTS, LINE_BREAK, Lexer, MULTILINER, MULTI_DENT, NEXT_CHARACTER, NOT_REGEX, NO_NEWLINE, NUMBER, OPERATOR, REGEX_END, REGEX_ESCAPE, REGEX_INTERPOLATION, REGEX_START, RESERVED, Rewriter, STRING_NEWLINES, WHITESPACE, _a, _b, _c, compact, count, helpers, include, starts;
var ASSIGNED, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, CONVERSIONS, HEREDOC, HEREDOC_INDENT, IDENTIFIER, JS_CLEANER, JS_FORBIDDEN, JS_KEYWORDS, LAST_DENT, LAST_DENTS, LINE_BREAK, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NEXT_CHARACTER, NOT_REGEX, NO_NEWLINE, NUMBER, OPERATOR, REGEX_END, REGEX_ESCAPE, REGEX_INTERPOLATION, REGEX_START, RESERVED, Rewriter, SHIFT, STRING_NEWLINES, UNARY, WHITESPACE, _a, _b, _c, compact, count, helpers, include, starts;
var __slice = Array.prototype.slice;
if ((typeof process !== "undefined" && process !== null)) {
_a = require('./rewriter');
@ -86,6 +86,9 @@
if (id === 'all' && this.tag() === 'FOR') {
tag = 'ALL';
}
if (include(UNARY, tag)) {
tag = 'UNARY';
}
if (include(JS_FORBIDDEN, id)) {
if (forcedIdentifier) {
tag = 'STRING';
@ -105,6 +108,12 @@
if (include(COFFEE_ALIASES, id)) {
tag = (id = CONVERSIONS[id]);
}
if (include(LOGIC, id)) {
tag = 'LOGIC';
}
if (id === '!') {
tag = 'UNARY';
}
}
this.token(tag, id);
if (close_index) {
@ -305,7 +314,7 @@
return true;
};
Lexer.prototype.literalToken = function() {
var _d, match, prevSpaced, space, tag, value;
var _d, match, space, spaced, tag, value;
match = this.chunk.match(OPERATOR);
value = match && match[1];
space = match && match[2];
@ -314,21 +323,32 @@
}
value = value || this.chunk.substr(0, 1);
this.i += value.length;
prevSpaced = this.prev() && this.prev().spaced;
spaced = this.prev() && this.prev().spaced;
tag = value;
if (value === '=') {
if (include(JS_FORBIDDEN, this.value())) {
this.assignmentError();
}
if (('or' === (_d = this.value()) || 'and' === _d)) {
return this.tag(1, CONVERSIONS[this.value()] + '=');
this.tokens.splice(this.tokens.length - 1, 1, ['COMPOUND_ASSIGN', CONVERSIONS[this.value()] + '=', this.prev()[2]]);
return true;
}
}
if (value === ';') {
tag = 'TERMINATOR';
} else if (value === '?' && prevSpaced) {
tag = 'OP?';
} else if (include(CALLABLE, this.tag()) && !prevSpaced) {
} else if ((value === '?' && spaced) || include(LOGIC, value)) {
tag = 'LOGIC';
} else if (include(MATH, value)) {
tag = 'MATH';
} else if (include(COMPARE, value)) {
tag = 'COMPARE';
} else if (include(COMPOUND_ASSIGN, value)) {
tag = 'COMPOUND_ASSIGN';
} else if (include(UNARY, value)) {
tag = 'UNARY';
} else if (include(SHIFT, value)) {
tag = 'SHIFT';
} else if (include(CALLABLE, this.tag()) && !spaced) {
if (value === '(') {
tag = 'CALL_START';
} else if (value === '[') {
@ -597,6 +617,12 @@
HEREDOC_INDENT = /(\n+([ \t]*)|^([ \t]+))/g;
ASSIGNED = /^\s*(([a-zA-Z\$_@]\w*|["'][^\r\n]+?["']|\d+)[ \t]*?[:=][^=])/;
NEXT_CHARACTER = /^\s*(\S)/;
COMPOUND_ASSIGN = ['-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|='];
UNARY = ['UMINUS', 'UPLUS', '!', '!!', '~', 'TYPEOF', 'DELETE'];
LOGIC = ['&', '|', '^', '&&', '||'];
SHIFT = ['<<', '>>', '>>>'];
COMPARE = ['<=', '<', '>', '>='];
MATH = ['*', '/', '%'];
NOT_REGEX = ['NUMBER', 'REGEX', '++', '--', 'FALSE', 'NULL', 'TRUE', ']'];
CALLABLE = ['IDENTIFIER', 'SUPER', ')', ']', '}', 'STRING', '@', 'THIS', '?', '::'];
LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR'];

File diff suppressed because one or more lines are too long

View file

@ -181,7 +181,7 @@
return this.scanTokens(function(token, i) {
var _c, action, condition, prev;
prev = this.tokens[i - 1];
if (prev && prev.spaced && include(IMPLICIT_FUNC, prev[0]) && include(IMPLICIT_CALL, token[0]) && !(token[0] === '!' && (('IN' === (_c = this.tag(i + 1)) || 'OF' === _c)))) {
if (prev && prev.spaced && include(IMPLICIT_FUNC, prev[0]) && include(IMPLICIT_CALL, token[0]) && !(token[0] === 'UNARY' && (('IN' === (_c = this.tag(i + 1)) || 'OF' === _c)))) {
this.tokens.splice(i, 0, ['CALL_START', '(', token[2]]);
condition = function(token, i) {
return (!token.generated && this.tokens[i - 1][0] !== ',' && include(IMPLICIT_END, token[0]) && !(token[0] === 'INDENT' && (include(IMPLICIT_BLOCK, this.tag(i - 1)) || this.tag(i - 2) === 'CLASS'))) || token[0] === 'PROPERTY_ACCESS' && this.tag(i - 1) === 'OUTDENT';
@ -375,7 +375,7 @@
})();
EXPRESSION_CLOSE = ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat(EXPRESSION_END);
IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@'];
IMPLICIT_CALL = ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'CLASS', 'IF', 'UNLESS', 'TRY', 'DELETE', 'TYPEOF', 'SWITCH', 'THIS', 'NULL', 'TRUE', 'FALSE', 'YES', 'NO', 'ON', 'OFF', '!', '!!', '@', '->', '=>', '[', '(', '{'];
IMPLICIT_CALL = ['IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'CLASS', 'IF', 'UNLESS', 'TRY', 'SWITCH', 'THIS', 'NULL', 'UNARY', 'TRUE', 'FALSE', 'YES', 'NO', 'ON', 'OFF', '@', '->', '=>', '[', '(', '{'];
IMPLICIT_BLOCK = ['->', '=>', '{', '[', ','];
IMPLICIT_END = ['POST_IF', 'POST_UNLESS', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'TERMINATOR', 'INDENT'];
SINGLE_LINERS = ['ELSE', "->", "=>", 'TRY', 'FINALLY', 'THEN'];

View file

@ -520,65 +520,31 @@ grammar =
# -type rule, but in order to make the precedence binding possible, separate
# rules are necessary.
Operation: [
o "! Expression", -> new OpNode '!', $2
o "!! Expression", -> new OpNode '!!', $2
o("- Expression", (-> new OpNode('-', $2)), {prec: 'UMINUS'})
o("+ Expression", (-> new OpNode('+', $2)), {prec: 'UPLUS'})
o "~ Expression", -> new OpNode '~', $2
o "UNARY Expression", -> new OpNode $1, $2
o("- Expression", (-> new OpNode('-', $2)), {prec: 'UNARY'})
o("+ Expression", (-> new OpNode('+', $2)), {prec: 'UNARY'})
o "-- Expression", -> new OpNode '--', $2
o "++ Expression", -> new OpNode '++', $2
o "DELETE Expression", -> new OpNode 'delete', $2
o "TYPEOF Expression", -> new OpNode 'typeof', $2
o "Expression --", -> new OpNode '--', $1, null, true
o "Expression ++", -> new OpNode '++', $1, null, true
o "Expression * Expression", -> new OpNode '*', $1, $3
o "Expression / Expression", -> new OpNode '/', $1, $3
o "Expression % Expression", -> new OpNode '%', $1, $3
o "Expression + Expression", -> new OpNode '+', $1, $3
o "Expression - Expression", -> new OpNode '-', $1, $3
o "Expression << Expression", -> new OpNode '<<', $1, $3
o "Expression >> Expression", -> new OpNode '>>', $1, $3
o "Expression >>> Expression", -> new OpNode '>>>', $1, $3
o "Expression & Expression", -> new OpNode '&', $1, $3
o "Expression | Expression", -> new OpNode '|', $1, $3
o "Expression ^ Expression", -> new OpNode '^', $1, $3
o "Expression <<= Expression", -> new OpNode '<<=', $1, $3
o "Expression >>= Expression", -> new OpNode '>>=', $1, $3
o "Expression >>>= Expression", -> new OpNode '>>>=', $1, $3
o "Expression &= Expression", -> new OpNode '&=', $1, $3
o "Expression |= Expression", -> new OpNode '|=', $1, $3
o "Expression ^= Expression", -> new OpNode '^=', $1, $3
o "Expression <= Expression", -> new OpNode '<=', $1, $3
o "Expression < Expression", -> new OpNode '<', $1, $3
o "Expression > Expression", -> new OpNode '>', $1, $3
o "Expression >= Expression", -> new OpNode '>=', $1, $3
o "Expression == Expression", -> new OpNode '==', $1, $3
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
o "Expression /= Expression", -> new OpNode '/=', $1, $3
o "Expression *= Expression", -> new OpNode '*=', $1, $3
o "Expression %= Expression", -> new OpNode '%=', $1, $3
o "Expression ||= Expression", -> new OpNode '||=', $1, $3
o "Expression &&= Expression", -> new OpNode '&&=', $1, $3
o "Expression ?= Expression", -> new OpNode '?=', $1, $3
o "Expression MATH Expression", -> new OpNode $2, $1, $3
o "Expression SHIFT Expression", -> new OpNode $2, $1, $3
o "Expression COMPARE Expression", -> new OpNode $2, $1, $3
o "Expression LOGIC Expression", -> new OpNode $2, $1, $3
o "Expression COMPOUND_ASSIGN Expression", -> new OpNode $2, $1, $3
o "Expression INSTANCEOF Expression", -> new OpNode 'instanceof', $1, $3
o "Expression IN Expression", -> new InNode $1, $3
o "Expression OF Expression", -> new OpNode 'in', $1, $3
o "Expression ! IN Expression", -> new OpNode '!', new InNode $1, $4
o "Expression ! OF Expression", -> new OpNode '!', new ParentheticalNode new OpNode 'in', $1, $4
o "Expression UNARY IN Expression", -> new OpNode $2, new InNode $1, $4
o "Expression UNARY OF Expression", -> new OpNode $2, new ParentheticalNode new OpNode 'in', $1, $4
]
@ -595,22 +561,20 @@ grammar =
# (2 + 3) * 4
operators = [
["left", '?']
["nonassoc", 'UMINUS', 'UPLUS', '!', '!!', '~', '++', '--']
["left", '*', '/', '%']
["nonassoc", '++', '--']
["right", 'UNARY']
["left", 'MATH']
["left", '+', '-']
["left", '<<', '>>', '>>>']
["left", '<=', '<', '>', '>=']
["right", 'DELETE', 'INSTANCEOF', 'TYPEOF']
["left", 'SHIFT']
["left", 'COMPARE']
["left", 'INSTANCEOF']
["left", '==', '!=']
["left", '&', '|', '^']
["left", '&&', '||', 'OP?']
["right", '-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|=']
["left", 'LOGIC']
["right", 'COMPOUND_ASSIGN']
["left", '.']
["right", 'INDENT']
["left", 'OUTDENT']
["nonassoc", 'INDENT', 'OUTDENT']
["right", 'WHEN', 'LEADING_WHEN', 'IN', 'OF', 'BY', 'THROW']
["right", 'IF', 'UNLESS', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'NEW', 'SUPER', 'CLASS']
["left", 'EXTENDS']
["right", 'IF', 'UNLESS', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'NEW', 'SUPER', 'CLASS', 'EXTENDS']
["right", '=', ':', 'RETURN']
["right", '->', '=>', 'UNLESS', 'POST_IF', 'POST_UNLESS']
]

View file

@ -88,6 +88,7 @@ exports.Lexer = class Lexer
tag = id.toUpperCase() if include(JS_KEYWORDS, id) or (not forcedIdentifier and include(COFFEE_KEYWORDS, id))
tag = 'LEADING_WHEN' if tag is 'WHEN' and include LINE_BREAK, @tag()
tag = 'ALL' if id is 'all' and @tag() is 'FOR'
tag = 'UNARY' if include UNARY, tag
if include(JS_FORBIDDEN, id)
if forcedIdentifier
tag = 'STRING'
@ -100,6 +101,8 @@ exports.Lexer = class Lexer
@identifierError id
unless forcedIdentifier
tag = id = CONVERSIONS[id] if include COFFEE_ALIASES, id
tag = 'LOGIC' if include LOGIC, id
tag = 'UNARY' if id is '!'
@token tag, id
@token ']', ']' if close_index
true
@ -269,17 +272,21 @@ exports.Lexer = class Lexer
@tagParameters() if value and value.match CODE
value or= @chunk.substr 0, 1
@i += value.length
prevSpaced = @prev() and @prev().spaced
spaced = @prev() and @prev().spaced
tag = value
if value is '='
@assignmentError() if include JS_FORBIDDEN, @value()
if @value() in ['or', 'and']
return @tag 1, CONVERSIONS[@value()] + '='
if value is ';'
tag = 'TERMINATOR'
else if value is '?' and prevSpaced
tag = 'OP?'
else if include(CALLABLE, @tag()) and not prevSpaced
@tokens.splice(@tokens.length - 1, 1, ['COMPOUND_ASSIGN', CONVERSIONS[@value()] + '=', @prev()[2]])
return true
if value is ';' then tag = 'TERMINATOR'
else if (value is '?' and spaced) or include(LOGIC, value) then tag = 'LOGIC'
else if include(MATH, value) then tag = 'MATH'
else if include(COMPARE, value) then tag = 'COMPARE'
else if include(COMPOUND_ASSIGN, value) then tag = 'COMPOUND_ASSIGN'
else if include(UNARY, value) then tag = 'UNARY'
else if include(SHIFT, value) then tag = 'SHIFT'
else if include(CALLABLE, @tag()) and not spaced
if value is '('
tag = 'CALL_START'
else if value is '['
@ -534,6 +541,24 @@ HEREDOC_INDENT = /(\n+([ \t]*)|^([ \t]+))/g
ASSIGNED = /^\s*(([a-zA-Z\$_@]\w*|["'][^\r\n]+?["']|\d+)[ \t]*?[:=][^=])/
NEXT_CHARACTER = /^\s*(\S)/
# Compound assignment tokens.
COMPOUND_ASSIGN = ['-=', '+=', '/=', '*=', '%=', '||=', '&&=', '?=', '<<=', '>>=', '>>>=', '&=', '^=', '|=']
# Unary tokens.
UNARY = ['UMINUS', 'UPLUS', '!', '!!', '~', 'TYPEOF', 'DELETE']
# Logical tokens.
LOGIC = ['&', '|', '^', '&&', '||']
# Bit-shifting tokens.
SHIFT = ['<<', '>>', '>>>']
# Comparison tokens.
COMPARE = ['<=', '<', '>', '>=']
# Mathmatical tokens.
MATH = ['*', '/', '%']
# Tokens which a regular expression will never immediately follow, but which
# a division operator might.
#

View file

@ -154,7 +154,7 @@ exports.Rewriter = class Rewriter
@scanTokens (token, i) ->
prev = @tokens[i - 1]
if prev and prev.spaced and include(IMPLICIT_FUNC, prev[0]) and include(IMPLICIT_CALL, token[0]) and
not (token[0] is '!' and (@tag(i + 1) in ['IN', 'OF']))
not (token[0] is 'UNARY' and (@tag(i + 1) in ['IN', 'OF']))
@tokens.splice i, 0, ['CALL_START', '(', token[2]]
condition = (token, i) ->
(not token.generated and @tokens[i - 1][0] isnt ',' and include(IMPLICIT_END, token[0]) and
@ -316,9 +316,9 @@ IMPLICIT_FUNC = ['IDENTIFIER', 'SUPER', ')', 'CALL_END', ']', 'INDEX_END', '@
# If preceded by an `IMPLICIT_FUNC`, indicates a function invocation.
IMPLICIT_CALL = [
'IDENTIFIER', 'NUMBER', 'STRING', 'JS', 'REGEX', 'NEW', 'PARAM_START', 'CLASS',
'IF', 'UNLESS', 'TRY', 'DELETE', 'TYPEOF', 'SWITCH', 'THIS', 'NULL',
'IF', 'UNLESS', 'TRY', 'SWITCH', 'THIS', 'NULL', 'UNARY'
'TRUE', 'FALSE', 'YES', 'NO', 'ON', 'OFF',
'!', '!!', '@', '->', '=>', '[', '(', '{'
'@', '->', '=>', '[', '(', '{'
]
# Tokens indicating that the implicit call must enclose a block of expressions.