properly implemented $(( )) substitution
This commit is contained in:
170
src/expand.rs
170
src/expand.rs
@@ -23,8 +23,6 @@ pub const SNG_QUOTE: char = '\u{fdd2}';
|
|||||||
pub const TILDE_SUB: char = '\u{fdd3}';
|
pub const TILDE_SUB: char = '\u{fdd3}';
|
||||||
/// Subshell marker
|
/// Subshell marker
|
||||||
pub const SUBSH: char = '\u{fdd4}';
|
pub const SUBSH: char = '\u{fdd4}';
|
||||||
/// Arithmetic substitution marker
|
|
||||||
pub const ARITH_SUB: char = '\u{fdd5}';
|
|
||||||
|
|
||||||
impl Tk {
|
impl Tk {
|
||||||
/// Create a new expanded token
|
/// Create a new expanded token
|
||||||
@@ -59,7 +57,8 @@ impl Expander {
|
|||||||
Self { raw: unescaped }
|
Self { raw: unescaped }
|
||||||
}
|
}
|
||||||
pub fn expand(&mut self) -> ShResult<Vec<String>> {
|
pub fn expand(&mut self) -> ShResult<Vec<String>> {
|
||||||
self.raw = self.expand_raw()?;
|
let mut chars = self.raw.chars().peekable();
|
||||||
|
self.raw = expand_raw(&mut chars)?;
|
||||||
if let Ok(glob_exp) = expand_glob(&self.raw) {
|
if let Ok(glob_exp) = expand_glob(&self.raw) {
|
||||||
if !glob_exp.is_empty() {
|
if !glob_exp.is_empty() {
|
||||||
self.raw = glob_exp;
|
self.raw = glob_exp;
|
||||||
@@ -95,10 +94,10 @@ impl Expander {
|
|||||||
}
|
}
|
||||||
words
|
words
|
||||||
}
|
}
|
||||||
pub fn expand_raw(&self) -> ShResult<String> {
|
}
|
||||||
let mut chars = self.raw.chars().peekable();
|
|
||||||
|
pub fn expand_raw(chars: &mut Peekable<Chars<'_>>) -> ShResult<String> {
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
flog!(INFO, self.raw);
|
|
||||||
|
|
||||||
while let Some(ch) = chars.next() {
|
while let Some(ch) = chars.next() {
|
||||||
match ch {
|
match ch {
|
||||||
@@ -106,40 +105,15 @@ impl Expander {
|
|||||||
let home = env::var("HOME").unwrap_or_default();
|
let home = env::var("HOME").unwrap_or_default();
|
||||||
result.push_str(&home);
|
result.push_str(&home);
|
||||||
}
|
}
|
||||||
ARITH_SUB => {
|
|
||||||
let mut body = String::new();
|
|
||||||
while let Some(arith_ch) = chars.next() {
|
|
||||||
match arith_ch {
|
|
||||||
VAR_SUB => {
|
VAR_SUB => {
|
||||||
let expanded = expand_var(&mut chars)?;
|
flog!(INFO, chars);
|
||||||
if is_a_number(&expanded) {
|
let expanded = expand_var(chars)?;
|
||||||
body.push_str(&expanded);
|
|
||||||
} else {
|
|
||||||
return Err(
|
|
||||||
ShErr::Simple {
|
|
||||||
kind: ShErrKind::ParseErr,
|
|
||||||
msg: format!("Expected a number during substitution, found '{}'",&expanded),
|
|
||||||
notes: vec![],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ARITH_SUB => break,
|
|
||||||
_ => body.push(arith_ch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let expanded = expand_arithmetic(&body)?;
|
|
||||||
result.push_str(&expanded);
|
|
||||||
}
|
|
||||||
VAR_SUB => {
|
|
||||||
let expanded = expand_var(&mut chars)?;
|
|
||||||
result.push_str(&expanded);
|
result.push_str(&expanded);
|
||||||
}
|
}
|
||||||
_ => result.push(ch)
|
_ => result.push(ch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expand_var(chars: &mut Peekable<Chars<'_>>) -> ShResult<String> {
|
pub fn expand_var(chars: &mut Peekable<Chars<'_>>) -> ShResult<String> {
|
||||||
@@ -170,8 +144,11 @@ pub fn expand_var(chars: &mut Peekable<Chars<'_>>) -> ShResult<String> {
|
|||||||
chars.next(); // safe to consume
|
chars.next(); // safe to consume
|
||||||
var_name.push(ch);
|
var_name.push(ch);
|
||||||
}
|
}
|
||||||
ch if is_hard_sep(ch) || ch == ARITH_SUB || ch == DUB_QUOTE || ch == SUBSH || ch == '/' => {
|
ch if is_hard_sep(ch) || !(ch.is_alphanumeric() || ch == '_' || ch == '-') => {
|
||||||
let val = read_vars(|v| v.get_var(&var_name));
|
let val = read_vars(|v| v.get_var(&var_name));
|
||||||
|
flog!(INFO,var_name);
|
||||||
|
flog!(INFO,val);
|
||||||
|
flog!(INFO,ch);
|
||||||
return Ok(val);
|
return Ok(val);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
@@ -182,6 +159,7 @@ pub fn expand_var(chars: &mut Peekable<Chars<'_>>) -> ShResult<String> {
|
|||||||
}
|
}
|
||||||
if !var_name.is_empty() {
|
if !var_name.is_empty() {
|
||||||
let var_val = read_vars(|v| v.get_var(&var_name));
|
let var_val = read_vars(|v| v.get_var(&var_name));
|
||||||
|
flog!(INFO,var_val);
|
||||||
Ok(var_val)
|
Ok(var_val)
|
||||||
} else {
|
} else {
|
||||||
Ok(String::new())
|
Ok(String::new())
|
||||||
@@ -367,7 +345,14 @@ impl FromStr for ArithOp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn expand_arithmetic(raw: &str) -> ShResult<String> {
|
pub fn expand_arithmetic(raw: &str) -> ShResult<String> {
|
||||||
let tokens = ArithTk::tokenize(raw)?;
|
let body = raw
|
||||||
|
.strip_prefix('(')
|
||||||
|
.unwrap()
|
||||||
|
.strip_suffix(')')
|
||||||
|
.unwrap(); // Unwraps are safe here, we already checked for the parens
|
||||||
|
let unescaped = unescape_math(body);
|
||||||
|
let expanded = expand_raw(&mut unescaped.chars().peekable())?;
|
||||||
|
let tokens = ArithTk::tokenize(&expanded)?;
|
||||||
let rpn = ArithTk::to_rpn(tokens)?;
|
let rpn = ArithTk::to_rpn(tokens)?;
|
||||||
let result = ArithTk::eval_rpn(rpn)?;
|
let result = ArithTk::eval_rpn(rpn)?;
|
||||||
Ok(result.to_string())
|
Ok(result.to_string())
|
||||||
@@ -377,6 +362,11 @@ pub fn expand_arithmetic(raw: &str) -> ShResult<String> {
|
|||||||
pub fn expand_cmd_sub(raw: &str) -> ShResult<String> {
|
pub fn expand_cmd_sub(raw: &str) -> ShResult<String> {
|
||||||
flog!(DEBUG, "in expand_cmd_sub");
|
flog!(DEBUG, "in expand_cmd_sub");
|
||||||
flog!(DEBUG, raw);
|
flog!(DEBUG, raw);
|
||||||
|
if raw.starts_with('(') && raw.ends_with(')') {
|
||||||
|
if let Ok(output) = expand_arithmetic(raw) {
|
||||||
|
return Ok(output) // It's actually an arithmetic sub
|
||||||
|
}
|
||||||
|
}
|
||||||
let (rpipe,wpipe) = IoMode::get_pipes();
|
let (rpipe,wpipe) = IoMode::get_pipes();
|
||||||
let cmd_sub_redir = Redir::new(wpipe, RedirType::Output);
|
let cmd_sub_redir = Redir::new(wpipe, RedirType::Output);
|
||||||
let mut cmd_sub_io_frame = IoFrame::from_redir(cmd_sub_redir);
|
let mut cmd_sub_io_frame = IoFrame::from_redir(cmd_sub_redir);
|
||||||
@@ -431,63 +421,6 @@ pub fn unescape_str(raw: &str) -> String {
|
|||||||
result.push(next_ch)
|
result.push(next_ch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'`' => {
|
|
||||||
result.push(ARITH_SUB);
|
|
||||||
let mut closed = false;
|
|
||||||
while let Some(arith_ch) = chars.next() {
|
|
||||||
match arith_ch {
|
|
||||||
'\\' => {
|
|
||||||
result.push(arith_ch);
|
|
||||||
if let Some(next_ch) = chars.next() {
|
|
||||||
result.push(next_ch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
'`' => {
|
|
||||||
closed = true;
|
|
||||||
result.push(ARITH_SUB);
|
|
||||||
break
|
|
||||||
}
|
|
||||||
'$' if chars.peek() == Some(&'(') => {
|
|
||||||
result.push(VAR_SUB);
|
|
||||||
result.push(SUBSH);
|
|
||||||
chars.next();
|
|
||||||
let mut cmdsub_count = 1;
|
|
||||||
while let Some(cmdsub_ch) = chars.next() {
|
|
||||||
flog!(DEBUG, cmdsub_count);
|
|
||||||
flog!(DEBUG, cmdsub_ch);
|
|
||||||
match cmdsub_ch {
|
|
||||||
'\\' => {
|
|
||||||
result.push(cmdsub_ch);
|
|
||||||
if let Some(next_ch) = chars.next() {
|
|
||||||
result.push(next_ch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
'$' if chars.peek() == Some(&'(') => {
|
|
||||||
cmdsub_count += 1;
|
|
||||||
result.push(cmdsub_ch);
|
|
||||||
result.push(chars.next().unwrap());
|
|
||||||
}
|
|
||||||
')' => {
|
|
||||||
cmdsub_count -= 1;
|
|
||||||
flog!(DEBUG, cmdsub_count);
|
|
||||||
if cmdsub_count == 0 {
|
|
||||||
result.push(SUBSH);
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
result.push(cmdsub_ch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => result.push(cmdsub_ch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
'$' => {
|
|
||||||
result.push(VAR_SUB);
|
|
||||||
}
|
|
||||||
_ => result.push(arith_ch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
'(' => {
|
'(' => {
|
||||||
result.push(SUBSH);
|
result.push(SUBSH);
|
||||||
let mut paren_count = 1;
|
let mut paren_count = 1;
|
||||||
@@ -585,6 +518,59 @@ pub fn unescape_str(raw: &str) -> String {
|
|||||||
}
|
}
|
||||||
first_char = false;
|
first_char = false;
|
||||||
}
|
}
|
||||||
|
flog!(DEBUG, result);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unescape_math(raw: &str) -> String {
|
||||||
|
let mut chars = raw.chars().peekable();
|
||||||
|
let mut result = String::new();
|
||||||
|
|
||||||
|
while let Some(ch) = chars.next() {
|
||||||
|
flog!(DEBUG,result);
|
||||||
|
match ch {
|
||||||
|
'\\' => {
|
||||||
|
if let Some(next_ch) = chars.next() {
|
||||||
|
result.push(next_ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'$' => {
|
||||||
|
result.push(VAR_SUB);
|
||||||
|
if chars.peek() == Some(&'(') {
|
||||||
|
result.push(SUBSH);
|
||||||
|
chars.next();
|
||||||
|
let mut paren_count = 1;
|
||||||
|
while let Some(subsh_ch) = chars.next() {
|
||||||
|
match subsh_ch {
|
||||||
|
'\\' => {
|
||||||
|
result.push(subsh_ch);
|
||||||
|
if let Some(next_ch) = chars.next() {
|
||||||
|
result.push(next_ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'$' if chars.peek() != Some(&'(') => result.push(VAR_SUB),
|
||||||
|
'(' => {
|
||||||
|
paren_count += 1;
|
||||||
|
result.push(subsh_ch)
|
||||||
|
}
|
||||||
|
')' => {
|
||||||
|
paren_count -= 1;
|
||||||
|
if paren_count == 0 {
|
||||||
|
result.push(SUBSH);
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
result.push(subsh_ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => result.push(subsh_ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => result.push(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flog!(INFO, result);
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -493,6 +493,7 @@ impl LexStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if paren_count != 0 && !self.flags.contains(LexFlags::LEX_UNFINISHED) {
|
if paren_count != 0 && !self.flags.contains(LexFlags::LEX_UNFINISHED) {
|
||||||
|
self.cursor = pos;
|
||||||
return Err(
|
return Err(
|
||||||
ShErr::full(
|
ShErr::full(
|
||||||
ShErrKind::ParseErr,
|
ShErrKind::ParseErr,
|
||||||
|
|||||||
Reference in New Issue
Block a user