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

View File

@ -99,7 +99,7 @@ exports.Lexer: class Lexer
return false unless string:
@balanced_token(['"', '"'], ['${', '}']) or
@balanced_token ["'", "'"]
@interpolate_string string.replace(STRING_NEWLINES, " \\\n"), merge: true
@interpolate_string string.replace(STRING_NEWLINES, " \\\n")
@line += count string, "\n"
@i += string.length
true
@ -134,13 +134,7 @@ exports.Lexer: class Lexer
str: regex.substring(1).split('/')[0]
str: str.replace REGEX_ESCAPE, (escaped) -> '\\' + escaped
@tokens: @tokens.concat [['(', '('], ['NEW', 'new'], ['IDENTIFIER', 'RegExp'], ['CALL_START', '(']]
interp_tokens: @interpolate_string "\"$str\"", merge: false
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
@interpolate_string "\"$str\"", yes
@tokens: @tokens.concat [[',', ','], ['STRING', "'$flags'"], [')', ')'], [')', ')']]
else
@token 'REGEX', regex
@ -348,7 +342,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.
interpolate_string: (str, merge) ->
interpolate_string: (str, escape_quotes) ->
if str.length < 3 or not starts str, '"'
@token 'STRING', str
else
@ -380,13 +374,16 @@ exports.Lexer: class Lexer
i += 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'
if (merge ? true)
for each, i in tokens
if each[0] is 'TOKENS'
@tokens: @tokens.concat each[1]
else
@token each[0], each[1]
@token '+', '+' if i < tokens.length - 1
for token, i in tokens
[tag, value]: token
if tag is 'TOKENS'
@tokens: @tokens.concat value
else if tag is 'STRING' and escape_quotes
escaped: value.substring(1, value.length - 1).replace(/"/g, '\\"')
@token tag, "\"$escaped\""
else
@token tag, value
@token '+', '+' if i < tokens.length - 1
tokens
# Helpers

View File

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