From 90ef005901dc3fe6e2416333d31935b521a1c9e5 Mon Sep 17 00:00:00 2001 From: pagedmov Date: Wed, 5 Mar 2025 01:37:51 -0500 Subject: [PATCH] Implemented loops and redirection for shell structures --- src/builtin/pwd.rs | 2 +- src/execute/ifthen.rs | 17 ++-- src/execute/loops.rs | 48 ++++++++++ src/execute/mod.rs | 6 ++ src/expand/cmdsub.rs | 2 +- src/expand/mod.rs | 1 - src/libsh/utils.rs | 9 +- src/parse/lex.rs | 2 +- src/parse/parse.rs | 196 ++++++++++++++++++++++++--------------- src/prelude.rs | 1 + src/prompt/mod.rs | 4 +- src/prompt/readline.rs | 2 +- src/shellenv/exec_ctx.rs | 55 +++++++++++ src/shellenv/input.rs | 1 - src/shellenv/jobs.rs | 2 - src/shellenv/shenv.rs | 27 +++++- 16 files changed, 277 insertions(+), 98 deletions(-) create mode 100644 src/execute/loops.rs diff --git a/src/builtin/pwd.rs b/src/builtin/pwd.rs index e27aaf0..1e28d54 100644 --- a/src/builtin/pwd.rs +++ b/src/builtin/pwd.rs @@ -1,6 +1,6 @@ use shellenv::jobs::{ChildProc, JobBldr}; -use crate::{parse::parse::{NdFlag, Node, NdRule}, prelude::*}; +use crate::{parse::parse::{Node, NdRule}, prelude::*}; pub fn pwd(node: Node, shenv: &mut ShEnv) -> ShResult<()> { let rule = node.into_rule(); diff --git a/src/execute/ifthen.rs b/src/execute/ifthen.rs index 8aff436..c923244 100644 --- a/src/execute/ifthen.rs +++ b/src/execute/ifthen.rs @@ -1,8 +1,9 @@ -use crate::{parse::parse::SynTree, prelude::*}; +use crate::prelude::*; pub fn exec_if(node: Node, shenv: &mut ShEnv) -> ShResult<()> { let rule = node.into_rule(); - if let NdRule::IfThen { cond_blocks, else_block } = rule { + if let NdRule::IfThen { cond_blocks, else_block, redirs } = rule { + shenv.collect_redirs(redirs); if shenv.ctx().flags().contains(ExecFlags::NO_FORK) { shenv.ctx_mut().unset_flag(ExecFlags::NO_FORK); } @@ -11,17 +12,15 @@ pub fn exec_if(node: Node, shenv: &mut ShEnv) -> ShResult<()> { while let Some(block) = cond_blocks.next() { let cond = block.0; let body = block.1; - let ast = SynTree::from_vec(cond); - Executor::new(ast,shenv).walk()?; - if shenv.get_code() == 0 { - let ast = SynTree::from_vec(body); - return Executor::new(ast,shenv).walk() + let ret = shenv.exec_as_cond(cond)?; + if ret == 0 { + shenv.exec_as_body(body)?; + return Ok(()) } } if let Some(block) = else_block { - let ast = SynTree::from_vec(block); - Executor::new(ast,shenv).walk()?; + shenv.exec_as_body(block)?; } } else { unreachable!() } Ok(()) diff --git a/src/execute/loops.rs b/src/execute/loops.rs new file mode 100644 index 0000000..0c40162 --- /dev/null +++ b/src/execute/loops.rs @@ -0,0 +1,48 @@ +use crate::{parse::parse::LoopKind, prelude::*}; + +pub fn exec_loop(node: Node, shenv: &mut ShEnv) -> ShResult<()> { + let rule = node.into_rule(); + + if let NdRule::Loop { kind, cond, body, redirs } = rule { + shenv.collect_redirs(redirs); + + if shenv.ctx().flags().contains(ExecFlags::NO_FORK) { + shenv.ctx_mut().unset_flag(ExecFlags::NO_FORK); + } + + loop { + let ret = shenv.exec_as_cond(cond.clone())?; + match kind { + LoopKind::While => { + if ret == 0 { + match shenv.exec_as_body(body.clone()) { + Ok(_) => continue, + Err(e) => { + match e.kind() { + ShErrKind::LoopContinue => continue, + ShErrKind::LoopBreak => break, + _ => return Err(e.into()) + } + } + } + } else { break } + } + LoopKind::Until => { + if ret != 0 { + match shenv.exec_as_body(body.clone()) { + Ok(_) => continue, + Err(e) => { + match e.kind() { + ShErrKind::LoopContinue => continue, + ShErrKind::LoopBreak => break, + _ => return Err(e.into()) + } + } + } + } else { break } + } + } + } + } else { unreachable!() } + Ok(()) +} diff --git a/src/execute/mod.rs b/src/execute/mod.rs index 52a4af6..9b41f1e 100644 --- a/src/execute/mod.rs +++ b/src/execute/mod.rs @@ -5,6 +5,7 @@ use shellenv::jobs::{ChildProc, JobBldr}; use crate::{builtin::export::export, libsh::{error::Blame, sys::{execvpe, get_bin_path}, utils::{ArgVec, StrOps}}, parse::{lex::Token, parse::{CmdGuard, NdFlag, Node, NdRule, SynTree}}, prelude::*}; pub mod ifthen; +pub mod loops; pub fn exec_input>(input: S, shenv: &mut ShEnv) -> ShResult<()> { let input = input.into(); @@ -41,6 +42,7 @@ impl<'a> Executor<'a> { Self { ast, shenv } } pub fn walk(&mut self) -> ShResult<()> { + self.shenv.ctx_mut().descend()?; log!(DEBUG, "Starting walk"); while let Some(node) = self.ast.next_node() { if let NdRule::CmdList { cmds } = node.clone().into_rule() { @@ -48,6 +50,7 @@ impl<'a> Executor<'a> { exec_list(cmds, self.shenv).try_blame(node.as_raw(self.shenv),node.span())? } else { unreachable!() } } + self.shenv.ctx_mut().ascend(); Ok(()) } } @@ -77,6 +80,7 @@ fn exec_list(list: Vec<(Option, Node)>, shenv: &mut ShEnv) -> ShResult NdRule::Command {..} => dispatch_command(cmd, shenv).try_blame(cmd_raw, span)?, NdRule::Subshell {..} => exec_subshell(cmd,shenv).try_blame(cmd_raw, span)?, NdRule::IfThen {..} => ifthen::exec_if(cmd, shenv).try_blame(cmd_raw, span)?, + NdRule::Loop {..} => loops::exec_loop(cmd, shenv).try_blame(cmd_raw, span)?, NdRule::FuncDef {..} => exec_funcdef(cmd,shenv).try_blame(cmd_raw, span)?, NdRule::Assignment {..} => exec_assignment(cmd,shenv).try_blame(cmd_raw, span)?, NdRule::Pipeline {..} => exec_pipeline(cmd, shenv).try_blame(cmd_raw, span)?, @@ -244,6 +248,8 @@ fn exec_builtin(node: Node, shenv: &mut ShEnv) -> ShResult<()> { "alias" => alias(node, shenv)?, "exit" => sh_flow(node, shenv, ShErrKind::CleanExit)?, "return" => sh_flow(node, shenv, ShErrKind::FuncReturn)?, + "break" => sh_flow(node, shenv, ShErrKind::LoopBreak)?, + "continue" => sh_flow(node, shenv, ShErrKind::LoopContinue)?, _ => unimplemented!("Have not yet implemented support for builtin `{}'",command) } log!(TRACE, "done"); diff --git a/src/expand/cmdsub.rs b/src/expand/cmdsub.rs index ab92ef3..0dc7a64 100644 --- a/src/expand/cmdsub.rs +++ b/src/expand/cmdsub.rs @@ -1,7 +1,7 @@ use crate::prelude::*; pub fn expand_cmdsub(token: Token, shenv: &mut ShEnv) -> ShResult> { - let mut new_tokens = vec![]; + let new_tokens = vec![]; let cmdsub_raw = token.as_raw(shenv); let body = &cmdsub_raw[2..cmdsub_raw.len() - 1].to_string(); // From '$(this)' to 'this' diff --git a/src/expand/mod.rs b/src/expand/mod.rs index 812b797..d563edc 100644 --- a/src/expand/mod.rs +++ b/src/expand/mod.rs @@ -3,7 +3,6 @@ pub mod tilde; pub mod alias; pub mod cmdsub; -use alias::expand_aliases; use vars::{expand_dquote, expand_var}; use tilde::expand_tilde; diff --git a/src/libsh/utils.rs b/src/libsh/utils.rs index cc0bcdb..75e2b6d 100644 --- a/src/libsh/utils.rs +++ b/src/libsh/utils.rs @@ -1,7 +1,6 @@ -use core::{arch::asm, fmt::{self, Debug, Display, Write}, ops::Deref}; -use std::{os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd}, str::FromStr}; +use core::fmt::{Debug, Display, Write}; +use std::{os::fd::{AsRawFd, BorrowedFd}, str::FromStr}; -use nix::libc::getpgrp; use crate::prelude::*; @@ -311,7 +310,7 @@ impl CmdRedirs { let Redir { src, op, tgt } = redir; let src = borrow_fd(src); - let mut file_fd = if let RedirTarget::File(path) = tgt { + let file_fd = if let RedirTarget::File(path) = tgt { let flags = match op { RedirType::Input => OFlag::O_RDONLY, RedirType::Output => OFlag::O_WRONLY | OFlag::O_CREAT | OFlag::O_TRUNC, @@ -331,7 +330,7 @@ impl CmdRedirs { pub fn open_fd_tgts(&mut self) -> ShResult<()> { while let Some(redir) = self.targets_fd.pop() { let Redir { src, op: _, tgt } = redir; - let mut tgt = if let RedirTarget::Fd(fd) = tgt { + let tgt = if let RedirTarget::Fd(fd) = tgt { borrow_fd(fd) } else { unreachable!() }; let src = borrow_fd(src); diff --git a/src/parse/lex.rs b/src/parse/lex.rs index 7562264..0e54fc2 100644 --- a/src/parse/lex.rs +++ b/src/parse/lex.rs @@ -1,4 +1,4 @@ -use std::{cell::Ref, fmt::{Debug, Display}}; +use std::fmt::Debug; use crate::prelude::*; diff --git a/src/parse/parse.rs b/src/parse/parse.rs index 522cdbe..3b87dd2 100644 --- a/src/parse/parse.rs +++ b/src/parse/parse.rs @@ -1,5 +1,4 @@ -use core::fmt::Display; -use std::{cell::Ref, str::FromStr}; +use std::{iter::Peekable, str::FromStr}; use crate::prelude::*; @@ -86,9 +85,9 @@ pub enum NdRule { Command { argv: Vec, redirs: Vec }, Assignment { assignments: Vec, cmd: Option> }, FuncDef { name: Token, body: Token }, - IfThen { cond_blocks: Vec<(Vec,Vec)>, else_block: Option> }, - Loop { kind: LoopKind, cond: Vec, body: Vec }, - ForLoop { vars: Vec, arr: Vec, body: Vec }, + IfThen { cond_blocks: Vec<(Vec,Vec)>, else_block: Option>, redirs: Vec }, + Loop { kind: LoopKind, cond: Vec, body: Vec, redirs: Vec }, + ForLoop { vars: Vec, arr: Vec, body: Vec, redirs: Vec }, Subshell { body: Token, argv: Vec, redirs: Vec }, CmdList { cmds: Vec<(Option,Node)> }, Pipeline { cmds: Vec } @@ -175,7 +174,7 @@ impl<'a> Parser<'a> { } } -fn get_span(toks: &Vec, shenv: &mut ShEnv) -> ShResult>> { +pub fn get_span(toks: &Vec, shenv: &mut ShEnv) -> ShResult>> { if toks.is_empty() { Err(ShErr::simple(ShErrKind::InternalErr, "Get_span was given an empty token list")) } else { @@ -202,6 +201,38 @@ fn get_lists(mut tokens: &[Token], shenv: &mut ShEnv) -> (usize,Vec) { (tokens_eaten,lists) } +fn get_redir(token: Token, token_slice: &[Token], shenv: &mut ShEnv) -> ShResult<(usize,Redir)> { + let mut tokens_eaten = 0; + let mut tokens_iter = token_slice.into_iter(); + let redir_raw = shenv.input_slice(token.span()); + let mut redir_bldr = RedirBldr::from_str(&redir_raw).unwrap(); + // If there isn't an FD target, get the next token and use it as the filename + if redir_bldr.tgt().is_none() { + if let Some(filename) = tokens_iter.next() { + // Make sure it's a word and not an operator or something + if !matches!(filename.rule(), TkRule::SQuote | TkRule::DQuote | TkRule::Ident) || KEYWORDS.contains(&filename.rule()) { + let mut err = ShErr::simple(ShErrKind::ParseErr, "Did not find a target for this redirection"); + let input = shenv.input_slice(token.span()).to_string(); + err.blame(input, token.span()); + return Err(err) + } + tokens_eaten += 1; + // Construct the Path object + let filename_raw = shenv.input_slice(filename.span()).to_string(); + let filename_path = PathBuf::from(filename_raw); + let tgt = RedirTarget::File(filename_path); + // Update the builder + redir_bldr = redir_bldr.with_tgt(tgt); + } else { + let mut err = ShErr::simple(ShErrKind::ParseErr, "Did not find a target for this redirection"); + let input = shenv.input_slice(token.span()).to_string(); + err.blame(input, token.span()); + return Err(err) + } + } + Ok((tokens_eaten,redir_bldr.build())) +} + // TODO: Redirs with FD sources appear to be looping endlessly for some reason ndrule_def!(Main, shenv, |tokens: &[Token], shenv: &mut ShEnv| { @@ -316,6 +347,7 @@ ndrule_def!(ForLoop, shenv, |mut tokens: &[Token], shenv: &mut ShEnv| { let mut node_toks = vec![]; let mut vars = vec![]; let mut arr = vec![]; + let mut redirs = vec![]; let body: Vec; if let Some(token) = tokens_iter.next() { @@ -376,20 +408,46 @@ ndrule_def!(ForLoop, shenv, |mut tokens: &[Token], shenv: &mut ShEnv| { body = lists; tokens_iter = tokens.iter().peekable(); - if let Some(token) = tokens_iter.next() { - node_toks.push(token.clone()); - if token.rule() != TkRule::Done { - let span = get_span(&node_toks, shenv)?; - return Err(err("Expected `done` after for loop",span,shenv)) + let mut closed = false; + while let Some(token) = tokens_iter.next() { + match token.rule() { + TkRule::Done => { + node_toks.push(token.clone()); + tokens = &tokens[1..]; + closed = true; + } + TkRule::Sep => { + node_toks.push(token.clone()); + tokens = &tokens[1..]; + if closed { break } + } + TkRule::RedirOp if closed => { + node_toks.push(token.clone()); + tokens = &tokens[1..]; + let (used,redir) = get_redir(token.clone(), tokens, shenv)?; + for _ in 0..used { + if let Some(token) = tokens_iter.next() { + node_toks.push(token.clone()); + } + } + tokens = &tokens[used..]; + redirs.push(redir); + } + _ => { + let span = get_span(&node_toks, shenv)?; + return Err(err("Expected `done` after for loop",span,shenv)) + } } - } else { + } + + if !closed { let span = get_span(&node_toks, shenv)?; return Err(err("Expected `done` after for loop",span,shenv)) } let span = get_span(&node_toks, shenv)?; let node = Node { - node_rule: NdRule::ForLoop { vars, arr, body }, + node_rule: NdRule::ForLoop { vars, arr, body, redirs }, tokens: node_toks, span, flags: NdFlag::empty() @@ -405,6 +463,7 @@ ndrule_def!(IfThen, shenv, |mut tokens: &[Token], shenv: &mut ShEnv| { let mut tokens_iter = tokens.iter().peekable(); let mut node_toks = vec![]; let mut cond_blocks = vec![]; + let mut redirs = vec![]; let mut else_block: Option> = None; if let Some(token) = tokens_iter.next() { @@ -518,14 +577,32 @@ ndrule_def!(IfThen, shenv, |mut tokens: &[Token], shenv: &mut ShEnv| { node_toks.push(token.clone()); tokens = &tokens[1..]; } - TkRule::Sep | TkRule::Whitespace => { + TkRule::Whitespace => { + node_toks.push(token.clone()); + tokens = &tokens[1..]; + } + TkRule::Sep => { node_toks.push(token.clone()); tokens = &tokens[1..]; if closed { break } } + TkRule::RedirOp if closed => { + node_toks.push(token.clone()); + tokens = &tokens[1..]; + let (used,redir) = get_redir(token.clone(), tokens, shenv)?; + for _ in 0..used { + if let Some(token) = tokens_iter.next() { + node_toks.push(token.clone()); + } + } + tokens = &tokens[used..]; + log!(DEBUG,redir); + log!(DEBUG,tokens); + redirs.push(redir); + } _ => { let span = get_span(&node_toks, shenv)?; - return Err(err("Unexpected token in if statement",span,shenv)) + return Err(err(&format!("Unexpected token in if statement: {:?}",token.rule()),span,shenv)) } } } @@ -537,7 +614,7 @@ ndrule_def!(IfThen, shenv, |mut tokens: &[Token], shenv: &mut ShEnv| { let span = get_span(&node_toks, shenv)?; let node = Node { - node_rule: NdRule::IfThen { cond_blocks, else_block }, + node_rule: NdRule::IfThen { cond_blocks, else_block, redirs }, tokens: node_toks, span, flags: NdFlag::empty() @@ -552,6 +629,7 @@ ndrule_def!(Loop, shenv, |mut tokens: &[Token], shenv: &mut ShEnv| { }; let mut tokens_iter = tokens.iter().peekable(); let mut node_toks = vec![]; + let mut redirs = vec![]; let kind: LoopKind; let cond: Vec; let body: Vec; @@ -621,6 +699,18 @@ ndrule_def!(Loop, shenv, |mut tokens: &[Token], shenv: &mut ShEnv| { node_toks.push(token.clone()); tokens = &tokens[1..]; } + TkRule::RedirOp if closed => { + node_toks.push(token.clone()); + tokens = &tokens[1..]; + let (used,redir) = get_redir(token.clone(), tokens, shenv)?; + for _ in 0..used { + if let Some(token) = tokens_iter.next() { + node_toks.push(token.clone()); + } + } + tokens = &tokens[used..]; + redirs.push(redir); + } _ => { let span = get_span(&node_toks,shenv)?; return Err(err("Unexpected token in loop",span,shenv)) @@ -635,7 +725,7 @@ ndrule_def!(Loop, shenv, |mut tokens: &[Token], shenv: &mut ShEnv| { let span = get_span(&node_toks, shenv)?; let node = Node { - node_rule: NdRule::Loop { kind, cond, body }, + node_rule: NdRule::Loop { kind, cond, body, redirs }, tokens: node_toks, span, flags: NdFlag::empty() @@ -684,7 +774,7 @@ ndrule_def!(FuncDef, shenv, |tokens: &[Token], shenv: &mut ShEnv| { }); ndrule_def!(Subshell, shenv, |tokens: &[Token], shenv: &mut ShEnv| { - let mut tokens_iter = tokens.iter(); + let mut tokens_iter = tokens.into_iter(); let mut node_toks = vec![]; let mut argv = vec![]; let mut redirs = vec![]; @@ -715,34 +805,14 @@ ndrule_def!(Subshell, shenv, |tokens: &[Token], shenv: &mut ShEnv| { } TkRule::RedirOp => { node_toks.push(token.clone()); - // Get the raw redirection text, e.g. "1>&2" or "2>" or ">>" or something - let redir_raw = shenv.input_slice(token.span()); - let mut redir_bldr = RedirBldr::from_str(&redir_raw).unwrap(); - // If there isn't an FD target, get the next token and use it as the filename - if redir_bldr.tgt().is_none() { - if let Some(filename) = tokens_iter.next() { - // Make sure it's a word and not an operator or something - if !matches!(filename.rule(), TkRule::SQuote | TkRule::DQuote | TkRule::Ident) || KEYWORDS.contains(&filename.rule()) { - let mut err = ShErr::simple(ShErrKind::ParseErr, "Did not find a target for this redirection"); - let input = shenv.input_slice(token.span()).to_string(); - err.blame(input, token.span()); - return Err(err) - } - node_toks.push(filename.clone()); - // Construct the Path object - let filename_raw = shenv.input_slice(filename.span()).to_string(); - let filename_path = PathBuf::from(filename_raw); - let tgt = RedirTarget::File(filename_path); - // Update the builder - redir_bldr = redir_bldr.with_tgt(tgt); - } else { - let mut err = ShErr::simple(ShErrKind::ParseErr, "Did not find a target for this redirection"); - let input = shenv.input_slice(token.span()).to_string(); - err.blame(input, token.span()); - return Err(err) + let slice = &tokens_iter.clone().map(|tk| tk.clone()).collect::>(); + let (used,redir) = get_redir(token.clone(), slice, shenv)?; + for _ in 0..used { + if let Some(token) = tokens_iter.next() { + node_toks.push(token.clone()); } } - redirs.push(redir_bldr.build()); + redirs.push(redir); } _ => break } @@ -841,19 +911,19 @@ ndrule_def!(Pipeline, shenv, |mut tokens: &[Token], shenv: &mut ShEnv| { ndrule_def!(Command, shenv, |tokens: &[Token], shenv: &mut ShEnv| { log!(TRACE, "Parsing command"); - let mut tokens = tokens.iter().peekable(); + let mut tokens_iter = tokens.iter().peekable(); let mut node_toks = vec![]; let mut argv = vec![]; let mut redirs = vec![]; - while let Some(token) = tokens.peek() { + while let Some(token) = tokens_iter.peek() { match token.rule() { TkRule::AndOp | TkRule::OrOp | TkRule::PipeOp | TkRule::ErrPipeOp => { break } _ => { /* Keep going */ } } - let token = tokens.next().unwrap(); + let token = tokens_iter.next().unwrap(); node_toks.push(token.clone()); match token.rule() { TkRule::Ident | @@ -865,34 +935,14 @@ ndrule_def!(Command, shenv, |tokens: &[Token], shenv: &mut ShEnv| { argv.push(token.clone()); } TkRule::RedirOp => { - // Get the raw redirection text, e.g. "1>&2" or "2>" or ">>" or something - let redir_raw = shenv.input_slice(token.span()).to_string(); - let mut redir_bldr = RedirBldr::from_str(&redir_raw).unwrap(); - // If there isn't an FD target, get the next token and use it as the filename - if redir_bldr.tgt().is_none() { - if let Some(filename) = tokens.next() { - // Make sure it's a word and not an operator or something - if !matches!(filename.rule(), TkRule::SQuote | TkRule::DQuote | TkRule::Ident) || KEYWORDS.contains(&filename.rule()) { - let mut err = ShErr::simple(ShErrKind::ParseErr, "Did not find a target for this redirection"); - let input = shenv.input_slice(token.span()).to_string(); - err.blame(input, token.span()); - return Err(err) - } - node_toks.push(filename.clone()); - // Construct the Path object - let filename_raw = shenv.input_slice(filename.span()).to_string(); - let filename_path = PathBuf::from(filename_raw); - let tgt = RedirTarget::File(filename_path); - // Update the builder - redir_bldr = redir_bldr.with_tgt(tgt); - } else { - let mut err = ShErr::simple(ShErrKind::ParseErr, "Did not find a target for this redirection"); - let input = shenv.input_slice(token.span()).to_string(); - err.blame(input, token.span()); - return Err(err) + let slice = &tokens_iter.clone().map(|tk| tk.clone()).collect::>(); + let (used,redir) = get_redir(token.clone(), slice, shenv)?; + for _ in 0..used { + if let Some(token) = tokens_iter.next() { + node_toks.push(token.clone()); } } - redirs.push(redir_bldr.build()); + redirs.push(redir); } TkRule::Sep => break, _ => return Err( diff --git a/src/prelude.rs b/src/prelude.rs index c2d1bfb..f4a4519 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -150,6 +150,7 @@ pub use crate::{ }, parse::{ parse::{ + SynTree, Node, NdRule, Parser, diff --git a/src/prompt/mod.rs b/src/prompt/mod.rs index 037e67b..c7378bd 100644 --- a/src/prompt/mod.rs +++ b/src/prompt/mod.rs @@ -1,6 +1,6 @@ use crate::prelude::*; use readline::SynHelper; -use rustyline::{config::Configurer, history::{DefaultHistory, History}, ColorMode, CompletionType, Config, DefaultEditor, EditMode, Editor}; +use rustyline::{config::Configurer, history::{DefaultHistory, History}, ColorMode, CompletionType, Config, EditMode, Editor}; pub mod readline; pub mod highlight; @@ -8,7 +8,7 @@ pub mod validate; fn init_rl<'a>(shenv: &'a mut ShEnv) -> Editor, DefaultHistory> { let hist_path = std::env::var("FERN_HIST").unwrap_or_default(); - let mut config = Config::builder() + let config = Config::builder() .max_history_size(1000).unwrap() .history_ignore_dups(true).unwrap() .completion_prompt_limit(100) diff --git a/src/prompt/readline.rs b/src/prompt/readline.rs index 94e146d..7112d13 100644 --- a/src/prompt/readline.rs +++ b/src/prompt/readline.rs @@ -1,4 +1,4 @@ -use rustyline::{completion::{Completer, FilenameCompleter}, highlight::Highlighter, hint::{Hint, Hinter}, history::{History, SearchDirection}, validate::{ValidationResult, Validator}, Helper}; +use rustyline::{completion::{Completer, FilenameCompleter}, hint::{Hint, Hinter}, history::{History, SearchDirection}, Helper}; use crate::prelude::*; diff --git a/src/shellenv/exec_ctx.rs b/src/shellenv/exec_ctx.rs index 60b67df..48690b8 100644 --- a/src/shellenv/exec_ctx.rs +++ b/src/shellenv/exec_ctx.rs @@ -10,6 +10,9 @@ bitflags! { #[derive(Clone,Debug)] pub struct ExecCtx { redirs: Vec, + depth: usize, + state_stack: Vec, + max_depth: usize, flags: ExecFlags, io_masks: IoMasks, saved_io: Option @@ -19,11 +22,63 @@ impl ExecCtx { pub fn new() -> Self { Self { redirs: vec![], + depth: 0, + state_stack: vec![], + max_depth: 1500, flags: ExecFlags::empty(), io_masks: IoMasks::new(), saved_io: None } } + pub fn push_state(&mut self) { + self.state_stack.push(self.clone()); + } + pub fn pop_state(&mut self) { + if let Some(state) = self.state_stack.pop() { + *self = state; + } + } + pub fn descend(&mut self) -> ShResult<()> { + self.push_state(); + self.depth += 1; + log!(DEBUG, "{}",self.depth); + if self.depth > self.max_depth { + return Err( + ShErr::simple(ShErrKind::ExecFail,"Exceeded maximum execution depth") + ) + } + Ok(()) + } + pub fn ascend(&mut self) { + self.pop_state(); + self.depth = self.depth.saturating_sub(1); + } + pub fn as_cond(&self) -> Self { + let mut clone = self.clone(); + let (cond_redirs,_) = self.sort_redirs(); + clone.redirs = cond_redirs; + clone + } + pub fn as_body(&self) -> Self { + let mut clone = self.clone(); + let (_,body_redirs) = self.sort_redirs(); + clone.redirs = body_redirs; + clone + } + pub fn sort_redirs(&self) -> (Vec,Vec) { + let mut cond_redirs = vec![]; + let mut body_redirs = vec![]; + for redir in self.redirs.clone() { + match redir.op { + RedirType::Input | + RedirType::HereString | + RedirType::HereDoc => cond_redirs.push(redir), + RedirType::Output | + RedirType::Append => body_redirs.push(redir) + } + } + (cond_redirs,body_redirs) + } pub fn masks(&self) -> &IoMasks { &self.io_masks } diff --git a/src/shellenv/input.rs b/src/shellenv/input.rs index 8cd99fd..8d31c51 100644 --- a/src/shellenv/input.rs +++ b/src/shellenv/input.rs @@ -1,4 +1,3 @@ -use std::cell::Ref; use crate::prelude::*; diff --git a/src/shellenv/jobs.rs b/src/shellenv/jobs.rs index 9c72912..83ebbbe 100644 --- a/src/shellenv/jobs.rs +++ b/src/shellenv/jobs.rs @@ -1,8 +1,6 @@ use std::{fmt, sync::{Arc, LazyLock, RwLock}}; use nix::unistd::setpgid; -use shellenv::{disable_reaping, enable_reaping}; -use sys::SIG_EXIT_OFFSET; use crate::prelude::*; diff --git a/src/shellenv/shenv.rs b/src/shellenv/shenv.rs index 7108d74..027059c 100644 --- a/src/shellenv/shenv.rs +++ b/src/shellenv/shenv.rs @@ -63,7 +63,32 @@ impl ShEnv { } } self.input_man.clamp_all(); - new_tokens + log!(DEBUG, new_tokens); + if new_tokens.is_empty() { + let empty = Token::new( + TkRule::Ident, + self.inputman_mut().new_span(repl_start, repl_start) + ); + vec![empty] + } else { + new_tokens + } + } + pub fn exec_as_cond(&mut self, nodes: Vec) -> ShResult { + let saved = self.ctx().clone(); + self.ctx = self.ctx().as_cond(); + let ast = SynTree::from_vec(nodes); + Executor::new(ast, self).walk()?; + self.ctx = saved; + Ok(self.get_code()) + } + pub fn exec_as_body(&mut self, nodes: Vec) -> ShResult { + let saved = self.ctx().clone(); + self.ctx = self.ctx().as_body(); + let ast = SynTree::from_vec(nodes); + Executor::new(ast, self).walk()?; + self.ctx = saved; + Ok(self.get_code()) } pub fn new_input(&mut self, input: &str) { self.input_man.clear();