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

Added string interpolation for identifiers

This commit is contained in:
Stan Angeloff 2010-03-06 00:30:49 +02:00 committed by Jeremy Ashkenas
parent 965034e16e
commit fe7d5dfd19
3 changed files with 101 additions and 2 deletions

View file

@ -168,7 +168,7 @@
return false; return false;
} }
escaped = string.replace(STRING_NEWLINES, " \\\n"); escaped = string.replace(STRING_NEWLINES, " \\\n");
this.token('STRING', escaped); this.interpolate_string(escaped);
this.line += count(string, "\n"); this.line += count(string, "\n");
this.i += string.length; this.i += string.length;
return true; return true;
@ -397,6 +397,57 @@
Lexer.prototype.assignment_error = function assignment_error() { Lexer.prototype.assignment_error = function assignment_error() {
throw new Error('SyntaxError: Reserved word "' + this.value() + '" on line ' + this.line + ' can\'t be assigned'); throw new Error('SyntaxError: Reserved word "' + this.value() + '" on line ' + this.line + ' can\'t be assigned');
}; };
// Replace variables and block calls inside double-quoted strings.
Lexer.prototype.interpolate_string = function interpolate_string(escaped) {
var _a, _b, _c, _d, _e, _f, _g, _h, before, each, group, i, identifier, identifier_match, quote, tokens;
if (escaped.length < 3 || escaped.indexOf('"') !== 0) {
return this.token('STRING', escaped);
} else {
tokens = [];
quote = escaped.substring(0, 1);
escaped = escaped.substring(1, escaped.length - 1);
while (escaped.length) {
identifier_match = escaped.match(/(^|[\s\S]*?(?:[\\]|\\\\)?)(\$([a-zA-Z_]\w*))/);
if (identifier_match) {
_a = identifier_match;
group = _a[0];
before = _a[1];
identifier = _a[2];
if (before.substring(before.length - 1) === '\\') {
if (before.length) {
tokens.push(['STRING', quote + before.substring(0, before.length - 1) + identifier + quote]);
}
} else {
if (before.length) {
tokens.push(['STRING', quote + before + quote]);
}
tokens.push(['IDENTIFIER', identifier.substring(1)]);
}
escaped = escaped.substring(group.length);
} else {
tokens.push(['STRING', quote + escaped + quote]);
escaped = '';
}
}
if (tokens.length > 1) {
_d = tokens.length - 1; _e = 1;
for (_c = 0, i = _d; (_d <= _e ? i <= _e : i >= _e); (_d <= _e ? i += 1 : i -= 1), _c++) {
tokens[i][0] === 'STRING' && tokens[i - 1][0] === 'STRING' ? tokens.splice(i - 1, 2, ['STRING', quote + tokens[i - 1][1].substring(1, tokens[i - 1][1].length - 1) + tokens[i][1].substring(1, tokens[i][1].length - 1) + quote]) : null;
}
}
_f = []; _g = tokens;
for (i = 0, _h = _g.length; i < _h; i++) {
each = _g[i];
_f.push((function() {
this.token(each[0], each[1]);
if (i < tokens.length - 1) {
return this.token('+', '+');
}
}).call(this));
}
return _f;
}
};
// Helpers // Helpers
// ------- // -------
// Add a token to the results, taking note of the line number. // Add a token to the results, taking note of the line number.

View file

@ -166,7 +166,7 @@ exports.Lexer: class Lexer
string_token: -> string_token: ->
return false unless string: @match STRING, 1 return false unless string: @match STRING, 1
escaped: string.replace STRING_NEWLINES, " \\\n" escaped: string.replace STRING_NEWLINES, " \\\n"
@token 'STRING', escaped @interpolate_string escaped
@line += count string, "\n" @line += count string, "\n"
@i += string.length @i += string.length
true true
@ -340,6 +340,36 @@ exports.Lexer: class Lexer
assignment_error: -> assignment_error: ->
throw new Error 'SyntaxError: Reserved word "' + @value() + '" on line ' + @line + ' can\'t be assigned' throw new Error 'SyntaxError: Reserved word "' + @value() + '" on line ' + @line + ' can\'t be assigned'
# Replace variables and block calls inside double-quoted strings.
interpolate_string: (escaped) ->
if escaped.length < 3 or escaped.indexOf('"') isnt 0
@token 'STRING', escaped
else
tokens: []
quote: escaped.substring(0, 1)
escaped: escaped.substring(1, escaped.length - 1)
while escaped.length
identifier_match: escaped.match /(^|[\s\S]*?(?:[\\]|\\\\)?)(\$([a-zA-Z_]\w*))/
if identifier_match
[group, before, identifier]: identifier_match
if before.substring(before.length - 1) is '\\'
tokens.push ['STRING', quote + before.substring(0, before.length - 1) + identifier + quote] if before.length
else
tokens.push ['STRING', quote + before + quote] if before.length
tokens.push ['IDENTIFIER', identifier.substring(1)]
escaped: escaped.substring(group.length)
else
tokens.push ['STRING', quote + escaped + quote]
escaped: ''
if tokens.length > 1
for i in [tokens.length - 1..1]
if tokens[i][0] is 'STRING' and tokens[i - 1][0] is 'STRING'
tokens.splice i - 1, 2, ['STRING', quote + tokens[i - 1][1].substring(1, tokens[i - 1][1].length - 1) +
tokens[i][1].substring(1, tokens[i][1].length - 1) + quote]
for each, i in tokens
@token each[0], each[1]
@token '+', '+' if i < tokens.length - 1
# Helpers # Helpers
# ------- # -------

View file

@ -0,0 +1,18 @@
hello: 'Hello'
world: 'World'
ok '$hello $world!' is '$hello $world!'
ok "$hello $world!" is 'Hello World!'
ok "[$hello$world]" is '[HelloWorld]'
ok "$hello$$world" is 'Hello$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 '$s$t$r$i$n$g'
ok "\\$string" is '$string'
ok "\\$Escaping first" is '$Escaping first'
ok "Escaping \\$in middle" is 'Escaping $in middle'
ok "Escaping \\$last" is 'Escaping $last'
ok "$$" is '$$'
ok "\\\\$$" is '\\\\$$'