From 27934e2a48254b4b948b846680afe213e61fdfc0 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Sat, 8 Jan 2022 17:28:58 -0500 Subject: add parsing for control statements --- src/parse/ast.rs | 55 ++++++++++++++++++++++++++++++++----- src/parse/test_ast.rs | 75 ++++++++++++++++++++++++++++----------------------- 2 files changed, 90 insertions(+), 40 deletions(-) (limited to 'src/parse') diff --git a/src/parse/ast.rs b/src/parse/ast.rs index ba13bc1..2216dcb 100644 --- a/src/parse/ast.rs +++ b/src/parse/ast.rs @@ -8,7 +8,7 @@ struct Shell; #[derive(Debug, PartialEq, Eq)] pub struct Commands { - pipelines: Vec, + commands: Vec, input_string: String, } @@ -25,8 +25,8 @@ impl Commands { )) } - pub fn pipelines(&self) -> &[Pipeline] { - &self.pipelines + pub fn commands(&self) -> &[Command] { + &self.commands } pub fn input_string(&self) -> &str { @@ -37,15 +37,56 @@ impl Commands { assert!(matches!(commands.as_rule(), Rule::commands)); let input_string = commands.as_str().to_string(); Self { - pipelines: commands - .into_inner() - .map(Pipeline::build_ast) - .collect(), + commands: commands.into_inner().map(Command::build_ast).collect(), input_string, } } } +#[derive(Debug, PartialEq, Eq)] +pub enum Command { + Pipeline(Pipeline), + If(Pipeline), + While(Pipeline), + For(String, Pipeline), + End, +} + +impl Command { + fn build_ast(command: pest::iterators::Pair) -> Self { + assert!(matches!(command.as_rule(), Rule::command)); + let next = command.into_inner().next().unwrap(); + match next.as_rule() { + Rule::pipeline => Self::Pipeline(Pipeline::build_ast(next)), + Rule::command => { + let control = next.into_inner().next().unwrap(); + assert!(matches!(control.as_rule(), Rule::control)); + let ty = control.into_inner().next().unwrap(); + match ty.as_rule() { + Rule::control_if => Self::If(Pipeline::build_ast( + ty.into_inner().next().unwrap(), + )), + Rule::control_while => Self::While(Pipeline::build_ast( + ty.into_inner().next().unwrap(), + )), + Rule::control_for => { + let mut inner = ty.into_inner(); + let var = inner.next().unwrap(); + assert!(matches!(var.as_rule(), Rule::bareword)); + Self::For( + var.as_str().to_string(), + Pipeline::build_ast(inner.next().unwrap()), + ) + } + Rule::control_end => Self::End, + _ => unreachable!(), + } + } + _ => unreachable!(), + } + } +} + #[derive(Debug, PartialEq, Eq)] pub struct Pipeline { exes: Vec, diff --git a/src/parse/test_ast.rs b/src/parse/test_ast.rs index 18a8f89..81d6ef6 100644 --- a/src/parse/test_ast.rs +++ b/src/parse/test_ast.rs @@ -1,9 +1,18 @@ use super::*; -macro_rules! c { - ($input_string:expr, $($pipelines:expr),*) => { +impl From for Command { + fn from(pipeline: Pipeline) -> Self { + Self::Pipeline(pipeline) + } +} + +macro_rules! cs { + ($input_string:expr, $($commands:expr),*) => { Commands { - pipelines: vec![$($pipelines),*], + commands: [$($commands),*] + .into_iter() + .map(|c| c.into()) + .collect(), input_string: $input_string.to_string(), } }; @@ -104,25 +113,25 @@ macro_rules! parse_eq { #[test] fn test_basic() { - parse_eq!("foo", c!("foo", p!("foo", e!(w!("foo"))))); + parse_eq!("foo", cs!("foo", p!("foo", e!(w!("foo"))))); parse_eq!( "foo bar", - c!("foo bar", p!("foo bar", e!(w!("foo"), w!("bar")))) + cs!("foo bar", p!("foo bar", e!(w!("foo"), w!("bar")))) ); parse_eq!( "foo bar baz", - c!( + cs!( "foo bar baz", p!("foo bar baz", e!(w!("foo"), w!("bar"), w!("baz"))) ) ); parse_eq!( "foo | bar", - c!("foo | bar", p!("foo | bar", e!(w!("foo")), e!(w!("bar")))) + cs!("foo | bar", p!("foo | bar", e!(w!("foo")), e!(w!("bar")))) ); parse_eq!( "command ls; perl -E 'say foo' | tr a-z A-Z; builtin echo bar", - c!( + cs!( "command ls; perl -E 'say foo' | tr a-z A-Z; builtin echo bar", p!("command ls", e!(w!("command"), w!("ls"))), p!( @@ -137,15 +146,15 @@ fn test_basic() { #[test] fn test_whitespace() { - parse_eq!(" foo ", c!("foo", p!("foo", e!(w!("foo"))))); + parse_eq!(" foo ", cs!("foo", p!("foo", e!(w!("foo"))))); parse_eq!( " foo # this is a comment", - c!("foo", p!("foo", e!(w!("foo")))) + cs!("foo", p!("foo", e!(w!("foo")))) ); - parse_eq!("foo#comment", c!("foo", p!("foo", e!(w!("foo"))))); + parse_eq!("foo#comment", cs!("foo", p!("foo", e!(w!("foo"))))); parse_eq!( "foo;bar|baz;quux#comment", - c!( + cs!( "foo;bar|baz;quux", p!("foo", e!(w!("foo"))), p!("bar|baz", e!(w!("bar")), e!(w!("baz"))), @@ -154,14 +163,14 @@ fn test_whitespace() { ); parse_eq!( "foo | bar ", - c!( + cs!( "foo | bar", p!("foo | bar", e!(w!("foo")), e!(w!("bar"))) ) ); parse_eq!( " abc def ghi |jkl mno| pqr stu; vwxyz # comment", - c!( + cs!( "abc def ghi |jkl mno| pqr stu; vwxyz", p!( "abc def ghi |jkl mno| pqr stu", @@ -174,7 +183,7 @@ fn test_whitespace() { ); parse_eq!( "foo 'bar # baz' \"quux # not a comment\" # comment", - c!( + cs!( "foo 'bar # baz' \"quux # not a comment\"", p!( "foo 'bar # baz' \"quux # not a comment\"", @@ -192,21 +201,21 @@ fn test_whitespace() { fn test_redirect() { parse_eq!( "foo > bar", - c!( + cs!( "foo > bar", p!("foo > bar", e!(w!("foo") ; r!(1, w!("bar"), Out))) ) ); parse_eq!( "foo /dev/null 2>&1", - c!( + cs!( "foo > /dev/null 2>&1", p!( "foo > /dev/null 2>&1", @@ -219,21 +228,21 @@ fn test_redirect() { ); parse_eq!( "foo >>bar", - c!( + cs!( "foo >>bar", p!("foo >>bar", e!(w!("foo") ; r!(1, w!("bar"), Append))) ) ); parse_eq!( "foo >> bar", - c!( + cs!( "foo >> bar", p!("foo >> bar", e!(w!("foo") ; r!(1, w!("bar"), Append))) ) ); parse_eq!( "foo > 'bar baz'", - c!( + cs!( "foo > 'bar baz'", p!( "foo > 'bar baz'", @@ -247,36 +256,36 @@ fn test_redirect() { fn test_escape() { parse_eq!( "foo\\ bar", - c!("foo\\ bar", p!("foo\\ bar", e!(w!("foo bar")))) + cs!("foo\\ bar", p!("foo\\ bar", e!(w!("foo bar")))) ); parse_eq!( "'foo\\ bar'", - c!("'foo\\ bar'", p!("'foo\\ bar'", e!(w!(wps!("foo\\ bar"))))) + cs!("'foo\\ bar'", p!("'foo\\ bar'", e!(w!(wps!("foo\\ bar"))))) ); parse_eq!( "\"foo\\ bar\"", - c!( + cs!( "\"foo\\ bar\"", p!("\"foo\\ bar\"", e!(w!(wpd!("foo bar")))) ) ); parse_eq!( "\"foo\\\"bar\"", - c!( + cs!( "\"foo\\\"bar\"", p!("\"foo\\\"bar\"", e!(w!(wpd!("foo\"bar")))) ) ); parse_eq!( "'foo\\'bar\\\\'", - c!( + cs!( "'foo\\'bar\\\\'", p!("'foo\\'bar\\\\'", e!(w!(wps!("foo'bar\\")))) ) ); parse_eq!( "foo > bar\\ baz", - c!( + cs!( "foo > bar\\ baz", p!("foo > bar\\ baz", e!(w!("foo") ; r!(1, w!("bar baz"), Out))) ) @@ -287,7 +296,7 @@ fn test_escape() { fn test_parts() { parse_eq!( "echo \"$HOME/bin\"", - c!( + cs!( "echo \"$HOME/bin\"", p!( "echo \"$HOME/bin\"", @@ -297,7 +306,7 @@ fn test_parts() { ); parse_eq!( "echo $HOME/bin", - c!( + cs!( "echo $HOME/bin", p!( "echo $HOME/bin", @@ -307,14 +316,14 @@ fn test_parts() { ); parse_eq!( "echo '$HOME/bin'", - c!( + cs!( "echo '$HOME/bin'", p!("echo '$HOME/bin'", e!(w!("echo"), w!(wps!("$HOME/bin")))) ) ); parse_eq!( "echo \"foo\"\"bar\"", - c!( + cs!( "echo \"foo\"\"bar\"", p!( "echo \"foo\"\"bar\"", @@ -324,7 +333,7 @@ fn test_parts() { ); parse_eq!( "echo $foo$bar$baz", - c!( + cs!( "echo $foo$bar$baz", p!( "echo $foo$bar$baz", @@ -334,7 +343,7 @@ fn test_parts() { ); parse_eq!( "perl -E'say \"foo\"'", - c!( + cs!( "perl -E'say \"foo\"'", p!( "perl -E'say \"foo\"'", -- cgit v1.2.3-54-g00ecf