Improved logic surrounding expansion of assignments
This commit is contained in:
@@ -8,7 +8,7 @@ pub fn alias(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||
while let Some(arg) = argv_iter.next() {
|
||||
let arg_raw = shenv.input_slice(arg.span()).to_string();
|
||||
if let Some((alias,body)) = arg_raw.split_once('=') {
|
||||
let clean_body = trim_quotes(&body);
|
||||
let clean_body = clean_string(&body);
|
||||
shenv.logic_mut().set_alias(alias, &clean_body);
|
||||
} else {
|
||||
return Err(ShErr::full(ShErrKind::SyntaxErr, "Expected an assignment in alias args", shenv.get_input(), arg.span().clone()))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::os::fd::AsRawFd;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{expand::vars::expand_string, prelude::*};
|
||||
use shellenv::jobs::{ChildProc, JobBldr};
|
||||
|
||||
pub mod shellcmd;
|
||||
@@ -274,22 +274,30 @@ fn exec_assignment(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||
log!(TRACE, "Command: {:?}", cmd);
|
||||
let mut assigns = assignments.into_iter();
|
||||
if let Some(cmd) = cmd {
|
||||
while let Some(assign) = assigns.next() {
|
||||
let assign_raw = assign.as_raw(shenv);
|
||||
if let Some((var,val)) = assign_raw.split_once('=') {
|
||||
shenv.vars_mut().export(var, val);
|
||||
while let Some((var,val)) = assigns.next() {
|
||||
let var_raw = var.as_raw(shenv);
|
||||
let val_raw = val.as_raw(shenv);
|
||||
if check_expansion(&val_raw).is_some() {
|
||||
let exp = expand_token(val.clone(), shenv)?;
|
||||
let val_exp = exp.into_iter().next().unwrap_or(val);
|
||||
let val_exp_raw = val_exp.as_raw(shenv);
|
||||
shenv.vars_mut().export(&var_raw, &val_exp_raw);
|
||||
} else {
|
||||
shenv.vars_mut().export(&var_raw, &val_raw);
|
||||
}
|
||||
}
|
||||
if cmd.flags().contains(NdFlag::BUILTIN) {
|
||||
exec_builtin(*cmd, shenv)?;
|
||||
} else {
|
||||
exec_cmd(*cmd, shenv)?;
|
||||
}
|
||||
dispatch_command(*cmd, shenv)?;
|
||||
} else {
|
||||
while let Some(assign) = assigns.next() {
|
||||
let assign_raw = assign.as_raw(shenv);
|
||||
if let Some((var,val)) = assign_raw.split_once('=') {
|
||||
shenv.vars_mut().set_var(var, val);
|
||||
while let Some((var,val)) = assigns.next() {
|
||||
let var_raw = var.as_raw(shenv);
|
||||
let val_raw = val.as_raw(shenv);
|
||||
if check_expansion(&val_raw).is_some() {
|
||||
let exp = expand_token(val.clone(), shenv)?;
|
||||
let val_exp = exp.into_iter().next().unwrap_or(val);
|
||||
let val_exp_raw = val_exp.as_raw(shenv);
|
||||
shenv.vars_mut().export(&var_raw, &val_exp_raw);
|
||||
} else {
|
||||
shenv.vars_mut().export(&var_raw, &val_raw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
use super::vars::expand_dquote;
|
||||
use super::vars::expand_string;
|
||||
|
||||
#[derive(Clone,PartialEq,Debug)]
|
||||
pub enum ExprToken {
|
||||
@@ -154,12 +154,15 @@ pub fn eval_rpn(tokens: Vec<ExprToken>) -> ShResult<f64> {
|
||||
}
|
||||
|
||||
pub fn expand_arithmetic(token: Token, shenv: &mut ShEnv) -> ShResult<Token> {
|
||||
// I mean hey it works (I think)
|
||||
let dummy_token = Token::new(TkRule::DQuote, token.span());
|
||||
let expanded = expand_dquote(dummy_token, shenv);
|
||||
log!(INFO, "{}", token.as_raw(shenv));
|
||||
// I mean hey it works
|
||||
let token_raw = token.as_raw(shenv);
|
||||
log!(INFO, token_raw);
|
||||
let expanded = expand_string(token_raw, shenv);
|
||||
token.span().borrow_mut().expanded = false;
|
||||
let expanded_raw = expanded.as_raw(shenv);
|
||||
let arith_raw = expanded_raw.trim_matches('`');
|
||||
|
||||
log!(INFO, expanded);
|
||||
let arith_raw = expanded.trim_matches('`');
|
||||
|
||||
let expr_tokens = shunting_yard(tokenize_expr(arith_raw)?)?;
|
||||
log!(DEBUG,expr_tokens);
|
||||
|
||||
@@ -5,7 +5,7 @@ pub mod cmdsub;
|
||||
pub mod arithmetic;
|
||||
|
||||
use arithmetic::expand_arithmetic;
|
||||
use vars::{expand_dquote, expand_var};
|
||||
use vars::{expand_string, expand_var};
|
||||
use tilde::expand_tilde;
|
||||
|
||||
use crate::prelude::*;
|
||||
@@ -25,8 +25,9 @@ pub fn expand_token(token: Token, shenv: &mut ShEnv) -> ShResult<Vec<Token>> {
|
||||
let mut processed = vec![];
|
||||
match token.rule() {
|
||||
TkRule::DQuote => {
|
||||
let dquote_exp = expand_dquote(token.clone(), shenv);
|
||||
processed.push(dquote_exp);
|
||||
let dquote_exp = expand_string(token.as_raw(shenv), shenv);
|
||||
let mut expanded = shenv.expand_input(&dquote_exp, token.span());
|
||||
processed.append(&mut expanded);
|
||||
}
|
||||
TkRule::VarSub => {
|
||||
let mut varsub_exp = expand_var(token.clone(), shenv);
|
||||
|
||||
@@ -8,11 +8,10 @@ pub fn expand_var(var_sub: Token, shenv: &mut ShEnv) -> Vec<Token> {
|
||||
shenv.expand_input(&value, var_sub.span())
|
||||
}
|
||||
|
||||
pub fn expand_dquote(dquote: Token, shenv: &mut ShEnv) -> Token {
|
||||
let dquote_raw = dquote.as_raw(shenv);
|
||||
pub fn expand_string(s: String, shenv: &mut ShEnv) -> String {
|
||||
let mut result = String::new();
|
||||
let mut var_name = String::new();
|
||||
let mut chars = dquote_raw.chars().peekable();
|
||||
let mut chars = s.chars().peekable();
|
||||
let mut in_brace = false;
|
||||
|
||||
while let Some(ch) = chars.next() {
|
||||
@@ -23,6 +22,7 @@ pub fn expand_dquote(dquote: Token, shenv: &mut ShEnv) -> Token {
|
||||
}
|
||||
}
|
||||
'$' => {
|
||||
let mut expanded = false;
|
||||
while let Some(ch) = chars.peek() {
|
||||
if *ch == '"' {
|
||||
break
|
||||
@@ -33,31 +33,42 @@ pub fn expand_dquote(dquote: Token, shenv: &mut ShEnv) -> Token {
|
||||
in_brace = true;
|
||||
}
|
||||
'}' if in_brace => {
|
||||
let value = shenv.vars().get_var(&var_name);
|
||||
result.push_str(value);
|
||||
expanded = true;
|
||||
break
|
||||
}
|
||||
_ if ch.is_ascii_digit() && var_name.is_empty() && !in_brace => {
|
||||
var_name.push(ch);
|
||||
let value = shenv.vars().get_var(&var_name);
|
||||
result.push_str(value);
|
||||
expanded = true;
|
||||
break
|
||||
}
|
||||
'@' | '#' | '*' | '-' | '?' | '!' | '$' if var_name.is_empty() => {
|
||||
var_name.push(ch);
|
||||
let value = shenv.vars().get_var(&var_name);
|
||||
result.push_str(value);
|
||||
expanded = true;
|
||||
break
|
||||
}
|
||||
' ' | '\t' => {
|
||||
let value = shenv.vars().get_var(&var_name);
|
||||
result.push_str(value);
|
||||
result.push(ch);
|
||||
expanded = true;
|
||||
break
|
||||
}
|
||||
_ => var_name.push(ch)
|
||||
}
|
||||
}
|
||||
log!(TRACE, var_name);
|
||||
let value = shenv.vars().get_var(&var_name);
|
||||
log!(TRACE, value);
|
||||
result.push_str(value);
|
||||
if !expanded {
|
||||
let value = shenv.vars().get_var(&var_name);
|
||||
result.push_str(value);
|
||||
}
|
||||
}
|
||||
_ => result.push(ch)
|
||||
}
|
||||
}
|
||||
|
||||
let token = shenv.expand_input(&result, dquote.span()).pop().unwrap_or(dquote);
|
||||
token
|
||||
result
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use core::fmt::{Debug, Display, Write};
|
||||
use std::{os::fd::{AsRawFd, BorrowedFd}, str::FromStr};
|
||||
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{parse::lex::EXPANSIONS, prelude::*};
|
||||
|
||||
use super::term::StyleSet;
|
||||
|
||||
@@ -46,7 +46,7 @@ impl ArgVec for Vec<Token> {
|
||||
let mut argv_iter = self.into_iter();
|
||||
let mut argv_processed = vec![];
|
||||
while let Some(arg) = argv_iter.next() {
|
||||
let cleaned = trim_quotes(&arg.as_raw(shenv));
|
||||
let cleaned = clean_string(&arg.as_raw(shenv));
|
||||
argv_processed.push(cleaned);
|
||||
}
|
||||
argv_processed
|
||||
@@ -342,12 +342,24 @@ impl CmdRedirs {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trim_quotes(s: impl ToString) -> String {
|
||||
pub fn check_expansion(s: &str) -> Option<TkRule> {
|
||||
let rule = Lexer::get_rule(s);
|
||||
if EXPANSIONS.contains(&rule) {
|
||||
Some(rule)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clean_string(s: impl ToString) -> String {
|
||||
let s = s.to_string();
|
||||
if s.starts_with('"') && s.ends_with('"') {
|
||||
s.trim_matches('"').to_string()
|
||||
} else if s.starts_with('\'') && s.ends_with('\'') {
|
||||
s.trim_matches('\'').to_string()
|
||||
if (s.starts_with('"') && s.ends_with('"')) ||
|
||||
(s.starts_with('\'') && s.ends_with('\'')) ||
|
||||
(s.starts_with('`') && s.ends_with('`'))
|
||||
{
|
||||
s[1..s.len() - 1].to_string()
|
||||
} else if s.starts_with("$(") && s.ends_with(')') {
|
||||
s[2..s.len() - 1].to_string()
|
||||
} else {
|
||||
s
|
||||
}
|
||||
|
||||
@@ -37,6 +37,15 @@ pub const SEPARATORS: [TkRule;7] = [
|
||||
TkRule::CasePat
|
||||
];
|
||||
|
||||
pub const EXPANSIONS: [TkRule;6] = [
|
||||
TkRule::DQuote,
|
||||
TkRule::VarSub,
|
||||
TkRule::CmdSub,
|
||||
TkRule::ProcSub,
|
||||
TkRule::TildeSub,
|
||||
TkRule::ArithSub
|
||||
];
|
||||
|
||||
pub trait LexRule {
|
||||
fn try_match(input: &str) -> Option<usize>;
|
||||
}
|
||||
@@ -74,6 +83,10 @@ impl<'a> Lexer<'a> {
|
||||
self.consumed += len;
|
||||
input = &input[len..];
|
||||
self.tokens.push(token);
|
||||
|
||||
if input.is_empty() {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !input.is_empty() {
|
||||
log!(WARN, "unconsumed input: {}", input)
|
||||
@@ -81,6 +94,13 @@ impl<'a> Lexer<'a> {
|
||||
self.tokens
|
||||
}
|
||||
}
|
||||
pub fn get_rule(s: &str) -> TkRule {
|
||||
if let Some((rule,_)) = TkRule::try_match(s) {
|
||||
rule
|
||||
} else {
|
||||
TkRule::Ident
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -146,8 +166,14 @@ impl Span {
|
||||
}
|
||||
}
|
||||
pub fn shift(&mut self, delta: isize) {
|
||||
self.start = self.start.saturating_add_signed(delta);
|
||||
self.end = self.end.saturating_add_signed(delta);
|
||||
self.shift_start(delta);
|
||||
self.shift_end(delta);
|
||||
}
|
||||
pub fn shift_start(&mut self, delta: isize) {
|
||||
self.start = self.start.saturating_add_signed(delta)
|
||||
}
|
||||
pub fn shift_end(&mut self, delta: isize) {
|
||||
self.end = self.end.saturating_add_signed(delta)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,7 +241,6 @@ pub enum TkRule {
|
||||
Case,
|
||||
Esac,
|
||||
CasePat,
|
||||
Assign,
|
||||
Ident,
|
||||
Sep,
|
||||
}
|
||||
@@ -244,7 +269,6 @@ impl TkRule {
|
||||
try_match!(Subshell,input);
|
||||
try_match!(CasePat,input);
|
||||
try_match!(Sep,input);
|
||||
try_match!(Assign,input);
|
||||
try_match!(If,input);
|
||||
try_match!(Then,input);
|
||||
try_match!(Elif,input);
|
||||
@@ -901,37 +925,6 @@ tkrule_def!(FuncName, |input: &str| {
|
||||
None
|
||||
});
|
||||
|
||||
tkrule_def!(Assign, |input: &str| {
|
||||
let mut chars = input.chars();
|
||||
let mut len = 0;
|
||||
let mut found_equals = false;
|
||||
|
||||
while let Some(ch) = chars.next() {
|
||||
match ch {
|
||||
'\\' => {
|
||||
len += 2;
|
||||
chars.next();
|
||||
}
|
||||
'=' if len == 0 => return None,
|
||||
'=' => {
|
||||
len += 1;
|
||||
found_equals = true;
|
||||
}
|
||||
' ' | '\t' | ';' | '\n' => {
|
||||
match len {
|
||||
_ if found_equals && len > 1 => return Some(len),
|
||||
_ => return None
|
||||
}
|
||||
}
|
||||
_ => len += 1
|
||||
}
|
||||
}
|
||||
match len {
|
||||
_ if found_equals && len > 1 => return Some(len),
|
||||
_ => return None
|
||||
}
|
||||
});
|
||||
|
||||
tkrule_def!(BraceGrp, |input: &str| {
|
||||
// A group of commands inside of braces
|
||||
// Currently just holds a raw string to be re-parsed later
|
||||
|
||||
@@ -85,7 +85,7 @@ pub enum LoopKind {
|
||||
pub enum NdRule {
|
||||
Main { cmd_lists: Vec<Node> },
|
||||
Command { argv: Vec<Token>, redirs: Vec<Redir> },
|
||||
Assignment { assignments: Vec<Token>, cmd: Option<Box<Node>> },
|
||||
Assignment { assignments: Vec<(Token,Token)>, cmd: Option<Box<Node>> },
|
||||
FuncDef { name: Token, body: Token },
|
||||
Case { pat: Token, blocks: Vec<(Token,Vec<Node>)>, redirs: Vec<Redir> },
|
||||
IfThen { cond_blocks: Vec<(Vec<Node>,Vec<Node>)>, else_block: Option<Vec<Node>>, redirs: Vec<Redir> },
|
||||
@@ -1003,7 +1003,6 @@ ndrule_def!(Subshell, shenv, |tokens: &[Token], shenv: &mut ShEnv| {
|
||||
TkRule::Ident |
|
||||
TkRule::SQuote |
|
||||
TkRule::DQuote |
|
||||
TkRule::Assign |
|
||||
TkRule::TildeSub |
|
||||
TkRule::VarSub => {
|
||||
node_toks.push(token.clone());
|
||||
@@ -1139,7 +1138,6 @@ ndrule_def!(Command, shenv, |tokens: &[Token], shenv: &mut ShEnv| {
|
||||
TkRule::Ident |
|
||||
TkRule::SQuote |
|
||||
TkRule::DQuote |
|
||||
TkRule::Assign |
|
||||
TkRule::TildeSub |
|
||||
TkRule::ArithSub |
|
||||
TkRule::VarSub => {
|
||||
@@ -1188,10 +1186,38 @@ ndrule_def!(Assignment, shenv, |tokens: &[Token], shenv: &mut ShEnv| {
|
||||
let mut tokens = tokens.into_iter().peekable();
|
||||
let mut node_toks = vec![];
|
||||
let mut assignments = vec![];
|
||||
while tokens.peek().is_some_and(|tk| tk.rule() == TkRule::Assign) {
|
||||
let token = tokens.next().unwrap();
|
||||
node_toks.push(token.clone());
|
||||
assignments.push(token.clone());
|
||||
while let Some(token) = tokens.peek() {
|
||||
if matches!(token.rule(), TkRule::Ident | TkRule::ArithSub | TkRule::CmdSub | TkRule::DQuote) {
|
||||
let raw = token.as_raw(shenv);
|
||||
// We are going to deconstruct this Ident into two separate tokens
|
||||
// This makes expanding it easier later
|
||||
if let Some((var,val)) = raw.split_once('=') {
|
||||
const LEN_DELTA: usize = 1; // The distance covered by the '=' that we just split at
|
||||
let token = tokens.next().unwrap();
|
||||
let var_span = shenv.inputman_mut().new_span(
|
||||
token.span().borrow().start(),
|
||||
var.len()
|
||||
);
|
||||
let val_span = shenv.inputman_mut().new_span(
|
||||
var.len() + LEN_DELTA,
|
||||
token.span().borrow().end()
|
||||
);
|
||||
let var_rule = TkRule::Ident;
|
||||
let val_rule = if let Some(rule) = check_expansion(&val) {
|
||||
rule
|
||||
} else {
|
||||
TkRule::Ident
|
||||
};
|
||||
let var_token = Token::new(var_rule,var_span);
|
||||
let val_token = Token::new(val_rule,val_span);
|
||||
node_toks.push(token.clone());
|
||||
assignments.push((var_token.clone(),val_token.clone()));
|
||||
} else {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if assignments.is_empty() {
|
||||
return Ok(None)
|
||||
|
||||
@@ -90,7 +90,8 @@ pub use crate::{
|
||||
RedirTarget,
|
||||
CmdRedirs,
|
||||
borrow_fd,
|
||||
trim_quotes
|
||||
check_expansion,
|
||||
clean_string
|
||||
},
|
||||
collections::{
|
||||
VecDequeAliases
|
||||
|
||||
@@ -96,13 +96,6 @@ impl<'a> Highlighter for SynHelper<'a> {
|
||||
let styled = raw.styled(Style::Magenta);
|
||||
result.push_str(&styled);
|
||||
}
|
||||
TkRule::Assign => {
|
||||
let (var,val) = raw.split_once('=').unwrap();
|
||||
let var_styled = var.styled(Style::Magenta);
|
||||
let val_styled = val.styled(Style::Cyan);
|
||||
let rebuilt = vec![var_styled,val_styled].join("=");
|
||||
result.push_str(&rebuilt);
|
||||
}
|
||||
TkRule::Ident => {
|
||||
if in_array || in_case {
|
||||
if &raw == "in" {
|
||||
@@ -113,6 +106,11 @@ impl<'a> Highlighter for SynHelper<'a> {
|
||||
let styled = &raw.styled(Style::Magenta);
|
||||
result.push_str(&styled);
|
||||
}
|
||||
} else if let Some((var,val)) = raw.split_once('=') {
|
||||
let var_styled = var.styled(Style::Magenta);
|
||||
let val_styled = val.styled(Style::Cyan);
|
||||
let rebuilt = vec![var_styled,val_styled].join("=");
|
||||
result.push_str(&rebuilt);
|
||||
} else if raw.starts_with(['"','\'']) {
|
||||
let styled = &raw.styled(Style::BrightYellow);
|
||||
result.push_str(&styled);
|
||||
|
||||
@@ -51,6 +51,11 @@ impl InputMan {
|
||||
Rc::new(RefCell::new(Span::new(0,0)))
|
||||
}
|
||||
}
|
||||
pub fn remove_span(&mut self, span: Rc<RefCell<Span>>) {
|
||||
if let Some(idx) = self.spans.iter().position(|iter_span| *iter_span == span) {
|
||||
self.spans.remove(idx);
|
||||
}
|
||||
}
|
||||
pub fn spans_mut(&mut self) -> &mut Vec<Rc<RefCell<Span>>> {
|
||||
&mut self.spans
|
||||
}
|
||||
|
||||
@@ -51,9 +51,12 @@ impl ShEnv {
|
||||
if repl_span.borrow().expanded {
|
||||
return vec![];
|
||||
}
|
||||
log!(INFO, repl_span);
|
||||
log!(INFO, new);
|
||||
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();
|
||||
log!(INFO, new_tokens);
|
||||
*self.input_man.spans_mut() = saved_spans;
|
||||
|
||||
let offset = repl_span.borrow().start();
|
||||
@@ -68,8 +71,9 @@ impl ShEnv {
|
||||
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!(INFO, input);
|
||||
log!(INFO, range);
|
||||
input.replace_range(range, new);
|
||||
let expanded = input.clone();
|
||||
|
||||
for span in self.input_man.spans_mut() {
|
||||
let mut span_mut = span.borrow_mut();
|
||||
|
||||
Reference in New Issue
Block a user