1
0
Fork 0
mirror of https://github.com/jashkenas/coffeescript.git synced 2022-11-09 12:23:24 -05:00

#923 ... correct interpolation.

This commit is contained in:
Jeremy Ashkenas 2010-12-18 13:20:14 -05:00
parent dd168373a1
commit fba165408c
3 changed files with 40 additions and 28 deletions

View file

@ -128,7 +128,7 @@
this.token('STRING', (string = match[0]).replace(MULTILINER, '\\\n')); this.token('STRING', (string = match[0]).replace(MULTILINER, '\\\n'));
break; break;
case '"': case '"':
if (!(string = this.balancedString(this.chunk, [['"', '"'], ['#{', '}']]))) { if (!(string = this.balancedString(this.chunk, '"', '"'))) {
return 0; return 0;
} }
if (0 < string.indexOf('#{', 1)) { if (0 < string.indexOf('#{', 1)) {
@ -447,32 +447,30 @@
Lexer.prototype.assignmentError = function() { Lexer.prototype.assignmentError = function() {
throw SyntaxError("Reserved word \"" + (this.value()) + "\" on line " + (this.line + 1) + " can't be assigned"); throw SyntaxError("Reserved word \"" + (this.value()) + "\" on line " + (this.line + 1) + " can't be assigned");
}; };
Lexer.prototype.balancedString = function(str, delimited, options) { Lexer.prototype.balancedString = function(str, start, end) {
var i, open, pair, stack, _i, _len, _ref; var i, letter, prev, stack, _ref;
if (options == null) { stack = [end];
options = {};
}
stack = [delimited[0]];
for (i = 1, _ref = str.length; (1 <= _ref ? i < _ref : i > _ref); (1 <= _ref ? i += 1 : i -= 1)) { for (i = 1, _ref = str.length; (1 <= _ref ? i < _ref : i > _ref); (1 <= _ref ? i += 1 : i -= 1)) {
switch (str.charAt(i)) { switch (letter = str.charAt(i)) {
case '\\': case '\\':
i++; i++;
continue; continue;
case stack[stack.length - 1][1]: case end:
stack.pop(); stack.pop();
if (!stack.length) { if (!stack.length) {
return str.slice(0, i + 1); return str.slice(0, i + 1);
} }
end = stack[stack.length - 1];
continue; continue;
} }
for (_i = 0, _len = delimited.length; _i < _len; _i++) { if (end === '}' && (letter === '"' || letter === "'")) {
pair = delimited[_i]; stack.push(end = letter);
if ((open = pair[0]) === str.substr(i, open.length)) { } else if (end === '}' && letter === '{') {
stack.push(pair); stack.push(end = '}');
i += open.length - 1; } else if (end === '"' && prev === '#' && letter === '{') {
break; stack.push(end = '}');
}
} }
prev = letter;
} }
throw new Error("unterminated " + (stack.pop()[0]) + " on line " + (this.line + 1)); throw new Error("unterminated " + (stack.pop()[0]) + " on line " + (this.line + 1));
}; };
@ -490,7 +488,7 @@
i += 1; i += 1;
continue; continue;
} }
if (!(letter === '#' && str.charAt(i + 1) === '{' && (expr = this.balancedString(str.slice(i + 1), [['{', '}']])))) { if (!(letter === '#' && str.charAt(i + 1) === '{' && (expr = this.balancedString(str.slice(i + 1), '#{', '}')))) {
continue; continue;
} }
if (pi < i) { if (pi < i) {

View file

@ -139,7 +139,7 @@ exports.Lexer = class Lexer
return 0 unless match = SIMPLESTR.exec @chunk return 0 unless match = SIMPLESTR.exec @chunk
@token 'STRING', (string = match[0]).replace MULTILINER, '\\\n' @token 'STRING', (string = match[0]).replace MULTILINER, '\\\n'
when '"' when '"'
return 0 unless string = @balancedString @chunk, [['"', '"'], ['#{', '}']] return 0 unless string = @balancedString @chunk, '"', '"'
if 0 < string.indexOf '#{', 1 if 0 < string.indexOf '#{', 1
@interpolateString string.slice 1, -1 @interpolateString string.slice 1, -1
else else
@ -387,21 +387,26 @@ exports.Lexer = class Lexer
# a series of delimiters, all of which must be nested correctly within the # a series of delimiters, all of which must be nested correctly within the
# contents of the string. This method allows us to have strings within # contents of the string. This method allows us to have strings within
# interpolations within strings, ad infinitum. # interpolations within strings, ad infinitum.
balancedString: (str, delimited, options = {}) -> balancedString: (str, start, end) ->
stack = [delimited[0]] stack = [end]
for i in [1...str.length] for i in [1...str.length]
switch str.charAt i switch letter = str.charAt i
when '\\' when '\\'
i++ i++
continue continue
when stack[stack.length - 1][1] when end
stack.pop() stack.pop()
return str.slice 0, i + 1 unless stack.length unless stack.length
return str.slice 0, i + 1
end = stack[stack.length - 1]
continue continue
for pair in delimited when (open = pair[0]) is str.substr i, open.length if end is '}' and letter in ['"', "'"]
stack.push pair stack.push end = letter
i += open.length - 1 else if end is '}' and letter is '{'
break stack.push end = '}'
else if end is '"' and prev is '#' and letter is '{'
stack.push end = '}'
prev = letter
throw new Error "unterminated #{ stack.pop()[0] } on line #{ @line + 1 }" throw new Error "unterminated #{ stack.pop()[0] } on line #{ @line + 1 }"
@ -423,7 +428,7 @@ exports.Lexer = class Lexer
i += 1 i += 1
continue continue
unless letter is '#' and str.charAt(i+1) is '{' and unless letter is '#' and str.charAt(i+1) is '{' and
(expr = @balancedString str.slice(i+1), [['{', '}']]) (expr = @balancedString str.slice(i + 1), '#{', '}')
continue continue
tokens.push ['NEOSTRING', str.slice(pi, i)] if pi < i tokens.push ['NEOSTRING', str.slice(pi, i)] if pi < i
inner = expr.slice(1, -1) inner = expr.slice(1, -1)

View file

@ -107,3 +107,12 @@ eq 'multiline nested "interpolations" work', """multiline #{
"\"interpolations\"" "\"interpolations\""
}" }"
} work""" } work"""
# Issue #923: Tricky interpolation.
eq "#{ "{" }", "{"
eq "#{ '#{}}' } }", '#{}} }'
eq "#{"'#{ ({a: "b#{1}"}['a']) }'"}", "'b1'"