diff --git a/lib/lexer.js b/lib/lexer.js index 13c33e37..e6c6a934 100644 --- a/lib/lexer.js +++ b/lib/lexer.js @@ -459,42 +459,32 @@ throw SyntaxError("Reserved word \"" + (this.value()) + "\" on line " + (this.line + 1) + " can't be assigned"); }; Lexer.prototype.balancedString = function(str, delimited, options) { - var close, i, levels, open, pair, slen, _i, _len; + var i, open, pair, stack, _i, _len, _to; options == null && (options = {}); - levels = []; - i = 0; - slen = str.length; - while (i < slen) { - if (levels.length && str.charAt(i) === '\\') { - i += 1; - } else { - for (_i = 0, _len = delimited.length; _i < _len; _i++) { - pair = delimited[_i]; - open = pair[0], close = pair[1]; - if (levels.length && starts(str, close, i) && last(levels) === pair) { - levels.pop(); - i += close.length - 1; - if (!levels.length) { - i += 1; - } - break; - } - if (starts(str, open, i)) { - levels.push(pair); - i += open.length - 1; - break; + stack = [delimited[0]]; + for (i = 1, _to = str.length - 1; 1 <= _to ? i <= _to : i >= _to; 1 <= _to ? i++ : i--) { + switch (str.charAt(i)) { + case '\\': + i++; + continue; + break; + case stack[stack.length - 1][1]: + stack.pop(); + if (!stack.length) { + return str.slice(0, i + 1); } + continue; + } + for (_i = 0, _len = delimited.length; _i < _len; _i++) { + pair = delimited[_i]; + if ((open = pair[0]) === str.substr(i, open.length)) { + stack.push(pair); + i += open.length - 1; + break; } } - if (!levels.length) { - break; - } - i += 1; } - if (levels.length) { - throw SyntaxError("Unterminated " + (levels.pop()[0]) + " starting on line " + (this.line + 1)); - } - return i && str.slice(0, i); + throw new Error("unterminated " + (stack.pop()[0]) + " on line " + (this.line + 1)); }; Lexer.prototype.interpolateString = function(str, options) { var expr, heredoc, i, inner, interpolated, letter, nested, pi, regex, tag, tokens, value, _len, _ref, _this; diff --git a/src/lexer.coffee b/src/lexer.coffee index 605faa14..c04d1992 100644 --- a/src/lexer.coffee +++ b/src/lexer.coffee @@ -398,29 +398,22 @@ exports.Lexer = class Lexer # contents of the string. This method allows us to have strings within # interpolations within strings, ad infinitum. balancedString: (str, delimited, options = {}) -> - levels = [] - i = 0 - slen = str.length - while i < slen - if levels.length and str.charAt(i) is '\\' - i += 1 - else - for pair in delimited - [open, close] = pair - if levels.length and starts(str, close, i) and last(levels) is pair - levels.pop() - i += close.length - 1 - i += 1 unless levels.length - break - if starts str, open, i - levels.push(pair) - i += open.length - 1 - break - break if not levels.length - i += 1 - if levels.length - throw SyntaxError "Unterminated #{levels.pop()[0]} starting on line #{@line + 1}" - i and str.slice 0, i + stack = [delimited[0]] + for i from 1 to str.length - 1 + switch str.charAt i + when '\\' + i++ + continue + when stack[stack.length - 1][1] + stack.pop() + return str.slice 0, i + 1 unless stack.length + continue + for pair in delimited when (open = pair[0]) is str.substr i, open.length + stack.push pair + i += open.length - 1 + break + throw new Error "unterminated #{ stack.pop()[0] } on line #{ @line + 1 }" + # Expand variables and expressions inside double-quoted strings using # Ruby-like notation for substitution of arbitrary expressions.