mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
Merge pull request #3261 from xixixao/issue1273
Fix #1273, Handle backslashes at the end of heredocs
This commit is contained in:
commit
13f205404c
4 changed files with 77 additions and 56 deletions
|
@ -166,30 +166,25 @@
|
|||
};
|
||||
|
||||
Lexer.prototype.stringToken = function() {
|
||||
var match, octalEsc, string;
|
||||
switch (this.chunk.charAt(0)) {
|
||||
var octalEsc, quote, string, trimmed;
|
||||
switch (quote = this.chunk.charAt(0)) {
|
||||
case "'":
|
||||
if (!(match = SIMPLESTR.exec(this.chunk))) {
|
||||
return 0;
|
||||
}
|
||||
string = match[0];
|
||||
this.token('STRING', this.removeNewlines(string), 0, string.length);
|
||||
string = SIMPLESTR.exec(this.chunk)[0];
|
||||
break;
|
||||
case '"':
|
||||
if (!(string = this.balancedString(this.chunk, '"'))) {
|
||||
return 0;
|
||||
}
|
||||
if (0 < string.indexOf('#{', 1)) {
|
||||
this.interpolateString(string.slice(1, -1), {
|
||||
strOffset: 1,
|
||||
lexedLength: string.length
|
||||
});
|
||||
} else {
|
||||
this.token('STRING', this.removeNewlines(string), 0, string.length);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
string = this.balancedString(this.chunk, '"');
|
||||
}
|
||||
if (!string) {
|
||||
return 0;
|
||||
}
|
||||
trimmed = this.removeNewlines(string.slice(1, -1));
|
||||
if (quote === '"' && 0 < string.indexOf('#{', 1)) {
|
||||
this.interpolateString(trimmed, {
|
||||
strOffset: 1,
|
||||
lexedLength: string.length
|
||||
});
|
||||
} else {
|
||||
this.token('STRING', quote + this.escapeLines(trimmed) + quote, 0, string.length);
|
||||
}
|
||||
if (octalEsc = /^(?:\\.|[^\\])*\\(?:0[0-7]|[1-7])/.test(string)) {
|
||||
this.error("octal escape sequences " + string + " are not allowed");
|
||||
|
@ -198,13 +193,14 @@
|
|||
};
|
||||
|
||||
Lexer.prototype.heredocToken = function() {
|
||||
var doc, heredoc, match, quote;
|
||||
var doc, heredoc, match, quote, trimmed;
|
||||
if (!(match = HEREDOC.exec(this.chunk))) {
|
||||
return 0;
|
||||
}
|
||||
heredoc = match[0];
|
||||
quote = heredoc.charAt(0);
|
||||
doc = this.sanitizeHeredoc(match[2], {
|
||||
trimmed = match[2].replace(/(([^\\]|\\\\)\s*)\n[^\n\S]*$/, '$1');
|
||||
doc = this.sanitizeHeredoc(trimmed, {
|
||||
quote: quote,
|
||||
indent: null
|
||||
});
|
||||
|
@ -601,10 +597,6 @@
|
|||
offsetInChunk = offsetInChunk || 0;
|
||||
strOffset = strOffset || 0;
|
||||
lexedLength = lexedLength || str.length;
|
||||
if (heredoc && str.length > 0 && str[0] === '\n') {
|
||||
str = str.slice(1);
|
||||
strOffset++;
|
||||
}
|
||||
tokens = [];
|
||||
pi = 0;
|
||||
i = -1;
|
||||
|
@ -763,7 +755,7 @@
|
|||
};
|
||||
|
||||
Lexer.prototype.removeNewlines = function(str) {
|
||||
return this.escapeLines(str.replace(/^(.)\s*\n\s*/, '$1').replace(/\s*\n\s*(.)$/, '$1'));
|
||||
return str.replace(/^\s*\n\s*/, '').replace(/([^\\]|\\\\)\s*\n\s*$/, '$1');
|
||||
};
|
||||
|
||||
Lexer.prototype.escapeLines = function(str, heredoc) {
|
||||
|
@ -855,13 +847,13 @@
|
|||
|
||||
NUMBER = /^0b[01]+|^0o[0-7]+|^0x[\da-f]+|^\d*\.?\d+(?:e[+-]?\d+)?/i;
|
||||
|
||||
HEREDOC = /^("""|''')([\s\S]*?)(?:\n[^\n\S]*)?\1/;
|
||||
HEREDOC = /^("""|''')(([\s\S]*?([^\\]|\\\\))?)\1/;
|
||||
|
||||
OPERATOR = /^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>])\2=?|\?(\.|::)|\.{2,3})/;
|
||||
|
||||
WHITESPACE = /^[^\n\S]+/;
|
||||
|
||||
COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|(?:###)$)|^(?:\s*#(?!##[^#]).*)+/;
|
||||
COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|###$)|^(?:\s*#(?!##[^#]).*)+/;
|
||||
|
||||
CODE = /^[-=]>/;
|
||||
|
||||
|
|
|
@ -186,19 +186,15 @@ exports.Lexer = class Lexer
|
|||
# Matches strings, including multi-line strings. Ensures that quotation marks
|
||||
# are balanced within the string's contents, and within nested interpolations.
|
||||
stringToken: ->
|
||||
switch @chunk.charAt 0
|
||||
when "'"
|
||||
return 0 unless match = SIMPLESTR.exec @chunk
|
||||
string = match[0]
|
||||
@token 'STRING', @removeNewlines(string), 0, string.length
|
||||
when '"'
|
||||
return 0 unless string = @balancedString @chunk, '"'
|
||||
if 0 < string.indexOf '#{', 1
|
||||
@interpolateString string[1...-1], strOffset: 1, lexedLength: string.length
|
||||
else
|
||||
@token 'STRING', @removeNewlines(string), 0, string.length
|
||||
else
|
||||
return 0
|
||||
switch quote = @chunk.charAt 0
|
||||
when "'" then [string] = SIMPLESTR.exec @chunk
|
||||
when '"' then string = @balancedString @chunk, '"'
|
||||
return 0 unless string
|
||||
trimmed = @removeNewlines string[1...-1]
|
||||
if quote is '"' and 0 < string.indexOf '#{', 1
|
||||
@interpolateString trimmed, strOffset: 1, lexedLength: string.length
|
||||
else
|
||||
@token 'STRING', quote + @escapeLines(trimmed) + quote, 0, string.length
|
||||
if octalEsc = /^(?:\\.|[^\\])*\\(?:0[0-7]|[1-7])/.test string
|
||||
@error "octal escape sequences #{string} are not allowed"
|
||||
string.length
|
||||
|
@ -209,7 +205,9 @@ exports.Lexer = class Lexer
|
|||
return 0 unless match = HEREDOC.exec @chunk
|
||||
heredoc = match[0]
|
||||
quote = heredoc.charAt 0
|
||||
doc = @sanitizeHeredoc match[2], quote: quote, indent: null
|
||||
# Trim last newline if it's not escaped
|
||||
trimmed = match[2].replace /(([^\\]|\\\\)\s*)\n[^\n\S]*$/, '$1'
|
||||
doc = @sanitizeHeredoc trimmed, quote: quote, indent: null
|
||||
if quote is '"' and 0 <= doc.indexOf '#{'
|
||||
@interpolateString doc, heredoc: yes, strOffset: 3, lexedLength: heredoc.length
|
||||
else
|
||||
|
@ -528,11 +526,6 @@ exports.Lexer = class Lexer
|
|||
strOffset = strOffset || 0
|
||||
lexedLength = lexedLength || str.length
|
||||
|
||||
# Clip leading \n from heredoc
|
||||
if heredoc and str.length > 0 and str[0] == '\n'
|
||||
str = str[1...]
|
||||
strOffset++
|
||||
|
||||
# Parse the string.
|
||||
tokens = []
|
||||
pi = 0
|
||||
|
@ -684,10 +677,10 @@ exports.Lexer = class Lexer
|
|||
@tag() in ['\\', '.', '?.', '?::', 'UNARY', 'MATH', '+', '-', 'SHIFT', 'RELATION'
|
||||
'COMPARE', 'LOGIC', 'THROW', 'EXTENDS']
|
||||
|
||||
# Remove newlines from beginning and end of string literals.
|
||||
# `str` includes quotes.
|
||||
# Remove newlines from beginning and (non escaped) from end of string literals.
|
||||
removeNewlines: (str) ->
|
||||
@escapeLines str.replace(/^(.)\s*\n\s*/, '$1').replace(/\s*\n\s*(.)$/, '$1')
|
||||
str.replace(/^\s*\n\s*/, '')
|
||||
.replace(/([^\\]|\\\\)\s*\n\s*$/, '$1')
|
||||
|
||||
# Converts newlines for string literals.
|
||||
escapeLines: (str, heredoc) ->
|
||||
|
@ -779,7 +772,7 @@ NUMBER = ///
|
|||
^ \d*\.?\d+ (?:e[+-]?\d+)? # decimal
|
||||
///i
|
||||
|
||||
HEREDOC = /// ^ ("""|''') ([\s\S]*?) (?:\n[^\n\S]*)? \1 ///
|
||||
HEREDOC = /// ^ ("""|''') (( [\s\S]*? ([^\\]|\\\\) )?) \1 ///
|
||||
|
||||
OPERATOR = /// ^ (
|
||||
?: [-=]> # function
|
||||
|
@ -793,7 +786,7 @@ OPERATOR = /// ^ (
|
|||
|
||||
WHITESPACE = /^[^\n\S]+/
|
||||
|
||||
COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|(?:###)$)|^(?:\s*#(?!##[^#]).*)+/
|
||||
COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|###$)|^(?:\s*#(?!##[^#]).*)+/
|
||||
|
||||
CODE = /^[-=]>/
|
||||
|
||||
|
|
|
@ -68,6 +68,9 @@ test "#1026", ->
|
|||
test "#1050", ->
|
||||
cantCompile "### */ ###"
|
||||
|
||||
test "#1273: escaping quotes at the end of heredocs", ->
|
||||
cantCompile '"""\\"""' # """\"""
|
||||
|
||||
test "#1106: __proto__ compilation", ->
|
||||
object = eq
|
||||
@["__proto__"] = true
|
||||
|
|
|
@ -63,6 +63,15 @@ test "#3229, multiline strings", ->
|
|||
eq ' \
|
||||
ok', ' ok'
|
||||
|
||||
# #1273, empty strings.
|
||||
eq '\
|
||||
', ''
|
||||
eq '
|
||||
', ''
|
||||
eq '
|
||||
', ''
|
||||
eq ' ', ' '
|
||||
|
||||
# Same behavior in interpolated strings.
|
||||
eq "interpolation #{1}
|
||||
follows #{2} \
|
||||
|
@ -72,6 +81,9 @@ test "#3229, multiline strings", ->
|
|||
'string ' + "inside
|
||||
interpolation"
|
||||
}", "a string inside interpolation"
|
||||
eq "
|
||||
#{1}
|
||||
", '1'
|
||||
|
||||
# Handle escaped backslashes correctly.
|
||||
eq '\\', `'\\'`
|
||||
|
@ -79,6 +91,10 @@ test "#3229, multiline strings", ->
|
|||
next line', 'escaped backslash at EOL\\ next line'
|
||||
eq '\\
|
||||
next line', '\\ next line'
|
||||
eq '\\
|
||||
', '\\'
|
||||
eq '\\\\\\
|
||||
', '\\\\\\'
|
||||
eq "#{1}\\
|
||||
after interpolation", '1\\ after interpolation'
|
||||
eq 'escaped backslash before slash\\ \
|
||||
|
@ -120,11 +136,14 @@ test "#3249, escape newlines in heredocs with backslashes", ->
|
|||
normal indentation
|
||||
""", 'Set whitespace <- this is ignorednone\n normal indentation'
|
||||
|
||||
# Changed from #647
|
||||
# Changed from #647, trailing backslash.
|
||||
eq '''
|
||||
Hello, World\
|
||||
|
||||
''', 'Hello, World'
|
||||
eq '''
|
||||
\\
|
||||
''', '\\'
|
||||
|
||||
# Backslash at the beginning of a literal string.
|
||||
eq '''\
|
||||
|
@ -139,6 +158,11 @@ test "#3249, escape newlines in heredocs with backslashes", ->
|
|||
too #{3}\
|
||||
!
|
||||
""", 'interpolation 1\n follows 2 too 3!'
|
||||
eq """
|
||||
|
||||
#{1} #{2}
|
||||
|
||||
""", '\n1 2\n'
|
||||
|
||||
# TODO: uncomment when #2388 is fixed
|
||||
# eq """a heredoc #{
|
||||
|
@ -151,6 +175,9 @@ test "#3249, escape newlines in heredocs with backslashes", ->
|
|||
escaped backslash at EOL\\
|
||||
next line
|
||||
''', 'escaped backslash at EOL\\\n next line'
|
||||
eq '''\\
|
||||
|
||||
''', '\\\n'
|
||||
|
||||
# Backslashes at beginning of lines.
|
||||
eq '''first line
|
||||
|
@ -158,7 +185,7 @@ test "#3249, escape newlines in heredocs with backslashes", ->
|
|||
eq """first line\
|
||||
\ backslash at BOL""", 'first line\ backslash at BOL'
|
||||
|
||||
# Edge case.
|
||||
# Edge cases.
|
||||
eq '''lone
|
||||
|
||||
\
|
||||
|
@ -166,6 +193,8 @@ test "#3249, escape newlines in heredocs with backslashes", ->
|
|||
|
||||
|
||||
backslash''', 'lone\n\n backslash'
|
||||
eq '''\
|
||||
''', ''
|
||||
|
||||
#647
|
||||
eq "''Hello, World\\''", '''
|
||||
|
@ -175,6 +204,10 @@ eq '""Hello, World\\""', """
|
|||
"\"Hello, World\\\""
|
||||
"""
|
||||
|
||||
test "#1273, escaping quotes at the end of heredocs.", ->
|
||||
# """\""" no longer compiles
|
||||
eq """\\""", '\\'
|
||||
|
||||
a = """
|
||||
basic heredoc
|
||||
on two lines
|
||||
|
|
Loading…
Add table
Reference in a new issue