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:
parent
965034e16e
commit
fe7d5dfd19
3 changed files with 101 additions and 2 deletions
53
lib/lexer.js
53
lib/lexer.js
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
# -------
|
# -------
|
||||||
|
|
||||||
|
|
18
test/test_string_interpolation.coffee
Normal file
18
test/test_string_interpolation.coffee
Normal 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 '\\\\$$'
|
Loading…
Reference in a new issue