764: made boolean/number/regex indexable

This commit is contained in:
satyr 2010-10-12 19:30:10 +09:00
parent c0ec479e60
commit 8f29574b16
4 changed files with 39 additions and 32 deletions

View File

@ -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);

View File

@ -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

View File

@ -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']

View File

@ -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']