summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2022-01-10 03:33:43 -0500
committerJesse Luehrs <doy@tozt.net>2022-01-10 03:33:43 -0500
commitb8ab245fa35143ec6c1884d7aa685f25a5213940 (patch)
treee2ccaa44017560065c35ee850788e688f16b76e5
parentfeb02a8048a31a46ab57fa268d3f22b40af73c61 (diff)
downloadnbsh-b8ab245fa35143ec6c1884d7aa685f25a5213940.tar.gz
nbsh-b8ab245fa35143ec6c1884d7aa685f25a5213940.zip
parse alternations
-rw-r--r--src/parse/ast.rs19
-rw-r--r--src/parse/test_ast.rs56
-rw-r--r--src/shell.pest14
3 files changed, 85 insertions, 4 deletions
diff --git a/src/parse/ast.rs b/src/parse/ast.rs
index 053f07f..2f4ae86 100644
--- a/src/parse/ast.rs
+++ b/src/parse/ast.rs
@@ -158,7 +158,10 @@ pub struct Word {
impl Word {
fn build_ast(pair: pest::iterators::Pair<Rule>) -> Self {
- assert!(matches!(pair.as_rule(), Rule::word));
+ assert!(matches!(
+ pair.as_rule(),
+ Rule::word | Rule::alternation_word
+ ));
Self {
parts: pair.into_inner().flat_map(WordPart::build_ast).collect(),
}
@@ -175,6 +178,7 @@ impl Word {
#[derive(Debug, Clone, PartialEq, Eq)]
enum WordPart {
+ Alternation(Vec<Word>),
Var(String),
Bareword(String),
DoubleQuoted(String),
@@ -186,7 +190,10 @@ impl WordPart {
fn build_ast(
pair: pest::iterators::Pair<Rule>,
) -> impl Iterator<Item = Self> + '_ {
- assert!(matches!(pair.as_rule(), Rule::word_part));
+ assert!(matches!(
+ pair.as_rule(),
+ Rule::word_part | Rule::alternation_word_part
+ ));
pair.into_inner().map(|pair| match pair.as_rule() {
Rule::var => {
let s = pair.as_str();
@@ -200,19 +207,25 @@ impl WordPart {
.to_string(),
)
}
- Rule::bareword => Self::Bareword(strip_escape(pair.as_str())),
+ Rule::bareword | Rule::alternation_bareword => {
+ Self::Bareword(strip_escape(pair.as_str()))
+ }
Rule::double_string => {
Self::DoubleQuoted(strip_escape(pair.as_str()))
}
Rule::single_string => {
Self::SingleQuoted(strip_basic_escape(pair.as_str()))
}
+ Rule::alternation => Self::Alternation(
+ pair.into_inner().map(Word::build_ast).collect(),
+ ),
_ => unreachable!(),
})
}
fn eval(self, env: &Env) -> String {
match self {
+ Self::Alternation(_) => todo!(),
Self::Var(name) => env.var(&name),
Self::Bareword(s)
| Self::DoubleQuoted(s)
diff --git a/src/parse/test_ast.rs b/src/parse/test_ast.rs
index b16eba4..19bf7af 100644
--- a/src/parse/test_ast.rs
+++ b/src/parse/test_ast.rs
@@ -79,6 +79,12 @@ macro_rules! w {
}
}
+macro_rules! wpa {
+ ($($word:expr),*) => {
+ WordPart::Alternation(vec![$($word),*])
+ }
+}
+
macro_rules! wpv {
($var:literal) => {
WordPart::Var($var.to_string())
@@ -231,3 +237,53 @@ fn test_parts() {
cs!(p!(e!(w!("perl"), w!(wpb!("-E"), wps!("say \"foo\"")))))
);
}
+
+#[test]
+fn test_alternation() {
+ parse_eq!(
+ "echo {foo,bar}",
+ cs!(p!(e!(w!("echo"), w!(wpa!(w!("foo"), w!("bar"))))))
+ );
+ parse_eq!(
+ "echo {foo,bar}.rs",
+ cs!(p!(e!(
+ w!("echo"),
+ w!(wpa!(w!("foo"), w!("bar")), wpb!(".rs"))
+ )))
+ );
+ parse_eq!(
+ "echo {foo,bar,baz}.rs",
+ cs!(p!(e!(
+ w!("echo"),
+ w!(wpa!(w!("foo"), w!("bar"), w!("baz")), wpb!(".rs"))
+ )))
+ );
+ parse_eq!(
+ "echo {foo,}.rs",
+ cs!(p!(e!(w!("echo"), w!(wpa!(w!("foo"), w!()), wpb!(".rs")))))
+ );
+ parse_eq!("echo {foo}", cs!(p!(e!(w!("echo"), w!(wpa!(w!("foo")))))));
+ parse_eq!("echo {}", cs!(p!(e!(w!("echo"), w!(wpa!(w!()))))));
+ parse_eq!(
+ "echo {foo,bar}.{rs,c}",
+ cs!(p!(e!(
+ w!("echo"),
+ w!(
+ wpa!(w!("foo"), w!("bar")),
+ wpb!("."),
+ wpa!(w!("rs"), w!("c"))
+ )
+ )))
+ );
+ parse_eq!(
+ "echo {$foo,\"${HOME}/bin\"}.{'r'\"s\",c}",
+ cs!(p!(e!(
+ w!("echo"),
+ w!(
+ wpa!(w!(wpv!("foo")), w!(wpv!("HOME"), wpd!("/bin"))),
+ wpb!("."),
+ wpa!(w!(wps!("r"), wpd!("s")), w!("c"))
+ )
+ )))
+ );
+}
diff --git a/src/shell.pest b/src/shell.pest
index 393e60e..d09ccbc 100644
--- a/src/shell.pest
+++ b/src/shell.pest
@@ -3,7 +3,7 @@ escape_char = @{ "\\" ~ ANY }
bareword_char = @{
escape_char |
- !("|" | ";" | "\"" | "'" | "$" | WHITESPACE | COMMENT) ~ ANY
+ !("|" | ";" | "\"" | "'" | "$" | "{" | WHITESPACE | COMMENT) ~ ANY
}
single_string_char = @{ basic_escape_char | (!"'" ~ ANY) }
double_string_char = @{ escape_char | (!"\"" ~ ANY) }
@@ -21,7 +21,19 @@ bareword = @{ bareword_char+ }
single_string = @{ single_string_char+ }
double_string = @{ double_string_char+ }
+alternation_bareword_char = @{ !("," | "}") ~ bareword_char }
+alternation_bareword = @{ alternation_bareword_char+ }
+alternation_word_part = ${
+ var |
+ alternation_bareword |
+ "'" ~ single_string ~ "'" |
+ "\"" ~ (var | double_string)+ ~ "\""
+}
+alternation_word = ${ alternation_word_part* }
+alternation = ${ "{" ~ alternation_word ~ ("," ~ alternation_word)* ~ "}" }
+
word_part = ${
+ alternation |
var |
bareword |
"'" ~ single_string ~ "'" |