Parser tweaks

This commit is contained in:
2025-05-08 21:18:58 -04:00
parent 7523944d63
commit 18d0b669b3
10 changed files with 21067 additions and 70 deletions

View File

@@ -253,7 +253,7 @@ impl Dispatcher {
.map(|s| s.to_string())
.unwrap_or_default();
for block in case_blocks {
'outer: for block in case_blocks {
let CaseNode { pattern, body } = block;
let block_pattern_raw = pattern.span.as_str().trim_end_matches(')').trim();
// Split at '|' to allow for multiple patterns like `foo|bar)`
@@ -264,6 +264,7 @@ impl Dispatcher {
for node in &body {
self.dispatch_node(node.clone())?;
}
break 'outer
}
}
}
@@ -432,7 +433,7 @@ impl Dispatcher {
let NdRule::Command { ref mut assignments, ref mut argv } = &mut cmd.class else {
unreachable!()
};
let env_vars_to_unset = self.set_assignments(mem::take(assignments), AssignBehavior::Export);
let env_vars_to_unset = self.set_assignments(mem::take(assignments), AssignBehavior::Export)?;
let cmd_raw = argv.first().unwrap();
let curr_job_mut = self.job_stack.curr_job_mut().unwrap();
let io_stack_mut = &mut self.io_stack;
@@ -488,7 +489,7 @@ impl Dispatcher {
} else {
AssignBehavior::Export
};
env_vars_to_unset = self.set_assignments(assignments, assign_behavior);
env_vars_to_unset = self.set_assignments(assignments, assign_behavior)?;
}
if argv.is_empty() {
@@ -513,7 +514,7 @@ impl Dispatcher {
Ok(())
}
fn set_assignments(&self, assigns: Vec<Node>, behavior: AssignBehavior) -> Vec<String> {
fn set_assignments(&self, assigns: Vec<Node>, behavior: AssignBehavior) -> ShResult<Vec<String>> {
let mut new_env_vars = vec![];
match behavior {
AssignBehavior::Export => {
@@ -522,9 +523,9 @@ impl Dispatcher {
unreachable!()
};
let var = var.span.as_str();
let val = val.span.as_str();
let val = val.expand()?.get_words().join(" ");
match kind {
AssignKind::Eq => write_vars(|v| v.set_var(var, val, true)),
AssignKind::Eq => write_vars(|v| v.set_var(var, &val, true)),
AssignKind::PlusEq => todo!(),
AssignKind::MinusEq => todo!(),
AssignKind::MultEq => todo!(),
@@ -539,9 +540,9 @@ impl Dispatcher {
unreachable!()
};
let var = var.span.as_str();
let val = val.span.as_str();
let val = val.expand()?.get_words().join(" ");
match kind {
AssignKind::Eq => write_vars(|v| v.set_var(var, val, false)),
AssignKind::Eq => write_vars(|v| v.set_var(var, &val, true)),
AssignKind::PlusEq => todo!(),
AssignKind::MinusEq => todo!(),
AssignKind::MultEq => todo!(),
@@ -550,7 +551,7 @@ impl Dispatcher {
}
}
}
new_env_vars
Ok(new_env_vars)
}
}

View File

@@ -340,7 +340,7 @@ impl LexStream {
'$' if chars.peek() == Some(&'(') => {
pos += 2;
chars.next();
let mut paren_stack = vec!['('];
let mut paren_count = 0;
let paren_pos = pos;
while let Some(ch) = chars.next() {
match ch {
@@ -352,19 +352,19 @@ impl LexStream {
}
'(' => {
pos += 1;
paren_stack.push(ch);
paren_count += 1;
}
')' => {
pos += 1;
paren_stack.pop();
if paren_stack.is_empty() {
paren_count -= 1;
if paren_count >= 0 {
break
}
}
_ => pos += ch.len_utf8()
}
}
if !paren_stack.is_empty() && !self.flags.contains(LexFlags::LEX_UNFINISHED) {
if !paren_count == 0 && !self.flags.contains(LexFlags::LEX_UNFINISHED) {
return Err(
ShErr::full(
ShErrKind::ParseErr,
@@ -434,7 +434,7 @@ impl LexStream {
self.cursor = pos;
return Ok(tk)
}
'"' | '\'' => {
'\'' => {
self.in_quote = true;
pos += 1;
while let Some(q_ch) = chars.next() {
@@ -445,7 +445,7 @@ impl LexStream {
pos += 1;
}
}
_ if q_ch == ch => {
_ if q_ch == '\'' => {
pos += 1;
self.in_quote = false;
break
@@ -458,6 +458,58 @@ impl LexStream {
}
}
}
'"' => {
self.in_quote = true;
pos += 1;
while let Some(q_ch) = chars.next() {
match q_ch {
'\\' => {
pos += 1;
if chars.next().is_some() {
pos += 1;
}
}
'$' if chars.peek() == Some(&'(') => {
pos += 2;
chars.next();
let mut cmdsub_count = 1;
while let Some(cmdsub_ch) = chars.next() {
match cmdsub_ch {
'\\' => {
pos += 1;
if chars.next().is_some() {
pos += 1;
}
}
'$' if chars.peek() == Some(&'(') => {
cmdsub_count += 1;
pos += 2;
chars.next();
}
')' => {
cmdsub_count -= 1;
pos += 1;
if cmdsub_count <= 0 {
break
}
}
_ => pos += cmdsub_ch.len_utf8(),
}
}
}
_ if q_ch == '"' => {
pos += 1;
self.in_quote = false;
break
}
// Any time an ambiguous character is found
// we must push the cursor by the length of the character
// instead of just assuming a length of 1.
// Allows spans to work for wide characters
_ => pos += q_ch.len_utf8(),
}
}
}
_ if !self.in_quote && is_op(ch) => break,
_ if is_hard_sep(ch) => break,
_ => pos += ch.len_utf8()
@@ -480,6 +532,7 @@ impl LexStream {
"case" | "select" | "for" => {
new_tk.mark(TkFlags::KEYWORD);
self.flags |= LexFlags::EXPECTING_IN;
self.set_next_is_cmd(false);
}
"in" if self.flags.contains(LexFlags::EXPECTING_IN) => {
new_tk.mark(TkFlags::KEYWORD);
@@ -492,16 +545,17 @@ impl LexStream {
new_tk.mark(TkFlags::ASSIGN);
}
_ if is_cmd_sub(text) => {
new_tk.mark(TkFlags::IS_CMDSUB)
new_tk.mark(TkFlags::IS_CMDSUB);
self.set_next_is_cmd(false);
}
_ => {
new_tk.flags |= TkFlags::IS_CMD;
if BUILTINS.contains(&text) {
new_tk.mark(TkFlags::BUILTIN);
}
self.set_next_is_cmd(false);
}
}
self.set_next_is_cmd(false);
} else if self.flags.contains(LexFlags::EXPECTING_IN) && text == "in" {
new_tk.mark(TkFlags::KEYWORD);
self.flags &= !LexFlags::EXPECTING_IN;

View File

@@ -416,6 +416,7 @@ impl ParseStream {
/// Ordered from specialized to general, with more generally matchable stuff appearing at the bottom
/// The check_pipelines parameter is used to prevent left-recursion issues in self.parse_pipeln()
fn parse_block(&mut self, check_pipelines: bool) -> ShResult<Option<Node>> {
flog!(DEBUG, self.tokens);
try_match!(self.parse_func_def()?);
try_match!(self.parse_brc_grp(false /* from_func_def */)?);
try_match!(self.parse_case()?);
@@ -913,59 +914,61 @@ impl ParseStream {
.as_str()
.parse() // LoopKind implements FromStr
.unwrap();
node_tks.push(loop_tk);
self.catch_separator(&mut node_tks);
let Some(cond) = self.parse_block(true)? else {
self.panic_mode(&mut node_tks);
return Err(parse_err_full(
&format!("Expected an expression after '{loop_kind}'"), // It also implements Display
&node_tks.get_span().unwrap()
))
};
node_tks.extend(cond.tokens.clone());
node_tks.push(loop_tk);
self.catch_separator(&mut node_tks);
if !self.check_keyword("do") || !self.next_tk_is_some() {
self.panic_mode(&mut node_tks);
return Err(parse_err_full(
"Expected 'do' after loop condition",
&node_tks.get_span().unwrap()
))
}
node_tks.push(self.next_tk().unwrap());
self.catch_separator(&mut node_tks);
flog!(DEBUG, node_tks);
let Some(cond) = self.parse_block(true)? else {
self.panic_mode(&mut node_tks);
return Err(parse_err_full(
&format!("Expected an expression after '{loop_kind}'"), // It also implements Display
&node_tks.get_span().unwrap()
))
};
node_tks.extend(cond.tokens.clone());
let mut body = vec![];
while let Some(block) = self.parse_block(true)? {
node_tks.extend(block.tokens.clone());
body.push(block);
}
if body.is_empty() {
self.panic_mode(&mut node_tks);
return Err(parse_err_full(
"Expected an expression after 'do'",
&node_tks.get_span().unwrap()
))
};
if !self.check_keyword("do") || !self.next_tk_is_some() {
self.panic_mode(&mut node_tks);
return Err(parse_err_full(
"Expected 'do' after loop condition",
&node_tks.get_span().unwrap()
))
}
node_tks.push(self.next_tk().unwrap());
self.catch_separator(&mut node_tks);
if !self.check_keyword("done") || !self.next_tk_is_some() {
self.panic_mode(&mut node_tks);
return Err(parse_err_full(
"Expected 'done' after loop body",
&node_tks.get_span().unwrap()
))
}
node_tks.push(self.next_tk().unwrap());
self.assert_separator(&mut node_tks)?;
let mut body = vec![];
while let Some(block) = self.parse_block(true)? {
node_tks.extend(block.tokens.clone());
body.push(block);
}
if body.is_empty() {
self.panic_mode(&mut node_tks);
return Err(parse_err_full(
"Expected an expression after 'do'",
&node_tks.get_span().unwrap()
))
};
cond_node = CondNode { cond: Box::new(cond), body };
let loop_node = Node {
class: NdRule::LoopNode { kind: loop_kind, cond_node },
flags: NdFlags::empty(),
redirs: vec![],
tokens: node_tks
};
Ok(Some(loop_node))
if !self.check_keyword("done") || !self.next_tk_is_some() {
self.panic_mode(&mut node_tks);
return Err(parse_err_full(
"Expected 'done' after loop body",
&node_tks.get_span().unwrap()
))
}
node_tks.push(self.next_tk().unwrap());
self.assert_separator(&mut node_tks)?;
cond_node = CondNode { cond: Box::new(cond), body };
let loop_node = Node {
class: NdRule::LoopNode { kind: loop_kind, cond_node },
flags: NdFlags::empty(),
redirs: vec![],
tokens: node_tks
};
Ok(Some(loop_node))
}
fn parse_pipeln(&mut self) -> ShResult<Option<Node>> {
let mut cmds = vec![];
@@ -1023,6 +1026,7 @@ impl ParseStream {
return Ok(None)
}
}
flog!(DEBUG, argv);
if argv.is_empty() && assignments.is_empty() {
return Ok(None)
@@ -1213,6 +1217,7 @@ impl Iterator for ParseStream {
}
}
let result = self.parse_cmd_list();
flog!(DEBUG, result);
match result {
Ok(Some(node)) => {
Some(Ok(node))