Implemented brace expansion
This commit is contained in:
@@ -68,7 +68,9 @@ pub fn echo(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||
break
|
||||
}
|
||||
}
|
||||
let argv = argv_iter.collect::<Vec<_>>().as_strings(shenv);
|
||||
let mut argv = argv_iter.collect::<Vec<_>>().as_strings(shenv);
|
||||
argv.retain(|arg| arg != "\n");
|
||||
log!(DEBUG,argv);
|
||||
let mut formatted = argv.join(" ");
|
||||
if !echo_flags.contains(EchoFlags::NO_NEWLINE) {
|
||||
formatted.push('\n');
|
||||
|
||||
149
src/expand/brace.rs
Normal file
149
src/expand/brace.rs
Normal file
@@ -0,0 +1,149 @@
|
||||
use crate::{expand::vars::expand_string, prelude::*};
|
||||
|
||||
pub fn expand_brace_token(token: Token, shenv: &mut ShEnv) -> ShResult<Vec<Token>> {
|
||||
let raw = token.as_raw(shenv);
|
||||
let raw_exp = expand_string(&raw, shenv)?;
|
||||
log!(DEBUG, raw_exp);
|
||||
let expanded = expand_brace_string(&raw_exp);
|
||||
log!(DEBUG, expanded);
|
||||
let mut new_tokens = shenv.expand_input(&expanded, token.span());
|
||||
new_tokens.retain(|tk| tk.rule() != TkRule::Whitespace);
|
||||
log!(DEBUG, new_tokens);
|
||||
Ok(new_tokens)
|
||||
}
|
||||
|
||||
pub fn expand_brace_string(raw: &str) -> String {
|
||||
let mut result = VecDeque::new();
|
||||
let mut stack = vec![];
|
||||
stack.push(raw.to_string());
|
||||
|
||||
while let Some(current) = stack.pop() {
|
||||
if let Some((prefix,braces,suffix)) = get_brace_positions(¤t) {
|
||||
let expanded = expand_brace_inner(&braces);
|
||||
for part in expanded {
|
||||
let formatted = format!("{prefix}{part}{suffix}");
|
||||
stack.push(formatted);
|
||||
}
|
||||
} else {
|
||||
result.fpush(current);
|
||||
}
|
||||
}
|
||||
|
||||
result.into_iter().collect::<Vec<_>>().join(" ")
|
||||
}
|
||||
|
||||
pub fn get_brace_positions(slice: &str) -> Option<(String, String, String)> {
|
||||
let mut chars = slice.chars().enumerate();
|
||||
let mut start = None;
|
||||
let mut brc_count = 0;
|
||||
while let Some((i,ch)) = chars.next() {
|
||||
match ch {
|
||||
'{' => {
|
||||
if brc_count == 0 {
|
||||
start = Some(i);
|
||||
}
|
||||
brc_count += 1;
|
||||
}
|
||||
'}' => {
|
||||
brc_count -= 1;
|
||||
if brc_count == 0 {
|
||||
if let Some(start) = start {
|
||||
let prefix = slice[..start].to_string();
|
||||
let braces = slice[start+1..i].to_string();
|
||||
let suffix = slice[i+1..].to_string();
|
||||
return Some((prefix,braces,suffix))
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => continue
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn expand_brace_inner(inner: &str) -> Vec<String> {
|
||||
if inner.split_once("..").is_some() && !inner.contains(['{','}']) {
|
||||
expand_range(inner)
|
||||
} else {
|
||||
split_list(inner)
|
||||
}
|
||||
}
|
||||
|
||||
fn split_list(list: &str) -> Vec<String> {
|
||||
log!(DEBUG, list);
|
||||
let mut chars = list.chars();
|
||||
let mut items = vec![];
|
||||
let mut curr_item = String::new();
|
||||
let mut brc_count = 0;
|
||||
|
||||
while let Some(ch) = chars.next() {
|
||||
match ch {
|
||||
',' if brc_count == 0 => {
|
||||
if !curr_item.is_empty() {
|
||||
items.push(std::mem::take(&mut curr_item));
|
||||
}
|
||||
}
|
||||
'{' => {
|
||||
brc_count += 1;
|
||||
curr_item.push(ch);
|
||||
}
|
||||
'}' => {
|
||||
if brc_count == 0 {
|
||||
return vec![list.to_string()];
|
||||
}
|
||||
brc_count -= 1;
|
||||
curr_item.push(ch);
|
||||
}
|
||||
_ => curr_item.push(ch),
|
||||
}
|
||||
}
|
||||
if !curr_item.is_empty() {
|
||||
items.push(std::mem::take(&mut curr_item))
|
||||
}
|
||||
log!(DEBUG,items);
|
||||
items
|
||||
}
|
||||
|
||||
fn expand_range(range: &str) -> Vec<String> {
|
||||
if let Some((left,right)) = range.split_once("..") {
|
||||
// I know, I know
|
||||
// This is checking to see if the range looks like "a..b" or "A..B"
|
||||
// one character on both sides, both are letters, and both are uppercase OR both are lowercase
|
||||
if (left.len() == 1 && right.len() == 1) &&
|
||||
(left.chars().all(|ch| ch.is_ascii_alphanumeric() && right.chars().all(|ch| ch.is_ascii_alphanumeric()))) &&
|
||||
(
|
||||
(left.chars().all(|ch| ch.is_uppercase()) && right.chars().all(|ch| ch.is_uppercase())) ||
|
||||
(left.chars().all(|ch| ch.is_lowercase()) && right.chars().all(|ch| ch.is_lowercase()))
|
||||
)
|
||||
{
|
||||
expand_range_alpha(left, right)
|
||||
}
|
||||
else if right.chars().all(|ch| ch.is_ascii_digit()) && left.chars().all(|ch| ch.is_ascii_digit())
|
||||
{
|
||||
expand_range_numeric(left, right)
|
||||
}
|
||||
else
|
||||
{
|
||||
vec![range.to_string()]
|
||||
}
|
||||
} else {
|
||||
vec![range.to_string()]
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_range_alpha(left: &str, right: &str) -> Vec<String> {
|
||||
let start = left.chars().next().unwrap() as u8;
|
||||
let end = right.chars().next().unwrap() as u8;
|
||||
|
||||
if start > end {
|
||||
(end..=start).rev().map(|c| (c as char).to_string()).collect()
|
||||
} else {
|
||||
(start..=end).map(|c| (c as char).to_string()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_range_numeric(left: &str, right: &str) -> Vec<String> {
|
||||
let start = left.parse::<i32>().unwrap();
|
||||
let end = right.parse::<i32>().unwrap();
|
||||
(start..=end).map(|i| i.to_string()).collect()
|
||||
}
|
||||
@@ -29,5 +29,7 @@ pub fn expand_cmdsub_string(mut s: &str, shenv: &mut ShEnv) -> ShResult<String>
|
||||
close(w_pipe).ok();
|
||||
}
|
||||
}
|
||||
Ok(read_to_string(r_pipe)?)
|
||||
let result = read_to_string(r_pipe);
|
||||
close(r_pipe)?;
|
||||
Ok(result?)
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@ pub mod alias;
|
||||
pub mod cmdsub;
|
||||
pub mod arithmetic;
|
||||
pub mod prompt;
|
||||
pub mod brace;
|
||||
|
||||
use arithmetic::expand_arith_token;
|
||||
use brace::expand_brace_token;
|
||||
use cmdsub::expand_cmdsub_token;
|
||||
use vars::{expand_string, expand_var};
|
||||
use tilde::expand_tilde_token;
|
||||
@@ -43,6 +45,10 @@ pub fn expand_token(token: Token, shenv: &mut ShEnv) -> ShResult<Vec<Token>> {
|
||||
let arith_exp = expand_arith_token(token.clone(), shenv)?;
|
||||
processed.push(arith_exp);
|
||||
}
|
||||
TkRule::BraceExp => {
|
||||
let mut brace_exp = expand_brace_token(token, shenv)?;
|
||||
processed.append(&mut brace_exp);
|
||||
}
|
||||
TkRule::CmdSub => {
|
||||
let mut cmdsub_exp = expand_cmdsub_token(token.clone(), shenv)?;
|
||||
processed.append(&mut cmdsub_exp);
|
||||
|
||||
@@ -32,8 +32,9 @@ pub fn expand_string(s: &str, shenv: &mut ShEnv) -> ShResult<String> {
|
||||
break
|
||||
}
|
||||
let ch = chars.next().unwrap();
|
||||
log!(DEBUG,var_name);
|
||||
match ch {
|
||||
'{' => {
|
||||
'{' if var_name.is_empty() => {
|
||||
in_brace = true;
|
||||
}
|
||||
'}' if in_brace => {
|
||||
@@ -80,7 +81,7 @@ pub fn expand_string(s: &str, shenv: &mut ShEnv) -> ShResult<String> {
|
||||
expanded = true;
|
||||
break
|
||||
}
|
||||
' ' | '\t' | '\n' | ';' => {
|
||||
' ' | '\t' | '\n' | ';' | ',' | '{' => {
|
||||
let value = shenv.vars().get_var(&var_name);
|
||||
result.push_str(value);
|
||||
result.push(ch);
|
||||
|
||||
@@ -83,6 +83,11 @@ pub fn sh_quit(code: i32) -> ! {
|
||||
if let Some(termios) = crate::get_saved_termios() {
|
||||
termios::tcsetattr(std::io::stdin(), termios::SetArg::TCSANOW, &termios).unwrap();
|
||||
}
|
||||
if code == 0 {
|
||||
write_err("exit\n").ok();
|
||||
} else {
|
||||
write_err(format!("exit {code}\n")).ok();
|
||||
}
|
||||
exit(code);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 = clean_string(&arg.as_raw(shenv));
|
||||
let cleaned = clean_string(&arg.as_raw(shenv)).trim_matches(' ').to_string();
|
||||
argv_processed.push(cleaned);
|
||||
}
|
||||
argv_processed
|
||||
|
||||
@@ -37,10 +37,11 @@ pub const SEPARATORS: [TkRule;7] = [
|
||||
TkRule::CasePat
|
||||
];
|
||||
|
||||
pub const EXPANSIONS: [TkRule;6] = [
|
||||
pub const EXPANSIONS: [TkRule;7] = [
|
||||
TkRule::DQuote,
|
||||
TkRule::VarSub,
|
||||
TkRule::CmdSub,
|
||||
TkRule::BraceExp,
|
||||
TkRule::ProcSub,
|
||||
TkRule::TildeSub,
|
||||
TkRule::ArithSub
|
||||
@@ -223,6 +224,7 @@ pub enum TkRule {
|
||||
BgOp,
|
||||
RedirOp,
|
||||
FuncName,
|
||||
BraceExp,
|
||||
BraceGrp,
|
||||
ProcSub,
|
||||
VarSub,
|
||||
@@ -270,6 +272,7 @@ impl TkRule {
|
||||
try_match!(SQuote,input);
|
||||
try_match!(DQuote,input);
|
||||
try_match!(FuncName,input);
|
||||
try_match!(BraceExp,input);
|
||||
try_match!(BraceGrp,input);
|
||||
try_match!(TildeSub,input);
|
||||
try_match!(Subshell,input);
|
||||
@@ -294,6 +297,50 @@ impl TkRule {
|
||||
}
|
||||
}
|
||||
|
||||
tkrule_def!(BraceExp, |input: &str| {
|
||||
let mut chars = input.chars().peekable();
|
||||
let mut len = 0;
|
||||
let mut brc_count = 0;
|
||||
let mut is_brc_exp = false;
|
||||
|
||||
while let Some(ch) = chars.next() {
|
||||
match ch {
|
||||
'\\' => {
|
||||
len += 1;
|
||||
if let Some(ch) = chars.next() {
|
||||
len += ch.len_utf8();
|
||||
}
|
||||
}
|
||||
'{' => {
|
||||
len += 1;
|
||||
brc_count += 1;
|
||||
}
|
||||
'}' => {
|
||||
if brc_count == 0 {
|
||||
return None
|
||||
}
|
||||
len += 1;
|
||||
brc_count -= 1;
|
||||
if brc_count == 0 {
|
||||
is_brc_exp = true;
|
||||
}
|
||||
}
|
||||
' ' | '\t' | '\n' | ';' => {
|
||||
match is_brc_exp {
|
||||
true => return Some(len),
|
||||
false => return None
|
||||
}
|
||||
}
|
||||
_ => len += ch.len_utf8()
|
||||
}
|
||||
}
|
||||
|
||||
match is_brc_exp {
|
||||
true => return Some(len),
|
||||
false => return None
|
||||
}
|
||||
});
|
||||
|
||||
tkrule_def!(Comment, |input: &str| {
|
||||
let mut chars = input.chars().peekable();
|
||||
let mut len = 0;
|
||||
@@ -932,7 +979,7 @@ tkrule_def!(VarSub, |input: &str| {
|
||||
'{' => {
|
||||
match len {
|
||||
0 => return None,
|
||||
_ => {
|
||||
1 => {
|
||||
while let Some(ch) = chars.next() {
|
||||
match ch {
|
||||
'\\' => {
|
||||
@@ -947,6 +994,7 @@ tkrule_def!(VarSub, |input: &str| {
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => return Some(len)
|
||||
}
|
||||
}
|
||||
'$' => {
|
||||
|
||||
@@ -1185,6 +1185,7 @@ ndrule_def!(Command, shenv, |tokens: &[Token], shenv: &mut ShEnv| {
|
||||
TkRule::ArithSub |
|
||||
TkRule::CmdSub |
|
||||
TkRule::BraceGrp |
|
||||
TkRule::BraceExp |
|
||||
TkRule::VarSub => {
|
||||
argv.push(token.clone());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user