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

more refactors to interpolation

This commit is contained in:
Jeremy Ashkenas 2010-03-08 21:55:06 -05:00
parent b5af5f66fb
commit 4b97b15c0c
3 changed files with 39 additions and 46 deletions

View file

@ -129,14 +129,14 @@
// Matches strings, including multi-line strings. Ensures that quotation marks // Matches strings, including multi-line strings. Ensures that quotation marks
// are balanced within the string's contents, and within nested interpolations. // are balanced within the string's contents, and within nested interpolations.
Lexer.prototype.string_token = function string_token() { Lexer.prototype.string_token = function string_token() {
var merge, string; var string;
if (!(starts(this.chunk, '"') || starts(this.chunk, "'"))) { if (!(starts(this.chunk, '"') || starts(this.chunk, "'"))) {
return false; return false;
} }
if (!((string = this.balanced_token(['"', '"'], ['${', '}']) || this.balanced_token(["'", "'"])))) { if (!((string = this.balanced_token(['"', '"'], ['${', '}']) || this.balanced_token(["'", "'"])))) {
return false; return false;
} }
this.interpolate_string(string.replace(STRING_NEWLINES, " \\\n"), (merge = true)); this.interpolate_string(string.replace(STRING_NEWLINES, " \\\n"));
this.line += count(string, "\n"); this.line += count(string, "\n");
this.i += string.length; this.i += string.length;
return true; return true;
@ -171,7 +171,7 @@
// to distinguish from division, so we borrow some basic heuristics from // to distinguish from division, so we borrow some basic heuristics from
// JavaScript and Ruby. // JavaScript and Ruby.
Lexer.prototype.regex_token = function regex_token() { Lexer.prototype.regex_token = function regex_token() {
var _a, _b, _c, _d, each, flags, i, interp_tokens, merge, regex, str; var _a, flags, regex, str;
if (!(this.chunk.match(REGEX_START))) { if (!(this.chunk.match(REGEX_START))) {
return false; return false;
} }
@ -182,27 +182,13 @@
return false; return false;
} }
regex += ((flags = this.chunk.substr(regex.length).match(REGEX_FLAGS))); regex += ((flags = this.chunk.substr(regex.length).match(REGEX_FLAGS)));
if (((0 < (_d = regex.indexOf('${'))) && (_d < regex.indexOf('}'))) || regex.match(REGEX_INTERPOLATION)) { if (((0 < (_a = regex.indexOf('${'))) && (_a < regex.indexOf('}'))) || regex.match(REGEX_INTERPOLATION)) {
str = regex.substring(1).split('/')[0]; str = regex.substring(1).split('/')[0];
str = str.replace(REGEX_ESCAPE, function(escaped) { str = str.replace(REGEX_ESCAPE, function(escaped) {
return '\\' + escaped; return '\\' + escaped;
}); });
this.tokens = this.tokens.concat([['(', '('], ['NEW', 'new'], ['IDENTIFIER', 'RegExp'], ['CALL_START', '(']]); this.tokens = this.tokens.concat([['(', '('], ['NEW', 'new'], ['IDENTIFIER', 'RegExp'], ['CALL_START', '(']]);
interp_tokens = this.interpolate_string("\"" + str + "\"", (merge = false)); this.interpolate_string("\"" + str + "\"", true);
_a = interp_tokens;
for (i = 0, _b = _a.length; i < _b; i++) {
each = _a[i];
if ((_c = each[0]) === 'TOKENS') {
this.tokens = this.tokens.concat(each[1]);
} else if (_c === 'STRING') {
this.token(each[0], each[1].substring(0, 1) + each[1].substring(1, each[1].length - 1).replace(/"/g, '\\"') + each[1].substring(0, 1));
} else {
this.token(each[0], each[1]);
}
if (i < interp_tokens.length - 1) {
this.token('+', '+');
}
}
this.tokens = this.tokens.concat([[',', ','], ['STRING', "'" + flags + "'"], [')', ')'], [')', ')']]); this.tokens = this.tokens.concat([[',', ','], ['STRING', "'" + flags + "'"], [')', ')'], [')', ')']]);
} else { } else {
this.token('REGEX', regex); this.token('REGEX', regex);
@ -471,8 +457,8 @@
// If it encounters an interpolation, this method will recursively create a // If it encounters an interpolation, this method will recursively create a
// new Lexer, tokenize the interpolated contents, and merge them into the // new Lexer, tokenize the interpolated contents, and merge them into the
// token stream. // token stream.
Lexer.prototype.interpolate_string = function interpolate_string(str, merge) { Lexer.prototype.interpolate_string = function interpolate_string(str, escape_quotes) {
var _a, _b, _c, _d, each, expr, group, i, inner, interp, lexer, match, nested, pi, quote, tokens; var _a, _b, _c, _d, _e, escaped, expr, group, i, inner, interp, lexer, match, nested, pi, quote, tag, token, tokens, value;
if (str.length < 3 || !starts(str, '"')) { if (str.length < 3 || !starts(str, '"')) {
return this.token('STRING', str); return this.token('STRING', str);
} else { } else {
@ -524,14 +510,22 @@
if (!(tokens[0][0] === 'STRING')) { if (!(tokens[0][0] === 'STRING')) {
tokens.unshift(['STRING', "''"]); tokens.unshift(['STRING', "''"]);
} }
if (((typeof merge !== "undefined" && merge !== null) ? merge : true)) { _c = tokens;
_c = tokens; for (i = 0, _d = _c.length; i < _d; i++) {
for (i = 0, _d = _c.length; i < _d; i++) { token = _c[i];
each = _c[i]; _e = token;
each[0] === 'TOKENS' ? (this.tokens = this.tokens.concat(each[1])) : this.token(each[0], each[1]); tag = _e[0];
if (i < tokens.length - 1) { value = _e[1];
this.token('+', '+'); if (tag === 'TOKENS') {
} this.tokens = this.tokens.concat(value);
} else if (tag === 'STRING' && escape_quotes) {
escaped = value.substring(1, value.length - 1).replace(/"/g, '\\"');
this.token(tag, "\"" + escaped + "\"");
} else {
this.token(tag, value);
}
if (i < tokens.length - 1) {
this.token('+', '+');
} }
} }
return tokens; return tokens;

View file

@ -99,7 +99,7 @@ exports.Lexer: class Lexer
return false unless string: return false unless string:
@balanced_token(['"', '"'], ['${', '}']) or @balanced_token(['"', '"'], ['${', '}']) or
@balanced_token ["'", "'"] @balanced_token ["'", "'"]
@interpolate_string string.replace(STRING_NEWLINES, " \\\n"), merge: true @interpolate_string string.replace(STRING_NEWLINES, " \\\n")
@line += count string, "\n" @line += count string, "\n"
@i += string.length @i += string.length
true true
@ -134,13 +134,7 @@ exports.Lexer: class Lexer
str: regex.substring(1).split('/')[0] str: regex.substring(1).split('/')[0]
str: str.replace REGEX_ESCAPE, (escaped) -> '\\' + escaped str: str.replace REGEX_ESCAPE, (escaped) -> '\\' + escaped
@tokens: @tokens.concat [['(', '('], ['NEW', 'new'], ['IDENTIFIER', 'RegExp'], ['CALL_START', '(']] @tokens: @tokens.concat [['(', '('], ['NEW', 'new'], ['IDENTIFIER', 'RegExp'], ['CALL_START', '(']]
interp_tokens: @interpolate_string "\"$str\"", merge: false @interpolate_string "\"$str\"", yes
for each, i in interp_tokens
switch each[0]
when 'TOKENS' then @tokens: @tokens.concat each[1]
when 'STRING' then @token each[0], each[1].substring(0, 1) + each[1].substring(1, each[1].length - 1).replace(/"/g, '\\"') + each[1].substring(0, 1)
else @token each[0], each[1]
@token '+', '+' if i < interp_tokens.length - 1
@tokens: @tokens.concat [[',', ','], ['STRING', "'$flags'"], [')', ')'], [')', ')']] @tokens: @tokens.concat [[',', ','], ['STRING', "'$flags'"], [')', ')'], [')', ')']]
else else
@token 'REGEX', regex @token 'REGEX', regex
@ -348,7 +342,7 @@ exports.Lexer: class Lexer
# If it encounters an interpolation, this method will recursively create a # If it encounters an interpolation, this method will recursively create a
# new Lexer, tokenize the interpolated contents, and merge them into the # new Lexer, tokenize the interpolated contents, and merge them into the
# token stream. # token stream.
interpolate_string: (str, merge) -> interpolate_string: (str, escape_quotes) ->
if str.length < 3 or not starts str, '"' if str.length < 3 or not starts str, '"'
@token 'STRING', str @token 'STRING', str
else else
@ -380,13 +374,16 @@ exports.Lexer: class Lexer
i += 1 i += 1
tokens.push ['STRING', "$quote${ str.substring(pi, i) }$quote"] if pi < i and pi < str.length - 1 tokens.push ['STRING', "$quote${ str.substring(pi, i) }$quote"] if pi < i and pi < str.length - 1
tokens.unshift ['STRING', "''"] unless tokens[0][0] is 'STRING' tokens.unshift ['STRING', "''"] unless tokens[0][0] is 'STRING'
if (merge ? true) for token, i in tokens
for each, i in tokens [tag, value]: token
if each[0] is 'TOKENS' if tag is 'TOKENS'
@tokens: @tokens.concat each[1] @tokens: @tokens.concat value
else else if tag is 'STRING' and escape_quotes
@token each[0], each[1] escaped: value.substring(1, value.length - 1).replace(/"/g, '\\"')
@token '+', '+' if i < tokens.length - 1 @token tag, "\"$escaped\""
else
@token tag, value
@token '+', '+' if i < tokens.length - 1
tokens tokens
# Helpers # Helpers

View file

@ -13,5 +13,7 @@ ok '$a$b$c'.match(/\$A\$B\$C/i)
a: 1 a: 1
b: 2 b: 2
c: 3 c: 3
ok '123'.match(/$a$b$c/i) ok '123'.match(/$a$b$c/i)
[a, b, c]: [1, 2, /\d+/]
ok (/$a$b$c$/i).toString() is '/12/\\d+/$/i'