about to implement readline myself
This commit is contained in:
@@ -39,7 +39,7 @@ impl ExecArgs {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exec_input(input: String) -> ShResult<()> {
|
||||
pub fn exec_input(input: String, io_stack: Option<IoStack>) -> ShResult<()> {
|
||||
write_meta(|m| m.start_timer());
|
||||
let log_tab = LOGIC_TABLE.read().unwrap();
|
||||
let input = expand_aliases(input, HashSet::new(), &log_tab);
|
||||
@@ -53,6 +53,9 @@ pub fn exec_input(input: String) -> ShResult<()> {
|
||||
}
|
||||
|
||||
let mut dispatcher = Dispatcher::new(parser.extract_nodes());
|
||||
if let Some(mut stack) = io_stack {
|
||||
dispatcher.io_stack.extend(stack.drain(..));
|
||||
}
|
||||
dispatcher.begin_dispatch()
|
||||
}
|
||||
|
||||
@@ -176,7 +179,7 @@ impl Dispatcher {
|
||||
let subsh_body = subsh.0.to_string();
|
||||
let snapshot = get_snapshots();
|
||||
|
||||
if let Err(e) = exec_input(subsh_body) {
|
||||
if let Err(e) = exec_input(subsh_body, None) {
|
||||
restore_snapshot(snapshot);
|
||||
return Err(e)
|
||||
}
|
||||
|
||||
170
src/parse/lex.rs
170
src/parse/lex.rs
@@ -2,7 +2,7 @@ use std::{collections::VecDeque, fmt::Display, iter::Peekable, ops::{Bound, Dere
|
||||
|
||||
use bitflags::bitflags;
|
||||
|
||||
use crate::{builtin::BUILTINS, libsh::{error::{ShErr, ShErrKind, ShResult}, utils::CharDequeUtils}, parse::parse_err_full, prelude::*};
|
||||
use crate::{builtin::BUILTINS, libsh::{error::{ShErr, ShErrKind, ShResult}, utils::CharDequeUtils}, prelude::*};
|
||||
|
||||
pub const KEYWORDS: [&str;16] = [
|
||||
"if",
|
||||
@@ -136,15 +136,16 @@ 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_SUBSH = 0b0000000000001000;
|
||||
const IS_CMDSUB = 0b0000000000010000;
|
||||
const IS_OP = 0b0000000000100000;
|
||||
const ASSIGN = 0b0000000001000000;
|
||||
const BUILTIN = 0b0000000010000000;
|
||||
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_CMDSUB = 0b0000000000010000;
|
||||
const IS_OP = 0b0000000000100000;
|
||||
const ASSIGN = 0b0000000001000000;
|
||||
const BUILTIN = 0b0000000010000000;
|
||||
const IS_PROCSUB = 0b0000000100000000;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,6 +243,9 @@ impl LexStream {
|
||||
while let Some(ch) = chars.next() {
|
||||
match ch {
|
||||
'>' => {
|
||||
if chars.peek() == Some(&'(') {
|
||||
return None // It's a process sub
|
||||
}
|
||||
pos += 1;
|
||||
if let Some('>') = chars.peek() {
|
||||
chars.next();
|
||||
@@ -277,6 +281,9 @@ impl LexStream {
|
||||
}
|
||||
}
|
||||
'<' => {
|
||||
if chars.peek() == Some(&'(') {
|
||||
return None // It's a process sub
|
||||
}
|
||||
pos += 1;
|
||||
|
||||
for _ in 0..2 {
|
||||
@@ -327,7 +334,6 @@ impl LexStream {
|
||||
}
|
||||
|
||||
while let Some(ch) = chars.next() {
|
||||
flog!(DEBUG, "we are in the loop");
|
||||
match ch {
|
||||
_ if self.flags.contains(LexFlags::RAW) => {
|
||||
if ch.is_whitespace() {
|
||||
@@ -342,61 +348,6 @@ impl LexStream {
|
||||
pos += ch.len_utf8();
|
||||
}
|
||||
}
|
||||
'`' => {
|
||||
let arith_pos = pos;
|
||||
pos += 1;
|
||||
let mut closed = false;
|
||||
while let Some(arith_ch) = chars.next() {
|
||||
match arith_ch {
|
||||
'\\' => {
|
||||
pos += 1;
|
||||
if let Some(ch) = chars.next() {
|
||||
pos += ch.len_utf8();
|
||||
}
|
||||
}
|
||||
'`' => {
|
||||
pos += 1;
|
||||
closed = true;
|
||||
break
|
||||
}
|
||||
'$' if chars.peek() == Some(&'(') => {
|
||||
pos += 2;
|
||||
chars.next();
|
||||
let mut cmdsub_count = 1;
|
||||
while let Some(cmdsub_ch) = chars.next() {
|
||||
match cmdsub_ch {
|
||||
'$' if chars.peek() == Some(&'(') => {
|
||||
pos += 2;
|
||||
chars.next();
|
||||
cmdsub_count += 1;
|
||||
}
|
||||
')' => {
|
||||
pos += 1;
|
||||
cmdsub_count -= 1;
|
||||
if cmdsub_count == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
_ => pos += cmdsub_ch.len_utf8()
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => pos += arith_ch.len_utf8()
|
||||
}
|
||||
}
|
||||
flog!(DEBUG, "we have left the loop");
|
||||
flog!(DEBUG, closed);
|
||||
if !closed && !self.flags.contains(LexFlags::LEX_UNFINISHED) {
|
||||
self.cursor = pos;
|
||||
return Err(
|
||||
ShErr::full(
|
||||
ShErrKind::ParseErr,
|
||||
"Unclosed arithmetic substitution",
|
||||
Span::new(arith_pos..arith_pos + 1, self.source.clone())
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
'$' if chars.peek() == Some(&'{') => {
|
||||
pos += 2;
|
||||
chars.next();
|
||||
@@ -424,6 +375,90 @@ impl LexStream {
|
||||
}
|
||||
}
|
||||
}
|
||||
'<' if chars.peek() == Some(&'(') => {
|
||||
pos += 2;
|
||||
chars.next();
|
||||
let mut paren_count = 1;
|
||||
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_count += 1;
|
||||
}
|
||||
')' => {
|
||||
pos += 1;
|
||||
paren_count -= 1;
|
||||
if paren_count <= 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
_ => pos += ch.len_utf8()
|
||||
}
|
||||
}
|
||||
if !paren_count == 0 && !self.flags.contains(LexFlags::LEX_UNFINISHED) {
|
||||
self.cursor = pos;
|
||||
return Err(
|
||||
ShErr::full(
|
||||
ShErrKind::ParseErr,
|
||||
"Unclosed subshell",
|
||||
Span::new(paren_pos..paren_pos + 1, self.source.clone())
|
||||
)
|
||||
)
|
||||
}
|
||||
let mut proc_sub_tk = self.get_token(self.cursor..pos, TkRule::Str);
|
||||
proc_sub_tk.flags |= TkFlags::IS_PROCSUB;
|
||||
self.cursor = pos;
|
||||
return Ok(proc_sub_tk)
|
||||
}
|
||||
'>' if chars.peek() == Some(&'(') => {
|
||||
pos += 2;
|
||||
chars.next();
|
||||
let mut paren_count = 1;
|
||||
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_count += 1;
|
||||
}
|
||||
')' => {
|
||||
pos += 1;
|
||||
paren_count -= 1;
|
||||
if paren_count <= 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
_ => pos += ch.len_utf8()
|
||||
}
|
||||
}
|
||||
if !paren_count == 0 && !self.flags.contains(LexFlags::LEX_UNFINISHED) {
|
||||
self.cursor = pos;
|
||||
return Err(
|
||||
ShErr::full(
|
||||
ShErrKind::ParseErr,
|
||||
"Unclosed subshell",
|
||||
Span::new(paren_pos..paren_pos + 1, self.source.clone())
|
||||
)
|
||||
)
|
||||
}
|
||||
let mut proc_sub_tk = self.get_token(self.cursor..pos, TkRule::Str);
|
||||
proc_sub_tk.flags |= TkFlags::IS_PROCSUB;
|
||||
self.cursor = pos;
|
||||
return Ok(proc_sub_tk)
|
||||
}
|
||||
'$' if chars.peek() == Some(&'(') => {
|
||||
pos += 2;
|
||||
chars.next();
|
||||
@@ -507,7 +542,6 @@ impl LexStream {
|
||||
subsh_tk.flags |= TkFlags::IS_SUBSH;
|
||||
self.cursor = pos;
|
||||
self.set_next_is_cmd(true);
|
||||
flog!(DEBUG, "returning subsh tk");
|
||||
return Ok(subsh_tk)
|
||||
}
|
||||
'{' if pos == self.cursor && self.next_is_cmd() => {
|
||||
@@ -672,8 +706,6 @@ impl LexStream {
|
||||
impl Iterator for LexStream {
|
||||
type Item = ShResult<Tk>;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
flog!(DEBUG,self.cursor);
|
||||
flog!(DEBUG,self.source.len());
|
||||
assert!(self.cursor <= self.source.len());
|
||||
// We are at the end of the input
|
||||
if self.cursor == self.source.len() {
|
||||
|
||||
@@ -529,7 +529,6 @@ impl ParseStream {
|
||||
/// Ordered from specialized to general, with more generally matchable stuff appearing at the bottom
|
||||
/// The check_pipelines parameter is used to prevent left-recursion issues in self.parse_pipeln()
|
||||
fn parse_block(&mut self, check_pipelines: bool) -> ShResult<Option<Node>> {
|
||||
flog!(DEBUG, self.tokens);
|
||||
try_match!(self.parse_func_def()?);
|
||||
try_match!(self.parse_brc_grp(false /* from_func_def */)?);
|
||||
try_match!(self.parse_case()?);
|
||||
@@ -618,15 +617,12 @@ impl ParseStream {
|
||||
fn parse_test(&mut self) -> ShResult<Option<Node>> {
|
||||
let mut node_tks: Vec<Tk> = vec![];
|
||||
let mut cases: Vec<TestCase> = vec![];
|
||||
flog!(INFO, self.check_keyword("[["));
|
||||
if !self.check_keyword("[[") || !self.next_tk_is_some() {
|
||||
return Ok(None)
|
||||
}
|
||||
node_tks.push(self.next_tk().unwrap());
|
||||
let mut case_builder = TestCaseBuilder::new();
|
||||
while let Some(tk) = self.next_tk() {
|
||||
flog!(DEBUG, case_builder);
|
||||
flog!(DEBUG, tk.as_str());
|
||||
node_tks.push(tk.clone());
|
||||
if tk.as_str() == "]]" {
|
||||
if case_builder.can_build() {
|
||||
@@ -642,24 +638,19 @@ impl ParseStream {
|
||||
}
|
||||
}
|
||||
if case_builder.is_empty() {
|
||||
flog!(DEBUG, "case builder is empty");
|
||||
match tk.as_str() {
|
||||
_ if TEST_UNARY_OPS.contains(&tk.as_str()) => case_builder = case_builder.with_operator(tk.clone()),
|
||||
_ => case_builder = case_builder.with_lhs(tk.clone())
|
||||
}
|
||||
continue
|
||||
} else if case_builder.operator.is_some() && case_builder.rhs.is_none() {
|
||||
flog!(DEBUG, "op is some, rhs is none");
|
||||
case_builder = case_builder.with_rhs(tk.clone());
|
||||
continue
|
||||
} else if case_builder.lhs.is_some() && case_builder.operator.is_none() {
|
||||
flog!(DEBUG, "lhs is some, op is none");
|
||||
// we got lhs, then rhs → treat it as operator maybe?
|
||||
case_builder = case_builder.with_operator(tk.clone());
|
||||
continue
|
||||
} else if let TkRule::And | TkRule::Or = tk.class {
|
||||
flog!(DEBUG, "found conjunction");
|
||||
flog!(DEBUG, tk.class);
|
||||
if case_builder.can_build() {
|
||||
if case_builder.conjunct.is_some() {
|
||||
return Err(
|
||||
@@ -674,7 +665,6 @@ impl ParseStream {
|
||||
case_builder = case_builder.with_conjunction(op);
|
||||
let case = case_builder.build_and_take();
|
||||
cases.push(case);
|
||||
flog!(DEBUG, case_builder);
|
||||
continue
|
||||
} else {
|
||||
return Err(
|
||||
@@ -694,7 +684,6 @@ impl ParseStream {
|
||||
redirs: vec![],
|
||||
tokens: node_tks
|
||||
};
|
||||
flog!(DEBUG, node);
|
||||
Ok(Some(node))
|
||||
}
|
||||
fn parse_brc_grp(&mut self, from_func_def: bool) -> ShResult<Option<Node>> {
|
||||
@@ -1114,7 +1103,6 @@ impl ParseStream {
|
||||
node_tks.push(loop_tk);
|
||||
self.catch_separator(&mut node_tks);
|
||||
|
||||
flog!(DEBUG, node_tks);
|
||||
let Some(cond) = self.parse_block(true)? else {
|
||||
self.panic_mode(&mut node_tks);
|
||||
return Err(parse_err_full(
|
||||
@@ -1225,7 +1213,6 @@ impl ParseStream {
|
||||
return Ok(None)
|
||||
}
|
||||
}
|
||||
flog!(DEBUG, argv);
|
||||
|
||||
if argv.is_empty() && assignments.is_empty() {
|
||||
return Ok(None)
|
||||
|
||||
Reference in New Issue
Block a user