Handle backslashes at the end of heredocs
This commit is contained in:
parent
873ed071d4
commit
42aa8d256c
|
@ -173,7 +173,7 @@
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
string = match[0];
|
string = match[0];
|
||||||
this.token('STRING', this.removeNewlines(string), 0, string.length);
|
this.token('STRING', this.escapeLines(string), 0, string.length);
|
||||||
break;
|
break;
|
||||||
case '"':
|
case '"':
|
||||||
if (!(string = this.balancedString(this.chunk, '"'))) {
|
if (!(string = this.balancedString(this.chunk, '"'))) {
|
||||||
|
@ -185,7 +185,7 @@
|
||||||
lexedLength: string.length
|
lexedLength: string.length
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.token('STRING', this.removeNewlines(string), 0, string.length);
|
this.token('STRING', this.escapeLines(string), 0, string.length);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -198,13 +198,14 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
Lexer.prototype.heredocToken = function() {
|
Lexer.prototype.heredocToken = function() {
|
||||||
var doc, heredoc, match, quote;
|
var doc, heredoc, match, quote, trimmed;
|
||||||
if (!(match = HEREDOC.exec(this.chunk))) {
|
if (!(match = HEREDOC.exec(this.chunk))) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
heredoc = match[0];
|
heredoc = match[0];
|
||||||
quote = heredoc.charAt(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,
|
quote: quote,
|
||||||
indent: null
|
indent: null
|
||||||
});
|
});
|
||||||
|
@ -762,10 +763,6 @@
|
||||||
return LINE_CONTINUER.test(this.chunk) || ((_ref2 = this.tag()) === '\\' || _ref2 === '.' || _ref2 === '?.' || _ref2 === '?::' || _ref2 === 'UNARY' || _ref2 === 'MATH' || _ref2 === '+' || _ref2 === '-' || _ref2 === 'SHIFT' || _ref2 === 'RELATION' || _ref2 === 'COMPARE' || _ref2 === 'LOGIC' || _ref2 === 'THROW' || _ref2 === 'EXTENDS');
|
return LINE_CONTINUER.test(this.chunk) || ((_ref2 = this.tag()) === '\\' || _ref2 === '.' || _ref2 === '?.' || _ref2 === '?::' || _ref2 === 'UNARY' || _ref2 === 'MATH' || _ref2 === '+' || _ref2 === '-' || _ref2 === 'SHIFT' || _ref2 === 'RELATION' || _ref2 === 'COMPARE' || _ref2 === 'LOGIC' || _ref2 === 'THROW' || _ref2 === 'EXTENDS');
|
||||||
};
|
};
|
||||||
|
|
||||||
Lexer.prototype.removeNewlines = function(str) {
|
|
||||||
return this.escapeLines(str.replace(/^(.)\s*\n\s*/, '$1').replace(/\s*\n\s*(.)$/, '$1'));
|
|
||||||
};
|
|
||||||
|
|
||||||
Lexer.prototype.escapeLines = function(str, heredoc) {
|
Lexer.prototype.escapeLines = function(str, heredoc) {
|
||||||
str = str.replace(/\\[^\S\n]*(\n|\\)\s*/g, function(escaped, character) {
|
str = str.replace(/\\[^\S\n]*(\n|\\)\s*/g, function(escaped, character) {
|
||||||
if (character === '\n') {
|
if (character === '\n') {
|
||||||
|
@ -777,7 +774,7 @@
|
||||||
if (heredoc) {
|
if (heredoc) {
|
||||||
return str.replace(MULTILINER, '\\n');
|
return str.replace(MULTILINER, '\\n');
|
||||||
} else {
|
} else {
|
||||||
return str.replace(/\s*\n\s*/g, ' ');
|
return str.replace(/^(.)\s*\n\s*/, '$1').replace(/\s*\n\s*(.)$/, '$1').replace(/\s*\n\s*/g, ' ');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -855,13 +852,13 @@
|
||||||
|
|
||||||
NUMBER = /^0b[01]+|^0o[0-7]+|^0x[\da-f]+|^\d*\.?\d+(?:e[+-]?\d+)?/i;
|
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})/;
|
OPERATOR = /^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>])\2=?|\?(\.|::)|\.{2,3})/;
|
||||||
|
|
||||||
WHITESPACE = /^[^\n\S]+/;
|
WHITESPACE = /^[^\n\S]+/;
|
||||||
|
|
||||||
COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|(?:###)$)|^(?:\s*#(?!##[^#]).*)+/;
|
COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|###$)|^(?:\s*#(?!##[^#]).*)+/;
|
||||||
|
|
||||||
CODE = /^[-=]>/;
|
CODE = /^[-=]>/;
|
||||||
|
|
||||||
|
|
|
@ -190,13 +190,13 @@ exports.Lexer = class Lexer
|
||||||
when "'"
|
when "'"
|
||||||
return 0 unless match = SIMPLESTR.exec @chunk
|
return 0 unless match = SIMPLESTR.exec @chunk
|
||||||
string = match[0]
|
string = match[0]
|
||||||
@token 'STRING', @removeNewlines(string), 0, string.length
|
@token 'STRING', @escapeLines(string), 0, string.length
|
||||||
when '"'
|
when '"'
|
||||||
return 0 unless string = @balancedString @chunk, '"'
|
return 0 unless string = @balancedString @chunk, '"'
|
||||||
if 0 < string.indexOf '#{', 1
|
if 0 < string.indexOf '#{', 1
|
||||||
@interpolateString string[1...-1], strOffset: 1, lexedLength: string.length
|
@interpolateString string[1...-1], strOffset: 1, lexedLength: string.length
|
||||||
else
|
else
|
||||||
@token 'STRING', @removeNewlines(string), 0, string.length
|
@token 'STRING', @escapeLines(string), 0, string.length
|
||||||
else
|
else
|
||||||
return 0
|
return 0
|
||||||
if octalEsc = /^(?:\\.|[^\\])*\\(?:0[0-7]|[1-7])/.test string
|
if octalEsc = /^(?:\\.|[^\\])*\\(?:0[0-7]|[1-7])/.test string
|
||||||
|
@ -209,7 +209,9 @@ exports.Lexer = class Lexer
|
||||||
return 0 unless match = HEREDOC.exec @chunk
|
return 0 unless match = HEREDOC.exec @chunk
|
||||||
heredoc = match[0]
|
heredoc = match[0]
|
||||||
quote = heredoc.charAt 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 '#{'
|
if quote is '"' and 0 <= doc.indexOf '#{'
|
||||||
@interpolateString doc, heredoc: yes, strOffset: 3, lexedLength: heredoc.length
|
@interpolateString doc, heredoc: yes, strOffset: 3, lexedLength: heredoc.length
|
||||||
else
|
else
|
||||||
|
@ -684,11 +686,6 @@ exports.Lexer = class Lexer
|
||||||
@tag() in ['\\', '.', '?.', '?::', 'UNARY', 'MATH', '+', '-', 'SHIFT', 'RELATION'
|
@tag() in ['\\', '.', '?.', '?::', 'UNARY', 'MATH', '+', '-', 'SHIFT', 'RELATION'
|
||||||
'COMPARE', 'LOGIC', 'THROW', 'EXTENDS']
|
'COMPARE', 'LOGIC', 'THROW', 'EXTENDS']
|
||||||
|
|
||||||
# Remove newlines from beginning and end of string literals.
|
|
||||||
# `str` includes quotes.
|
|
||||||
removeNewlines: (str) ->
|
|
||||||
@escapeLines str.replace(/^(.)\s*\n\s*/, '$1').replace(/\s*\n\s*(.)$/, '$1')
|
|
||||||
|
|
||||||
# Converts newlines for string literals.
|
# Converts newlines for string literals.
|
||||||
escapeLines: (str, heredoc) ->
|
escapeLines: (str, heredoc) ->
|
||||||
# Ignore escaped backslashes and remove escaped newlines
|
# Ignore escaped backslashes and remove escaped newlines
|
||||||
|
@ -697,7 +694,10 @@ exports.Lexer = class Lexer
|
||||||
if heredoc
|
if heredoc
|
||||||
str.replace MULTILINER, '\\n'
|
str.replace MULTILINER, '\\n'
|
||||||
else
|
else
|
||||||
str.replace /\s*\n\s*/g, ' '
|
# Trim leading and trailing whitespace, string includes quotes
|
||||||
|
str.replace(/^(.)\s*\n\s*/, '$1')
|
||||||
|
.replace(/\s*\n\s*(.)$/, '$1')
|
||||||
|
.replace(/\s*\n\s*/g, ' ')
|
||||||
|
|
||||||
# Constructs a string token by escaping quotes and newlines.
|
# Constructs a string token by escaping quotes and newlines.
|
||||||
makeString: (body, quote, heredoc) ->
|
makeString: (body, quote, heredoc) ->
|
||||||
|
@ -779,7 +779,7 @@ NUMBER = ///
|
||||||
^ \d*\.?\d+ (?:e[+-]?\d+)? # decimal
|
^ \d*\.?\d+ (?:e[+-]?\d+)? # decimal
|
||||||
///i
|
///i
|
||||||
|
|
||||||
HEREDOC = /// ^ ("""|''') ([\s\S]*?) (?:\n[^\n\S]*)? \1 ///
|
HEREDOC = /// ^ ("""|''') (( [\s\S]*? ([^\\]|\\\\) )?) \1 ///
|
||||||
|
|
||||||
OPERATOR = /// ^ (
|
OPERATOR = /// ^ (
|
||||||
?: [-=]> # function
|
?: [-=]> # function
|
||||||
|
@ -793,7 +793,7 @@ OPERATOR = /// ^ (
|
||||||
|
|
||||||
WHITESPACE = /^[^\n\S]+/
|
WHITESPACE = /^[^\n\S]+/
|
||||||
|
|
||||||
COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|(?:###)$)|^(?:\s*#(?!##[^#]).*)+/
|
COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*|###$)|^(?:\s*#(?!##[^#]).*)+/
|
||||||
|
|
||||||
CODE = /^[-=]>/
|
CODE = /^[-=]>/
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,15 @@ test "#3229, multiline strings", ->
|
||||||
eq ' \
|
eq ' \
|
||||||
ok', ' ok'
|
ok', ' ok'
|
||||||
|
|
||||||
|
# #1273, empty strings.
|
||||||
|
eq '\
|
||||||
|
', ''
|
||||||
|
eq '
|
||||||
|
', ''
|
||||||
|
eq '
|
||||||
|
', ''
|
||||||
|
eq ' ', ' '
|
||||||
|
|
||||||
# Same behavior in interpolated strings.
|
# Same behavior in interpolated strings.
|
||||||
eq "interpolation #{1}
|
eq "interpolation #{1}
|
||||||
follows #{2} \
|
follows #{2} \
|
||||||
|
@ -79,6 +88,10 @@ test "#3229, multiline strings", ->
|
||||||
next line', 'escaped backslash at EOL\\ next line'
|
next line', 'escaped backslash at EOL\\ next line'
|
||||||
eq '\\
|
eq '\\
|
||||||
next line', '\\ next line'
|
next line', '\\ next line'
|
||||||
|
eq '\\
|
||||||
|
', '\\'
|
||||||
|
eq '\\\\\\
|
||||||
|
', '\\\\\\'
|
||||||
eq "#{1}\\
|
eq "#{1}\\
|
||||||
after interpolation", '1\\ after interpolation'
|
after interpolation", '1\\ after interpolation'
|
||||||
eq 'escaped backslash before slash\\ \
|
eq 'escaped backslash before slash\\ \
|
||||||
|
@ -120,11 +133,14 @@ test "#3249, escape newlines in heredocs with backslashes", ->
|
||||||
normal indentation
|
normal indentation
|
||||||
""", 'Set whitespace <- this is ignorednone\n normal indentation'
|
""", 'Set whitespace <- this is ignorednone\n normal indentation'
|
||||||
|
|
||||||
# Changed from #647
|
# Changed from #647, trailing backslash.
|
||||||
eq '''
|
eq '''
|
||||||
Hello, World\
|
Hello, World\
|
||||||
|
|
||||||
''', 'Hello, World'
|
''', 'Hello, World'
|
||||||
|
eq '''
|
||||||
|
\\
|
||||||
|
''', '\\'
|
||||||
|
|
||||||
# Backslash at the beginning of a literal string.
|
# Backslash at the beginning of a literal string.
|
||||||
eq '''\
|
eq '''\
|
||||||
|
@ -151,6 +167,9 @@ test "#3249, escape newlines in heredocs with backslashes", ->
|
||||||
escaped backslash at EOL\\
|
escaped backslash at EOL\\
|
||||||
next line
|
next line
|
||||||
''', 'escaped backslash at EOL\\\n next line'
|
''', 'escaped backslash at EOL\\\n next line'
|
||||||
|
eq '''\\
|
||||||
|
|
||||||
|
''', '\\\n'
|
||||||
|
|
||||||
# Backslashes at beginning of lines.
|
# Backslashes at beginning of lines.
|
||||||
eq '''first line
|
eq '''first line
|
||||||
|
@ -158,7 +177,7 @@ test "#3249, escape newlines in heredocs with backslashes", ->
|
||||||
eq """first line\
|
eq """first line\
|
||||||
\ backslash at BOL""", 'first line\ backslash at BOL'
|
\ backslash at BOL""", 'first line\ backslash at BOL'
|
||||||
|
|
||||||
# Edge case.
|
# Edge cases.
|
||||||
eq '''lone
|
eq '''lone
|
||||||
|
|
||||||
\
|
\
|
||||||
|
@ -166,6 +185,8 @@ test "#3249, escape newlines in heredocs with backslashes", ->
|
||||||
|
|
||||||
|
|
||||||
backslash''', 'lone\n\n backslash'
|
backslash''', 'lone\n\n backslash'
|
||||||
|
eq '''\
|
||||||
|
''', ''
|
||||||
|
|
||||||
#647
|
#647
|
||||||
eq "''Hello, World\\''", '''
|
eq "''Hello, World\\''", '''
|
||||||
|
@ -175,6 +196,10 @@ eq '""Hello, World\\""', """
|
||||||
"\"Hello, World\\\""
|
"\"Hello, World\\\""
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
test "#1273, escaping quotes at the end of heredocs.", ->
|
||||||
|
# """\""" no longer compiles
|
||||||
|
eq """\\""", '\\'
|
||||||
|
|
||||||
a = """
|
a = """
|
||||||
basic heredoc
|
basic heredoc
|
||||||
on two lines
|
on two lines
|
||||||
|
|
Loading…
Reference in New Issue