Parser tweaks
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
101
src/parse/mod.rs
101
src/parse/mod.rs
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user