summaryrefslogtreecommitdiffstats
path: root/src/parse.rs
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2022-01-07 04:39:33 -0500
committerJesse Luehrs <doy@tozt.net>2022-01-07 04:39:33 -0500
commit94ff8b9091c077ca605df4141d3d1c8d0be10966 (patch)
treeec29c3cadb9a07158065a6da8cb20469a32d51b2 /src/parse.rs
parentde5db374da5c53e44c52bf896140efaf4b7ae7ec (diff)
downloadnbsh-94ff8b9091c077ca605df4141d3d1c8d0be10966.tar.gz
nbsh-94ff8b9091c077ca605df4141d3d1c8d0be10966.zip
fix quoted redirect targets
Diffstat (limited to 'src/parse.rs')
-rw-r--r--src/parse.rs79
1 files changed, 55 insertions, 24 deletions
diff --git a/src/parse.rs b/src/parse.rs
index 229f387..9d0d230 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -108,31 +108,41 @@ pub struct Word {
quoted: bool,
}
-impl Word {
+enum WordOrRedirect {
+ Word(Word),
+ Redirect(Redirect),
+}
+
+impl WordOrRedirect {
fn build_ast(pair: pest::iterators::Pair<Rule>) -> Self {
assert!(matches!(pair.as_rule(), Rule::word));
let mut inner = pair.into_inner();
let mut word = inner.next().unwrap();
- let mut word_str = String::new();
if matches!(word.as_rule(), Rule::redir_prefix) {
- word_str = word.as_str().trim().to_string();
+ let mut word_str = word.as_str().trim().to_string();
word = inner.next().unwrap();
- }
- assert!(matches!(
- word.as_rule(),
- Rule::bareword | Rule::single_string | Rule::double_string
- ));
- word_str.push_str(word.as_str());
- Self {
- word: word_str,
- interpolate: matches!(
+ assert!(matches!(
word.as_rule(),
- Rule::bareword | Rule::double_string
- ),
- quoted: matches!(
+ Rule::bareword | Rule::single_string | Rule::double_string
+ ));
+ word_str.push_str(word.as_str());
+ Self::Redirect(Redirect::parse(&word_str))
+ } else {
+ assert!(matches!(
word.as_rule(),
- Rule::single_string | Rule::double_string
- ),
+ Rule::bareword | Rule::single_string | Rule::double_string
+ ));
+ Self::Word(Word {
+ word: word.as_str().to_string(),
+ interpolate: matches!(
+ word.as_rule(),
+ Rule::bareword | Rule::double_string
+ ),
+ quoted: matches!(
+ word.as_rule(),
+ Rule::single_string | Rule::double_string
+ ),
+ })
}
}
}
@@ -148,13 +158,27 @@ impl Exe {
fn build_ast(pair: pest::iterators::Pair<Rule>) -> Self {
assert!(matches!(pair.as_rule(), Rule::exe));
let mut iter = pair.into_inner();
- let exe = Word::build_ast(iter.next().unwrap());
- let (args, redirects): (_, Vec<_>) =
- iter.map(Word::build_ast).partition(|word| {
- word.quoted || !word.word.contains(&['<', '>'][..])
- });
- let redirects =
- redirects.iter().map(|r| Redirect::parse(&r.word)).collect();
+ let exe = match WordOrRedirect::build_ast(iter.next().unwrap()) {
+ WordOrRedirect::Word(word) => word,
+ WordOrRedirect::Redirect(_) => todo!(),
+ };
+ let (args, redirects): (_, Vec<_>) = iter
+ .map(WordOrRedirect::build_ast)
+ .partition(|word| matches!(word, WordOrRedirect::Word(_)));
+ let args = args
+ .into_iter()
+ .map(|word| match word {
+ WordOrRedirect::Word(word) => word,
+ WordOrRedirect::Redirect(_) => unreachable!(),
+ })
+ .collect();
+ let redirects = redirects
+ .into_iter()
+ .map(|word| match word {
+ WordOrRedirect::Word(_) => unreachable!(),
+ WordOrRedirect::Redirect(redirect) => redirect,
+ })
+ .collect();
Self {
exe,
args,
@@ -525,5 +549,12 @@ mod test {
p!("foo >> bar", e!(w!("foo") ; r!(1, "bar", Append)))
)
);
+ parse_eq!(
+ "foo > 'bar baz'",
+ c!(
+ "foo > 'bar baz'",
+ p!("foo > 'bar baz'", e!(w!("foo") ; r!(1, "bar baz", Out)))
+ )
+ );
}
}