diff --git a/lib/coffee-script/lexer.js b/lib/coffee-script/lexer.js index 2f0ed8a3..db9fe5ee 100644 --- a/lib/coffee-script/lexer.js +++ b/lib/coffee-script/lexer.js @@ -288,7 +288,7 @@ }; 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) { case !(match = REGEX_ILLEGAL.exec(this.chunk)): this.error("regular expressions cannot begin with " + match[2], match.index + match[1].length); @@ -301,7 +301,7 @@ index = regex.length; prev = last(this.tokens); 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)) { return 0; } @@ -345,7 +345,8 @@ this.token(',', ',', index, 0); this.token('STRING', '"' + flags + '"', index, flags.length); } - this.token(')', ')', end, 0); + rparen = this.token(')', ')', end, 0); + rparen.regexEnd = true; } return end; }; @@ -505,7 +506,7 @@ } else if (__indexOf.call(LOGIC, value) >= 0 || value === '?' && (prev != null ? prev.spaced : void 0)) { tag = 'LOGIC'; } 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] === '?') { prev[0] = 'FUNC_EXIST'; } diff --git a/lib/coffee-script/rewriter.js b/lib/coffee-script/rewriter.js index e26fcbe6..355822bd 100644 --- a/lib/coffee-script/rewriter.js +++ b/lib/coffee-script/rewriter.js @@ -257,14 +257,14 @@ } 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 === '?') { tag = token[0] = 'FUNC_EXIST'; } startImplicitCall(i + 1); 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); stack.push(['INDENT', i + 2]); return forward(3); diff --git a/src/lexer.coffee b/src/lexer.coffee index bd40b7d9..f5fbbb54 100644 --- a/src/lexer.coffee +++ b/src/lexer.coffee @@ -263,7 +263,7 @@ exports.Lexer = class Lexer index = regex.length prev = last @tokens 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 else if prev[0] in NOT_REGEX return 0 @@ -289,7 +289,8 @@ exports.Lexer = class Lexer if flags @token ',', ',', index, 0 @token 'STRING', '"' + flags + '"', index, flags.length - @token ')', ')', end, 0 + rparen = @token ')', ')', end, 0 + rparen.regexEnd = true end @@ -417,7 +418,7 @@ exports.Lexer = class Lexer else if value in SHIFT then tag = 'SHIFT' else if value in LOGIC or value is '?' and prev?.spaced then tag = 'LOGIC' 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 '?' tag = 'CALL_START' else if value is '[' and prev[0] in INDEXABLE diff --git a/src/rewriter.coffee b/src/rewriter.coffee index 6431adee..becfe591 100644 --- a/src/rewriter.coffee +++ b/src/rewriter.coffee @@ -209,7 +209,7 @@ class exports.Rewriter # Recognize standard implicit calls like # 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 (nextTag in IMPLICIT_CALL or nextTag in IMPLICIT_UNSPACED_CALL and @@ -243,7 +243,7 @@ class exports.Rewriter # which is probably always unintended. # Furthermore don't allow this in literal arrays, as # 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 not @findTagsBackwards(i, ['CLASS', 'EXTENDS', 'IF', 'CATCH', 'SWITCH', 'LEADING_WHEN', 'FOR', 'WHILE', 'UNTIL']) diff --git a/test/arrays.coffee b/test/arrays.coffee index b150593e..5947dbd8 100644 --- a/test/arrays.coffee +++ b/test/arrays.coffee @@ -91,3 +91,19 @@ test "#3194: string interpolation in array", -> eq 2, arr.length eq 'ab', arr[0] 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 diff --git a/test/regexps.coffee b/test/regexps.coffee index 05d7c348..36085f69 100644 --- a/test/regexps.coffee +++ b/test/regexps.coffee @@ -252,3 +252,16 @@ test '#2388: `///` in heregex interpolations', -> scan = (regex) -> regex.exec('\t foo')[0] 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 + '''