diff --git a/lib/lexer.js b/lib/lexer.js index 4eda209a..a4954b2b 100644 --- a/lib/lexer.js +++ b/lib/lexer.js @@ -149,7 +149,7 @@ doc = this.sanitizeHeredoc(match[2] || match[4], { quote: quote }); - this.interpolateString("" + quote + doc + quote); + this.interpolateString(("" + quote + doc + quote), false, true); this.line += count(match[1], "\n"); this.i += match[1].length; return true; @@ -453,7 +453,7 @@ } return !i ? false : str.substring(0, i); }; - Lexer.prototype.interpolateString = function(str, escapeQuotes) { + Lexer.prototype.interpolateString = function(str, escapeQuotes, heredoc) { var _d, _e, _f, _g, _h, _i, _j, escaped, expr, group, i, idx, inner, interp, interpolated, lexer, match, nested, pi, quote, tag, tok, token, tokens, value; if (str.length < 3 || !starts(str, '"')) { return this.token('STRING', str); @@ -486,7 +486,9 @@ } inner = expr.substring(2, expr.length - 1); if (inner.length) { - inner = inner.replace(new RegExp('\\\\' + quote, 'g'), quote); + if (heredoc) { + inner = inner.replace(new RegExp('\\\\' + quote, 'g'), quote); + } nested = lexer.tokenize(("(" + inner + ")"), { line: this.line }); diff --git a/src/lexer.coffee b/src/lexer.coffee index dee54605..ae224796 100644 --- a/src/lexer.coffee +++ b/src/lexer.coffee @@ -132,7 +132,7 @@ exports.Lexer: class Lexer return false unless match: @chunk.match(HEREDOC) quote: match[1].substr 0, 1 doc: @sanitizeHeredoc match[2] or match[4], {quote} - @interpolateString "$quote$doc$quote" + @interpolateString "$quote$doc$quote", no, yes @line: + count match[1], "\n" @i: + match[1].length true @@ -389,7 +389,7 @@ exports.Lexer: class Lexer # If it encounters an interpolation, this method will recursively create a # new Lexer, tokenize the interpolated contents, and merge them into the # token stream. - interpolateString: (str, escapeQuotes) -> + interpolateString: (str, escapeQuotes, heredoc) -> if str.length < 3 or not starts str, '"' @token 'STRING', str else @@ -411,7 +411,7 @@ exports.Lexer: class Lexer tokens.push ['STRING', "$quote${ str.substring(pi, i) }$quote"] if pi < i inner: expr.substring(2, expr.length - 1) if inner.length - inner: inner.replace new RegExp('\\\\' + quote, 'g'), quote + inner: inner.replace new RegExp('\\\\' + quote, 'g'), quote if heredoc nested: lexer.tokenize "($inner)", {line: @line} (tok[0]: ')') for tok, idx in nested when tok[0] is 'CALL_END' nested.pop() diff --git a/test/test_string_interpolation.coffee b/test/test_string_interpolation.coffee index 61a0a86f..81b5d370 100644 --- a/test/test_string_interpolation.coffee +++ b/test/test_string_interpolation.coffee @@ -11,6 +11,7 @@ ok "${hello}$${world}" is 'Hello$World' ok "Hello ${ 1 + 2 } World" is 'Hello 3 World' ok "$hello ${ 1 + 2 } $world" is "Hello 3 World" + [s, t, r, i, n, g]: ['s', 't', 'r', 'i', 'n', 'g'] ok "$s$t$r$i$n$g" is 'string' ok "${s}${t}${r}${i}${n}${g}" is 'string' @@ -20,6 +21,7 @@ ok "\${s}\${t}\${r}\${i}\${n}\${g}" is '${s}${t}${r}${i}${n}${g}' ok "\$string" is '$string' ok "\${string}" is '${string}' + ok "\$Escaping first" is '$Escaping first' ok "\${Escaping} first" is '${Escaping} first' ok "Escaping \$in middle" is 'Escaping $in middle' @@ -27,24 +29,29 @@ ok "Escaping \${in} middle" is 'Escaping ${in} middle' ok "Escaping \$last" is 'Escaping $last' ok "Escaping \${last}" is 'Escaping ${last}' + ok "$$" is '$$' ok "${}" is '' ok "${}A${} ${} ${}B${}" is 'A B' ok "\\\\\$$" is '\\\\\$$' ok "\\\${}" is '\\${}' + ok "I won $20 last night." is 'I won $20 last night.' ok "I won $${20} last night." is 'I won $20 last night.' ok "I won $#20 last night." is 'I won $#20 last night.' ok "I won $${'#20'} last night." is 'I won $#20 last night.' + ok "${hello + world}" is 'HelloWorld' ok "${hello + ' ' + world + '!'}" is 'Hello World!' + list: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ok "values: ${list.join(', ')}, length: ${list.length}." is 'values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, length: 10.' ok "values: ${list.join ' '}" is 'values: 0 1 2 3 4 5 6 7 8 9' + obj: { name: 'Joe' hi: -> "Hello $@name." @@ -53,6 +60,7 @@ obj: { ok obj.hi() is "Hello Joe." ok obj.cya() is "Goodbye Joe." + ok "With ${"quotes"}" is 'With quotes' ok 'With ${"quotes"}' is 'With ${"quotes"}' @@ -64,17 +72,24 @@ ok "Hello ${world ? "$hello"}" is 'Hello World' ok "Hello ${"${"${obj["name"]}" + '!'}"}" is 'Hello Joe!' + a: """ Hello ${ "Joe" } """ ok a is "Hello Joe" + a: 1 b: 2 c: 3 ok "$a$b$c" is '123' + result: null stash: (str) -> result: str stash "a ${ ('aa').replace /a/g, 'b' } c" -ok result is 'a bb c' \ No newline at end of file +ok result is 'a bb c' + + +foo: "hello" +ok "${foo.replace("\"", "")}" is 'hello' \ No newline at end of file