Implemented command substitution
This commit is contained in:
@@ -18,13 +18,13 @@ pub struct ExecArgs {
|
||||
}
|
||||
|
||||
impl ExecArgs {
|
||||
pub fn new(argv: Vec<Tk>) -> Self {
|
||||
pub fn new(argv: Vec<Tk>) -> ShResult<Self> {
|
||||
assert!(!argv.is_empty());
|
||||
let argv = prepare_argv(argv);
|
||||
let argv = prepare_argv(argv)?;
|
||||
let cmd = Self::get_cmd(&argv);
|
||||
let argv = Self::get_argv(argv);
|
||||
let envp = Self::get_envp();
|
||||
Self { cmd, argv, envp }
|
||||
Ok(Self { cmd, argv, envp })
|
||||
}
|
||||
pub fn get_cmd(argv: &[(String,Span)]) -> (CString,Span) {
|
||||
(CString::new(argv[0].0.as_str()).unwrap(),argv[0].1.clone())
|
||||
@@ -197,7 +197,7 @@ impl Dispatcher {
|
||||
self.io_stack.append_to_frame(case_stmt.redirs);
|
||||
|
||||
flog!(DEBUG,pattern.span.as_str());
|
||||
let exp_pattern = pattern.clone().expand(pattern.span.clone(), pattern.flags.clone());
|
||||
let exp_pattern = pattern.clone().expand(pattern.span.clone(), pattern.flags.clone())?;
|
||||
let pattern_raw = exp_pattern
|
||||
.get_words()
|
||||
.first()
|
||||
@@ -396,7 +396,7 @@ impl Dispatcher {
|
||||
|
||||
self.io_stack.append_to_frame(cmd.redirs);
|
||||
|
||||
let exec_args = ExecArgs::new(argv);
|
||||
let exec_args = ExecArgs::new(argv)?;
|
||||
let io_frame = self.io_stack.pop_frame();
|
||||
run_fork(
|
||||
io_frame,
|
||||
@@ -453,18 +453,18 @@ impl Dispatcher {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prepare_argv(argv: Vec<Tk>) -> Vec<(String,Span)> {
|
||||
pub fn prepare_argv(argv: Vec<Tk>) -> ShResult<Vec<(String,Span)>> {
|
||||
let mut args = vec![];
|
||||
|
||||
for arg in argv {
|
||||
let flags = arg.flags;
|
||||
let span = arg.span.clone();
|
||||
let expanded = arg.expand(span.clone(), flags);
|
||||
let expanded = arg.expand(span.clone(), flags)?;
|
||||
for exp in expanded.get_words() {
|
||||
args.push((exp,span.clone()))
|
||||
}
|
||||
}
|
||||
args
|
||||
Ok(args)
|
||||
}
|
||||
|
||||
pub fn run_fork<'t,C,P>(
|
||||
|
||||
124
src/parse/lex.rs
124
src/parse/lex.rs
@@ -1,8 +1,8 @@
|
||||
use std::{fmt::Display, ops::{Bound, Deref, Range, RangeBounds}, str::Chars};
|
||||
use std::{collections::VecDeque, fmt::Display, iter::Peekable, ops::{Bound, Deref, Range, RangeBounds}, str::Chars};
|
||||
|
||||
use bitflags::bitflags;
|
||||
|
||||
use crate::{builtin::BUILTINS, libsh::error::{ShErr, ShErrKind, ShResult}, prelude::*};
|
||||
use crate::{builtin::BUILTINS, libsh::{error::{ShErr, ShErrKind, ShResult}, utils::CharDequeUtils}, prelude::*};
|
||||
|
||||
pub const KEYWORDS: [&'static str;14] = [
|
||||
"if",
|
||||
@@ -133,13 +133,14 @@ impl Display for Tk {
|
||||
bitflags! {
|
||||
#[derive(Debug,Clone,Copy,PartialEq,Default)]
|
||||
pub struct TkFlags: u32 {
|
||||
const KEYWORD = 0b0000000000000001;
|
||||
/// This is a keyword that opens a new block statement, like 'if' and 'while'
|
||||
const OPENER = 0b0000000000000010;
|
||||
const IS_CMD = 0b0000000000000100;
|
||||
const IS_OP = 0b0000000000001000;
|
||||
const ASSIGN = 0b0000000000010000;
|
||||
const BUILTIN = 0b0000000000100000;
|
||||
const KEYWORD = 0b0000000000000001;
|
||||
/// This is a keyword that opens a new block statement, like 'if' and 'while'
|
||||
const OPENER = 0b0000000000000010;
|
||||
const IS_CMD = 0b0000000000000100;
|
||||
const IS_SUBSH = 0b0000000000001000;
|
||||
const IS_OP = 0b0000000000010000;
|
||||
const ASSIGN = 0b0000000000100000;
|
||||
const BUILTIN = 0b0000000001000000;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,7 +310,7 @@ impl LexStream {
|
||||
assert!(self.cursor <= self.source.len());
|
||||
let slice = self.slice_from_cursor().unwrap().to_string();
|
||||
let mut pos = self.cursor;
|
||||
let mut chars = slice.chars();
|
||||
let mut chars = slice.chars().peekable();
|
||||
let mut quote_pos = None;
|
||||
|
||||
if let Some(count) = case_pat_lookahead(chars.clone()) {
|
||||
@@ -331,10 +332,89 @@ impl LexStream {
|
||||
}
|
||||
'\\' => {
|
||||
pos += 1;
|
||||
if chars.next().is_some() {
|
||||
pos += 1;
|
||||
if let Some(ch) = chars.next() {
|
||||
pos += ch.len_utf8();
|
||||
}
|
||||
}
|
||||
'$' if chars.peek() == Some(&'(') => {
|
||||
pos += 2;
|
||||
chars.next();
|
||||
let mut paren_stack = vec!['('];
|
||||
let paren_pos = pos;
|
||||
while let Some(ch) = chars.next() {
|
||||
match ch {
|
||||
'\\' => {
|
||||
pos += 1;
|
||||
if let Some(next_ch) = chars.next() {
|
||||
pos += next_ch.len_utf8();
|
||||
}
|
||||
}
|
||||
'(' => {
|
||||
pos += 1;
|
||||
paren_stack.push(ch);
|
||||
}
|
||||
')' => {
|
||||
pos += 1;
|
||||
paren_stack.pop();
|
||||
if paren_stack.is_empty() {
|
||||
break
|
||||
}
|
||||
}
|
||||
_ => pos += ch.len_utf8()
|
||||
}
|
||||
}
|
||||
if !paren_stack.is_empty() {
|
||||
return Err(
|
||||
ShErr::full(
|
||||
ShErrKind::ParseErr,
|
||||
"Unclosed subshell",
|
||||
Span::new(paren_pos..paren_pos + 1, self.source.clone())
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
'(' if self.next_is_cmd() => {
|
||||
let mut paren_stack = vec!['('];
|
||||
let paren_pos = pos;
|
||||
while let Some(ch) = chars.next() {
|
||||
pos += ch.len_utf8();
|
||||
match ch {
|
||||
'\\' => {
|
||||
if let Some(next_ch) = chars.next() {
|
||||
pos += next_ch.len_utf8();
|
||||
}
|
||||
}
|
||||
'(' => {
|
||||
pos += 1;
|
||||
paren_stack.push(ch);
|
||||
}
|
||||
')' => {
|
||||
pos += 1;
|
||||
paren_stack.pop();
|
||||
if paren_stack.is_empty() {
|
||||
break
|
||||
}
|
||||
}
|
||||
_ => continue
|
||||
}
|
||||
}
|
||||
if !paren_stack.is_empty() {
|
||||
return Err(
|
||||
ShErr::full(
|
||||
ShErrKind::ParseErr,
|
||||
"Unclosed subshell",
|
||||
Span::new(paren_pos..paren_pos + 1, self.source.clone())
|
||||
)
|
||||
)
|
||||
}
|
||||
let mut subsh_tk = self.get_token(self.cursor..pos, TkRule::Str);
|
||||
subsh_tk.flags |= TkFlags::IS_CMD;
|
||||
subsh_tk.flags |= TkFlags::IS_SUBSH;
|
||||
self.cursor = pos;
|
||||
self.set_next_is_cmd(true);
|
||||
flog!(DEBUG, subsh_tk);
|
||||
return Ok(subsh_tk)
|
||||
}
|
||||
'{' if pos == self.cursor && self.next_is_cmd() => {
|
||||
pos += 1;
|
||||
let mut tk = self.get_token(self.cursor..pos, TkRule::BraceGrpStart);
|
||||
@@ -384,6 +464,7 @@ impl LexStream {
|
||||
}
|
||||
}
|
||||
let mut new_tk = self.get_token(self.cursor..pos, TkRule::Str);
|
||||
flog!(DEBUG,new_tk);
|
||||
if self.in_quote && !self.flags.contains(LexFlags::LEX_UNFINISHED) {
|
||||
return Err(
|
||||
ShErr::full(
|
||||
@@ -428,6 +509,7 @@ impl LexStream {
|
||||
}
|
||||
}
|
||||
self.cursor = pos;
|
||||
flog!(DEBUG, self.slice_from_cursor());
|
||||
Ok(new_tk)
|
||||
}
|
||||
pub fn get_token(&self, range: Range<usize>, class: TkRule) -> Tk {
|
||||
@@ -595,7 +677,23 @@ pub fn is_keyword(slice: &str) -> bool {
|
||||
(slice.ends_with("()") && !slice.ends_with("\\()"))
|
||||
}
|
||||
|
||||
pub fn case_pat_lookahead(mut chars: Chars) -> Option<usize> {
|
||||
pub fn lookahead(pat: &str, mut chars: Chars) -> Option<usize> {
|
||||
let mut pos = 0;
|
||||
let mut char_deque = VecDeque::new();
|
||||
while let Some(ch) = chars.next() {
|
||||
char_deque.push_back(ch);
|
||||
if char_deque.len() > pat.len() {
|
||||
char_deque.pop_front();
|
||||
}
|
||||
if char_deque.starts_with(pat) {
|
||||
return Some(pos)
|
||||
}
|
||||
pos += 1;
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn case_pat_lookahead(mut chars: Peekable<Chars>) -> Option<usize> {
|
||||
let mut pos = 0;
|
||||
while let Some(ch) = chars.next() {
|
||||
pos += 1;
|
||||
|
||||
@@ -42,6 +42,7 @@ impl ParsedSrc {
|
||||
for token in LexStream::new(self.src.clone(), LexFlags::empty()) {
|
||||
tokens.push(token?);
|
||||
}
|
||||
flog!(DEBUG,tokens);
|
||||
|
||||
let mut nodes = vec![];
|
||||
for result in ParseStream::new(tokens) {
|
||||
|
||||
Reference in New Issue
Block a user