implemented functions
This commit is contained in:
@@ -13,6 +13,7 @@ pub mod tests;
|
|||||||
|
|
||||||
use libsh::error::ShResult;
|
use libsh::error::ShResult;
|
||||||
use parse::{execute::Dispatcher, lex::{LexFlags, LexStream}, ParseStream};
|
use parse::{execute::Dispatcher, lex::{LexFlags, LexStream}, ParseStream};
|
||||||
|
use procio::IoFrame;
|
||||||
use signal::sig_setup;
|
use signal::sig_setup;
|
||||||
use termios::{LocalFlags, Termios};
|
use termios::{LocalFlags, Termios};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
@@ -46,7 +47,7 @@ fn set_termios() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exec_input(input: &str) -> ShResult<()> {
|
pub fn exec_input(input: &str, io_frame: Option<IoFrame>) -> ShResult<()> {
|
||||||
let parse_start = Instant::now();
|
let parse_start = Instant::now();
|
||||||
let mut tokens = vec![];
|
let mut tokens = vec![];
|
||||||
for token in LexStream::new(&input, LexFlags::empty()) {
|
for token in LexStream::new(&input, LexFlags::empty()) {
|
||||||
@@ -61,6 +62,9 @@ pub fn exec_input(input: &str) -> ShResult<()> {
|
|||||||
|
|
||||||
let exec_start = Instant::now();
|
let exec_start = Instant::now();
|
||||||
let mut dispatcher = Dispatcher::new(nodes);
|
let mut dispatcher = Dispatcher::new(nodes);
|
||||||
|
if let Some(frame) = io_frame {
|
||||||
|
dispatcher.io_stack.push(frame)
|
||||||
|
}
|
||||||
dispatcher.begin_dispatch()?;
|
dispatcher.begin_dispatch()?;
|
||||||
flog!(INFO, "cmd duration: {:?}", exec_start.elapsed());
|
flog!(INFO, "cmd duration: {:?}", exec_start.elapsed());
|
||||||
|
|
||||||
@@ -77,7 +81,7 @@ fn main() {
|
|||||||
loop {
|
loop {
|
||||||
let input = prompt::read_line().unwrap();
|
let input = prompt::read_line().unwrap();
|
||||||
|
|
||||||
if let Err(e) = exec_input(&input) {
|
if let Err(e) = exec_input(&input,None) {
|
||||||
eprintln!("{e}");
|
eprintln!("{e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -613,6 +613,7 @@ pub fn enable_reaping() -> ShResult<()> {
|
|||||||
pub fn wait_fg(job: Job) -> ShResult<()> {
|
pub fn wait_fg(job: Job) -> ShResult<()> {
|
||||||
flog!(TRACE, "Waiting on foreground job");
|
flog!(TRACE, "Waiting on foreground job");
|
||||||
let mut code = 0;
|
let mut code = 0;
|
||||||
|
flog!(DEBUG,job.pgid());
|
||||||
attach_tty(job.pgid())?;
|
attach_tty(job.pgid())?;
|
||||||
disable_reaping()?;
|
disable_reaping()?;
|
||||||
let statuses = write_jobs(|j| j.new_fg(job))?;
|
let statuses = write_jobs(|j| j.new_fg(job))?;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
|
||||||
use crate::{builtin::{cd::cd, echo::echo, export::export, jobctl::{continue_job, jobs, JobBehavior}, pwd::pwd, shift::shift, source::source}, jobs::{dispatch_job, ChildProc, Job, JobBldr, JobStack}, libsh::{error::{ShErrKind, ShResult}, utils::RedirVecUtils}, prelude::*, procio::{IoFrame, IoMode, IoStack}, state::{self, write_vars}};
|
use crate::{builtin::{cd::cd, echo::echo, export::export, jobctl::{continue_job, jobs, JobBehavior}, pwd::pwd, shift::shift, source::source}, exec_input, jobs::{dispatch_job, ChildProc, Job, JobBldr, JobStack}, libsh::{error::{ErrSpan, ShErr, ShErrKind, ShResult}, utils::RedirVecUtils}, prelude::*, procio::{IoFrame, IoMode, IoStack}, state::{self, read_logic, read_vars, write_logic, write_vars}};
|
||||||
|
|
||||||
use super::{lex::{Span, Tk, TkFlags}, AssignKind, CondNode, ConjunctNode, ConjunctOp, LoopKind, NdFlags, NdRule, Node, Redir, RedirType};
|
use super::{lex::{LexFlags, LexStream, Span, Tk, TkFlags}, AssignKind, CondNode, ConjunctNode, ConjunctOp, LoopKind, NdFlags, NdRule, Node, ParseStream, Redir, RedirType};
|
||||||
|
|
||||||
pub enum AssignBehavior {
|
pub enum AssignBehavior {
|
||||||
Export,
|
Export,
|
||||||
@@ -61,6 +61,8 @@ impl<'t> Dispatcher<'t> {
|
|||||||
NdRule::Pipeline {..} => self.exec_pipeline(node)?,
|
NdRule::Pipeline {..} => self.exec_pipeline(node)?,
|
||||||
NdRule::IfNode {..} => self.exec_if(node)?,
|
NdRule::IfNode {..} => self.exec_if(node)?,
|
||||||
NdRule::LoopNode {..} => self.exec_loop(node)?,
|
NdRule::LoopNode {..} => self.exec_loop(node)?,
|
||||||
|
NdRule::BraceGrp {..} => self.exec_brc_grp(node)?,
|
||||||
|
NdRule::FuncDef {..} => self.exec_func_def(node)?,
|
||||||
NdRule::Command {..} => self.dispatch_cmd(node)?,
|
NdRule::Command {..} => self.dispatch_cmd(node)?,
|
||||||
_ => unreachable!()
|
_ => unreachable!()
|
||||||
}
|
}
|
||||||
@@ -68,10 +70,12 @@ impl<'t> Dispatcher<'t> {
|
|||||||
}
|
}
|
||||||
pub fn dispatch_cmd(&mut self, node: Node<'t>) -> ShResult<()> {
|
pub fn dispatch_cmd(&mut self, node: Node<'t>) -> ShResult<()> {
|
||||||
let Some(cmd) = node.get_command() else {
|
let Some(cmd) = node.get_command() else {
|
||||||
return self.exec_cmd(node)
|
return self.exec_cmd(node) // Argv is empty, probably an assignment
|
||||||
};
|
};
|
||||||
if cmd.flags.contains(TkFlags::BUILTIN) {
|
if cmd.flags.contains(TkFlags::BUILTIN) {
|
||||||
self.exec_builtin(node)
|
self.exec_builtin(node)
|
||||||
|
} else if is_func(node.get_command().cloned()) {
|
||||||
|
self.exec_func(node)
|
||||||
} else {
|
} else {
|
||||||
self.exec_cmd(node)
|
self.exec_cmd(node)
|
||||||
}
|
}
|
||||||
@@ -95,6 +99,66 @@ impl<'t> Dispatcher<'t> {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
pub fn exec_func_def(&mut self, func_def: Node<'t>) -> ShResult<()> {
|
||||||
|
let NdRule::FuncDef { name, body } = func_def.class else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
let body_span = body.get_span();
|
||||||
|
let body = body_span.as_str();
|
||||||
|
let name = name.span.as_str().strip_suffix("()").unwrap();
|
||||||
|
write_logic(|l| l.insert_func(name, body));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn exec_func(&mut self, func: Node<'t>) -> ShResult<()> {
|
||||||
|
let blame: ErrSpan = func.get_span().into();
|
||||||
|
// TODO: Find a way to store functions as pre-parsed nodes so we don't have to re-parse them
|
||||||
|
let NdRule::Command { assignments, mut argv } = func.class else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
self.set_assignments(assignments, AssignBehavior::Export);
|
||||||
|
|
||||||
|
let mut io_frame = self.io_stack.pop_frame();
|
||||||
|
io_frame.extend(func.redirs);
|
||||||
|
|
||||||
|
let func_name = argv.remove(0).span.as_str().to_string();
|
||||||
|
if let Some(func_body) = read_logic(|l| l.get_func(&func_name)) {
|
||||||
|
let saved_sh_args = read_vars(|v| v.sh_argv().clone());
|
||||||
|
write_vars(|v| {
|
||||||
|
v.clear_args();
|
||||||
|
for arg in argv {
|
||||||
|
v.bpush_arg(arg.to_string());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let result = exec_input(&func_body, Some(io_frame));
|
||||||
|
|
||||||
|
write_vars(|v| *v.sh_argv_mut() = saved_sh_args);
|
||||||
|
Ok(result?)
|
||||||
|
} else {
|
||||||
|
Err(
|
||||||
|
ShErr::full(
|
||||||
|
ShErrKind::InternalErr,
|
||||||
|
format!("Failed to find function '{}'",func_name),
|
||||||
|
blame
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn exec_brc_grp(&mut self, brc_grp: Node<'t>) -> ShResult<()> {
|
||||||
|
let NdRule::BraceGrp { body } = brc_grp.class else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
let mut io_frame = self.io_stack.pop_frame();
|
||||||
|
io_frame.extend(brc_grp.redirs);
|
||||||
|
|
||||||
|
for node in body {
|
||||||
|
self.io_stack.push_frame(io_frame.clone());
|
||||||
|
self.dispatch_node(node)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
pub fn exec_loop(&mut self, loop_stmt: Node<'t>) -> ShResult<()> {
|
pub fn exec_loop(&mut self, loop_stmt: Node<'t>) -> ShResult<()> {
|
||||||
let NdRule::LoopNode { kind, cond_node } = loop_stmt.class else {
|
let NdRule::LoopNode { kind, cond_node } = loop_stmt.class else {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
@@ -413,3 +477,10 @@ pub fn get_pipe_stack(num_cmds: usize) -> Vec<(Option<Redir>,Option<Redir>)> {
|
|||||||
}
|
}
|
||||||
stack
|
stack
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_func<'t>(tk: Option<Tk<'t>>) -> bool {
|
||||||
|
let Some(tk) = tk else {
|
||||||
|
return false
|
||||||
|
};
|
||||||
|
read_logic(|l| l.get_func(&tk.to_string())).is_some()
|
||||||
|
}
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ pub enum TkRule {
|
|||||||
Bg,
|
Bg,
|
||||||
Sep,
|
Sep,
|
||||||
Redir,
|
Redir,
|
||||||
|
BraceGrpStart,
|
||||||
|
BraceGrpEnd,
|
||||||
Expanded { exp: Vec<String> },
|
Expanded { exp: Vec<String> },
|
||||||
Comment,
|
Comment,
|
||||||
EOI, // End-of-Input
|
EOI, // End-of-Input
|
||||||
@@ -156,6 +158,7 @@ bitflags! {
|
|||||||
const FRESH = 0b00010000;
|
const FRESH = 0b00010000;
|
||||||
/// The lexer has no more tokens to produce
|
/// The lexer has no more tokens to produce
|
||||||
const STALE = 0b00100000;
|
const STALE = 0b00100000;
|
||||||
|
const IN_BRC_GRP = 0b01000000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,14 +195,27 @@ impl<'t> LexStream<'t> {
|
|||||||
pub fn slice_from_cursor(&self) -> Option<&'t str> {
|
pub fn slice_from_cursor(&self) -> Option<&'t str> {
|
||||||
self.slice(self.cursor..)
|
self.slice(self.cursor..)
|
||||||
}
|
}
|
||||||
/// The next string token is a command name
|
pub fn in_brc_grp(&self) -> bool {
|
||||||
pub fn next_is_cmd(&mut self) {
|
self.flags.contains(LexFlags::IN_BRC_GRP)
|
||||||
self.flags |= LexFlags::NEXT_IS_CMD;
|
|
||||||
}
|
}
|
||||||
/// The next string token is not a command name
|
pub fn set_in_brc_grp(&mut self, is: bool) {
|
||||||
pub fn next_is_not_cmd(&mut self) {
|
if is {
|
||||||
|
self.flags |= LexFlags::IN_BRC_GRP;
|
||||||
|
} else {
|
||||||
|
self.flags &= !LexFlags::IN_BRC_GRP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn next_is_cmd(&self) -> bool {
|
||||||
|
self.flags.contains(LexFlags::NEXT_IS_CMD)
|
||||||
|
}
|
||||||
|
/// Set whether the next string token is a command name
|
||||||
|
pub fn set_next_is_cmd(&mut self, is: bool) {
|
||||||
|
if is {
|
||||||
|
self.flags |= LexFlags::NEXT_IS_CMD;
|
||||||
|
} else {
|
||||||
self.flags &= !LexFlags::NEXT_IS_CMD;
|
self.flags &= !LexFlags::NEXT_IS_CMD;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
pub fn read_redir(&mut self) -> Option<ShResult<Tk<'t>>> {
|
pub fn read_redir(&mut self) -> Option<ShResult<Tk<'t>>> {
|
||||||
assert!(self.cursor <= self.source.len());
|
assert!(self.cursor <= self.source.len());
|
||||||
let slice = self.slice(self.cursor..)?;
|
let slice = self.slice(self.cursor..)?;
|
||||||
@@ -299,6 +315,24 @@ impl<'t> LexStream<'t> {
|
|||||||
pos += 1;
|
pos += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
'{' if pos == self.cursor && self.next_is_cmd() => {
|
||||||
|
pos += 1;
|
||||||
|
let mut tk = self.get_token(self.cursor..pos, TkRule::BraceGrpStart);
|
||||||
|
tk.flags |= TkFlags::IS_CMD;
|
||||||
|
self.set_in_brc_grp(true);
|
||||||
|
self.set_next_is_cmd(true);
|
||||||
|
|
||||||
|
self.cursor = pos;
|
||||||
|
return Ok(tk)
|
||||||
|
}
|
||||||
|
'}' if pos == self.cursor && self.in_brc_grp() => {
|
||||||
|
pos += 1;
|
||||||
|
let tk = self.get_token(self.cursor..pos, TkRule::BraceGrpEnd);
|
||||||
|
self.set_in_brc_grp(false);
|
||||||
|
self.set_next_is_cmd(true);
|
||||||
|
self.cursor = pos;
|
||||||
|
return Ok(tk)
|
||||||
|
}
|
||||||
'"' | '\'' => {
|
'"' | '\'' => {
|
||||||
self.in_quote = true;
|
self.in_quote = true;
|
||||||
quote_pos = Some(pos);
|
quote_pos = Some(pos);
|
||||||
@@ -340,7 +374,9 @@ impl<'t> LexStream<'t> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if self.flags.contains(LexFlags::NEXT_IS_CMD) {
|
if self.flags.contains(LexFlags::NEXT_IS_CMD) {
|
||||||
if KEYWORDS.contains(&new_tk.span.as_str()) {
|
flog!(DEBUG,&new_tk.span.as_str());
|
||||||
|
if is_keyword(&new_tk.span.as_str()) {
|
||||||
|
flog!(DEBUG,"is keyword");
|
||||||
new_tk.flags |= TkFlags::KEYWORD;
|
new_tk.flags |= TkFlags::KEYWORD;
|
||||||
} else if is_assignment(&new_tk.span.as_str()) {
|
} else if is_assignment(&new_tk.span.as_str()) {
|
||||||
new_tk.flags |= TkFlags::ASSIGN;
|
new_tk.flags |= TkFlags::ASSIGN;
|
||||||
@@ -351,7 +387,7 @@ impl<'t> LexStream<'t> {
|
|||||||
new_tk.flags |= TkFlags::BUILTIN;
|
new_tk.flags |= TkFlags::BUILTIN;
|
||||||
}
|
}
|
||||||
flog!(TRACE, new_tk.flags);
|
flog!(TRACE, new_tk.flags);
|
||||||
self.next_is_not_cmd();
|
self.set_next_is_cmd(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.cursor = pos;
|
self.cursor = pos;
|
||||||
@@ -411,7 +447,7 @@ impl<'t> Iterator for LexStream<'t> {
|
|||||||
'\r' | '\n' | ';' => {
|
'\r' | '\n' | ';' => {
|
||||||
let ch_idx = self.cursor;
|
let ch_idx = self.cursor;
|
||||||
self.cursor += 1;
|
self.cursor += 1;
|
||||||
self.next_is_cmd();
|
self.set_next_is_cmd(true);
|
||||||
|
|
||||||
while let Some(ch) = get_char(self.source, self.cursor) {
|
while let Some(ch) = get_char(self.source, self.cursor) {
|
||||||
if is_hard_sep(ch) { // Combine consecutive separators into one, including whitespace
|
if is_hard_sep(ch) { // Combine consecutive separators into one, including whitespace
|
||||||
@@ -438,7 +474,7 @@ impl<'t> Iterator for LexStream<'t> {
|
|||||||
'|' => {
|
'|' => {
|
||||||
let ch_idx = self.cursor;
|
let ch_idx = self.cursor;
|
||||||
self.cursor += 1;
|
self.cursor += 1;
|
||||||
self.next_is_cmd();
|
self.set_next_is_cmd(true);
|
||||||
|
|
||||||
let tk_type = if let Some('|') = get_char(self.source, self.cursor) {
|
let tk_type = if let Some('|') = get_char(self.source, self.cursor) {
|
||||||
self.cursor += 1;
|
self.cursor += 1;
|
||||||
@@ -455,7 +491,7 @@ impl<'t> Iterator for LexStream<'t> {
|
|||||||
'&' => {
|
'&' => {
|
||||||
let ch_idx = self.cursor;
|
let ch_idx = self.cursor;
|
||||||
self.cursor += 1;
|
self.cursor += 1;
|
||||||
self.next_is_cmd();
|
self.set_next_is_cmd(true);
|
||||||
|
|
||||||
let tk_type = if let Some('&') = get_char(self.source, self.cursor) {
|
let tk_type = if let Some('&') = get_char(self.source, self.cursor) {
|
||||||
self.cursor += 1;
|
self.cursor += 1;
|
||||||
@@ -467,7 +503,7 @@ impl<'t> Iterator for LexStream<'t> {
|
|||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
if let Some(tk) = self.read_redir() {
|
if let Some(tk) = self.read_redir() {
|
||||||
self.next_is_not_cmd();
|
self.set_next_is_cmd(false);
|
||||||
match tk {
|
match tk {
|
||||||
Ok(tk) => tk,
|
Ok(tk) => tk,
|
||||||
Err(e) => return Some(Err(e))
|
Err(e) => return Some(Err(e))
|
||||||
@@ -516,3 +552,8 @@ pub fn is_hard_sep(ch: char) -> bool {
|
|||||||
pub fn is_field_sep(ch: char) -> bool {
|
pub fn is_field_sep(ch: char) -> bool {
|
||||||
matches!(ch, ' ' | '\t')
|
matches!(ch, ' ' | '\t')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_keyword(slice: &str) -> bool {
|
||||||
|
KEYWORDS.contains(&slice) ||
|
||||||
|
(slice.ends_with("()") && !slice.ends_with("\\()"))
|
||||||
|
}
|
||||||
|
|||||||
154
src/parse/mod.rs
154
src/parse/mod.rs
@@ -9,6 +9,18 @@ use crate::{libsh::{error::{ShErr, ShErrKind, ShResult}, utils::TkVecUtils}, pre
|
|||||||
pub mod lex;
|
pub mod lex;
|
||||||
pub mod execute;
|
pub mod execute;
|
||||||
|
|
||||||
|
/// Try to match a specific parsing rule
|
||||||
|
///
|
||||||
|
/// # Notes
|
||||||
|
/// * If the match fails, execution continues.
|
||||||
|
/// * If the match succeeds, the matched node is returned.
|
||||||
|
macro_rules! try_match {
|
||||||
|
($expr:expr) => {
|
||||||
|
if let Some(node) = $expr {
|
||||||
|
return Ok(Some(node))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone,Debug)]
|
#[derive(Clone,Debug)]
|
||||||
pub struct Node<'t> {
|
pub struct Node<'t> {
|
||||||
@@ -244,6 +256,8 @@ pub enum NdRule<'t> {
|
|||||||
Pipeline { cmds: Vec<Node<'t>>, pipe_err: bool },
|
Pipeline { cmds: Vec<Node<'t>>, pipe_err: bool },
|
||||||
Conjunction { elements: Vec<ConjunctNode<'t>> },
|
Conjunction { elements: Vec<ConjunctNode<'t>> },
|
||||||
Assignment { kind: AssignKind, var: Tk<'t>, val: Tk<'t> },
|
Assignment { kind: AssignKind, var: Tk<'t>, val: Tk<'t> },
|
||||||
|
BraceGrp { body: Vec<Node<'t>> },
|
||||||
|
FuncDef { name: Tk<'t>, body: Box<Node<'t>> }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -270,6 +284,9 @@ impl<'t> ParseStream<'t> {
|
|||||||
&TkRule::Null
|
&TkRule::Null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn peek_tk(&self) -> Option<&Tk<'t>> {
|
||||||
|
self.tokens.first()
|
||||||
|
}
|
||||||
fn next_tk(&mut self) -> Option<Tk<'t>> {
|
fn next_tk(&mut self) -> Option<Tk<'t>> {
|
||||||
if !self.tokens.is_empty() {
|
if !self.tokens.is_empty() {
|
||||||
if *self.next_tk_class() == TkRule::EOI {
|
if *self.next_tk_class() == TkRule::EOI {
|
||||||
@@ -301,6 +318,7 @@ impl<'t> ParseStream<'t> {
|
|||||||
TkRule::Or |
|
TkRule::Or |
|
||||||
TkRule::Bg |
|
TkRule::Bg |
|
||||||
TkRule::And |
|
TkRule::And |
|
||||||
|
TkRule::BraceGrpEnd |
|
||||||
TkRule::Pipe => Ok(()),
|
TkRule::Pipe => Ok(()),
|
||||||
|
|
||||||
TkRule::Sep => {
|
TkRule::Sep => {
|
||||||
@@ -371,25 +389,127 @@ impl<'t> ParseStream<'t> {
|
|||||||
/// This tries to match on different stuff that can appear in a command position
|
/// This tries to match on different stuff that can appear in a command position
|
||||||
/// Matches shell commands like if-then-fi, pipelines, etc.
|
/// Matches shell commands like if-then-fi, pipelines, etc.
|
||||||
/// Ordered from specialized to general, with more generally matchable stuff appearing at the bottom
|
/// Ordered from specialized to general, with more generally matchable stuff appearing at the bottom
|
||||||
/// The check_pipelines parameter is used to prevent infinite recursion in parse_pipeline
|
/// The check_pipelines parameter is used to prevent left-recursion issues in self.parse_pipeline()
|
||||||
fn parse_block(&mut self, check_pipelines: bool) -> ShResult<Option<Node<'t>>> {
|
fn parse_block(&mut self, check_pipelines: bool) -> ShResult<Option<Node<'t>>> {
|
||||||
if let Some(node) = self.parse_loop()? {
|
try_match!(self.parse_func_def()?);
|
||||||
return Ok(Some(node))
|
try_match!(self.parse_brc_grp(false /* from_func_def */)?);
|
||||||
}
|
try_match!(self.parse_loop()?);
|
||||||
if let Some(node) = self.parse_if()? {
|
try_match!(self.parse_if()?);
|
||||||
return Ok(Some(node))
|
|
||||||
}
|
|
||||||
if check_pipelines {
|
if check_pipelines {
|
||||||
if let Some(node) = self.parse_pipeline()? {
|
try_match!(self.parse_pipeline()?);
|
||||||
return Ok(Some(node))
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if let Some(node) = self.parse_cmd()? {
|
try_match!(self.parse_cmd()?);
|
||||||
return Ok(Some(node))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
fn parse_func_def(&mut self) -> ShResult<Option<Node<'t>>> {
|
||||||
|
let mut node_tks: Vec<Tk> = vec![];
|
||||||
|
let name;
|
||||||
|
let body;
|
||||||
|
|
||||||
|
if !is_func_name(self.peek_tk()) {
|
||||||
|
return Ok(None)
|
||||||
|
}
|
||||||
|
let name_tk = self.next_tk().unwrap();
|
||||||
|
node_tks.push(name_tk.clone());
|
||||||
|
name = name_tk;
|
||||||
|
|
||||||
|
let Some(brc_grp) = self.parse_brc_grp(true /* from_func_def */)? else {
|
||||||
|
return Err(parse_err_full(
|
||||||
|
"Expected a brace group after function name",
|
||||||
|
&node_tks.get_span().unwrap()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
};
|
||||||
|
body = Box::new(brc_grp);
|
||||||
|
|
||||||
|
let node = Node {
|
||||||
|
class: NdRule::FuncDef { name, body },
|
||||||
|
flags: NdFlags::empty(),
|
||||||
|
redirs: vec![],
|
||||||
|
tokens: node_tks
|
||||||
|
};
|
||||||
|
flog!(DEBUG,node);
|
||||||
|
|
||||||
|
Ok(Some(node))
|
||||||
|
}
|
||||||
|
fn parse_brc_grp(&mut self, from_func_def: bool) -> ShResult<Option<Node<'t>>> {
|
||||||
|
let mut node_tks: Vec<Tk> = vec![];
|
||||||
|
let mut body: Vec<Node> = vec![];
|
||||||
|
let mut redirs: Vec<Redir> = vec![];
|
||||||
|
|
||||||
|
if *self.next_tk_class() != TkRule::BraceGrpStart {
|
||||||
|
return Ok(None)
|
||||||
|
}
|
||||||
|
node_tks.push(self.next_tk().unwrap());
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if *self.next_tk_class() == TkRule::BraceGrpEnd {
|
||||||
|
node_tks.push(self.next_tk().unwrap());
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if let Some(node) = self.parse_block(true)? {
|
||||||
|
node_tks.extend(node.tokens.clone());
|
||||||
|
body.push(node);
|
||||||
|
}
|
||||||
|
if !self.next_tk_is_some() {
|
||||||
|
return Err(parse_err_full(
|
||||||
|
"Expected a closing brace for this brace group",
|
||||||
|
&node_tks.get_span().unwrap()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !from_func_def {
|
||||||
|
while self.check_redir() {
|
||||||
|
let tk = self.next_tk().unwrap();
|
||||||
|
node_tks.push(tk.clone());
|
||||||
|
let redir_bldr = tk.span.as_str().parse::<RedirBldr>().unwrap();
|
||||||
|
if redir_bldr.io_mode.is_none() {
|
||||||
|
let path_tk = self.next_tk();
|
||||||
|
|
||||||
|
if path_tk.clone().is_none_or(|tk| tk.class == TkRule::EOI) {
|
||||||
|
self.flags |= ParseFlags::ERROR;
|
||||||
|
return Err(
|
||||||
|
ShErr::full(
|
||||||
|
ShErrKind::ParseErr,
|
||||||
|
"Expected a filename after this redirection",
|
||||||
|
tk.span.clone().into()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let path_tk = path_tk.unwrap();
|
||||||
|
node_tks.push(path_tk.clone());
|
||||||
|
let redir_class = redir_bldr.class.unwrap();
|
||||||
|
let pathbuf = PathBuf::from(path_tk.span.as_str());
|
||||||
|
|
||||||
|
let Ok(file) = get_redir_file(redir_class, pathbuf) else {
|
||||||
|
self.flags |= ParseFlags::ERROR;
|
||||||
|
return Err(parse_err_full(
|
||||||
|
"Error opening file for redirection",
|
||||||
|
&path_tk.span
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
let io_mode = IoMode::file(redir_bldr.tgt_fd.unwrap(), file);
|
||||||
|
let redir_bldr = redir_bldr.with_io_mode(io_mode);
|
||||||
|
let redir = redir_bldr.build();
|
||||||
|
redirs.push(redir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let node = Node {
|
||||||
|
class: NdRule::BraceGrp { body },
|
||||||
|
flags: NdFlags::empty(),
|
||||||
|
redirs,
|
||||||
|
tokens: node_tks
|
||||||
|
};
|
||||||
|
flog!(DEBUG, node);
|
||||||
|
Ok(Some(node))
|
||||||
|
}
|
||||||
fn parse_if(&mut self) -> ShResult<Option<Node<'t>>> {
|
fn parse_if(&mut self) -> ShResult<Option<Node<'t>>> {
|
||||||
// Needs at last one 'if-then',
|
// Needs at last one 'if-then',
|
||||||
// Any number of 'elif-then',
|
// Any number of 'elif-then',
|
||||||
@@ -647,6 +767,7 @@ impl<'t> ParseStream<'t> {
|
|||||||
TkRule::EOI |
|
TkRule::EOI |
|
||||||
TkRule::Pipe |
|
TkRule::Pipe |
|
||||||
TkRule::And |
|
TkRule::And |
|
||||||
|
TkRule::BraceGrpEnd |
|
||||||
TkRule::Or => {
|
TkRule::Or => {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -878,3 +999,10 @@ fn parse_err_full<'t>(reason: &str, blame: &Span<'t>) -> ShErr {
|
|||||||
blame.clone().into()
|
blame.clone().into()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_func_name<'t>(tk: Option<&Tk<'t>>) -> bool {
|
||||||
|
tk.is_some_and(|tk| {
|
||||||
|
tk.flags.contains(TkFlags::KEYWORD) &&
|
||||||
|
(tk.span.as_str().ends_with("()") && !tk.span.as_str().ends_with("\\()"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
74
src/state.rs
74
src/state.rs
@@ -1,11 +1,43 @@
|
|||||||
use std::{collections::{HashMap, VecDeque}, sync::{LazyLock, RwLock, RwLockReadGuard, RwLockWriteGuard}};
|
use std::{cell::RefCell, collections::{HashMap, VecDeque}, ops::Range, sync::{LazyLock, RwLock, RwLockReadGuard, RwLockWriteGuard}};
|
||||||
|
|
||||||
use crate::{exec_input, jobs::JobTab, libsh::{error::ShResult, utils::VecDequeExt}, parse::lex::get_char, prelude::*};
|
use crate::{exec_input, jobs::JobTab, libsh::{error::ShResult, utils::VecDequeExt}, parse::{lex::{get_char, Tk}, Node}, prelude::*};
|
||||||
|
|
||||||
pub static JOB_TABLE: LazyLock<RwLock<JobTab>> = LazyLock::new(|| RwLock::new(JobTab::new()));
|
pub static JOB_TABLE: LazyLock<RwLock<JobTab>> = LazyLock::new(|| RwLock::new(JobTab::new()));
|
||||||
|
|
||||||
pub static VAR_TABLE: LazyLock<RwLock<VarTab>> = LazyLock::new(|| RwLock::new(VarTab::new()));
|
pub static VAR_TABLE: LazyLock<RwLock<VarTab>> = LazyLock::new(|| RwLock::new(VarTab::new()));
|
||||||
|
|
||||||
|
pub static LOGIC_TABLE: LazyLock<RwLock<LogTab>> = LazyLock::new(|| RwLock::new(LogTab::new()));
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
pub static LAST_INPUT: RefCell<String> = RefCell::new(String::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The logic table for the shell
|
||||||
|
///
|
||||||
|
/// Contains aliases and functions
|
||||||
|
pub struct LogTab {
|
||||||
|
// TODO: Find a way to store actual owned nodes instead of strings that must be re-parsed
|
||||||
|
functions: HashMap<String,String>,
|
||||||
|
aliases: HashMap<String,String>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LogTab {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { functions: HashMap::new(), aliases: HashMap::new() }
|
||||||
|
}
|
||||||
|
pub fn insert_func(&mut self, name: &str, body: &str) {
|
||||||
|
self.functions.insert(name.into(), body.into());
|
||||||
|
}
|
||||||
|
pub fn get_func(&self, name: &str) -> Option<String> {
|
||||||
|
self.functions.get(name).cloned()
|
||||||
|
}
|
||||||
|
pub fn insert_alias(&mut self, name: &str, body: &str) {
|
||||||
|
self.aliases.insert(name.into(), body.into());
|
||||||
|
}
|
||||||
|
pub fn get_alias(&self, name: &str) -> Option<String> {
|
||||||
|
self.aliases.get(name).cloned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct VarTab {
|
pub struct VarTab {
|
||||||
vars: HashMap<String,String>,
|
vars: HashMap<String,String>,
|
||||||
@@ -35,6 +67,15 @@ impl VarTab {
|
|||||||
self.bpush_arg(arg);
|
self.bpush_arg(arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn sh_argv(&self) -> &VecDeque<String> {
|
||||||
|
&self.sh_argv
|
||||||
|
}
|
||||||
|
pub fn sh_argv_mut(&mut self) -> &mut VecDeque<String> {
|
||||||
|
&mut self.sh_argv
|
||||||
|
}
|
||||||
|
pub fn clear_args(&mut self) {
|
||||||
|
self.sh_argv.clear()
|
||||||
|
}
|
||||||
fn update_arg_params(&mut self) {
|
fn update_arg_params(&mut self) {
|
||||||
self.set_param('@', &self.sh_argv.clone().to_vec().join(" "));
|
self.set_param('@', &self.sh_argv.clone().to_vec().join(" "));
|
||||||
self.set_param('#', &self.sh_argv.len().to_string());
|
self.set_param('#', &self.sh_argv.len().to_string());
|
||||||
@@ -131,6 +172,33 @@ pub fn write_vars<T, F: FnOnce(&mut RwLockWriteGuard<VarTab>) -> T>(f: F) -> T {
|
|||||||
f(lock)
|
f(lock)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read from the logic table
|
||||||
|
pub fn read_logic<T, F: FnOnce(RwLockReadGuard<LogTab>) -> T>(f: F) -> T {
|
||||||
|
let lock = LOGIC_TABLE.read().unwrap();
|
||||||
|
f(lock)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write to the logic table
|
||||||
|
pub fn write_logic<T, F: FnOnce(&mut RwLockWriteGuard<LogTab>) -> T>(f: F) -> T {
|
||||||
|
let lock = &mut LOGIC_TABLE.write().unwrap();
|
||||||
|
f(lock)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_last_input(input: &str) {
|
||||||
|
LAST_INPUT.with(|input_ref| {
|
||||||
|
let mut last_input = input_ref.borrow_mut();
|
||||||
|
last_input.clear();
|
||||||
|
last_input.push_str(input);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn slice_last_input(range: Range<usize>) -> String {
|
||||||
|
LAST_INPUT.with(|input_ref| {
|
||||||
|
let input = input_ref.borrow();
|
||||||
|
input[range].to_string()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_status() -> i32 {
|
pub fn get_status() -> i32 {
|
||||||
read_vars(|v| v.get_param('?')).parse::<i32>().unwrap()
|
read_vars(|v| v.get_param('?')).parse::<i32>().unwrap()
|
||||||
}
|
}
|
||||||
@@ -145,6 +213,6 @@ pub fn source_file(path: PathBuf) -> ShResult<()> {
|
|||||||
|
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
file.read_to_string(&mut buf)?;
|
file.read_to_string(&mut buf)?;
|
||||||
exec_input(&buf)?;
|
exec_input(&buf, None)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user