diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 70585a3198de2cf9644fed0f4b5d955cfbc83226..32354af8d78e1fc9b5cb113772627da834bcec7c 100755 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -862,6 +862,11 @@ describe "Parser" do it_parses "1 \\\n + 2", Call.new(1.int32, "+", [2.int32] of ASTNode) it_parses "1\\\n + 2", Call.new(1.int32, "+", [2.int32] of ASTNode) + it_parses %("hello " \\\n "world"), StringLiteral.new("hello world") + it_parses %("hello "\\\n"world"), StringLiteral.new("hello world") + it_parses %("hello \#{1}" \\\n "\#{2} world"), StringInterpolation.new(["hello ".string, 1.int32, 2.int32, " world".string] of ASTNode) + assert_syntax_error %("foo" "bar") + %w(def macro class struct module fun alias abstract include extend lib).each do |keyword| assert_syntax_error "def foo\n#{keyword}\nend", Def.new("foo", body: keyword.call) end diff --git a/src/compiler/crystal/syntax/lexer.cr b/src/compiler/crystal/syntax/lexer.cr index f33ef7d270fbc8956012f92c0a25702cd8f0745a..a94d8d81b6487f035a492496a3da63737ab596b4 100644 --- a/src/compiler/crystal/syntax/lexer.cr +++ b/src/compiler/crystal/syntax/lexer.cr @@ -53,6 +53,7 @@ module Crystal if next_char == '\n' @line_number += 1 @column_number = 1 + @token.passed_backslash_newline = true consume_whitespace reset_wants_regex = false else @@ -883,6 +884,7 @@ module Crystal next_char @line_number += 1 @column_number = 1 + @token.passed_backslash_newline = true else unknown_token end @@ -1935,6 +1937,7 @@ module Crystal @token.column_number = @column_number @token.filename = @filename @token.location = nil + @token.passed_backslash_newline = false end def next_token_skip_space diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 0c573f7aadbbbf2195200ca88e297bc7e8cb7bb4..a0a0a48ea5e23be8657406ef95f10ba47280b068 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -1452,7 +1452,6 @@ module Crystal end delimiter_state = @token.delimiter_state - modifiers = 0 check :DELIMITER_START @@ -1462,6 +1461,48 @@ module Crystal pieces = [] of ASTNode | String has_interpolation = false + delimiter_state, has_interpolation, modifiers = consume_delimiter pieces, delimiter_state, has_interpolation + + if delimiter_state.kind == :string + while true + passed_backslash_newline = @token.passed_backslash_newline + skip_space + + if passed_backslash_newline && @token.type == :DELIMITER_START && @token.delimiter_state.kind == :string + next_string_token(delimiter_state) + delimiter_state = @token.delimiter_state + delimiter_state, has_interpolation, modifiers = consume_delimiter pieces, delimiter_state, has_interpolation + else + break + end + end + end + + if has_interpolation + pieces = pieces.map do |piece| + piece.is_a?(String) ? StringLiteral.new(piece) : piece + end + if pieces.length == 1 + result = Call.new(pieces.first, "to_s") + else + result = StringInterpolation.new(pieces) + end + else + result = StringLiteral.new pieces.join + end + + case delimiter_state.kind + when :command + result = Call.new(nil, "`", [result] of ASTNode) + when :regex + result = RegexLiteral.new(result, modifiers) + end + + result + end + + def consume_delimiter(pieces, delimiter_state, has_interpolation) + modifiers = 0 while true case @token.type when :STRING @@ -1504,27 +1545,7 @@ module Crystal end end - if has_interpolation - pieces = pieces.map do |piece| - piece.is_a?(String) ? StringLiteral.new(piece) : piece - end - if pieces.length == 1 - result = Call.new(pieces.first, "to_s") - else - result = StringInterpolation.new(pieces) - end - else - result = StringLiteral.new pieces.join - end - - case delimiter_state.kind - when :command - result = Call.new(nil, "`", [result] of ASTNode) - when :regex - result = RegexLiteral.new(result, modifiers) - end - - result + {delimiter_state, has_interpolation, modifiers} end def consume_regex_modifiers diff --git a/src/compiler/crystal/syntax/token.cr b/src/compiler/crystal/syntax/token.cr index 766ce919003bba43ebefc0709f41c2b290807e2c..56366c5ac4b1029508fea1e56e6f3263696cc7ed 100644 --- a/src/compiler/crystal/syntax/token.cr +++ b/src/compiler/crystal/syntax/token.cr @@ -10,6 +10,7 @@ module Crystal property filename property delimiter_state property macro_state + property passed_backslash_newline record(MacroState, whitespace, nest, delimiter_state, beginning_of_line, yields) do def self.default @@ -36,6 +37,7 @@ module Crystal @column_number = 0 @delimiter_state = DelimiterState.default @macro_state = MacroState.default + @passed_backslash_newline = false end def location