summaryrefslogtreecommitdiffstats
path: root/src/parse
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2022-01-08 17:28:58 -0500
committerJesse Luehrs <doy@tozt.net>2022-01-08 17:28:58 -0500
commit27934e2a48254b4b948b846680afe213e61fdfc0 (patch)
tree4de94e29b90e35be9ebc972a099aa34b7443994f /src/parse
parentbfc458a5ce75762624115feaa50cfd3a3edd7bc0 (diff)
downloadnbsh-27934e2a48254b4b948b846680afe213e61fdfc0.tar.gz
nbsh-27934e2a48254b4b948b846680afe213e61fdfc0.zip
add parsing for control statements
Diffstat (limited to 'src/parse')
-rw-r--r--src/parse/ast.rs55
-rw-r--r--src/parse/test_ast.rs75
2 files changed, 90 insertions, 40 deletions
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<Pipeline>,
+ commands: Vec<Command>,
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,16 +37,57 @@ 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<Rule>) -> 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<Exe>,
input_string: String,
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<Pipeline> 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 <bar",
- c!(
+ cs!(
"foo <bar",
p!("foo <bar", e!(w!("foo") ; r!(0, w!("bar"), In)))
)
);
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\"'",