implemented for loops
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use std::collections::{HashSet, VecDeque};
|
||||
|
||||
|
||||
use crate::{builtin::{alias::alias, cd::cd, echo::echo, export::export, flowctl::flowctl, jobctl::{continue_job, jobs, JobBehavior}, pwd::pwd, shift::shift, shopt::shopt, source::source, zoltraak::zoltraak}, expand::expand_aliases, jobs::{dispatch_job, ChildProc, JobBldr, JobStack}, libsh::{error::{ShErr, ShErrKind, ShResult, ShResultExt}, utils::RedirVecUtils}, prelude::*, procio::{IoFrame, IoMode, IoStack}, state::{self, get_snapshots, read_logic, read_vars, restore_snapshot, write_logic, write_meta, write_vars, ShFunc, VarTab, LOGIC_TABLE}};
|
||||
use crate::{builtin::{alias::{alias, unalias}, cd::cd, echo::echo, export::export, flowctl::flowctl, jobctl::{continue_job, jobs, JobBehavior}, pwd::pwd, shift::shift, shopt::shopt, source::source, zoltraak::zoltraak}, expand::expand_aliases, jobs::{dispatch_job, ChildProc, JobBldr, JobStack}, libsh::{error::{ShErr, ShErrKind, ShResult, ShResultExt}, utils::RedirVecUtils}, prelude::*, procio::{IoFrame, IoMode, IoStack}, state::{self, get_snapshots, read_logic, restore_snapshot, write_logic, write_meta, write_vars, ShFunc, VarTab, LOGIC_TABLE}};
|
||||
|
||||
use super::{lex::{Span, Tk, TkFlags, KEYWORDS}, AssignKind, CaseNode, CondNode, ConjunctNode, ConjunctOp, LoopKind, NdFlags, NdRule, Node, ParsedSrc, Redir, RedirType};
|
||||
|
||||
@@ -76,11 +76,13 @@ impl Dispatcher {
|
||||
Ok(())
|
||||
}
|
||||
pub fn dispatch_node(&mut self, node: Node) -> ShResult<()> {
|
||||
flog!(DEBUG, node.class);
|
||||
match node.class {
|
||||
NdRule::Conjunction {..} => self.exec_conjunction(node)?,
|
||||
NdRule::Pipeline {..} => self.exec_pipeline(node)?,
|
||||
NdRule::IfNode {..} => self.exec_if(node)?,
|
||||
NdRule::LoopNode {..} => self.exec_loop(node)?,
|
||||
NdRule::ForNode {..} => self.exec_for(node)?,
|
||||
NdRule::CaseNode {..} => self.exec_case(node)?,
|
||||
NdRule::BraceGrp {..} => self.exec_brc_grp(node)?,
|
||||
NdRule::FuncDef {..} => self.exec_func_def(node)?,
|
||||
@@ -169,7 +171,7 @@ impl Dispatcher {
|
||||
|
||||
if let Err(e) = exec_input(subsh_body) {
|
||||
restore_snapshot(snapshot);
|
||||
return Err(e.into())
|
||||
return Err(e)
|
||||
}
|
||||
|
||||
restore_snapshot(snapshot);
|
||||
@@ -205,7 +207,7 @@ impl Dispatcher {
|
||||
return Ok(())
|
||||
}
|
||||
_ => return {
|
||||
Err(e.into())
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,7 +297,7 @@ impl Dispatcher {
|
||||
|
||||
if let Err(e) = self.dispatch_node(*cond.clone()) {
|
||||
state::set_status(1);
|
||||
return Err(e.into());
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
let status = state::get_status();
|
||||
@@ -312,7 +314,7 @@ impl Dispatcher {
|
||||
state::set_status(*code);
|
||||
continue 'outer
|
||||
}
|
||||
_ => return Err(e.into())
|
||||
_ => return Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -323,6 +325,48 @@ impl Dispatcher {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn exec_for(&mut self, for_stmt: Node) -> ShResult<()> {
|
||||
let NdRule::ForNode { vars, arr, body } = for_stmt.class else {
|
||||
unreachable!();
|
||||
};
|
||||
|
||||
let io_frame = self.io_stack.pop_frame();
|
||||
let (_, mut body_frame) = io_frame.split_frame();
|
||||
let (_, out_redirs) = for_stmt.redirs.split_by_channel();
|
||||
body_frame.extend(out_redirs);
|
||||
|
||||
'outer: for chunk in arr.chunks(vars.len()) {
|
||||
let empty = Tk::default();
|
||||
let chunk_iter = vars.iter().zip(
|
||||
chunk.iter().chain(std::iter::repeat(&empty)) // Or however you define an empty token
|
||||
);
|
||||
|
||||
for (var, val) in chunk_iter {
|
||||
write_vars(|v| v.set_var(&var.to_string(), &val.to_string(), false));
|
||||
}
|
||||
|
||||
self.io_stack.push(body_frame.clone());
|
||||
|
||||
for node in body.clone() {
|
||||
if let Err(e) = self.dispatch_node(node) {
|
||||
match e.kind() {
|
||||
ShErrKind::LoopBreak(code) => {
|
||||
state::set_status(*code);
|
||||
break 'outer
|
||||
}
|
||||
ShErrKind::LoopContinue(code) => {
|
||||
state::set_status(*code);
|
||||
continue 'outer
|
||||
}
|
||||
_ => return Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn exec_if(&mut self, if_stmt: Node) -> ShResult<()> {
|
||||
let NdRule::IfNode { cond_nodes, else_block } = if_stmt.class else {
|
||||
unreachable!();
|
||||
@@ -340,7 +384,7 @@ impl Dispatcher {
|
||||
|
||||
if let Err(e) = self.dispatch_node(*cond) {
|
||||
state::set_status(1);
|
||||
return Err(e.into());
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
match state::get_status() {
|
||||
@@ -409,6 +453,7 @@ impl Dispatcher {
|
||||
"bg" => continue_job(cmd, curr_job_mut, JobBehavior::Background),
|
||||
"jobs" => jobs(cmd, io_stack_mut, curr_job_mut),
|
||||
"alias" => alias(cmd, io_stack_mut, curr_job_mut),
|
||||
"unalias" => unalias(cmd, io_stack_mut, curr_job_mut),
|
||||
"return" => flowctl(cmd, ShErrKind::FuncReturn(0)),
|
||||
"break" => flowctl(cmd, ShErrKind::LoopBreak(0)),
|
||||
"continue" => flowctl(cmd, ShErrKind::LoopContinue(0)),
|
||||
@@ -424,7 +469,7 @@ impl Dispatcher {
|
||||
|
||||
if let Err(e) = result {
|
||||
state::set_status(1);
|
||||
return Err(e.into())
|
||||
return Err(e)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -518,7 +563,7 @@ pub fn prepare_argv(argv: Vec<Tk>) -> ShResult<Vec<(String,Span)>> {
|
||||
Ok(args)
|
||||
}
|
||||
|
||||
pub fn run_fork<'t,C,P>(
|
||||
pub fn run_fork<C,P>(
|
||||
io_frame: IoFrame,
|
||||
exec_args: Option<ExecArgs>,
|
||||
job: &mut JobBldr,
|
||||
@@ -554,7 +599,7 @@ pub fn def_child_action(mut io_frame: IoFrame, exec_args: Option<ExecArgs>) {
|
||||
let cmd = &exec_args.cmd.0;
|
||||
let span = exec_args.cmd.1;
|
||||
|
||||
let Err(e) = execvpe(&cmd, &exec_args.argv, &exec_args.envp);
|
||||
let Err(e) = execvpe(cmd, &exec_args.argv, &exec_args.envp);
|
||||
|
||||
let cmd = cmd.to_str().unwrap().to_string();
|
||||
match e {
|
||||
|
||||
@@ -4,7 +4,7 @@ use bitflags::bitflags;
|
||||
|
||||
use crate::{builtin::BUILTINS, libsh::{error::{ShErr, ShErrKind, ShResult}, utils::CharDequeUtils}, prelude::*};
|
||||
|
||||
pub const KEYWORDS: [&'static str;14] = [
|
||||
pub const KEYWORDS: [&str;14] = [
|
||||
"if",
|
||||
"then",
|
||||
"elif",
|
||||
@@ -21,7 +21,7 @@ pub const KEYWORDS: [&'static str;14] = [
|
||||
"esac",
|
||||
];
|
||||
|
||||
pub const OPENERS: [&'static str;6] = [
|
||||
pub const OPENERS: [&str;6] = [
|
||||
"if",
|
||||
"while",
|
||||
"until",
|
||||
@@ -103,12 +103,6 @@ impl Tk {
|
||||
pub fn new(class: TkRule, span: Span) -> Self {
|
||||
Self { class, span, flags: TkFlags::empty() }
|
||||
}
|
||||
pub fn to_string(&self) -> String {
|
||||
match &self.class {
|
||||
TkRule::Expanded { exp } => exp.join(" "),
|
||||
_ => self.span.as_str().to_string()
|
||||
}
|
||||
}
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.span.as_str()
|
||||
}
|
||||
@@ -264,7 +258,7 @@ impl LexStream {
|
||||
ShErr::full(
|
||||
ShErrKind::ParseErr,
|
||||
"Invalid redirection",
|
||||
Span::new(self.cursor..pos, self.source.clone()).into()
|
||||
Span::new(self.cursor..pos, self.source.clone())
|
||||
)
|
||||
));
|
||||
} else {
|
||||
@@ -471,13 +465,13 @@ impl LexStream {
|
||||
ShErr::full(
|
||||
ShErrKind::ParseErr,
|
||||
"Unterminated quote",
|
||||
new_tk.span.into(),
|
||||
new_tk.span,
|
||||
)
|
||||
);
|
||||
}
|
||||
// TODO: clean up this mess
|
||||
if self.flags.contains(LexFlags::NEXT_IS_CMD) {
|
||||
if is_keyword(&new_tk.span.as_str()) {
|
||||
if is_keyword(new_tk.span.as_str()) {
|
||||
if matches!(new_tk.span.as_str(), "case" | "select" | "for") {
|
||||
self.flags |= LexFlags::EXPECTING_IN;
|
||||
new_tk.flags |= TkFlags::KEYWORD;
|
||||
@@ -485,7 +479,7 @@ impl LexStream {
|
||||
} else {
|
||||
new_tk.flags |= TkFlags::KEYWORD;
|
||||
}
|
||||
} else if is_assignment(&new_tk.span.as_str()) {
|
||||
} else if is_assignment(new_tk.span.as_str()) {
|
||||
new_tk.flags |= TkFlags::ASSIGN;
|
||||
} else {
|
||||
if self.flags.contains(LexFlags::EXPECTING_IN) {
|
||||
@@ -503,11 +497,9 @@ impl LexStream {
|
||||
}
|
||||
self.set_next_is_cmd(false);
|
||||
}
|
||||
} else if self.flags.contains(LexFlags::EXPECTING_IN) {
|
||||
if new_tk.span.as_str() == "in" {
|
||||
new_tk.flags |= TkFlags::KEYWORD;
|
||||
self.flags &= !LexFlags::EXPECTING_IN;
|
||||
}
|
||||
} else if self.flags.contains(LexFlags::EXPECTING_IN) && new_tk.span.as_str() == "in" {
|
||||
new_tk.flags |= TkFlags::KEYWORD;
|
||||
self.flags &= !LexFlags::EXPECTING_IN;
|
||||
}
|
||||
self.cursor = pos;
|
||||
Ok(new_tk)
|
||||
|
||||
185
src/parse/mod.rs
185
src/parse/mod.rs
@@ -49,7 +49,6 @@ impl ParsedSrc {
|
||||
let mut errors = vec![];
|
||||
let mut nodes = vec![];
|
||||
for parse_result in ParseStream::new(tokens) {
|
||||
flog!(DEBUG, parse_result);
|
||||
match parse_result {
|
||||
Ok(node) => nodes.push(node),
|
||||
Err(error) => errors.push(error)
|
||||
@@ -412,6 +411,24 @@ impl ParseStream {
|
||||
assert!(num_consumed <= self.tokens.len());
|
||||
self.tokens = self.tokens[num_consumed..].to_vec();
|
||||
}
|
||||
/// This tries to match on different stuff that can appear in a command position
|
||||
/// Matches shell commands like if-then-fi, pipelines, etc.
|
||||
/// 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>> {
|
||||
try_match!(self.parse_func_def()?);
|
||||
try_match!(self.parse_brc_grp(false /* from_func_def */)?);
|
||||
try_match!(self.parse_case()?);
|
||||
try_match!(self.parse_loop()?);
|
||||
try_match!(self.parse_for()?);
|
||||
try_match!(self.parse_if()?);
|
||||
if check_pipelines {
|
||||
try_match!(self.parse_pipeln()?);
|
||||
} else {
|
||||
try_match!(self.parse_cmd()?);
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
fn parse_cmd_list(&mut self) -> ShResult<Option<Node>> {
|
||||
let mut elements = vec![];
|
||||
let mut node_tks = vec![];
|
||||
@@ -446,26 +463,8 @@ impl ParseStream {
|
||||
}))
|
||||
}
|
||||
}
|
||||
/// This tries to match on different stuff that can appear in a command position
|
||||
/// Matches shell commands like if-then-fi, pipelines, etc.
|
||||
/// 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>> {
|
||||
try_match!(self.parse_func_def()?);
|
||||
try_match!(self.parse_brc_grp(false /* from_func_def */)?);
|
||||
try_match!(self.parse_case()?);
|
||||
try_match!(self.parse_loop()?);
|
||||
try_match!(self.parse_if()?);
|
||||
if check_pipelines {
|
||||
try_match!(self.parse_pipeln()?);
|
||||
} else {
|
||||
try_match!(self.parse_cmd()?);
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
fn parse_func_def(&mut self) -> ShResult<Option<Node>> {
|
||||
let mut node_tks: Vec<Tk> = vec![];
|
||||
let name;
|
||||
let body;
|
||||
|
||||
if !is_func_name(self.peek_tk()) {
|
||||
@@ -473,7 +472,7 @@ impl ParseStream {
|
||||
}
|
||||
let name_tk = self.next_tk().unwrap();
|
||||
node_tks.push(name_tk.clone());
|
||||
name = name_tk;
|
||||
let name = name_tk;
|
||||
|
||||
let Some(brc_grp) = self.parse_brc_grp(true /* from_func_def */)? else {
|
||||
return Err(parse_err_full(
|
||||
@@ -543,7 +542,7 @@ impl ParseStream {
|
||||
ShErr::full(
|
||||
ShErrKind::ParseErr,
|
||||
"Expected a filename after this redirection",
|
||||
tk.span.clone().into()
|
||||
tk.span.clone()
|
||||
)
|
||||
)
|
||||
};
|
||||
@@ -582,7 +581,7 @@ impl ParseStream {
|
||||
// Needs a pattern token
|
||||
// Followed by any number of CaseNodes
|
||||
let mut node_tks: Vec<Tk> = vec![];
|
||||
let pattern: Tk;
|
||||
|
||||
let mut case_blocks: Vec<CaseNode> = vec![];
|
||||
let redirs: Vec<Redir> = vec![];
|
||||
|
||||
@@ -610,7 +609,8 @@ impl ParseStream {
|
||||
return Err(pat_err)
|
||||
}
|
||||
|
||||
pattern = pat_tk;
|
||||
let pattern: Tk = pat_tk;
|
||||
|
||||
node_tks.push(pattern.clone());
|
||||
|
||||
if !self.check_keyword("in") || !self.next_tk_is_some() {
|
||||
@@ -764,7 +764,7 @@ impl ParseStream {
|
||||
ShErr::full(
|
||||
ShErrKind::ParseErr,
|
||||
"Expected a filename after this redirection",
|
||||
tk.span.clone().into()
|
||||
tk.span.clone()
|
||||
)
|
||||
)
|
||||
};
|
||||
@@ -799,9 +799,109 @@ impl ParseStream {
|
||||
};
|
||||
Ok(Some(node))
|
||||
}
|
||||
fn parse_for(&mut self) -> ShResult<Option<Node>> {
|
||||
let mut node_tks: Vec<Tk> = vec![];
|
||||
let mut vars: Vec<Tk> = vec![];
|
||||
let mut arr: Vec<Tk> = vec![];
|
||||
let mut body: Vec<Node> = vec![];
|
||||
let mut redirs: Vec<Redir> = vec![];
|
||||
|
||||
if !self.check_keyword("for") || !self.next_tk_is_some() {
|
||||
return Ok(None)
|
||||
}
|
||||
node_tks.push(self.next_tk().unwrap());
|
||||
|
||||
while let Some(tk) = self.next_tk() {
|
||||
node_tks.push(tk.clone());
|
||||
if tk.as_str() == "in" {
|
||||
break
|
||||
} else {
|
||||
vars.push(tk.clone());
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(tk) = self.next_tk() {
|
||||
node_tks.push(tk.clone());
|
||||
if tk.class == TkRule::Sep {
|
||||
break
|
||||
} else {
|
||||
arr.push(tk.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if vars.is_empty() {
|
||||
self.panic_mode(&mut node_tks);
|
||||
return Err(parse_err_full("This for loop is missing a variable", &node_tks.get_span().unwrap()))
|
||||
}
|
||||
if arr.is_empty() {
|
||||
self.panic_mode(&mut node_tks);
|
||||
return Err(parse_err_full("This for loop is missing an array", &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("Missing a 'do' for this for loop", &node_tks.get_span().unwrap()))
|
||||
}
|
||||
node_tks.push(self.next_tk().unwrap());
|
||||
self.catch_separator(&mut node_tks);
|
||||
|
||||
while let Some(node) = self.parse_block(true)? {
|
||||
body.push(node)
|
||||
}
|
||||
|
||||
if !self.check_keyword("done") || !self.next_tk_is_some() {
|
||||
self.panic_mode(&mut node_tks);
|
||||
return Err(parse_err_full("Missing a 'done' after this for loop", &node_tks.get_span().unwrap()))
|
||||
}
|
||||
node_tks.push(self.next_tk().unwrap());
|
||||
|
||||
while self.check_redir() {
|
||||
let tk = self.next_tk().unwrap();
|
||||
node_tks.push(tk.clone());
|
||||
let redir_bldr = tk.span.as_str().parse::<RedirBldr>().unwrap();
|
||||
if redir_bldr.io_mode.is_none() {
|
||||
let path_tk = self.next_tk();
|
||||
|
||||
if path_tk.clone().is_none_or(|tk| tk.class == TkRule::EOI) {
|
||||
return Err(
|
||||
ShErr::full(
|
||||
ShErrKind::ParseErr,
|
||||
"Expected a filename after this redirection",
|
||||
tk.span.clone()
|
||||
)
|
||||
)
|
||||
};
|
||||
|
||||
let path_tk = path_tk.unwrap();
|
||||
node_tks.push(path_tk.clone());
|
||||
let redir_class = redir_bldr.class.unwrap();
|
||||
let pathbuf = PathBuf::from(path_tk.span.as_str());
|
||||
|
||||
let Ok(file) = get_redir_file(redir_class, pathbuf) else {
|
||||
self.panic_mode(&mut node_tks);
|
||||
return Err(parse_err_full(
|
||||
"Error opening file for redirection",
|
||||
&path_tk.span
|
||||
));
|
||||
};
|
||||
|
||||
let io_mode = IoMode::file(redir_bldr.tgt_fd.unwrap(), file);
|
||||
let redir_bldr = redir_bldr.with_io_mode(io_mode);
|
||||
let redir = redir_bldr.build();
|
||||
redirs.push(redir);
|
||||
}
|
||||
}
|
||||
|
||||
let node = Node {
|
||||
class: NdRule::ForNode { vars, arr, body },
|
||||
flags: NdFlags::empty(),
|
||||
redirs,
|
||||
tokens: node_tks
|
||||
};
|
||||
Ok(Some(node))
|
||||
}
|
||||
fn parse_loop(&mut self) -> ShResult<Option<Node>> {
|
||||
// Requires a single CondNode and a LoopKind
|
||||
let loop_kind: LoopKind;
|
||||
|
||||
let cond_node: CondNode;
|
||||
let mut node_tks = vec![];
|
||||
|
||||
@@ -809,7 +909,7 @@ impl ParseStream {
|
||||
return Ok(None)
|
||||
}
|
||||
let loop_tk = self.next_tk().unwrap();
|
||||
loop_kind = loop_tk.span
|
||||
let loop_kind: LoopKind = loop_tk.span
|
||||
.as_str()
|
||||
.parse() // LoopKind implements FromStr
|
||||
.unwrap();
|
||||
@@ -876,12 +976,10 @@ impl ParseStream {
|
||||
cmds.push(cmd);
|
||||
if *self.next_tk_class() != TkRule::Pipe || is_punctuated {
|
||||
break
|
||||
} else if let Some(pipe) = self.next_tk() {
|
||||
node_tks.push(pipe)
|
||||
} else {
|
||||
if let Some(pipe) = self.next_tk() {
|
||||
node_tks.push(pipe)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if cmds.is_empty() {
|
||||
@@ -915,7 +1013,7 @@ impl ParseStream {
|
||||
break
|
||||
|
||||
} else if is_assignment {
|
||||
let Some(assign) = self.parse_assignment(&prefix_tk) else {
|
||||
let Some(assign) = self.parse_assignment(prefix_tk) else {
|
||||
break
|
||||
};
|
||||
node_tks.push(prefix_tk.clone());
|
||||
@@ -958,7 +1056,7 @@ impl ParseStream {
|
||||
ShErr::full(
|
||||
ShErrKind::ParseErr,
|
||||
"Expected a filename after this redirection",
|
||||
tk.span.clone().into()
|
||||
tk.span.clone()
|
||||
)
|
||||
)
|
||||
};
|
||||
@@ -1004,7 +1102,7 @@ impl ParseStream {
|
||||
let mut pos = token.span.start;
|
||||
|
||||
while let Some(ch) = chars.next() {
|
||||
if !assign_kind.is_none() {
|
||||
if assign_kind.is_some() {
|
||||
match ch {
|
||||
'\\' => {
|
||||
pos += ch.len_utf8();
|
||||
@@ -1100,8 +1198,6 @@ impl ParseStream {
|
||||
impl Iterator for ParseStream {
|
||||
type Item = ShResult<Node>;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
flog!(DEBUG, "parsing");
|
||||
flog!(DEBUG, self.tokens);
|
||||
// Empty token vector or only SOI/EOI tokens, nothing to do
|
||||
if self.tokens.is_empty() || self.tokens.len() == 1 {
|
||||
return None
|
||||
@@ -1117,21 +1213,19 @@ impl Iterator for ParseStream {
|
||||
}
|
||||
}
|
||||
let result = self.parse_cmd_list();
|
||||
flog!(DEBUG, result);
|
||||
flog!(DEBUG, self.tokens);
|
||||
match result {
|
||||
Ok(Some(node)) => {
|
||||
return Some(Ok(node));
|
||||
Some(Ok(node))
|
||||
}
|
||||
Ok(None) => return None,
|
||||
Ok(None) => None,
|
||||
Err(e) => {
|
||||
return Some(Err(e))
|
||||
Some(Err(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn node_is_punctuated(tokens: &Vec<Tk>) -> bool {
|
||||
fn node_is_punctuated(tokens: &[Tk]) -> bool {
|
||||
tokens.last().is_some_and(|tk| {
|
||||
matches!(tk.class, TkRule::Sep)
|
||||
})
|
||||
@@ -1153,7 +1247,6 @@ fn get_redir_file(class: RedirType, path: PathBuf) -> ShResult<File> {
|
||||
}
|
||||
RedirType::Append => {
|
||||
OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(Path::new(&path))
|
||||
@@ -1167,7 +1260,7 @@ fn parse_err_full(reason: &str, blame: &Span) -> ShErr {
|
||||
ShErr::full(
|
||||
ShErrKind::ParseErr,
|
||||
reason,
|
||||
blame.clone().into()
|
||||
blame.clone()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1192,7 +1285,7 @@ pub fn node_operation<F1,F2>(node: &mut Node, filter: &F1, operation: &mut F2)
|
||||
F2: FnMut(&mut Node)
|
||||
{
|
||||
let check_node = |node: &mut Node, filter: &F1, operation: &mut F2| {
|
||||
if filter(&node) {
|
||||
if filter(node) {
|
||||
operation(node);
|
||||
} else {
|
||||
node_operation::<F1,F2>(node, filter, operation);
|
||||
@@ -1254,7 +1347,7 @@ pub fn node_operation<F1,F2>(node: &mut Node, filter: &F1, operation: &mut F2)
|
||||
check_node(cmd,filter,operation);
|
||||
}
|
||||
}
|
||||
NdRule::Assignment { kind: _, var: _, val: _ } => return, // No nodes to check
|
||||
NdRule::Assignment { kind: _, var: _, val: _ } => (), // No nodes to check
|
||||
NdRule::BraceGrp { ref mut body } => {
|
||||
for body_node in body {
|
||||
check_node(body_node,filter,operation);
|
||||
|
||||
Reference in New Issue
Block a user