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<()> {
|
||||
let rule = node.into_rule();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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<()> {
|
||||
let rule = node.into_rule();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use shellenv::jobs::{ChildProc, JobBldr};
|
||||
|
||||
use crate::{parse::parse::{Node, NdRule}, prelude::*};
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn pwd(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||
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 crate::prelude::*;
|
||||
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<()> {
|
||||
let input = input.into();
|
||||
@@ -43,6 +42,7 @@ impl<'a> Executor<'a> {
|
||||
}
|
||||
pub fn walk(&mut self) -> ShResult<()> {
|
||||
self.shenv.ctx_mut().descend()?;
|
||||
self.shenv.inputman_mut().save_state();
|
||||
log!(DEBUG, "Starting walk");
|
||||
while let Some(node) = self.ast.next_node() {
|
||||
if let NdRule::CmdList { cmds } = node.clone().into_rule() {
|
||||
@@ -51,6 +51,8 @@ impl<'a> Executor<'a> {
|
||||
} else { unreachable!() }
|
||||
}
|
||||
self.shenv.ctx_mut().ascend();
|
||||
self.shenv.inputman_mut().load_state();
|
||||
log!(DEBUG, "passed");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -79,8 +81,10 @@ fn exec_list(list: Vec<(Option<CmdGuard>, Node)>, shenv: &mut ShEnv) -> ShResult
|
||||
match *cmd.rule() {
|
||||
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::IfThen {..} => shellcmd::exec_if(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::Assignment {..} => exec_assignment(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 {
|
||||
log!(DEBUG, "{}",arg.as_raw(shenv));
|
||||
log!(DEBUG, processed);
|
||||
match arg.rule() {
|
||||
let mut expanded = expand_token(arg, shenv);
|
||||
processed.append(&mut expanded);
|
||||
}
|
||||
processed
|
||||
}
|
||||
|
||||
pub fn expand_token(token: Token, shenv: &mut ShEnv) -> Vec<Token> {
|
||||
let mut processed = vec![];
|
||||
match token.rule() {
|
||||
TkRule::DQuote => {
|
||||
let dquote_exp = expand_dquote(arg.clone(), shenv);
|
||||
let dquote_exp = expand_dquote(token.clone(), shenv);
|
||||
processed.push(dquote_exp);
|
||||
}
|
||||
TkRule::VarSub => {
|
||||
let mut varsub_exp = expand_var(arg.clone(), shenv);
|
||||
let mut varsub_exp = expand_var(token.clone(), shenv);
|
||||
processed.append(&mut varsub_exp);
|
||||
}
|
||||
TkRule::TildeSub => {
|
||||
let tilde_exp = expand_tilde(arg.clone(), shenv);
|
||||
let tilde_exp = expand_tilde(token.clone(), shenv);
|
||||
processed.push(tilde_exp);
|
||||
}
|
||||
_ => {
|
||||
if arg.rule() != TkRule::Ident {
|
||||
log!(WARN, "found this in expand_argv: {:?}", arg.rule());
|
||||
}
|
||||
processed.push(arg.clone())
|
||||
if token.rule() != TkRule::Ident {
|
||||
log!(WARN, "found this in expand_token: {:?}", token.rule());
|
||||
}
|
||||
processed.push(token.clone())
|
||||
}
|
||||
}
|
||||
processed
|
||||
|
||||
@@ -219,7 +219,7 @@ impl ShErr {
|
||||
}
|
||||
}
|
||||
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 {
|
||||
let mut dist = 0;
|
||||
let mut line_no = 0;
|
||||
@@ -256,8 +256,7 @@ impl Display for ShErr {
|
||||
ShErr::Simple { kind: _, message } => format!("{}{}",self.display_kind(),message),
|
||||
ShErr::Full { kind: _, message, blame } => {
|
||||
let (offset,line_no,line_text) = self.get_line();
|
||||
log!(DEBUG, blame);
|
||||
let dist = blame.len();
|
||||
let dist = blame.end().saturating_sub(blame.start());
|
||||
let padding = " ".repeat(offset);
|
||||
let line_inner = "~".repeat(dist.saturating_sub(2));
|
||||
let err_kind = &self.display_kind().styled(Style::Red | Style::Bold);
|
||||
|
||||
@@ -19,13 +19,14 @@ pub const KEYWORDS: [TkRule;14] = [
|
||||
TkRule::Esac
|
||||
];
|
||||
|
||||
pub const SEPARATORS: [TkRule; 6] = [
|
||||
pub const SEPARATORS: [TkRule; 7] = [
|
||||
TkRule::Sep,
|
||||
TkRule::AndOp,
|
||||
TkRule::OrOp,
|
||||
TkRule::PipeOp,
|
||||
TkRule::ErrPipeOp,
|
||||
TkRule::BgOp,
|
||||
TkRule::CasePat
|
||||
];
|
||||
|
||||
pub trait LexRule {
|
||||
@@ -105,15 +106,16 @@ impl Debug for Token {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug,Clone)]
|
||||
#[derive(PartialEq,Debug,Clone)]
|
||||
pub struct Span {
|
||||
start: usize,
|
||||
end: usize
|
||||
end: usize,
|
||||
pub expanded: bool
|
||||
}
|
||||
|
||||
impl Span {
|
||||
pub fn new(start: usize, end: usize) -> Self {
|
||||
Self { start, end }
|
||||
Self { start, end, expanded: false }
|
||||
}
|
||||
pub fn start(&self) -> usize {
|
||||
self.start
|
||||
@@ -181,6 +183,7 @@ pub enum TkRule {
|
||||
ProcSub,
|
||||
VarSub,
|
||||
TildeSub,
|
||||
ArithSub,
|
||||
Subshell,
|
||||
CmdSub,
|
||||
DQuote,
|
||||
@@ -199,6 +202,7 @@ pub enum TkRule {
|
||||
Done,
|
||||
Case,
|
||||
Esac,
|
||||
CasePat,
|
||||
Assign,
|
||||
Ident,
|
||||
Sep,
|
||||
@@ -213,6 +217,7 @@ impl TkRule {
|
||||
try_match!(VarSub,input);
|
||||
try_match!(ProcSub,input);
|
||||
try_match!(CmdSub,input);
|
||||
try_match!(ArithSub,input);
|
||||
try_match!(AndOp,input);
|
||||
try_match!(OrOp,input);
|
||||
try_match!(PipeOp,input);
|
||||
@@ -225,6 +230,7 @@ impl TkRule {
|
||||
try_match!(BraceGrp,input);
|
||||
try_match!(TildeSub,input);
|
||||
try_match!(Subshell,input);
|
||||
try_match!(CasePat,input);
|
||||
try_match!(Sep,input);
|
||||
try_match!(Assign,input);
|
||||
try_match!(If,input);
|
||||
@@ -269,6 +275,16 @@ tkrule_def!(Whitespace, |input: &str| {
|
||||
let mut len = 0;
|
||||
while let Some(ch) = chars.next() {
|
||||
match ch {
|
||||
'\\' => {
|
||||
len += 1;
|
||||
if let Some(ch) = chars.next() {
|
||||
if matches!(ch, ' ' | '\t' | '\n') {
|
||||
len += 1;
|
||||
} else {
|
||||
return None
|
||||
}
|
||||
}
|
||||
}
|
||||
' ' | '\t' => len += 1,
|
||||
_ => {
|
||||
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| {
|
||||
let mut chars = input.chars();
|
||||
let mut len = 0;
|
||||
@@ -567,8 +648,14 @@ tkrule_def!(Sep, |input: &str| {
|
||||
while let Some(ch) = chars.next() {
|
||||
match ch {
|
||||
'\\' => {
|
||||
chars.next();
|
||||
len += 2;
|
||||
return None
|
||||
}
|
||||
' ' | '\t' => {
|
||||
if len == 0 {
|
||||
return None
|
||||
} else {
|
||||
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::{
|
||||
self,
|
||||
get_bin_path,
|
||||
sh_quit,
|
||||
read_to_string,
|
||||
write_err,
|
||||
@@ -106,6 +107,7 @@ pub use crate::{
|
||||
},
|
||||
error::{
|
||||
ResultExt,
|
||||
Blame,
|
||||
ShErrKind,
|
||||
ShErr,
|
||||
ShResult
|
||||
@@ -118,6 +120,7 @@ pub use crate::{
|
||||
read::read_builtin,
|
||||
alias::alias,
|
||||
control_flow::sh_flow,
|
||||
export::export,
|
||||
jobctl::{
|
||||
continue_job,
|
||||
jobs
|
||||
@@ -126,6 +129,7 @@ pub use crate::{
|
||||
},
|
||||
expand::{
|
||||
expand_argv,
|
||||
expand_token,
|
||||
alias::expand_aliases
|
||||
},
|
||||
shellenv::{
|
||||
@@ -148,14 +152,15 @@ pub use crate::{
|
||||
exec_input,
|
||||
Executor,
|
||||
},
|
||||
parse::{
|
||||
parse::{
|
||||
SynTree,
|
||||
LoopKind,
|
||||
Node,
|
||||
CmdGuard,
|
||||
NdFlag,
|
||||
NdRule,
|
||||
Parser,
|
||||
ParseRule
|
||||
},
|
||||
ParseRule,
|
||||
lex::{
|
||||
Span,
|
||||
Token,
|
||||
|
||||
@@ -7,13 +7,26 @@ use super::readline::SynHelper;
|
||||
pub fn check_delims(line: &str) -> bool {
|
||||
let mut delim_stack = vec![];
|
||||
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 `"`)
|
||||
|
||||
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 {
|
||||
'{' | '(' | '[' 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('(') => {
|
||||
if !in_case {
|
||||
return false
|
||||
}
|
||||
}
|
||||
']' if in_quote.is_none() && delim_stack.pop() != Some('[') => return false,
|
||||
'"' | '\'' => {
|
||||
if in_quote == Some(ch) {
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
pub struct InputMan {
|
||||
input: Option<String>,
|
||||
saved_input: Option<String>,
|
||||
spans: Vec<Rc<RefCell<Span>>>,
|
||||
saved_spans: Vec<Span>
|
||||
}
|
||||
|
||||
impl InputMan {
|
||||
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) {
|
||||
*self = Self::new();
|
||||
@@ -23,6 +24,24 @@ impl InputMan {
|
||||
pub fn get_input_mut(&mut self) -> Option<&mut String> {
|
||||
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>> {
|
||||
if let Some(_input) = &self.input {
|
||||
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()
|
||||
}
|
||||
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 mut new_tokens = Lexer::new(new.to_string(), self).lex();
|
||||
*self.input_man.spans_mut() = saved_spans;
|
||||
@@ -45,9 +50,10 @@ impl ShEnv {
|
||||
let repl_end = repl_span.borrow().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 delta: isize = new.len() as isize - old.len() as isize;
|
||||
log!(DEBUG, input);
|
||||
input.replace_range(range, new);
|
||||
let expanded = input.clone();
|
||||
log!(DEBUG, expanded);
|
||||
@@ -74,6 +80,11 @@ impl ShEnv {
|
||||
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> {
|
||||
let saved = self.ctx().clone();
|
||||
self.ctx = self.ctx().as_cond();
|
||||
@@ -82,6 +93,11 @@ impl ShEnv {
|
||||
self.ctx = saved;
|
||||
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> {
|
||||
let saved = self.ctx().clone();
|
||||
self.ctx = self.ctx().as_body();
|
||||
@@ -96,7 +112,7 @@ impl ShEnv {
|
||||
}
|
||||
pub fn get_input(&self) -> String {
|
||||
let input = self.input_man.get_input().map(|s| s.to_string()).unwrap_or_default();
|
||||
log!(DEBUG, input);
|
||||
log!(TRACE, input);
|
||||
input
|
||||
}
|
||||
pub fn inputman(&self) -> &shellenv::input::InputMan {
|
||||
|
||||
@@ -155,6 +155,9 @@ impl VarTab {
|
||||
pub fn set_var(&mut self, var: &str, val: &str) {
|
||||
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) {
|
||||
self.env.insert(var.to_string(),val.to_string());
|
||||
std::env::set_var(var, val);
|
||||
|
||||
Reference in New Issue
Block a user