summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2022-01-10 01:54:21 -0500
committerJesse Luehrs <doy@tozt.net>2022-01-10 01:54:21 -0500
commitfeb02a8048a31a46ab57fa268d3f22b40af73c61 (patch)
treef252b92ca9c6ab80d4acc21d296b9f1fac80d009
parentfd823c090b2a1cd95c237206f9887215c0f9230a (diff)
downloadnbsh-feb02a8048a31a46ab57fa268d3f22b40af73c61.tar.gz
nbsh-feb02a8048a31a46ab57fa268d3f22b40af73c61.zip
refactor the grammar a bit
-rw-r--r--src/parse/ast.rs17
-rw-r--r--src/shell.pest21
2 files changed, 21 insertions, 17 deletions
diff --git a/src/parse/ast.rs b/src/parse/ast.rs
index 9cf583a..053f07f 100644
--- a/src/parse/ast.rs
+++ b/src/parse/ast.rs
@@ -160,7 +160,7 @@ impl Word {
fn build_ast(pair: pest::iterators::Pair<Rule>) -> Self {
assert!(matches!(pair.as_rule(), Rule::word));
Self {
- parts: pair.into_inner().map(WordPart::build_ast).collect(),
+ parts: pair.into_inner().flat_map(WordPart::build_ast).collect(),
}
}
@@ -183,8 +183,11 @@ enum WordPart {
impl WordPart {
#[allow(clippy::needless_pass_by_value)]
- fn build_ast(pair: pest::iterators::Pair<Rule>) -> Self {
- match pair.as_rule() {
+ fn build_ast(
+ pair: pest::iterators::Pair<Rule>,
+ ) -> impl Iterator<Item = Self> + '_ {
+ assert!(matches!(pair.as_rule(), Rule::word_part));
+ pair.into_inner().map(|pair| match pair.as_rule() {
Rule::var => {
let s = pair.as_str();
let inner = s.strip_prefix('$').unwrap();
@@ -205,7 +208,7 @@ impl WordPart {
Self::SingleQuoted(strip_basic_escape(pair.as_str()))
}
_ => unreachable!(),
- }
+ })
}
fn eval(self, env: &Env) -> String {
@@ -282,7 +285,7 @@ enum WordOrRedirect {
impl WordOrRedirect {
fn build_ast(pair: pest::iterators::Pair<Rule>) -> Self {
- assert!(matches!(pair.as_rule(), Rule::word));
+ assert!(matches!(pair.as_rule(), Rule::word_or_redirect));
let mut inner = pair.into_inner().peekable();
let prefix = if matches!(
inner.peek().map(pest::iterators::Pair::as_rule),
@@ -292,9 +295,7 @@ impl WordOrRedirect {
} else {
None
};
- let word = Word {
- parts: inner.map(WordPart::build_ast).collect(),
- };
+ let word = Word::build_ast(inner.next().unwrap());
if let Some(prefix) = prefix {
Self::Redirect(Redirect::parse(&prefix, word))
} else {
diff --git a/src/shell.pest b/src/shell.pest
index dc3f247..393e60e 100644
--- a/src/shell.pest
+++ b/src/shell.pest
@@ -8,27 +8,30 @@ bareword_char = @{
single_string_char = @{ basic_escape_char | (!"'" ~ ANY) }
double_string_char = @{ escape_char | (!"\"" ~ ANY) }
+redir_prefix = @{
+ ("in" | "out" | "err" | ASCII_DIGIT*) ~ (">>" | ">" | "<") ~ WHITESPACE*
+}
+
var = @{
("$" ~ XID_START ~ XID_CONTINUE*) |
("$" ~ ("?" | "$" | "*" | ASCII_DIGIT)) |
("${" ~ (!"}" ~ ANY)+ ~ "}")
}
-
-redir_prefix = @{
- ("in" | "out" | "err" | ASCII_DIGIT*) ~ (">>" | ">" | "<") ~ WHITESPACE*
-}
bareword = @{ bareword_char+ }
single_string = @{ single_string_char+ }
double_string = @{ double_string_char+ }
-word = ${
- redir_prefix? ~
- (var | bareword |
+word_part = ${
+ var |
+ bareword |
"'" ~ single_string ~ "'" |
- "\"" ~ (var | double_string)+ ~ "\"")+
+ "\"" ~ (var | double_string)+ ~ "\""
}
+word = ${ word_part+ }
+
+word_or_redirect = ${ redir_prefix? ~ word }
-exe = ${ word ~ (w ~ word)* }
+exe = ${ word_or_redirect ~ (w ~ word_or_redirect)* }
list = ${ word ~ (w ~ word)* }
pipeline = ${ exe ~ (w? ~ "|" ~ w? ~ exe)* }