Implemented loops and redirection for shell structures
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
use shellenv::jobs::{ChildProc, JobBldr};
|
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<()> {
|
pub fn pwd(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||||
let rule = node.into_rule();
|
let rule = node.into_rule();
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
use crate::{parse::parse::SynTree, prelude::*};
|
use crate::prelude::*;
|
||||||
|
|
||||||
pub fn exec_if(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
pub fn exec_if(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||||
let rule = node.into_rule();
|
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) {
|
if shenv.ctx().flags().contains(ExecFlags::NO_FORK) {
|
||||||
shenv.ctx_mut().unset_flag(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() {
|
while let Some(block) = cond_blocks.next() {
|
||||||
let cond = block.0;
|
let cond = block.0;
|
||||||
let body = block.1;
|
let body = block.1;
|
||||||
let ast = SynTree::from_vec(cond);
|
let ret = shenv.exec_as_cond(cond)?;
|
||||||
Executor::new(ast,shenv).walk()?;
|
if ret == 0 {
|
||||||
if shenv.get_code() == 0 {
|
shenv.exec_as_body(body)?;
|
||||||
let ast = SynTree::from_vec(body);
|
return Ok(())
|
||||||
return Executor::new(ast,shenv).walk()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(block) = else_block {
|
if let Some(block) = else_block {
|
||||||
let ast = SynTree::from_vec(block);
|
shenv.exec_as_body(block)?;
|
||||||
Executor::new(ast,shenv).walk()?;
|
|
||||||
}
|
}
|
||||||
} else { unreachable!() }
|
} else { unreachable!() }
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
48
src/execute/loops.rs
Normal file
48
src/execute/loops.rs
Normal file
@@ -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(())
|
||||||
|
}
|
||||||
@@ -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::*};
|
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 ifthen;
|
||||||
|
pub mod loops;
|
||||||
|
|
||||||
pub fn exec_input<S: Into<String>>(input: S, shenv: &mut ShEnv) -> ShResult<()> {
|
pub fn exec_input<S: Into<String>>(input: S, shenv: &mut ShEnv) -> ShResult<()> {
|
||||||
let input = input.into();
|
let input = input.into();
|
||||||
@@ -41,6 +42,7 @@ impl<'a> Executor<'a> {
|
|||||||
Self { ast, shenv }
|
Self { ast, shenv }
|
||||||
}
|
}
|
||||||
pub fn walk(&mut self) -> ShResult<()> {
|
pub fn walk(&mut self) -> ShResult<()> {
|
||||||
|
self.shenv.ctx_mut().descend()?;
|
||||||
log!(DEBUG, "Starting walk");
|
log!(DEBUG, "Starting walk");
|
||||||
while let Some(node) = self.ast.next_node() {
|
while let Some(node) = self.ast.next_node() {
|
||||||
if let NdRule::CmdList { cmds } = node.clone().into_rule() {
|
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())?
|
exec_list(cmds, self.shenv).try_blame(node.as_raw(self.shenv),node.span())?
|
||||||
} else { unreachable!() }
|
} else { unreachable!() }
|
||||||
}
|
}
|
||||||
|
self.shenv.ctx_mut().ascend();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,6 +80,7 @@ fn exec_list(list: Vec<(Option<CmdGuard>, Node)>, shenv: &mut ShEnv) -> ShResult
|
|||||||
NdRule::Command {..} => dispatch_command(cmd, shenv).try_blame(cmd_raw, span)?,
|
NdRule::Command {..} => dispatch_command(cmd, shenv).try_blame(cmd_raw, span)?,
|
||||||
NdRule::Subshell {..} => exec_subshell(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::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::FuncDef {..} => exec_funcdef(cmd,shenv).try_blame(cmd_raw, span)?,
|
||||||
NdRule::Assignment {..} => exec_assignment(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)?,
|
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)?,
|
"alias" => alias(node, shenv)?,
|
||||||
"exit" => sh_flow(node, shenv, ShErrKind::CleanExit)?,
|
"exit" => sh_flow(node, shenv, ShErrKind::CleanExit)?,
|
||||||
"return" => sh_flow(node, shenv, ShErrKind::FuncReturn)?,
|
"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)
|
_ => unimplemented!("Have not yet implemented support for builtin `{}'",command)
|
||||||
}
|
}
|
||||||
log!(TRACE, "done");
|
log!(TRACE, "done");
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
pub fn expand_cmdsub(token: Token, shenv: &mut ShEnv) -> ShResult<Vec<Token>> {
|
pub fn expand_cmdsub(token: Token, shenv: &mut ShEnv) -> ShResult<Vec<Token>> {
|
||||||
let mut new_tokens = vec![];
|
let new_tokens = vec![];
|
||||||
let cmdsub_raw = token.as_raw(shenv);
|
let cmdsub_raw = token.as_raw(shenv);
|
||||||
let body = &cmdsub_raw[2..cmdsub_raw.len() - 1].to_string(); // From '$(this)' to 'this'
|
let body = &cmdsub_raw[2..cmdsub_raw.len() - 1].to_string(); // From '$(this)' to 'this'
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ pub mod tilde;
|
|||||||
pub mod alias;
|
pub mod alias;
|
||||||
pub mod cmdsub;
|
pub mod cmdsub;
|
||||||
|
|
||||||
use alias::expand_aliases;
|
|
||||||
use vars::{expand_dquote, expand_var};
|
use vars::{expand_dquote, expand_var};
|
||||||
use tilde::expand_tilde;
|
use tilde::expand_tilde;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use core::{arch::asm, fmt::{self, Debug, Display, Write}, ops::Deref};
|
use core::fmt::{Debug, Display, Write};
|
||||||
use std::{os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd}, str::FromStr};
|
use std::{os::fd::{AsRawFd, BorrowedFd}, str::FromStr};
|
||||||
|
|
||||||
use nix::libc::getpgrp;
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
@@ -311,7 +310,7 @@ impl CmdRedirs {
|
|||||||
let Redir { src, op, tgt } = redir;
|
let Redir { src, op, tgt } = redir;
|
||||||
let src = borrow_fd(src);
|
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 {
|
let flags = match op {
|
||||||
RedirType::Input => OFlag::O_RDONLY,
|
RedirType::Input => OFlag::O_RDONLY,
|
||||||
RedirType::Output => OFlag::O_WRONLY | OFlag::O_CREAT | OFlag::O_TRUNC,
|
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<()> {
|
pub fn open_fd_tgts(&mut self) -> ShResult<()> {
|
||||||
while let Some(redir) = self.targets_fd.pop() {
|
while let Some(redir) = self.targets_fd.pop() {
|
||||||
let Redir { src, op: _, tgt } = redir;
|
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)
|
borrow_fd(fd)
|
||||||
} else { unreachable!() };
|
} else { unreachable!() };
|
||||||
let src = borrow_fd(src);
|
let src = borrow_fd(src);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use std::{cell::Ref, fmt::{Debug, Display}};
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use core::fmt::Display;
|
use std::{iter::Peekable, str::FromStr};
|
||||||
use std::{cell::Ref, str::FromStr};
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
@@ -86,9 +85,9 @@ pub enum NdRule {
|
|||||||
Command { argv: Vec<Token>, redirs: Vec<Redir> },
|
Command { argv: Vec<Token>, redirs: Vec<Redir> },
|
||||||
Assignment { assignments: Vec<Token>, cmd: Option<Box<Node>> },
|
Assignment { assignments: Vec<Token>, cmd: Option<Box<Node>> },
|
||||||
FuncDef { name: Token, body: Token },
|
FuncDef { name: Token, body: Token },
|
||||||
IfThen { cond_blocks: Vec<(Vec<Node>,Vec<Node>)>, else_block: Option<Vec<Node>> },
|
IfThen { cond_blocks: Vec<(Vec<Node>,Vec<Node>)>, else_block: Option<Vec<Node>>, redirs: Vec<Redir> },
|
||||||
Loop { kind: LoopKind, cond: Vec<Node>, body: Vec<Node> },
|
Loop { kind: LoopKind, cond: Vec<Node>, body: Vec<Node>, redirs: Vec<Redir> },
|
||||||
ForLoop { vars: Vec<Token>, arr: Vec<Token>, body: Vec<Node> },
|
ForLoop { vars: Vec<Token>, arr: Vec<Token>, body: Vec<Node>, redirs: Vec<Redir> },
|
||||||
Subshell { body: Token, argv: Vec<Token>, redirs: Vec<Redir> },
|
Subshell { body: Token, argv: Vec<Token>, redirs: Vec<Redir> },
|
||||||
CmdList { cmds: Vec<(Option<CmdGuard>,Node)> },
|
CmdList { cmds: Vec<(Option<CmdGuard>,Node)> },
|
||||||
Pipeline { cmds: Vec<Node> }
|
Pipeline { cmds: Vec<Node> }
|
||||||
@@ -175,7 +174,7 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_span(toks: &Vec<Token>, shenv: &mut ShEnv) -> ShResult<Rc<RefCell<Span>>> {
|
pub fn get_span(toks: &Vec<Token>, shenv: &mut ShEnv) -> ShResult<Rc<RefCell<Span>>> {
|
||||||
if toks.is_empty() {
|
if toks.is_empty() {
|
||||||
Err(ShErr::simple(ShErrKind::InternalErr, "Get_span was given an empty token list"))
|
Err(ShErr::simple(ShErrKind::InternalErr, "Get_span was given an empty token list"))
|
||||||
} else {
|
} else {
|
||||||
@@ -202,6 +201,38 @@ fn get_lists(mut tokens: &[Token], shenv: &mut ShEnv) -> (usize,Vec<Node>) {
|
|||||||
(tokens_eaten,lists)
|
(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
|
// TODO: Redirs with FD sources appear to be looping endlessly for some reason
|
||||||
|
|
||||||
ndrule_def!(Main, shenv, |tokens: &[Token], shenv: &mut ShEnv| {
|
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 node_toks = vec![];
|
||||||
let mut vars = vec![];
|
let mut vars = vec![];
|
||||||
let mut arr = vec![];
|
let mut arr = vec![];
|
||||||
|
let mut redirs = vec![];
|
||||||
let body: Vec<Node>;
|
let body: Vec<Node>;
|
||||||
|
|
||||||
if let Some(token) = tokens_iter.next() {
|
if let Some(token) = tokens_iter.next() {
|
||||||
@@ -376,20 +408,46 @@ ndrule_def!(ForLoop, shenv, |mut tokens: &[Token], shenv: &mut ShEnv| {
|
|||||||
body = lists;
|
body = lists;
|
||||||
tokens_iter = tokens.iter().peekable();
|
tokens_iter = tokens.iter().peekable();
|
||||||
|
|
||||||
if let Some(token) = tokens_iter.next() {
|
let mut closed = false;
|
||||||
node_toks.push(token.clone());
|
while let Some(token) = tokens_iter.next() {
|
||||||
if token.rule() != TkRule::Done {
|
match token.rule() {
|
||||||
let span = get_span(&node_toks, shenv)?;
|
TkRule::Done => {
|
||||||
return Err(err("Expected `done` after for loop",span,shenv))
|
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)?;
|
let span = get_span(&node_toks, shenv)?;
|
||||||
return Err(err("Expected `done` after for loop",span,shenv))
|
return Err(err("Expected `done` after for loop",span,shenv))
|
||||||
}
|
}
|
||||||
|
|
||||||
let span = get_span(&node_toks, shenv)?;
|
let span = get_span(&node_toks, shenv)?;
|
||||||
let node = Node {
|
let node = Node {
|
||||||
node_rule: NdRule::ForLoop { vars, arr, body },
|
node_rule: NdRule::ForLoop { vars, arr, body, redirs },
|
||||||
tokens: node_toks,
|
tokens: node_toks,
|
||||||
span,
|
span,
|
||||||
flags: NdFlag::empty()
|
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 tokens_iter = tokens.iter().peekable();
|
||||||
let mut node_toks = vec![];
|
let mut node_toks = vec![];
|
||||||
let mut cond_blocks = vec![];
|
let mut cond_blocks = vec![];
|
||||||
|
let mut redirs = vec![];
|
||||||
let mut else_block: Option<Vec<Node>> = None;
|
let mut else_block: Option<Vec<Node>> = None;
|
||||||
|
|
||||||
if let Some(token) = tokens_iter.next() {
|
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());
|
node_toks.push(token.clone());
|
||||||
tokens = &tokens[1..];
|
tokens = &tokens[1..];
|
||||||
}
|
}
|
||||||
TkRule::Sep | TkRule::Whitespace => {
|
TkRule::Whitespace => {
|
||||||
|
node_toks.push(token.clone());
|
||||||
|
tokens = &tokens[1..];
|
||||||
|
}
|
||||||
|
TkRule::Sep => {
|
||||||
node_toks.push(token.clone());
|
node_toks.push(token.clone());
|
||||||
tokens = &tokens[1..];
|
tokens = &tokens[1..];
|
||||||
if closed { break }
|
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)?;
|
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 span = get_span(&node_toks, shenv)?;
|
||||||
let node = Node {
|
let node = Node {
|
||||||
node_rule: NdRule::IfThen { cond_blocks, else_block },
|
node_rule: NdRule::IfThen { cond_blocks, else_block, redirs },
|
||||||
tokens: node_toks,
|
tokens: node_toks,
|
||||||
span,
|
span,
|
||||||
flags: NdFlag::empty()
|
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 tokens_iter = tokens.iter().peekable();
|
||||||
let mut node_toks = vec![];
|
let mut node_toks = vec![];
|
||||||
|
let mut redirs = vec![];
|
||||||
let kind: LoopKind;
|
let kind: LoopKind;
|
||||||
let cond: Vec<Node>;
|
let cond: Vec<Node>;
|
||||||
let body: Vec<Node>;
|
let body: Vec<Node>;
|
||||||
@@ -621,6 +699,18 @@ ndrule_def!(Loop, shenv, |mut tokens: &[Token], shenv: &mut ShEnv| {
|
|||||||
node_toks.push(token.clone());
|
node_toks.push(token.clone());
|
||||||
tokens = &tokens[1..];
|
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)?;
|
let span = get_span(&node_toks,shenv)?;
|
||||||
return Err(err("Unexpected token in loop",span,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 span = get_span(&node_toks, shenv)?;
|
||||||
let node = Node {
|
let node = Node {
|
||||||
node_rule: NdRule::Loop { kind, cond, body },
|
node_rule: NdRule::Loop { kind, cond, body, redirs },
|
||||||
tokens: node_toks,
|
tokens: node_toks,
|
||||||
span,
|
span,
|
||||||
flags: NdFlag::empty()
|
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| {
|
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 node_toks = vec![];
|
||||||
let mut argv = vec![];
|
let mut argv = vec![];
|
||||||
let mut redirs = vec![];
|
let mut redirs = vec![];
|
||||||
@@ -715,34 +805,14 @@ ndrule_def!(Subshell, shenv, |tokens: &[Token], shenv: &mut ShEnv| {
|
|||||||
}
|
}
|
||||||
TkRule::RedirOp => {
|
TkRule::RedirOp => {
|
||||||
node_toks.push(token.clone());
|
node_toks.push(token.clone());
|
||||||
// Get the raw redirection text, e.g. "1>&2" or "2>" or ">>" or something
|
let slice = &tokens_iter.clone().map(|tk| tk.clone()).collect::<Vec<_>>();
|
||||||
let redir_raw = shenv.input_slice(token.span());
|
let (used,redir) = get_redir(token.clone(), slice, shenv)?;
|
||||||
let mut redir_bldr = RedirBldr::from_str(&redir_raw).unwrap();
|
for _ in 0..used {
|
||||||
// If there isn't an FD target, get the next token and use it as the filename
|
if let Some(token) = tokens_iter.next() {
|
||||||
if redir_bldr.tgt().is_none() {
|
node_toks.push(token.clone());
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
redirs.push(redir_bldr.build());
|
redirs.push(redir);
|
||||||
}
|
}
|
||||||
_ => break
|
_ => break
|
||||||
}
|
}
|
||||||
@@ -841,19 +911,19 @@ ndrule_def!(Pipeline, shenv, |mut tokens: &[Token], shenv: &mut ShEnv| {
|
|||||||
|
|
||||||
ndrule_def!(Command, shenv, |tokens: &[Token], shenv: &mut ShEnv| {
|
ndrule_def!(Command, shenv, |tokens: &[Token], shenv: &mut ShEnv| {
|
||||||
log!(TRACE, "Parsing command");
|
log!(TRACE, "Parsing command");
|
||||||
let mut tokens = tokens.iter().peekable();
|
let mut tokens_iter = tokens.iter().peekable();
|
||||||
let mut node_toks = vec![];
|
let mut node_toks = vec![];
|
||||||
let mut argv = vec![];
|
let mut argv = vec![];
|
||||||
let mut redirs = vec![];
|
let mut redirs = vec![];
|
||||||
|
|
||||||
while let Some(token) = tokens.peek() {
|
while let Some(token) = tokens_iter.peek() {
|
||||||
match token.rule() {
|
match token.rule() {
|
||||||
TkRule::AndOp | TkRule::OrOp | TkRule::PipeOp | TkRule::ErrPipeOp => {
|
TkRule::AndOp | TkRule::OrOp | TkRule::PipeOp | TkRule::ErrPipeOp => {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
_ => { /* Keep going */ }
|
_ => { /* Keep going */ }
|
||||||
}
|
}
|
||||||
let token = tokens.next().unwrap();
|
let token = tokens_iter.next().unwrap();
|
||||||
node_toks.push(token.clone());
|
node_toks.push(token.clone());
|
||||||
match token.rule() {
|
match token.rule() {
|
||||||
TkRule::Ident |
|
TkRule::Ident |
|
||||||
@@ -865,34 +935,14 @@ ndrule_def!(Command, shenv, |tokens: &[Token], shenv: &mut ShEnv| {
|
|||||||
argv.push(token.clone());
|
argv.push(token.clone());
|
||||||
}
|
}
|
||||||
TkRule::RedirOp => {
|
TkRule::RedirOp => {
|
||||||
// Get the raw redirection text, e.g. "1>&2" or "2>" or ">>" or something
|
let slice = &tokens_iter.clone().map(|tk| tk.clone()).collect::<Vec<_>>();
|
||||||
let redir_raw = shenv.input_slice(token.span()).to_string();
|
let (used,redir) = get_redir(token.clone(), slice, shenv)?;
|
||||||
let mut redir_bldr = RedirBldr::from_str(&redir_raw).unwrap();
|
for _ in 0..used {
|
||||||
// If there isn't an FD target, get the next token and use it as the filename
|
if let Some(token) = tokens_iter.next() {
|
||||||
if redir_bldr.tgt().is_none() {
|
node_toks.push(token.clone());
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
redirs.push(redir_bldr.build());
|
redirs.push(redir);
|
||||||
}
|
}
|
||||||
TkRule::Sep => break,
|
TkRule::Sep => break,
|
||||||
_ => return Err(
|
_ => return Err(
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ pub use crate::{
|
|||||||
},
|
},
|
||||||
parse::{
|
parse::{
|
||||||
parse::{
|
parse::{
|
||||||
|
SynTree,
|
||||||
Node,
|
Node,
|
||||||
NdRule,
|
NdRule,
|
||||||
Parser,
|
Parser,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use readline::SynHelper;
|
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 readline;
|
||||||
pub mod highlight;
|
pub mod highlight;
|
||||||
@@ -8,7 +8,7 @@ pub mod validate;
|
|||||||
|
|
||||||
fn init_rl<'a>(shenv: &'a mut ShEnv) -> Editor<SynHelper<'a>, DefaultHistory> {
|
fn init_rl<'a>(shenv: &'a mut ShEnv) -> Editor<SynHelper<'a>, DefaultHistory> {
|
||||||
let hist_path = std::env::var("FERN_HIST").unwrap_or_default();
|
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()
|
.max_history_size(1000).unwrap()
|
||||||
.history_ignore_dups(true).unwrap()
|
.history_ignore_dups(true).unwrap()
|
||||||
.completion_prompt_limit(100)
|
.completion_prompt_limit(100)
|
||||||
|
|||||||
@@ -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::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ bitflags! {
|
|||||||
#[derive(Clone,Debug)]
|
#[derive(Clone,Debug)]
|
||||||
pub struct ExecCtx {
|
pub struct ExecCtx {
|
||||||
redirs: Vec<Redir>,
|
redirs: Vec<Redir>,
|
||||||
|
depth: usize,
|
||||||
|
state_stack: Vec<Self>,
|
||||||
|
max_depth: usize,
|
||||||
flags: ExecFlags,
|
flags: ExecFlags,
|
||||||
io_masks: IoMasks,
|
io_masks: IoMasks,
|
||||||
saved_io: Option<SavedIo>
|
saved_io: Option<SavedIo>
|
||||||
@@ -19,11 +22,63 @@ impl ExecCtx {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
redirs: vec![],
|
redirs: vec![],
|
||||||
|
depth: 0,
|
||||||
|
state_stack: vec![],
|
||||||
|
max_depth: 1500,
|
||||||
flags: ExecFlags::empty(),
|
flags: ExecFlags::empty(),
|
||||||
io_masks: IoMasks::new(),
|
io_masks: IoMasks::new(),
|
||||||
saved_io: None
|
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<Redir>,Vec<Redir>) {
|
||||||
|
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 {
|
pub fn masks(&self) -> &IoMasks {
|
||||||
&self.io_masks
|
&self.io_masks
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use std::cell::Ref;
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
use std::{fmt, sync::{Arc, LazyLock, RwLock}};
|
use std::{fmt, sync::{Arc, LazyLock, RwLock}};
|
||||||
|
|
||||||
use nix::unistd::setpgid;
|
use nix::unistd::setpgid;
|
||||||
use shellenv::{disable_reaping, enable_reaping};
|
|
||||||
use sys::SIG_EXIT_OFFSET;
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,32 @@ impl ShEnv {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.input_man.clamp_all();
|
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<Node>) -> ShResult<i32> {
|
||||||
|
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<Node>) -> ShResult<i32> {
|
||||||
|
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) {
|
pub fn new_input(&mut self, input: &str) {
|
||||||
self.input_man.clear();
|
self.input_man.clear();
|
||||||
|
|||||||
Reference in New Issue
Block a user