diff options
author | Jesse Luehrs <doy@tozt.net> | 2022-01-07 18:29:10 -0500 |
---|---|---|
committer | Jesse Luehrs <doy@tozt.net> | 2022-01-07 18:29:10 -0500 |
commit | 0766964a90bef03df35de35d672995a385fc7172 (patch) | |
tree | 09d2565ba28a64b5a6c974f3393cec2679e6e1c8 | |
parent | 94ff8b9091c077ca605df4141d3d1c8d0be10966 (diff) | |
download | nbsh-0766964a90bef03df35de35d672995a385fc7172.tar.gz nbsh-0766964a90bef03df35de35d672995a385fc7172.zip |
implement escaping characters
-rw-r--r-- | src/parse.rs | 109 | ||||
-rw-r--r-- | src/shell.pest | 12 |
2 files changed, 112 insertions, 9 deletions
diff --git a/src/parse.rs b/src/parse.rs index 9d0d230..72de364 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -125,19 +125,35 @@ impl WordOrRedirect { word.as_rule(), Rule::bareword | Rule::single_string | Rule::double_string )); + let interpolate = matches!( + word.as_rule(), + Rule::bareword | Rule::double_string + ); word_str.push_str(word.as_str()); - Self::Redirect(Redirect::parse(&word_str)) + if interpolate { + word_str = strip_escape(&word_str); + } else { + word_str = strip_basic_escape(&word_str); + } + Self::Redirect(Redirect::parse(&strip_escape(&word_str))) } else { assert!(matches!( word.as_rule(), Rule::bareword | Rule::single_string | Rule::double_string )); + let interpolate = matches!( + word.as_rule(), + Rule::bareword | Rule::double_string + ); + let mut word_str = word.as_str().to_string(); + if interpolate { + word_str = strip_escape(&word_str); + } else { + word_str = strip_basic_escape(&word_str); + } Self::Word(Word { - word: word.as_str().to_string(), - interpolate: matches!( - word.as_rule(), - Rule::bareword | Rule::double_string - ), + word: word_str, + interpolate, quoted: matches!( word.as_rule(), Rule::single_string | Rule::double_string @@ -277,6 +293,44 @@ impl Commands { } } +fn strip_escape(s: &str) -> String { + let mut new = String::new(); + let mut escape = false; + for c in s.chars() { + if escape { + new.push(c); + escape = false; + } else { + match c { + '\\' => escape = true, + _ => new.push(c), + } + } + } + new +} + +fn strip_basic_escape(s: &str) -> String { + let mut new = String::new(); + let mut escape = false; + for c in s.chars() { + if escape { + match c { + '\\' | '\'' => {} + _ => new.push('\\'), + } + new.push(c); + escape = false; + } else { + match c { + '\\' => escape = true, + _ => new.push(c), + } + } + } + new +} + #[derive(Debug)] pub struct Error { input: String, @@ -557,4 +611,47 @@ mod test { ) ); } + + #[test] + fn test_escape() { + parse_eq!( + "foo\\ bar", + c!("foo\\ bar", p!("foo\\ bar", e!(w!("foo bar")))) + ); + parse_eq!( + "'foo\\ bar'", + c!( + "'foo\\ bar'", + p!("'foo\\ bar'", e!(w!("foo\\ bar", false, true))) + ) + ); + parse_eq!( + "\"foo\\ bar\"", + c!( + "\"foo\\ bar\"", + p!("\"foo\\ bar\"", e!(w!("foo bar", true, true))) + ) + ); + parse_eq!( + "\"foo\\\"bar\"", + c!( + "\"foo\\\"bar\"", + p!("\"foo\\\"bar\"", e!(w!("foo\"bar", true, true))) + ) + ); + parse_eq!( + "'foo\\'bar\\\\'", + c!( + "'foo\\'bar\\\\'", + p!("'foo\\'bar\\\\'", e!(w!("foo'bar\\", false, true))) + ) + ); + parse_eq!( + "foo > bar\\ baz", + c!( + "foo > bar\\ baz", + p!("foo > bar\\ baz", e!(w!("foo") ; r!(1, "bar baz", Out))) + ) + ); + } } diff --git a/src/shell.pest b/src/shell.pest index 1ff20c4..ffb0f42 100644 --- a/src/shell.pest +++ b/src/shell.pest @@ -1,6 +1,12 @@ -bareword_char = @{ !("|" | ";" | "\"" | "'" | WHITESPACE | COMMENT) ~ ANY } -single_string_char = @{ "\\'" | (!"'" ~ ANY) } -double_string_char = @{ "\\\"" | (!"\"" ~ ANY) } +basic_escape_char = @{ "\\\\" | "\\'" } +escape_char = @{ "\\" ~ ANY } + +bareword_char = @{ + escape_char | + !("|" | ";" | "\"" | "'" | WHITESPACE | COMMENT) ~ ANY +} +single_string_char = @{ basic_escape_char | (!"'" ~ ANY) } +double_string_char = @{ escape_char | (!"\"" ~ ANY) } redir_prefix = @{ ASCII_DIGIT* ~ (">>" | ">" | "<") ~ WHITESPACE* } bareword = @{ bareword_char+ } |