diff --git a/src/parser.rs b/src/parser.rs index f5b5efd..6f669cb 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -5,7 +5,7 @@ use crate::command::{Argument, Block, Command}; use nom::branch::alt; use nom::bytes::complete::{escaped_transform, is_not, tag, take, take_while_m_n}; use nom::character::complete::{ - alphanumeric1, anychar, char, line_ending, none_of, not_line_ending, one_of, space0, space1, + alphanumeric1, anychar, char, line_ending, not_line_ending, one_of, space0, space1, }; use nom::combinator::{consumed, eof, map_res, opt, peek, recognize, value, verify}; use nom::error::ErrorKind; @@ -258,20 +258,20 @@ fn comment(input: Span) -> IResult { recognize(preceded(alt((tag("//"), tag("#"))), not_line_ending))(input) } -/// Parses a line with \ line continuation escapes. -fn line_continuation(input: Span) -> IResult { - // Special case a blank line. - let (input, maybe_newline) = opt(line_ending)(input)?; - if maybe_newline.is_some() { - return Ok((input, "".to_string())); - } +/// Parses a raw line with optional \ line continuation escapes. Naïve but +/// sufficient implementation that e.g. doesn't support \\ escapes. +fn line_continuation(mut input: Span) -> IResult { + let mut result = String::new(); + loop { + let (i, line) = terminated(not_line_ending, line_ending)(input)?; + input = i; + result.push_str(line.as_ref()); - terminated( - escaped_transform( - none_of("\\\n"), - '\\', - alt((value("\\", tag("\\")), value("\n", line_ending))), - ), - line_ending, - )(input) + if line.ends_with('\\') { + // Remove \ and continue. + result.pop(); + continue; + } + return Ok((input, result)); + } } diff --git a/tests/scripts/commands b/tests/scripts/commands index 11c062f..691d5a3 100644 --- a/tests/scripts/commands +++ b/tests/scripts/commands @@ -84,16 +84,15 @@ Error: Command { name: "command arg", args: [], prefix: None, tags: {}, silent: Command { name: "command arg", args: [], prefix: None, tags: {"tag"}, silent: false, fail: false, line_number: 79 } prefix: Error: Command { name: "command arg", args: [], prefix: Some("prefix"), tags: {"tag"}, silent: false, fail: true, line_number: 80 } -# > respects \ line continuation, unless escaped. +# > respects \ line continuation, but it can't be escaped by \\. > a very \ long line \ with line \ continuation --- -Command { name: "a very \nlong line \nwith line \ncontinuation", args: [], prefix: None, tags: {}, silent: false, fail: false, line_number: 88 } +Command { name: "a very long line with line continuation", args: [], prefix: None, tags: {}, silent: false, fail: false, line_number: 88 } -> a line ending with \\ +> a line with \n ending with \\ another line --- -Command { name: "a line ending with \\", args: [], prefix: None, tags: {}, silent: false, fail: false, line_number: 95 } -Command { name: "another", args: [Argument { key: None, value: "line" }], prefix: None, tags: {}, silent: false, fail: false, line_number: 96 } +Command { name: "a line with \\n ending with \\another line", args: [], prefix: None, tags: {}, silent: false, fail: false, line_number: 95 }