Implemented case statements
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
use crate::{parse::parse::{Node, NdRule}, prelude::*};
|
use crate::prelude::*;
|
||||||
|
|
||||||
pub fn cd(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
pub fn cd(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||||
let rule = node.into_rule();
|
let rule = node.into_rule();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use shellenv::jobs::{ChildProc, JobBldr};
|
use shellenv::jobs::{ChildProc, JobBldr};
|
||||||
|
|
||||||
use crate::{libsh::utils::ArgVec, parse::parse::{Node, NdRule}, prelude::*};
|
use crate::prelude::*;
|
||||||
|
|
||||||
pub fn echo(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
pub fn echo(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||||
let rule = node.into_rule();
|
let rule = node.into_rule();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use shellenv::jobs::{ChildProc, JobBldr};
|
use shellenv::jobs::{ChildProc, JobBldr};
|
||||||
|
|
||||||
use crate::{parse::parse::{Node, NdRule}, prelude::*};
|
use crate::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,27 +0,0 @@
|
|||||||
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, redirs } = rule {
|
|
||||||
shenv.collect_redirs(redirs);
|
|
||||||
if shenv.ctx().flags().contains(ExecFlags::NO_FORK) {
|
|
||||||
shenv.ctx_mut().unset_flag(ExecFlags::NO_FORK);
|
|
||||||
}
|
|
||||||
let mut cond_blocks = cond_blocks.into_iter();
|
|
||||||
|
|
||||||
while let Some(block) = cond_blocks.next() {
|
|
||||||
let cond = block.0;
|
|
||||||
let body = block.1;
|
|
||||||
let ret = shenv.exec_as_cond(cond)?;
|
|
||||||
if ret == 0 {
|
|
||||||
shenv.exec_as_body(body)?;
|
|
||||||
return Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(block) = else_block {
|
|
||||||
shenv.exec_as_body(block)?;
|
|
||||||
}
|
|
||||||
} else { unreachable!() }
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
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(())
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
use std::os::fd::AsRawFd;
|
use std::os::fd::AsRawFd;
|
||||||
|
|
||||||
|
use crate::prelude::*;
|
||||||
use shellenv::jobs::{ChildProc, JobBldr};
|
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 shellcmd;
|
||||||
|
|
||||||
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();
|
||||||
@@ -43,6 +42,7 @@ impl<'a> Executor<'a> {
|
|||||||
}
|
}
|
||||||
pub fn walk(&mut self) -> ShResult<()> {
|
pub fn walk(&mut self) -> ShResult<()> {
|
||||||
self.shenv.ctx_mut().descend()?;
|
self.shenv.ctx_mut().descend()?;
|
||||||
|
self.shenv.inputman_mut().save_state();
|
||||||
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() {
|
||||||
@@ -51,6 +51,8 @@ impl<'a> Executor<'a> {
|
|||||||
} else { unreachable!() }
|
} else { unreachable!() }
|
||||||
}
|
}
|
||||||
self.shenv.ctx_mut().ascend();
|
self.shenv.ctx_mut().ascend();
|
||||||
|
self.shenv.inputman_mut().load_state();
|
||||||
|
log!(DEBUG, "passed");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,8 +81,10 @@ fn exec_list(list: Vec<(Option<CmdGuard>, Node)>, shenv: &mut ShEnv) -> ShResult
|
|||||||
match *cmd.rule() {
|
match *cmd.rule() {
|
||||||
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 {..} => shellcmd::exec_if(cmd, shenv).try_blame(cmd_raw, span)?,
|
||||||
NdRule::Loop {..} => loops::exec_loop(cmd, shenv).try_blame(cmd_raw, span)?,
|
NdRule::Loop {..} => shellcmd::exec_loop(cmd, shenv).try_blame(cmd_raw, span)?,
|
||||||
|
NdRule::ForLoop {..} => shellcmd::exec_for(cmd, shenv).try_blame(cmd_raw, span)?,
|
||||||
|
NdRule::Case {..} => shellcmd::exec_case(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)?,
|
||||||
|
|||||||
149
src/execute/shellcmd.rs
Normal file
149
src/execute/shellcmd.rs
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
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, redirs } = rule {
|
||||||
|
shenv.collect_redirs(redirs);
|
||||||
|
if shenv.ctx().flags().contains(ExecFlags::NO_FORK) {
|
||||||
|
shenv.ctx_mut().unset_flag(ExecFlags::NO_FORK);
|
||||||
|
}
|
||||||
|
let mut cond_blocks = cond_blocks.into_iter();
|
||||||
|
|
||||||
|
while let Some(block) = cond_blocks.next() {
|
||||||
|
let cond = block.0;
|
||||||
|
let body = block.1;
|
||||||
|
let ret = shenv.exec_as_cond(cond)?;
|
||||||
|
if ret == 0 {
|
||||||
|
shenv.exec_as_body(body)?;
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(block) = else_block {
|
||||||
|
shenv.exec_as_body(block)?;
|
||||||
|
}
|
||||||
|
} else { unreachable!() }
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exec_for(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||||
|
let rule = node.into_rule();
|
||||||
|
|
||||||
|
if let NdRule::ForLoop { vars, arr, body, redirs } = rule {
|
||||||
|
shenv.collect_redirs(redirs);
|
||||||
|
let saved_vars = shenv.vars().clone();
|
||||||
|
|
||||||
|
if shenv.ctx().flags().contains(ExecFlags::NO_FORK) {
|
||||||
|
shenv.ctx_mut().unset_flag(ExecFlags::NO_FORK);
|
||||||
|
}
|
||||||
|
log!(DEBUG, vars);
|
||||||
|
log!(DEBUG, arr);
|
||||||
|
|
||||||
|
for chunk in arr.chunks(vars.len()) {
|
||||||
|
log!(DEBUG, "input: {}", shenv.get_input());
|
||||||
|
for (var,value) in vars.iter().zip(chunk.iter()) {
|
||||||
|
let var = var.as_raw(shenv);
|
||||||
|
let val = value.as_raw(shenv);
|
||||||
|
log!(DEBUG,var);
|
||||||
|
log!(DEBUG,val);
|
||||||
|
shenv.vars_mut().set_var(&var, &val);
|
||||||
|
}
|
||||||
|
|
||||||
|
if chunk.len() < vars.len() {
|
||||||
|
for var in &vars[chunk.len()..] { // If 'vars' is longer than the chunk, then unset the orphaned vars
|
||||||
|
let var = var.as_raw(shenv);
|
||||||
|
log!(DEBUG, "unsetting");
|
||||||
|
log!(DEBUG, var);
|
||||||
|
shenv.vars_mut().unset_var(&var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shenv.exec_as_body(body.clone())?;
|
||||||
|
}
|
||||||
|
*shenv.vars_mut() = saved_vars;
|
||||||
|
|
||||||
|
} else { unreachable!() }
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exec_case(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||||
|
let rule = node.into_rule();
|
||||||
|
|
||||||
|
if let NdRule::Case { pat, blocks, redirs } = rule {
|
||||||
|
shenv.collect_redirs(redirs);
|
||||||
|
let mut blocks_iter = blocks.into_iter();
|
||||||
|
let pat_raw = expand_token(pat, shenv)
|
||||||
|
.iter()
|
||||||
|
.map(|tk| tk.as_raw(shenv))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" ");
|
||||||
|
|
||||||
|
while let Some((block_pat, block)) = blocks_iter.next() {
|
||||||
|
let block_pat_raw = block_pat.as_raw(shenv);
|
||||||
|
let block_pat_raw = block_pat_raw.trim_end_matches(')');
|
||||||
|
if block_pat_raw == "*" {
|
||||||
|
let _ret = shenv.exec_as_body(block)?;
|
||||||
|
return Ok(())
|
||||||
|
} else if block_pat_raw.contains('|') {
|
||||||
|
let pats = block_pat_raw.split('|');
|
||||||
|
for pat in pats {
|
||||||
|
if pat_raw.trim() == pat.trim() {
|
||||||
|
let _ret = shenv.exec_as_body(block)?;
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if pat_raw.trim() == block_pat_raw.trim() {
|
||||||
|
let _ret = shenv.exec_as_body(block)?;
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { unreachable!() }
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -13,25 +13,32 @@ pub fn expand_argv(argv: Vec<Token>, shenv: &mut ShEnv) -> Vec<Token> {
|
|||||||
for arg in argv {
|
for arg in argv {
|
||||||
log!(DEBUG, "{}",arg.as_raw(shenv));
|
log!(DEBUG, "{}",arg.as_raw(shenv));
|
||||||
log!(DEBUG, processed);
|
log!(DEBUG, processed);
|
||||||
match arg.rule() {
|
let mut expanded = expand_token(arg, shenv);
|
||||||
TkRule::DQuote => {
|
processed.append(&mut expanded);
|
||||||
let dquote_exp = expand_dquote(arg.clone(), shenv);
|
}
|
||||||
processed.push(dquote_exp);
|
processed
|
||||||
}
|
}
|
||||||
TkRule::VarSub => {
|
|
||||||
let mut varsub_exp = expand_var(arg.clone(), shenv);
|
pub fn expand_token(token: Token, shenv: &mut ShEnv) -> Vec<Token> {
|
||||||
processed.append(&mut varsub_exp);
|
let mut processed = vec![];
|
||||||
}
|
match token.rule() {
|
||||||
TkRule::TildeSub => {
|
TkRule::DQuote => {
|
||||||
let tilde_exp = expand_tilde(arg.clone(), shenv);
|
let dquote_exp = expand_dquote(token.clone(), shenv);
|
||||||
processed.push(tilde_exp);
|
processed.push(dquote_exp);
|
||||||
}
|
}
|
||||||
_ => {
|
TkRule::VarSub => {
|
||||||
if arg.rule() != TkRule::Ident {
|
let mut varsub_exp = expand_var(token.clone(), shenv);
|
||||||
log!(WARN, "found this in expand_argv: {:?}", arg.rule());
|
processed.append(&mut varsub_exp);
|
||||||
}
|
}
|
||||||
processed.push(arg.clone())
|
TkRule::TildeSub => {
|
||||||
|
let tilde_exp = expand_tilde(token.clone(), shenv);
|
||||||
|
processed.push(tilde_exp);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if token.rule() != TkRule::Ident {
|
||||||
|
log!(WARN, "found this in expand_token: {:?}", token.rule());
|
||||||
}
|
}
|
||||||
|
processed.push(token.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
processed
|
processed
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ impl ShErr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_line(&self) -> (usize,usize,String) {
|
pub fn get_line(&self) -> (usize,usize,String) {
|
||||||
if let ShErr::Full { kind, message, blame } = self {
|
if let ShErr::Full { kind: _, message: _, blame } = self {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut dist = 0;
|
let mut dist = 0;
|
||||||
let mut line_no = 0;
|
let mut line_no = 0;
|
||||||
@@ -256,8 +256,7 @@ impl Display for ShErr {
|
|||||||
ShErr::Simple { kind: _, message } => format!("{}{}",self.display_kind(),message),
|
ShErr::Simple { kind: _, message } => format!("{}{}",self.display_kind(),message),
|
||||||
ShErr::Full { kind: _, message, blame } => {
|
ShErr::Full { kind: _, message, blame } => {
|
||||||
let (offset,line_no,line_text) = self.get_line();
|
let (offset,line_no,line_text) = self.get_line();
|
||||||
log!(DEBUG, blame);
|
let dist = blame.end().saturating_sub(blame.start());
|
||||||
let dist = blame.len();
|
|
||||||
let padding = " ".repeat(offset);
|
let padding = " ".repeat(offset);
|
||||||
let line_inner = "~".repeat(dist.saturating_sub(2));
|
let line_inner = "~".repeat(dist.saturating_sub(2));
|
||||||
let err_kind = &self.display_kind().styled(Style::Red | Style::Bold);
|
let err_kind = &self.display_kind().styled(Style::Red | Style::Bold);
|
||||||
|
|||||||
@@ -19,13 +19,14 @@ pub const KEYWORDS: [TkRule;14] = [
|
|||||||
TkRule::Esac
|
TkRule::Esac
|
||||||
];
|
];
|
||||||
|
|
||||||
pub const SEPARATORS: [TkRule; 6] = [
|
pub const SEPARATORS: [TkRule; 7] = [
|
||||||
TkRule::Sep,
|
TkRule::Sep,
|
||||||
TkRule::AndOp,
|
TkRule::AndOp,
|
||||||
TkRule::OrOp,
|
TkRule::OrOp,
|
||||||
TkRule::PipeOp,
|
TkRule::PipeOp,
|
||||||
TkRule::ErrPipeOp,
|
TkRule::ErrPipeOp,
|
||||||
TkRule::BgOp,
|
TkRule::BgOp,
|
||||||
|
TkRule::CasePat
|
||||||
];
|
];
|
||||||
|
|
||||||
pub trait LexRule {
|
pub trait LexRule {
|
||||||
@@ -105,15 +106,16 @@ impl Debug for Token {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,Clone)]
|
#[derive(PartialEq,Debug,Clone)]
|
||||||
pub struct Span {
|
pub struct Span {
|
||||||
start: usize,
|
start: usize,
|
||||||
end: usize
|
end: usize,
|
||||||
|
pub expanded: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Span {
|
impl Span {
|
||||||
pub fn new(start: usize, end: usize) -> Self {
|
pub fn new(start: usize, end: usize) -> Self {
|
||||||
Self { start, end }
|
Self { start, end, expanded: false }
|
||||||
}
|
}
|
||||||
pub fn start(&self) -> usize {
|
pub fn start(&self) -> usize {
|
||||||
self.start
|
self.start
|
||||||
@@ -181,6 +183,7 @@ pub enum TkRule {
|
|||||||
ProcSub,
|
ProcSub,
|
||||||
VarSub,
|
VarSub,
|
||||||
TildeSub,
|
TildeSub,
|
||||||
|
ArithSub,
|
||||||
Subshell,
|
Subshell,
|
||||||
CmdSub,
|
CmdSub,
|
||||||
DQuote,
|
DQuote,
|
||||||
@@ -199,6 +202,7 @@ pub enum TkRule {
|
|||||||
Done,
|
Done,
|
||||||
Case,
|
Case,
|
||||||
Esac,
|
Esac,
|
||||||
|
CasePat,
|
||||||
Assign,
|
Assign,
|
||||||
Ident,
|
Ident,
|
||||||
Sep,
|
Sep,
|
||||||
@@ -213,6 +217,7 @@ impl TkRule {
|
|||||||
try_match!(VarSub,input);
|
try_match!(VarSub,input);
|
||||||
try_match!(ProcSub,input);
|
try_match!(ProcSub,input);
|
||||||
try_match!(CmdSub,input);
|
try_match!(CmdSub,input);
|
||||||
|
try_match!(ArithSub,input);
|
||||||
try_match!(AndOp,input);
|
try_match!(AndOp,input);
|
||||||
try_match!(OrOp,input);
|
try_match!(OrOp,input);
|
||||||
try_match!(PipeOp,input);
|
try_match!(PipeOp,input);
|
||||||
@@ -225,6 +230,7 @@ impl TkRule {
|
|||||||
try_match!(BraceGrp,input);
|
try_match!(BraceGrp,input);
|
||||||
try_match!(TildeSub,input);
|
try_match!(TildeSub,input);
|
||||||
try_match!(Subshell,input);
|
try_match!(Subshell,input);
|
||||||
|
try_match!(CasePat,input);
|
||||||
try_match!(Sep,input);
|
try_match!(Sep,input);
|
||||||
try_match!(Assign,input);
|
try_match!(Assign,input);
|
||||||
try_match!(If,input);
|
try_match!(If,input);
|
||||||
@@ -269,6 +275,16 @@ tkrule_def!(Whitespace, |input: &str| {
|
|||||||
let mut len = 0;
|
let mut len = 0;
|
||||||
while let Some(ch) = chars.next() {
|
while let Some(ch) = chars.next() {
|
||||||
match ch {
|
match ch {
|
||||||
|
'\\' => {
|
||||||
|
len += 1;
|
||||||
|
if let Some(ch) = chars.next() {
|
||||||
|
if matches!(ch, ' ' | '\t' | '\n') {
|
||||||
|
len += 1;
|
||||||
|
} else {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
' ' | '\t' => len += 1,
|
' ' | '\t' => len += 1,
|
||||||
_ => {
|
_ => {
|
||||||
match len {
|
match len {
|
||||||
@@ -284,6 +300,71 @@ tkrule_def!(Whitespace, |input: &str| {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
tkrule_def!(CasePat, |input:&str| {
|
||||||
|
let mut chars = input.chars();
|
||||||
|
let mut len = 0;
|
||||||
|
let mut is_casepat = false;
|
||||||
|
while let Some(ch) = chars.next() {
|
||||||
|
len += 1;
|
||||||
|
match ch {
|
||||||
|
'\\' => {
|
||||||
|
if chars.next().is_some() {
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
')' => {
|
||||||
|
while let Some(ch) = chars.next() {
|
||||||
|
if ch == ')' {
|
||||||
|
len += 1;
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is_casepat = true;
|
||||||
|
break
|
||||||
|
}
|
||||||
|
_ if ch.is_whitespace() => return None,
|
||||||
|
_ => { /* Continue */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if is_casepat { Some(len) } else { None }
|
||||||
|
});
|
||||||
|
|
||||||
|
tkrule_def!(ArithSub, |input: &str| {
|
||||||
|
let mut chars = input.chars();
|
||||||
|
let mut len = 0;
|
||||||
|
let mut is_arith_sub = false;
|
||||||
|
while let Some(ch) = chars.next() {
|
||||||
|
len += 1;
|
||||||
|
match ch {
|
||||||
|
'\\' => {
|
||||||
|
if chars.next().is_some() {
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'`' => {
|
||||||
|
while let Some(ch) = chars.next() {
|
||||||
|
len += 1;
|
||||||
|
match ch {
|
||||||
|
'\\' => {
|
||||||
|
if chars.next().is_some() {
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'`' => {
|
||||||
|
is_arith_sub = true;
|
||||||
|
break
|
||||||
|
}
|
||||||
|
_ => { /* Continue */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => { /* Continue */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if is_arith_sub { Some(len) } else { None }
|
||||||
|
});
|
||||||
|
|
||||||
tkrule_def!(TildeSub, |input: &str| {
|
tkrule_def!(TildeSub, |input: &str| {
|
||||||
let mut chars = input.chars();
|
let mut chars = input.chars();
|
||||||
let mut len = 0;
|
let mut len = 0;
|
||||||
@@ -567,8 +648,14 @@ tkrule_def!(Sep, |input: &str| {
|
|||||||
while let Some(ch) = chars.next() {
|
while let Some(ch) = chars.next() {
|
||||||
match ch {
|
match ch {
|
||||||
'\\' => {
|
'\\' => {
|
||||||
chars.next();
|
return None
|
||||||
len += 2;
|
}
|
||||||
|
' ' | '\t' => {
|
||||||
|
if len == 0 {
|
||||||
|
return None
|
||||||
|
} else {
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
';' | '\n' => len += 1,
|
';' | '\n' => len += 1,
|
||||||
_ => {
|
_ => {
|
||||||
|
|||||||
1179
src/parse/mod.rs
1179
src/parse/mod.rs
File diff suppressed because it is too large
Load Diff
1014
src/parse/parse.rs
1014
src/parse/parse.rs
File diff suppressed because it is too large
Load Diff
@@ -97,6 +97,7 @@ pub use crate::{
|
|||||||
},
|
},
|
||||||
sys::{
|
sys::{
|
||||||
self,
|
self,
|
||||||
|
get_bin_path,
|
||||||
sh_quit,
|
sh_quit,
|
||||||
read_to_string,
|
read_to_string,
|
||||||
write_err,
|
write_err,
|
||||||
@@ -106,6 +107,7 @@ pub use crate::{
|
|||||||
},
|
},
|
||||||
error::{
|
error::{
|
||||||
ResultExt,
|
ResultExt,
|
||||||
|
Blame,
|
||||||
ShErrKind,
|
ShErrKind,
|
||||||
ShErr,
|
ShErr,
|
||||||
ShResult
|
ShResult
|
||||||
@@ -118,6 +120,7 @@ pub use crate::{
|
|||||||
read::read_builtin,
|
read::read_builtin,
|
||||||
alias::alias,
|
alias::alias,
|
||||||
control_flow::sh_flow,
|
control_flow::sh_flow,
|
||||||
|
export::export,
|
||||||
jobctl::{
|
jobctl::{
|
||||||
continue_job,
|
continue_job,
|
||||||
jobs
|
jobs
|
||||||
@@ -126,6 +129,7 @@ pub use crate::{
|
|||||||
},
|
},
|
||||||
expand::{
|
expand::{
|
||||||
expand_argv,
|
expand_argv,
|
||||||
|
expand_token,
|
||||||
alias::expand_aliases
|
alias::expand_aliases
|
||||||
},
|
},
|
||||||
shellenv::{
|
shellenv::{
|
||||||
@@ -149,13 +153,14 @@ pub use crate::{
|
|||||||
Executor,
|
Executor,
|
||||||
},
|
},
|
||||||
parse::{
|
parse::{
|
||||||
parse::{
|
SynTree,
|
||||||
SynTree,
|
LoopKind,
|
||||||
Node,
|
Node,
|
||||||
NdRule,
|
CmdGuard,
|
||||||
Parser,
|
NdFlag,
|
||||||
ParseRule
|
NdRule,
|
||||||
},
|
Parser,
|
||||||
|
ParseRule,
|
||||||
lex::{
|
lex::{
|
||||||
Span,
|
Span,
|
||||||
Token,
|
Token,
|
||||||
|
|||||||
@@ -7,13 +7,26 @@ use super::readline::SynHelper;
|
|||||||
pub fn check_delims(line: &str) -> bool {
|
pub fn check_delims(line: &str) -> bool {
|
||||||
let mut delim_stack = vec![];
|
let mut delim_stack = vec![];
|
||||||
let mut chars = line.chars();
|
let mut chars = line.chars();
|
||||||
|
let mut in_case = false;
|
||||||
|
let mut case_check = String::new();
|
||||||
let mut in_quote = None; // Tracks which quote type is open (`'` or `"`)
|
let mut in_quote = None; // Tracks which quote type is open (`'` or `"`)
|
||||||
|
|
||||||
while let Some(ch) = chars.next() {
|
while let Some(ch) = chars.next() {
|
||||||
|
case_check.push(ch);
|
||||||
|
if case_check.ends_with("case") {
|
||||||
|
in_case = true;
|
||||||
|
}
|
||||||
|
if case_check.ends_with("esac") {
|
||||||
|
in_case = false;
|
||||||
|
}
|
||||||
match ch {
|
match ch {
|
||||||
'{' | '(' | '[' if in_quote.is_none() => delim_stack.push(ch),
|
'{' | '(' | '[' if in_quote.is_none() => delim_stack.push(ch),
|
||||||
'}' if in_quote.is_none() && delim_stack.pop() != Some('{') => return false,
|
'}' if in_quote.is_none() && delim_stack.pop() != Some('{') => return false,
|
||||||
')' if in_quote.is_none() && delim_stack.pop() != Some('(') => return false,
|
')' if in_quote.is_none() && delim_stack.pop() != Some('(') => {
|
||||||
|
if !in_case {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
']' if in_quote.is_none() && delim_stack.pop() != Some('[') => return false,
|
']' if in_quote.is_none() && delim_stack.pop() != Some('[') => return false,
|
||||||
'"' | '\'' => {
|
'"' | '\'' => {
|
||||||
if in_quote == Some(ch) {
|
if in_quote == Some(ch) {
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
#[derive(Clone,Debug)]
|
#[derive(Clone,Debug,PartialEq)]
|
||||||
pub struct InputMan {
|
pub struct InputMan {
|
||||||
input: Option<String>,
|
input: Option<String>,
|
||||||
|
saved_input: Option<String>,
|
||||||
spans: Vec<Rc<RefCell<Span>>>,
|
spans: Vec<Rc<RefCell<Span>>>,
|
||||||
|
saved_spans: Vec<Span>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputMan {
|
impl InputMan {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self { input: None, spans: vec![] }
|
Self { input: None, saved_input: None, spans: vec![], saved_spans: vec![] }
|
||||||
}
|
}
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
*self = Self::new();
|
*self = Self::new();
|
||||||
@@ -23,6 +24,24 @@ impl InputMan {
|
|||||||
pub fn get_input_mut(&mut self) -> Option<&mut String> {
|
pub fn get_input_mut(&mut self) -> Option<&mut String> {
|
||||||
self.input.as_mut()
|
self.input.as_mut()
|
||||||
}
|
}
|
||||||
|
pub fn save_state(&mut self) {
|
||||||
|
self.saved_input = self.input.clone();
|
||||||
|
self.saved_spans.clear();
|
||||||
|
for span in &self.spans {
|
||||||
|
self.saved_spans.push(span.borrow().clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn load_state(&mut self) {
|
||||||
|
if self.saved_input.is_some() {
|
||||||
|
self.input = self.saved_input.take();
|
||||||
|
|
||||||
|
for (span, saved_span) in self.spans.iter_mut().zip(self.saved_spans.iter()) {
|
||||||
|
*span.borrow_mut() = saved_span.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.saved_spans.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn new_span(&mut self, start: usize, end: usize) -> Rc<RefCell<Span>> {
|
pub fn new_span(&mut self, start: usize, end: usize) -> Rc<RefCell<Span>> {
|
||||||
if let Some(_input) = &self.input {
|
if let Some(_input) = &self.input {
|
||||||
let span = Rc::new(RefCell::new(Span::new(start, end)));
|
let span = Rc::new(RefCell::new(Span::new(start, end)));
|
||||||
|
|||||||
@@ -32,6 +32,11 @@ impl ShEnv {
|
|||||||
&self.input_man.get_slice(span).unwrap_or_default()
|
&self.input_man.get_slice(span).unwrap_or_default()
|
||||||
}
|
}
|
||||||
pub fn expand_input(&mut self, new: &str, repl_span: Rc<RefCell<Span>>) -> Vec<Token> {
|
pub fn expand_input(&mut self, new: &str, repl_span: Rc<RefCell<Span>>) -> Vec<Token> {
|
||||||
|
log!(DEBUG,repl_span);
|
||||||
|
if repl_span.borrow().expanded {
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
repl_span.borrow_mut().expanded = true;
|
||||||
let saved_spans = self.input_man.spans_mut().clone();
|
let saved_spans = self.input_man.spans_mut().clone();
|
||||||
let mut new_tokens = Lexer::new(new.to_string(), self).lex();
|
let mut new_tokens = Lexer::new(new.to_string(), self).lex();
|
||||||
*self.input_man.spans_mut() = saved_spans;
|
*self.input_man.spans_mut() = saved_spans;
|
||||||
@@ -45,9 +50,10 @@ impl ShEnv {
|
|||||||
let repl_end = repl_span.borrow().end();
|
let repl_end = repl_span.borrow().end();
|
||||||
let range = repl_start..repl_end;
|
let range = repl_start..repl_end;
|
||||||
|
|
||||||
if let Some(ref mut input) = self.input_man.get_input_mut() {
|
if let Some(input) = self.input_man.get_input_mut() {
|
||||||
let old = &input[range.clone()];
|
let old = &input[range.clone()];
|
||||||
let delta: isize = new.len() as isize - old.len() as isize;
|
let delta: isize = new.len() as isize - old.len() as isize;
|
||||||
|
log!(DEBUG, input);
|
||||||
input.replace_range(range, new);
|
input.replace_range(range, new);
|
||||||
let expanded = input.clone();
|
let expanded = input.clone();
|
||||||
log!(DEBUG, expanded);
|
log!(DEBUG, expanded);
|
||||||
@@ -74,6 +80,11 @@ impl ShEnv {
|
|||||||
new_tokens
|
new_tokens
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Executes a group of command lists, and only uses redirections that operate on input
|
||||||
|
/// For instance:
|
||||||
|
/// `if cat; then echo foo; fi < file.txt > otherfile.txt`
|
||||||
|
/// `cat` will be executed as a condition, meaning the input from file.txt will be the only
|
||||||
|
/// redirection used.
|
||||||
pub fn exec_as_cond(&mut self, nodes: Vec<Node>) -> ShResult<i32> {
|
pub fn exec_as_cond(&mut self, nodes: Vec<Node>) -> ShResult<i32> {
|
||||||
let saved = self.ctx().clone();
|
let saved = self.ctx().clone();
|
||||||
self.ctx = self.ctx().as_cond();
|
self.ctx = self.ctx().as_cond();
|
||||||
@@ -82,6 +93,11 @@ impl ShEnv {
|
|||||||
self.ctx = saved;
|
self.ctx = saved;
|
||||||
Ok(self.get_code())
|
Ok(self.get_code())
|
||||||
}
|
}
|
||||||
|
/// Executes a group of command lists, and only uses redirections that operate on output
|
||||||
|
/// For instance:
|
||||||
|
/// `if cat; then echo foo; fi < file.txt > otherfile.txt`
|
||||||
|
/// `echo foo` will be executed as a body, meaning the output to otherfile.txt will be the only
|
||||||
|
/// redirection used.
|
||||||
pub fn exec_as_body(&mut self, nodes: Vec<Node>) -> ShResult<i32> {
|
pub fn exec_as_body(&mut self, nodes: Vec<Node>) -> ShResult<i32> {
|
||||||
let saved = self.ctx().clone();
|
let saved = self.ctx().clone();
|
||||||
self.ctx = self.ctx().as_body();
|
self.ctx = self.ctx().as_body();
|
||||||
@@ -96,7 +112,7 @@ impl ShEnv {
|
|||||||
}
|
}
|
||||||
pub fn get_input(&self) -> String {
|
pub fn get_input(&self) -> String {
|
||||||
let input = self.input_man.get_input().map(|s| s.to_string()).unwrap_or_default();
|
let input = self.input_man.get_input().map(|s| s.to_string()).unwrap_or_default();
|
||||||
log!(DEBUG, input);
|
log!(TRACE, input);
|
||||||
input
|
input
|
||||||
}
|
}
|
||||||
pub fn inputman(&self) -> &shellenv::input::InputMan {
|
pub fn inputman(&self) -> &shellenv::input::InputMan {
|
||||||
|
|||||||
@@ -155,6 +155,9 @@ impl VarTab {
|
|||||||
pub fn set_var(&mut self, var: &str, val: &str) {
|
pub fn set_var(&mut self, var: &str, val: &str) {
|
||||||
self.vars.insert(var.to_string(), val.to_string());
|
self.vars.insert(var.to_string(), val.to_string());
|
||||||
}
|
}
|
||||||
|
pub fn unset_var(&mut self, var: &str) {
|
||||||
|
self.vars.remove(var);
|
||||||
|
}
|
||||||
pub fn export(&mut self, var: &str, val: &str) {
|
pub fn export(&mut self, var: &str, val: &str) {
|
||||||
self.env.insert(var.to_string(),val.to_string());
|
self.env.insert(var.to_string(),val.to_string());
|
||||||
std::env::set_var(var, val);
|
std::env::set_var(var, val);
|
||||||
|
|||||||
Reference in New Issue
Block a user