764: made boolean/number/regex indexable
This commit is contained in:
parent
c0ec479e60
commit
8f29574b16
29
lib/lexer.js
29
lib/lexer.js
|
@ -1,5 +1,5 @@
|
|||
(function() {
|
||||
var ASSIGNED, BOOL, 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, NEXT_ELLIPSIS, NOT_REGEX, NO_NEWLINE, NUMBER, OPERATOR, REGEX, RELATION, RESERVED, Rewriter, SHIFT, SIMPLESTR, TRAILING_SPACES, UNARY, WHITESPACE, _ref, compact, count, include, last, op, starts;
|
||||
var ASSIGNED, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HEREDOC, HEREDOC_INDENT, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDEXABLE, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LEADING_SPACES, LINE_BREAK, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NEXT_CHARACTER, NEXT_ELLIPSIS, 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;
|
||||
_ref = require('./helpers'), include = _ref.include, count = _ref.count, starts = _ref.starts, compact = _ref.compact, last = _ref.last;
|
||||
exports.Lexer = (function() {
|
||||
|
@ -231,7 +231,7 @@
|
|||
if (flags) {
|
||||
this.tokens.push([',', ','], ['STRING', '"' + flags + '"']);
|
||||
}
|
||||
this.tokens.push(['CALL_END', ')']);
|
||||
this.token(')', ')');
|
||||
return true;
|
||||
};
|
||||
Lexer.prototype.lineToken = function() {
|
||||
|
@ -280,8 +280,7 @@
|
|||
this.outdebt -= this.indents[len];
|
||||
moveOut -= this.indents[len];
|
||||
} else {
|
||||
dent = this.indents.pop();
|
||||
dent -= this.outdebt;
|
||||
dent = this.indents.pop() - this.outdebt;
|
||||
moveOut -= dent;
|
||||
this.outdebt = 0;
|
||||
this.token('OUTDENT', dent);
|
||||
|
@ -332,7 +331,7 @@
|
|||
this.i += value.length;
|
||||
tag = value;
|
||||
prev = last(this.tokens);
|
||||
if (value === '=') {
|
||||
if (value === '=' && prev) {
|
||||
if (!prev[1].reserved && include(JS_FORBIDDEN, prev[1])) {
|
||||
this.assignmentError();
|
||||
}
|
||||
|
@ -356,15 +355,15 @@
|
|||
tag = 'UNARY';
|
||||
} else if (include(SHIFT, value)) {
|
||||
tag = 'SHIFT';
|
||||
} else if (value === '?' && prev.spaced) {
|
||||
} else if (value === '?' && ((prev != null) ? prev.spaced : undefined)) {
|
||||
tag = 'LOGIC';
|
||||
} else if (prev && !prev.spaced && include(CALLABLE, prev[0])) {
|
||||
if (value === '(') {
|
||||
} else if (prev && !prev.spaced) {
|
||||
if (value === '(' && include(CALLABLE, prev[0])) {
|
||||
if (prev[0] === '?') {
|
||||
prev[0] = 'FUNC_EXIST';
|
||||
}
|
||||
tag = 'CALL_START';
|
||||
} else if (value === '[') {
|
||||
} else if (value === '[' && include(INDEXABLE, prev[0])) {
|
||||
tag = 'INDEX_START';
|
||||
switch (prev[0]) {
|
||||
case '?':
|
||||
|
@ -531,12 +530,12 @@
|
|||
if (!tokens.length) {
|
||||
return this.token('STRING', '""');
|
||||
}
|
||||
if (tokens[0][0] !== 'TO_BE_STRING') {
|
||||
tokens.unshift(['', '']);
|
||||
}
|
||||
if (interpolated = tokens.length > 1) {
|
||||
this.token('(', '(');
|
||||
}
|
||||
if (tokens[0][0] !== 'TO_BE_STRING') {
|
||||
this.tokens.push(['STRING', '""'], ['+', '+']);
|
||||
}
|
||||
for (i = 0, _len = tokens.length; i < _len; i++) {
|
||||
_ref3 = tokens[i], tag = _ref3[0], value = _ref3[1];
|
||||
if (i) {
|
||||
|
@ -572,6 +571,9 @@
|
|||
return str.replace(MULTILINER, heredoc ? '\\n' : '');
|
||||
};
|
||||
Lexer.prototype.makeString = function(body, quote, heredoc) {
|
||||
if (!body) {
|
||||
return quote + quote;
|
||||
}
|
||||
body = body.replace(/\\([\s\S])/g, function(match, contents) {
|
||||
return ('\n' === contents || quote === contents) ? contents : match;
|
||||
});
|
||||
|
@ -628,6 +630,7 @@
|
|||
RELATION = ['IN', 'OF', 'INSTANCEOF'];
|
||||
BOOL = ['TRUE', 'FALSE', 'NULL'];
|
||||
NOT_REGEX = ['NUMBER', 'REGEX', 'BOOL', '++', '--', ']'];
|
||||
CALLABLE = ['IDENTIFIER', 'SUPER', ')', ']', '}', 'STRING', '@', 'THIS', '?', '::'];
|
||||
CALLABLE = ['IDENTIFIER', 'STRING', 'REGEX', ')', ']', '}', '?', '::', '@', 'THIS', 'SUPER'];
|
||||
INDEXABLE = CALLABLE.concat('NUMBER', 'BOOL');
|
||||
LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR'];
|
||||
}).call(this);
|
||||
|
|
|
@ -216,7 +216,7 @@ exports.Lexer = class Lexer
|
|||
@tokens.push ['STRING', '""'], ['+', '+'] unless tokens[0]?[0] is 'STRING'
|
||||
@tokens.push tokens...
|
||||
@tokens.push [',', ','], ['STRING', '"' + flags + '"'] if flags
|
||||
@tokens.push ['CALL_END', ')']
|
||||
@token ')', ')'
|
||||
true
|
||||
|
||||
# Matches newlines, indents, and outdents, and determines which is which.
|
||||
|
@ -267,10 +267,9 @@ exports.Lexer = class Lexer
|
|||
@outdebt = 0
|
||||
else if @indents[len] < @outdebt
|
||||
@outdebt -= @indents[len]
|
||||
moveOut -= @indents[len]
|
||||
moveOut -= @indents[len]
|
||||
else
|
||||
dent = @indents.pop()
|
||||
dent -= @outdebt
|
||||
dent = @indents.pop() - @outdebt
|
||||
moveOut -= dent
|
||||
@outdebt = 0
|
||||
@token 'OUTDENT', dent
|
||||
|
@ -312,7 +311,7 @@ exports.Lexer = class Lexer
|
|||
@i += value.length
|
||||
tag = value
|
||||
prev = last @tokens
|
||||
if value is '='
|
||||
if value is '=' and prev
|
||||
@assignmentError() if not prev[1].reserved and include JS_FORBIDDEN, prev[1]
|
||||
if prev[1] in ['||', '&&']
|
||||
prev[0] = 'COMPOUND_ASSIGN'
|
||||
|
@ -325,12 +324,12 @@ exports.Lexer = class Lexer
|
|||
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 value is '?' and prev.spaced then tag = 'LOGIC'
|
||||
else if prev and not prev.spaced and include CALLABLE, prev[0]
|
||||
if value is '('
|
||||
else if value is '?' and prev?.spaced then tag = 'LOGIC'
|
||||
else if prev and not prev.spaced
|
||||
if value is '(' and include CALLABLE, prev[0]
|
||||
prev[0] = 'FUNC_EXIST' if prev[0] is '?'
|
||||
tag = 'CALL_START'
|
||||
else if value is '['
|
||||
else if value is '[' and include INDEXABLE, prev[0]
|
||||
tag = 'INDEX_START'
|
||||
switch prev[0]
|
||||
when '?' then prev[0] = 'INDEX_SOAK'
|
||||
|
@ -462,8 +461,8 @@ exports.Lexer = class Lexer
|
|||
tokens.push ['TO_BE_STRING', str[pi..]] if i > pi < str.length
|
||||
return tokens if regex
|
||||
return @token 'STRING', '""' unless tokens.length
|
||||
tokens.unshift ['', ''] unless tokens[0][0] is 'TO_BE_STRING'
|
||||
@token '(', '(' if interpolated = tokens.length > 1
|
||||
@tokens.push ['STRING', '""'], ['+', '+'] unless tokens[0][0] is 'TO_BE_STRING'
|
||||
for [tag, value], i in tokens
|
||||
@token '+', '+' if i
|
||||
if tag is 'TOKENS'
|
||||
|
@ -498,6 +497,7 @@ exports.Lexer = class Lexer
|
|||
|
||||
# Constructs a string token by escaping quotes and newlines.
|
||||
makeString: (body, quote, heredoc) ->
|
||||
return quote + quote unless body
|
||||
body = body.replace /\\([\s\S])/g, (match, contents) ->
|
||||
if contents in ['\n', quote] then contents else match
|
||||
body = body.replace /// #{quote} ///g, '\\$&'
|
||||
|
@ -621,7 +621,8 @@ NOT_REGEX = ['NUMBER', 'REGEX', 'BOOL', '++', '--', ']']
|
|||
# Tokens which could legitimately be invoked or indexed. A opening
|
||||
# parentheses or bracket following these tokens will be recorded as the start
|
||||
# of a function invocation or indexing operation.
|
||||
CALLABLE = ['IDENTIFIER', 'SUPER', ')', ']', '}', 'STRING', '@', 'THIS', '?', '::']
|
||||
CALLABLE = ['IDENTIFIER', 'STRING', 'REGEX', ')', ']', '}', '?', '::', '@', 'THIS', 'SUPER']
|
||||
INDEXABLE = CALLABLE.concat 'NUMBER', 'BOOL'
|
||||
|
||||
# Tokens that, when immediately preceding a `WHEN`, indicate that the `WHEN`
|
||||
# occurs at the start of a line. We disambiguate these from trailing whens to
|
||||
|
|
|
@ -240,3 +240,8 @@ eq result.two, obj.list()[1]
|
|||
|
||||
#542: Objects leading expression statement should be parenthesized.
|
||||
{f: -> ok yes }.f() + 1
|
||||
|
||||
|
||||
#764: Boolean/Number should be indexable.
|
||||
ok 42['toString']
|
||||
ok on['toString']
|
||||
|
|
|
@ -12,8 +12,6 @@ g = 1
|
|||
|
||||
ok y / x/g is 2
|
||||
|
||||
ok /:\/[/]goog/.test 'http://google.com'
|
||||
|
||||
obj = {
|
||||
width: -> 10
|
||||
height: -> 20
|
||||
|
@ -39,9 +37,9 @@ eq '\\\\#{}\\\\\\\"', ///
|
|||
eq /// /// + '', '/(?:)/'
|
||||
|
||||
|
||||
# Issue #584.
|
||||
regex = /[/]/
|
||||
s1 = "Hello there"
|
||||
s2 = "Hello / there"
|
||||
ok not regex.test(s1)
|
||||
ok regex.test(s2)
|
||||
#584: Unescaped slashes in character classes.
|
||||
ok /:\/[/]goog/.test 'http://google.com'
|
||||
|
||||
|
||||
#764: Should be indexable.
|
||||
eq /0/['source'], ///#{0}///['source']
|
||||
|
|
Loading…
Reference in New Issue