summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2022-01-10 04:32:20 -0500
committerJesse Luehrs <doy@tozt.net>2022-01-10 04:32:20 -0500
commitee5aef94bb86caf64ca5fdcd77a7619ce8680a16 (patch)
treeab789c2920f66508de1aaaca8bdb8549e60e686d
parentb8ab245fa35143ec6c1884d7aa685f25a5213940 (diff)
downloadnbsh-ee5aef94bb86caf64ca5fdcd77a7619ce8680a16.tar.gz
nbsh-ee5aef94bb86caf64ca5fdcd77a7619ce8680a16.zip
implement expanding alternations
-rw-r--r--src/parse/ast.rs71
-rw-r--r--src/runner/mod.rs2
2 files changed, 54 insertions, 19 deletions
diff --git a/src/parse/ast.rs b/src/parse/ast.rs
index 2f4ae86..1ae00b8 100644
--- a/src/parse/ast.rs
+++ b/src/parse/ast.rs
@@ -108,9 +108,16 @@ struct Exe {
impl Exe {
fn eval(self, env: &Env) -> super::Exe {
+ let exe = self.exe.eval(env);
+ assert_eq!(exe.len(), 1); // TODO
+ let exe = &exe[0];
super::Exe {
- exe: std::path::PathBuf::from(self.exe.eval(env)),
- args: self.args.into_iter().map(|arg| arg.eval(env)).collect(),
+ exe: std::path::PathBuf::from(exe),
+ args: self
+ .args
+ .into_iter()
+ .flat_map(|arg| arg.eval(env))
+ .collect(),
redirects: self
.redirects
.into_iter()
@@ -167,12 +174,37 @@ impl Word {
}
}
- pub fn eval(self, env: &Env) -> String {
- self.parts
- .into_iter()
- .map(|part| part.eval(env))
- .collect::<Vec<_>>()
- .join("")
+ pub fn eval(self, env: &Env) -> Vec<String> {
+ let mut alternations = vec![];
+ let mut cur = String::new();
+ for part in self.parts {
+ if let WordPart::Alternation(words) = part {
+ if !cur.is_empty() {
+ alternations.push(vec![cur.clone()]);
+ cur.clear();
+ }
+ alternations.push(
+ words.into_iter().flat_map(|w| w.eval(env)).collect(),
+ );
+ } else {
+ cur.push_str(&part.eval(env));
+ }
+ }
+ if !cur.is_empty() {
+ alternations.push(vec![cur]);
+ }
+ let mut words: Vec<_> = std::iter::repeat(String::new())
+ .take(alternations.iter().map(Vec::len).product())
+ .collect();
+ for i in 0..words.len() {
+ let mut len = words.len();
+ for alternation in &alternations {
+ let idx = (i * alternation.len() / len) % alternation.len();
+ words[i].push_str(&alternation[idx]);
+ len /= alternation.len();
+ }
+ }
+ words
}
}
@@ -225,7 +257,7 @@ impl WordPart {
fn eval(self, env: &Env) -> String {
match self {
- Self::Alternation(_) => todo!(),
+ Self::Alternation(_) => unreachable!(),
Self::Var(name) => env.var(&name),
Self::Bareword(s)
| Self::DoubleQuoted(s)
@@ -269,19 +301,22 @@ impl Redirect {
if let Some(fd) = s.strip_prefix('&') {
super::RedirectTarget::Fd(parse_fd(fd))
} else {
- super::RedirectTarget::File(std::path::PathBuf::from(
- self.to.eval(env),
- ))
+ let to = self.to.eval(env);
+ assert_eq!(to.len(), 1); // TODO
+ let to = &to[0];
+ super::RedirectTarget::File(std::path::PathBuf::from(to))
}
} else {
- super::RedirectTarget::File(std::path::PathBuf::from(
- self.to.eval(env),
- ))
+ let to = self.to.eval(env);
+ assert_eq!(to.len(), 1); // TODO
+ let to = &to[0];
+ super::RedirectTarget::File(std::path::PathBuf::from(to))
}
} else {
- super::RedirectTarget::File(std::path::PathBuf::from(
- self.to.eval(env),
- ))
+ let to = self.to.eval(env);
+ assert_eq!(to.len(), 1); // TODO
+ let to = &to[0];
+ super::RedirectTarget::File(std::path::PathBuf::from(to))
};
super::Redirect {
from: self.from,
diff --git a/src/runner/mod.rs b/src/runner/mod.rs
index 22121c2..6d080b8 100644
--- a/src/runner/mod.rs
+++ b/src/runner/mod.rs
@@ -143,7 +143,7 @@ async fn run_commands(
if stack.should_execute() {
list.clone()
.into_iter()
- .map(|w| w.eval(env))
+ .flat_map(|w| w.eval(env))
.collect()
} else {
vec![]