mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-02-18 11:31:20 -05:00
lexer now distinguishes between IN/OF and FORIN/FOROF to help grammar, fixing #737
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
49
lib/lexer.js
49
lib/lexer.js
@@ -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
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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']
|
||||||
|
|||||||
@@ -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': '!'
|
|
||||||
'===': '=='
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user