Make regexes always uncallable

No matter if they have interpolations or not.
This commit is contained in:
Simon Lydell 2015-01-15 19:44:14 +01:00
parent fce502ac98
commit 3db029f2c1
6 changed files with 42 additions and 11 deletions

View File

@ -288,7 +288,7 @@
}; };
Lexer.prototype.regexToken = function() { Lexer.prototype.regexToken = function() {
var closed, end, flags, index, match, prev, re, regex, tokens, _ref2, _ref3, _ref4; var closed, end, flags, index, match, prev, re, regex, rparen, tokens, _ref2, _ref3, _ref4;
switch (false) { switch (false) {
case !(match = REGEX_ILLEGAL.exec(this.chunk)): case !(match = REGEX_ILLEGAL.exec(this.chunk)):
this.error("regular expressions cannot begin with " + match[2], match.index + match[1].length); this.error("regular expressions cannot begin with " + match[2], match.index + match[1].length);
@ -301,7 +301,7 @@
index = regex.length; index = regex.length;
prev = last(this.tokens); prev = last(this.tokens);
if (prev) { if (prev) {
if (prev.spaced && (_ref3 = prev[0], __indexOf.call(CALLABLE, _ref3) >= 0) && !prev.stringEnd) { if (prev.spaced && (_ref3 = prev[0], __indexOf.call(CALLABLE, _ref3) >= 0) && !prev.stringEnd && !prev.regexEnd) {
if (!closed || POSSIBLY_DIVISION.test(regex)) { if (!closed || POSSIBLY_DIVISION.test(regex)) {
return 0; return 0;
} }
@ -345,7 +345,8 @@
this.token(',', ',', index, 0); this.token(',', ',', index, 0);
this.token('STRING', '"' + flags + '"', index, flags.length); this.token('STRING', '"' + flags + '"', index, flags.length);
} }
this.token(')', ')', end, 0); rparen = this.token(')', ')', end, 0);
rparen.regexEnd = true;
} }
return end; return end;
}; };
@ -505,7 +506,7 @@
} else if (__indexOf.call(LOGIC, value) >= 0 || value === '?' && (prev != null ? prev.spaced : void 0)) { } else if (__indexOf.call(LOGIC, value) >= 0 || value === '?' && (prev != null ? prev.spaced : void 0)) {
tag = 'LOGIC'; tag = 'LOGIC';
} else if (prev && !prev.spaced) { } else if (prev && !prev.spaced) {
if (value === '(' && (_ref4 = prev[0], __indexOf.call(CALLABLE, _ref4) >= 0) && !prev.stringEnd) { if (value === '(' && (_ref4 = prev[0], __indexOf.call(CALLABLE, _ref4) >= 0) && !prev.stringEnd && !prev.regexEnd) {
if (prev[0] === '?') { if (prev[0] === '?') {
prev[0] = 'FUNC_EXIST'; prev[0] = 'FUNC_EXIST';
} }

View File

@ -257,14 +257,14 @@
} }
stack.pop(); stack.pop();
} }
if ((__indexOf.call(IMPLICIT_FUNC, tag) >= 0 && token.spaced && !token.stringEnd || tag === '?' && i > 0 && !tokens[i - 1].spaced) && (__indexOf.call(IMPLICIT_CALL, nextTag) >= 0 || __indexOf.call(IMPLICIT_UNSPACED_CALL, nextTag) >= 0 && !((_ref = tokens[i + 1]) != null ? _ref.spaced : void 0) && !((_ref1 = tokens[i + 1]) != null ? _ref1.newLine : void 0))) { if ((__indexOf.call(IMPLICIT_FUNC, tag) >= 0 && token.spaced && !token.stringEnd && !token.regexEnd || tag === '?' && i > 0 && !tokens[i - 1].spaced) && (__indexOf.call(IMPLICIT_CALL, nextTag) >= 0 || __indexOf.call(IMPLICIT_UNSPACED_CALL, nextTag) >= 0 && !((_ref = tokens[i + 1]) != null ? _ref.spaced : void 0) && !((_ref1 = tokens[i + 1]) != null ? _ref1.newLine : void 0))) {
if (tag === '?') { if (tag === '?') {
tag = token[0] = 'FUNC_EXIST'; tag = token[0] = 'FUNC_EXIST';
} }
startImplicitCall(i + 1); startImplicitCall(i + 1);
return forward(2); return forward(2);
} }
if (__indexOf.call(IMPLICIT_FUNC, tag) >= 0 && !token.stringEnd && this.matchTags(i + 1, 'INDENT', null, ':') && !this.findTagsBackwards(i, ['CLASS', 'EXTENDS', 'IF', 'CATCH', 'SWITCH', 'LEADING_WHEN', 'FOR', 'WHILE', 'UNTIL'])) { if (__indexOf.call(IMPLICIT_FUNC, tag) >= 0 && !token.stringEnd && !token.regexEnd && this.matchTags(i + 1, 'INDENT', null, ':') && !this.findTagsBackwards(i, ['CLASS', 'EXTENDS', 'IF', 'CATCH', 'SWITCH', 'LEADING_WHEN', 'FOR', 'WHILE', 'UNTIL'])) {
startImplicitCall(i + 1); startImplicitCall(i + 1);
stack.push(['INDENT', i + 2]); stack.push(['INDENT', i + 2]);
return forward(3); return forward(3);

View File

@ -263,7 +263,7 @@ exports.Lexer = class Lexer
index = regex.length index = regex.length
prev = last @tokens prev = last @tokens
if prev if prev
if prev.spaced and prev[0] in CALLABLE and not prev.stringEnd if prev.spaced and prev[0] in CALLABLE and not prev.stringEnd and not prev.regexEnd
return 0 if not closed or POSSIBLY_DIVISION.test regex return 0 if not closed or POSSIBLY_DIVISION.test regex
else if prev[0] in NOT_REGEX else if prev[0] in NOT_REGEX
return 0 return 0
@ -289,7 +289,8 @@ exports.Lexer = class Lexer
if flags if flags
@token ',', ',', index, 0 @token ',', ',', index, 0
@token 'STRING', '"' + flags + '"', index, flags.length @token 'STRING', '"' + flags + '"', index, flags.length
@token ')', ')', end, 0 rparen = @token ')', ')', end, 0
rparen.regexEnd = true
end end
@ -417,7 +418,7 @@ exports.Lexer = class Lexer
else if value in SHIFT then tag = 'SHIFT' else if value in SHIFT then tag = 'SHIFT'
else if value in LOGIC or value is '?' and prev?.spaced then tag = 'LOGIC' else if value in LOGIC or value is '?' and prev?.spaced then tag = 'LOGIC'
else if prev and not prev.spaced else if prev and not prev.spaced
if value is '(' and prev[0] in CALLABLE and not prev.stringEnd if value is '(' and prev[0] in CALLABLE and not prev.stringEnd and not prev.regexEnd
prev[0] = 'FUNC_EXIST' if prev[0] is '?' prev[0] = 'FUNC_EXIST' if prev[0] is '?'
tag = 'CALL_START' tag = 'CALL_START'
else if value is '[' and prev[0] in INDEXABLE else if value is '[' and prev[0] in INDEXABLE

View File

@ -209,7 +209,7 @@ class exports.Rewriter
# Recognize standard implicit calls like # Recognize standard implicit calls like
# f a, f() b, f? c, h[0] d etc. # f a, f() b, f? c, h[0] d etc.
if (tag in IMPLICIT_FUNC and token.spaced and not token.stringEnd or if (tag in IMPLICIT_FUNC and token.spaced and not token.stringEnd and not token.regexEnd or
tag is '?' and i > 0 and not tokens[i - 1].spaced) and tag is '?' and i > 0 and not tokens[i - 1].spaced) and
(nextTag in IMPLICIT_CALL or (nextTag in IMPLICIT_CALL or
nextTag in IMPLICIT_UNSPACED_CALL and nextTag in IMPLICIT_UNSPACED_CALL and
@ -243,7 +243,7 @@ class exports.Rewriter
# which is probably always unintended. # which is probably always unintended.
# Furthermore don't allow this in literal arrays, as # Furthermore don't allow this in literal arrays, as
# that creates grammatical ambiguities. # that creates grammatical ambiguities.
if tag in IMPLICIT_FUNC and not token.stringEnd and if tag in IMPLICIT_FUNC and not token.stringEnd and not token.regexEnd and
@matchTags(i + 1, 'INDENT', null, ':') and @matchTags(i + 1, 'INDENT', null, ':') and
not @findTagsBackwards(i, ['CLASS', 'EXTENDS', 'IF', 'CATCH', not @findTagsBackwards(i, ['CLASS', 'EXTENDS', 'IF', 'CATCH',
'SWITCH', 'LEADING_WHEN', 'FOR', 'WHILE', 'UNTIL']) 'SWITCH', 'LEADING_WHEN', 'FOR', 'WHILE', 'UNTIL'])

View File

@ -91,3 +91,19 @@ test "#3194: string interpolation in array", ->
eq 2, arr.length eq 2, arr.length
eq 'ab', arr[0] eq 'ab', arr[0]
eq 'value', arr[1].key eq 'value', arr[1].key
test "regex interpolation in array", ->
arr = [ /a/
key: 'value'
]
eq 2, arr.length
eq 'a', arr[0].source
eq 'value', arr[1].key
b = 'b'
arr = [ ///a#{b}///
key: 'value'
]
eq 2, arr.length
eq 'ab', arr[0].source
eq 'value', arr[1].key

View File

@ -252,3 +252,16 @@ test '#2388: `///` in heregex interpolations', ->
scan = (regex) -> regex.exec('\t foo')[0] scan = (regex) -> regex.exec('\t foo')[0]
eq '/\t /', /// #{scan /// [#{ws}]* ///} /// + '' eq '/\t /', /// #{scan /// [#{ws}]* ///} /// + ''
test "regexes are not callable", ->
throws -> CoffeeScript.compile '/a/()'
throws -> CoffeeScript.compile '///a#{b}///()'
throws -> CoffeeScript.compile '/a/ 1'
throws -> CoffeeScript.compile '///a#{b}/// 1'
throws -> CoffeeScript.compile '''
/a/
k: v
'''
throws -> CoffeeScript.compile '''
///a#{b}///
k: v
'''