Implemented prompt expansion, and display for errors
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
|
||||
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 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::{ShErr, ShErrKind, ShResult, ShResultExt}, utils::RedirVecUtils}, prelude::*, procio::{IoFrame, IoMode, IoStack}, state::{self, read_logic, read_vars, write_logic, write_vars, ShFunc, VarTab}};
|
||||
|
||||
use super::{lex::{LexFlags, LexStream, Span, Tk, TkFlags}, AssignKind, CondNode, ConjunctNode, ConjunctOp, LoopKind, NdFlags, NdRule, Node, ParseStream, Redir, RedirType};
|
||||
use super::{lex::{LexFlags, LexStream, Span, Tk, TkFlags}, AssignKind, CondNode, ConjunctNode, ConjunctOp, LoopKind, NdFlags, NdRule, Node, ParseStream, ParsedSrc, Redir, RedirType};
|
||||
|
||||
pub enum AssignBehavior {
|
||||
Export,
|
||||
@@ -37,25 +37,26 @@ impl ExecArgs {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Dispatcher<'t> {
|
||||
nodes: VecDeque<Node<'t>>,
|
||||
pub struct Dispatcher {
|
||||
nodes: VecDeque<Node>,
|
||||
pub io_stack: IoStack,
|
||||
pub job_stack: JobStack
|
||||
}
|
||||
|
||||
impl<'t> Dispatcher<'t> {
|
||||
pub fn new(nodes: Vec<Node<'t>>) -> Self {
|
||||
impl Dispatcher {
|
||||
pub fn new(nodes: Vec<Node>) -> Self {
|
||||
let nodes = VecDeque::from(nodes);
|
||||
Self { nodes, io_stack: IoStack::new(), job_stack: JobStack::new() }
|
||||
}
|
||||
pub fn begin_dispatch(&mut self) -> ShResult<()> {
|
||||
flog!(TRACE, "beginning dispatch");
|
||||
while let Some(list) = self.nodes.pop_front() {
|
||||
self.dispatch_node(list)?;
|
||||
while let Some(node) = self.nodes.pop_front() {
|
||||
let blame = node.get_span();
|
||||
self.dispatch_node(node).try_blame(blame)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn dispatch_node(&mut self, node: Node<'t>) -> ShResult<()> {
|
||||
pub fn dispatch_node(&mut self, node: Node) -> ShResult<()> {
|
||||
match node.class {
|
||||
NdRule::Conjunction {..} => self.exec_conjunction(node)?,
|
||||
NdRule::Pipeline {..} => self.exec_pipeline(node)?,
|
||||
@@ -68,7 +69,7 @@ impl<'t> Dispatcher<'t> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn dispatch_cmd(&mut self, node: Node<'t>) -> ShResult<()> {
|
||||
pub fn dispatch_cmd(&mut self, node: Node) -> ShResult<()> {
|
||||
let Some(cmd) = node.get_command() else {
|
||||
return self.exec_cmd(node) // Argv is empty, probably an assignment
|
||||
};
|
||||
@@ -80,7 +81,7 @@ impl<'t> Dispatcher<'t> {
|
||||
self.exec_cmd(node)
|
||||
}
|
||||
}
|
||||
pub fn exec_conjunction(&mut self, conjunction: Node<'t>) -> ShResult<()> {
|
||||
pub fn exec_conjunction(&mut self, conjunction: Node) -> ShResult<()> {
|
||||
let NdRule::Conjunction { elements } = conjunction.class else {
|
||||
unreachable!()
|
||||
};
|
||||
@@ -99,42 +100,51 @@ impl<'t> Dispatcher<'t> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn exec_func_def(&mut self, func_def: Node<'t>) -> ShResult<()> {
|
||||
pub fn exec_func_def(&mut self, func_def: Node) -> ShResult<()> {
|
||||
let NdRule::FuncDef { name, body } = func_def.class else {
|
||||
unreachable!()
|
||||
};
|
||||
let body_span = body.get_span();
|
||||
let body = body_span.as_str();
|
||||
let body = body_span.as_str().to_string();
|
||||
let name = name.span.as_str().strip_suffix("()").unwrap();
|
||||
write_logic(|l| l.insert_func(name, body));
|
||||
|
||||
let mut func_parser = ParsedSrc::new(Rc::new(body));
|
||||
func_parser.parse_src()?; // Parse the function
|
||||
|
||||
let func = ShFunc::new(func_parser);
|
||||
write_logic(|l| l.insert_func(name, func)); // Store the AST
|
||||
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
|
||||
pub fn exec_func(&mut self, func: Node) -> ShResult<()> {
|
||||
let blame = func.get_span().clone();
|
||||
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);
|
||||
self.io_stack.append_to_frame(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());
|
||||
if let Some(func) = read_logic(|l| l.get_func(&func_name)) {
|
||||
let scope_snapshot = read_vars(|v| v.clone());
|
||||
// Set up the inner scope
|
||||
write_vars(|v| {
|
||||
**v = VarTab::new();
|
||||
v.clear_args();
|
||||
for arg in argv {
|
||||
v.bpush_arg(arg.to_string());
|
||||
}
|
||||
});
|
||||
|
||||
let result = exec_input(&func_body, Some(io_frame));
|
||||
if let Err(e) = self.exec_brc_grp((*func).clone()) {
|
||||
write_vars(|v| **v = scope_snapshot);
|
||||
return Err(e.into())
|
||||
}
|
||||
|
||||
write_vars(|v| *v.sh_argv_mut() = saved_sh_args);
|
||||
Ok(result?)
|
||||
// Return to the outer scope
|
||||
write_vars(|v| **v = scope_snapshot);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(
|
||||
ShErr::full(
|
||||
@@ -145,7 +155,7 @@ impl<'t> Dispatcher<'t> {
|
||||
)
|
||||
}
|
||||
}
|
||||
pub fn exec_brc_grp(&mut self, brc_grp: Node<'t>) -> ShResult<()> {
|
||||
pub fn exec_brc_grp(&mut self, brc_grp: Node) -> ShResult<()> {
|
||||
let NdRule::BraceGrp { body } = brc_grp.class else {
|
||||
unreachable!()
|
||||
};
|
||||
@@ -153,13 +163,14 @@ impl<'t> Dispatcher<'t> {
|
||||
io_frame.extend(brc_grp.redirs);
|
||||
|
||||
for node in body {
|
||||
let blame = node.get_span();
|
||||
self.io_stack.push_frame(io_frame.clone());
|
||||
self.dispatch_node(node)?;
|
||||
self.dispatch_node(node).try_blame(blame)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub fn exec_loop(&mut self, loop_stmt: Node<'t>) -> ShResult<()> {
|
||||
pub fn exec_loop(&mut self, loop_stmt: Node) -> ShResult<()> {
|
||||
let NdRule::LoopNode { kind, cond_node } = loop_stmt.class else {
|
||||
unreachable!();
|
||||
};
|
||||
@@ -204,7 +215,7 @@ impl<'t> Dispatcher<'t> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub fn exec_if(&mut self, if_stmt: Node<'t>) -> ShResult<()> {
|
||||
pub fn exec_if(&mut self, if_stmt: Node) -> ShResult<()> {
|
||||
let NdRule::IfNode { cond_nodes, else_block } = if_stmt.class else {
|
||||
unreachable!();
|
||||
};
|
||||
@@ -244,7 +255,7 @@ impl<'t> Dispatcher<'t> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub fn exec_pipeline(&mut self, pipeline: Node<'t>) -> ShResult<()> {
|
||||
pub fn exec_pipeline(&mut self, pipeline: Node) -> ShResult<()> {
|
||||
let NdRule::Pipeline { cmds, pipe_err } = pipeline.class else {
|
||||
unreachable!()
|
||||
};
|
||||
@@ -254,6 +265,7 @@ impl<'t> Dispatcher<'t> {
|
||||
.into_iter()
|
||||
.zip(cmds);
|
||||
|
||||
|
||||
for ((rpipe,wpipe), cmd) in pipes_and_cmds {
|
||||
if let Some(pipe) = rpipe {
|
||||
self.io_stack.push_to_frame(pipe);
|
||||
@@ -268,7 +280,7 @@ impl<'t> Dispatcher<'t> {
|
||||
dispatch_job(job, is_bg)?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn exec_builtin(&mut self, mut cmd: Node<'t>) -> ShResult<()> {
|
||||
pub fn exec_builtin(&mut self, mut cmd: Node) -> ShResult<()> {
|
||||
let NdRule::Command { ref mut assignments, argv } = &mut cmd.class else {
|
||||
unreachable!()
|
||||
};
|
||||
@@ -301,7 +313,7 @@ impl<'t> Dispatcher<'t> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn exec_cmd(&mut self, cmd: Node<'t>) -> ShResult<()> {
|
||||
pub fn exec_cmd(&mut self, cmd: Node) -> ShResult<()> {
|
||||
let NdRule::Command { assignments, argv } = cmd.class else {
|
||||
unreachable!()
|
||||
};
|
||||
@@ -337,7 +349,7 @@ impl<'t> Dispatcher<'t> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub fn set_assignments(&self, assigns: Vec<Node<'t>>, behavior: AssignBehavior) -> Vec<String> {
|
||||
pub fn set_assignments(&self, assigns: Vec<Node>, behavior: AssignBehavior) -> Vec<String> {
|
||||
let mut new_env_vars = vec![];
|
||||
match behavior {
|
||||
AssignBehavior::Export => {
|
||||
@@ -420,7 +432,7 @@ where
|
||||
}
|
||||
|
||||
/// The default behavior for the child process after forking
|
||||
pub fn def_child_action<'t>(mut io_frame: IoFrame, exec_args: Option<ExecArgs>) {
|
||||
pub fn def_child_action(mut io_frame: IoFrame, exec_args: Option<ExecArgs>) {
|
||||
if let Err(e) = io_frame.redirect() {
|
||||
eprintln!("{e}");
|
||||
}
|
||||
@@ -435,7 +447,7 @@ pub fn def_child_action<'t>(mut io_frame: IoFrame, exec_args: Option<ExecArgs>)
|
||||
}
|
||||
|
||||
/// The default behavior for the parent process after forking
|
||||
pub fn def_parent_action<'t>(
|
||||
pub fn def_parent_action(
|
||||
io_frame: IoFrame,
|
||||
job: &mut JobBldr,
|
||||
cmd: Option<&str>,
|
||||
@@ -478,7 +490,7 @@ pub fn get_pipe_stack(num_cmds: usize) -> Vec<(Option<Redir>,Option<Redir>)> {
|
||||
stack
|
||||
}
|
||||
|
||||
pub fn is_func<'t>(tk: Option<Tk<'t>>) -> bool {
|
||||
pub fn is_func(tk: Option<Tk>) -> bool {
|
||||
let Some(tk) = tk else {
|
||||
return false
|
||||
};
|
||||
|
||||
@@ -31,14 +31,14 @@ pub const OPENERS: [&'static str;6] = [
|
||||
];
|
||||
|
||||
#[derive(Clone,PartialEq,Default,Debug)]
|
||||
pub struct Span<'s> {
|
||||
pub struct Span {
|
||||
range: Range<usize>,
|
||||
source: &'s str
|
||||
source: Rc<String>
|
||||
}
|
||||
|
||||
impl<'s> Span<'s> {
|
||||
impl Span {
|
||||
/// New `Span`. Wraps a range and a string slice that it refers to.
|
||||
pub fn new(range: Range<usize>, source: &'s str) -> Self {
|
||||
pub fn new(range: Range<usize>, source: Rc<String>) -> Self {
|
||||
Span {
|
||||
range,
|
||||
source,
|
||||
@@ -48,8 +48,8 @@ impl<'s> Span<'s> {
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.source[self.start..self.end]
|
||||
}
|
||||
pub fn get_source(&'s self) -> &'s str {
|
||||
self.source
|
||||
pub fn get_source(&self) -> Rc<String> {
|
||||
self.source.clone()
|
||||
}
|
||||
pub fn range(&self) -> Range<usize> {
|
||||
self.range.clone()
|
||||
@@ -57,7 +57,7 @@ impl<'s> Span<'s> {
|
||||
}
|
||||
|
||||
/// Allows simple access to the underlying range wrapped by the span
|
||||
impl<'s> Deref for Span<'s> {
|
||||
impl Deref for Span {
|
||||
type Target = Range<usize>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.range
|
||||
@@ -90,15 +90,15 @@ impl Default for TkRule {
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug,PartialEq,Default)]
|
||||
pub struct Tk<'s> {
|
||||
pub struct Tk {
|
||||
pub class: TkRule,
|
||||
pub span: Span<'s>,
|
||||
pub span: Span,
|
||||
pub flags: TkFlags
|
||||
}
|
||||
|
||||
// There's one impl here and then another in expand.rs which has the expansion logic
|
||||
impl<'s> Tk<'s> {
|
||||
pub fn new(class: TkRule, span: Span<'s>) -> Self {
|
||||
impl Tk {
|
||||
pub fn new(class: TkRule, span: Span) -> Self {
|
||||
Self { class, span, flags: TkFlags::empty() }
|
||||
}
|
||||
pub fn to_string(&self) -> String {
|
||||
@@ -107,12 +107,12 @@ impl<'s> Tk<'s> {
|
||||
_ => self.span.as_str().to_string()
|
||||
}
|
||||
}
|
||||
pub fn source(&self) -> &'s str {
|
||||
self.span.source
|
||||
pub fn source(&self) -> Rc<String> {
|
||||
self.span.source.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> Display for Tk<'s> {
|
||||
impl Display for Tk {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match &self.class {
|
||||
TkRule::Expanded { exp } => write!(f,"{}",exp.join(" ")),
|
||||
@@ -135,8 +135,8 @@ bitflags! {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LexStream<'t> {
|
||||
source: &'t str,
|
||||
pub struct LexStream {
|
||||
source: Rc<String>,
|
||||
pub cursor: usize,
|
||||
in_quote: bool,
|
||||
flags: LexFlags,
|
||||
@@ -162,8 +162,8 @@ bitflags! {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'t> LexStream<'t> {
|
||||
pub fn new(source: &'t str, flags: LexFlags) -> Self {
|
||||
impl LexStream {
|
||||
pub fn new(source: Rc<String>, flags: LexFlags) -> Self {
|
||||
flog!(TRACE, "new lex stream");
|
||||
let flags = flags | LexFlags::FRESH | LexFlags::NEXT_IS_CMD;
|
||||
Self { source, cursor: 0, in_quote: false, flags }
|
||||
@@ -178,7 +178,7 @@ impl<'t> LexStream<'t> {
|
||||
/// `LexStream.slice(..10)`
|
||||
/// `LexStream.slice(1..)`
|
||||
///
|
||||
pub fn slice<R: RangeBounds<usize>>(&self, range: R) -> Option<&'t str> {
|
||||
pub fn slice<R: RangeBounds<usize>>(&self, range: R) -> Option<&str> {
|
||||
// Sketchy downcast
|
||||
let start = match range.start_bound() {
|
||||
Bound::Included(&start) => start,
|
||||
@@ -192,7 +192,7 @@ impl<'t> LexStream<'t> {
|
||||
};
|
||||
self.source.get(start..end)
|
||||
}
|
||||
pub fn slice_from_cursor(&self) -> Option<&'t str> {
|
||||
pub fn slice_from_cursor(&self) -> Option<&str> {
|
||||
self.slice(self.cursor..)
|
||||
}
|
||||
pub fn in_brc_grp(&self) -> bool {
|
||||
@@ -216,7 +216,7 @@ impl<'t> LexStream<'t> {
|
||||
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>> {
|
||||
assert!(self.cursor <= self.source.len());
|
||||
let slice = self.slice(self.cursor..)?;
|
||||
let mut pos = self.cursor;
|
||||
@@ -248,7 +248,7 @@ impl<'t> LexStream<'t> {
|
||||
ShErr::full(
|
||||
ShErrKind::ParseErr,
|
||||
"Invalid redirection",
|
||||
Span::new(self.cursor..pos, self.source).into()
|
||||
Span::new(self.cursor..pos, self.source.clone()).into()
|
||||
)
|
||||
));
|
||||
} else {
|
||||
@@ -294,9 +294,9 @@ impl<'t> LexStream<'t> {
|
||||
self.cursor = pos;
|
||||
Some(Ok(tk))
|
||||
}
|
||||
pub fn read_string(&mut self) -> ShResult<Tk<'t>> {
|
||||
pub fn read_string(&mut self) -> ShResult<Tk> {
|
||||
assert!(self.cursor <= self.source.len());
|
||||
let slice = self.slice_from_cursor().unwrap();
|
||||
let slice = self.slice_from_cursor().unwrap().to_string();
|
||||
let mut pos = self.cursor;
|
||||
let mut chars = slice.chars();
|
||||
let mut quote_pos = None;
|
||||
@@ -393,14 +393,14 @@ impl<'t> LexStream<'t> {
|
||||
self.cursor = pos;
|
||||
Ok(new_tk)
|
||||
}
|
||||
pub fn get_token(&self, range: Range<usize>, class: TkRule) -> Tk<'t> {
|
||||
let span = Span::new(range, self.source);
|
||||
pub fn get_token(&self, range: Range<usize>, class: TkRule) -> Tk {
|
||||
let span = Span::new(range, self.source.clone());
|
||||
Tk::new(class, span)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'t> Iterator for LexStream<'t> {
|
||||
type Item = ShResult<Tk<'t>>;
|
||||
impl Iterator for LexStream {
|
||||
type Item = ShResult<Tk>;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
assert!(self.cursor <= self.source.len());
|
||||
// We are at the end of the input
|
||||
@@ -432,7 +432,7 @@ impl<'t> Iterator for LexStream<'t> {
|
||||
let pos = self.cursor;
|
||||
if self.slice(pos..pos+2) == Some("\\\n") {
|
||||
self.cursor += 2;
|
||||
} else if pos < self.source.len() && is_field_sep(get_char(self.source, pos).unwrap()) {
|
||||
} else if pos < self.source.len() && is_field_sep(get_char(&self.source, pos).unwrap()) {
|
||||
self.cursor += 1;
|
||||
} else {
|
||||
break
|
||||
@@ -443,13 +443,13 @@ impl<'t> Iterator for LexStream<'t> {
|
||||
return None
|
||||
}
|
||||
|
||||
let token = match get_char(self.source, self.cursor).unwrap() {
|
||||
let token = match get_char(&self.source, self.cursor).unwrap() {
|
||||
'\r' | '\n' | ';' => {
|
||||
let ch_idx = self.cursor;
|
||||
self.cursor += 1;
|
||||
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
|
||||
self.cursor += 1;
|
||||
} else {
|
||||
@@ -462,7 +462,7 @@ impl<'t> Iterator for LexStream<'t> {
|
||||
let ch_idx = self.cursor;
|
||||
self.cursor += 1;
|
||||
|
||||
while let Some(ch) = get_char(self.source, self.cursor) {
|
||||
while let Some(ch) = get_char(&self.source, self.cursor) {
|
||||
self.cursor += 1;
|
||||
if ch == '\n' {
|
||||
break
|
||||
@@ -476,10 +476,10 @@ impl<'t> Iterator for LexStream<'t> {
|
||||
self.cursor += 1;
|
||||
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;
|
||||
TkRule::Or
|
||||
} else if let Some('&') = get_char(self.source, self.cursor) {
|
||||
} else if let Some('&') = get_char(&self.source, self.cursor) {
|
||||
self.cursor += 1;
|
||||
TkRule::ErrPipe
|
||||
} else {
|
||||
@@ -493,7 +493,7 @@ impl<'t> Iterator for LexStream<'t> {
|
||||
self.cursor += 1;
|
||||
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;
|
||||
TkRule::And
|
||||
} else {
|
||||
|
||||
288
src/parse/mod.rs
288
src/parse/mod.rs
@@ -2,7 +2,7 @@ use std::str::FromStr;
|
||||
|
||||
use bitflags::bitflags;
|
||||
use fmt::Display;
|
||||
use lex::{Span, Tk, TkFlags, TkRule};
|
||||
use lex::{LexFlags, LexStream, Span, Tk, TkFlags, TkRule};
|
||||
|
||||
use crate::{libsh::{error::{ShErr, ShErrKind, ShResult}, utils::TkVecUtils}, prelude::*, procio::IoMode};
|
||||
|
||||
@@ -15,30 +15,78 @@ pub mod execute;
|
||||
/// * 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))
|
||||
}
|
||||
};
|
||||
($expr:expr) => {
|
||||
if let Some(node) = $expr {
|
||||
return Ok(Some(node))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// The parsed AST along with the source input it parsed
|
||||
///
|
||||
/// Uses Rc<String> instead of &str because the reference has to stay alive while errors are propagated upwards
|
||||
/// The string also has to stay alive in the case of pre-parsed shell function nodes, which live in the logic table
|
||||
/// Using &str for this use-case dramatically overcomplicates the code
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct ParsedSrc {
|
||||
pub src: Rc<String>,
|
||||
pub ast: Ast
|
||||
}
|
||||
|
||||
impl ParsedSrc {
|
||||
pub fn new(src: Rc<String>) -> Self {
|
||||
Self { src, ast: Ast::new(vec![]) }
|
||||
}
|
||||
pub fn parse_src(&mut self) -> ShResult<()> {
|
||||
let mut tokens = vec![];
|
||||
for token in LexStream::new(self.src.clone(), LexFlags::empty()) {
|
||||
tokens.push(token?);
|
||||
}
|
||||
|
||||
let mut nodes = vec![];
|
||||
for result in ParseStream::new(tokens) {
|
||||
nodes.push(result?);
|
||||
}
|
||||
*self.ast.tree_mut() = nodes;
|
||||
Ok(())
|
||||
}
|
||||
pub fn extract_nodes(&mut self) -> Vec<Node> {
|
||||
mem::take(self.ast.tree_mut())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct Node<'t> {
|
||||
pub class: NdRule<'t>,
|
||||
pub flags: NdFlags,
|
||||
pub redirs: Vec<Redir>,
|
||||
pub tokens: Vec<Tk<'t>>,
|
||||
pub struct Ast(Vec<Node>);
|
||||
|
||||
impl Ast {
|
||||
pub fn new(tree: Vec<Node>) -> Self {
|
||||
Self(tree)
|
||||
}
|
||||
pub fn into_inner(self) -> Vec<Node> {
|
||||
self.0
|
||||
}
|
||||
pub fn tree_mut(&mut self) -> &mut Vec<Node> {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'t> Node<'t> {
|
||||
pub fn get_command(&'t self) -> Option<&'t Tk<'t>> {
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct Node {
|
||||
pub class: NdRule,
|
||||
pub flags: NdFlags,
|
||||
pub redirs: Vec<Redir>,
|
||||
pub tokens: Vec<Tk>,
|
||||
}
|
||||
|
||||
impl Node {
|
||||
pub fn get_command(&self) -> Option<&Tk> {
|
||||
let NdRule::Command { assignments: _, argv } = &self.class else {
|
||||
return None
|
||||
};
|
||||
let command = argv.iter().find(|tk| tk.flags.contains(TkFlags::IS_CMD))?;
|
||||
Some(command)
|
||||
}
|
||||
pub fn get_span(&'t self) -> Span<'t> {
|
||||
pub fn get_span(&self) -> Span {
|
||||
let Some(first_tk) = self.tokens.first() else {
|
||||
unreachable!()
|
||||
};
|
||||
@@ -187,15 +235,15 @@ pub enum RedirType {
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct CondNode<'t> {
|
||||
pub cond: Box<Node<'t>>,
|
||||
pub body: Vec<Node<'t>>
|
||||
pub struct CondNode {
|
||||
pub cond: Box<Node>,
|
||||
pub body: Vec<Node>
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct CaseNode<'t> {
|
||||
pub pattern: Tk<'t>,
|
||||
pub body: Vec<Node<'t>>
|
||||
pub struct CaseNode {
|
||||
pub pattern: Tk,
|
||||
pub body: Vec<Node>
|
||||
}
|
||||
|
||||
#[derive(Clone,Copy,PartialEq,Debug)]
|
||||
@@ -206,8 +254,8 @@ pub enum ConjunctOp {
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct ConjunctNode<'t> {
|
||||
pub cmd: Box<Node<'t>>,
|
||||
pub struct ConjunctNode {
|
||||
pub cmd: Box<Node>,
|
||||
pub operator: ConjunctOp
|
||||
}
|
||||
|
||||
@@ -229,7 +277,7 @@ impl FromStr for LoopKind {
|
||||
}
|
||||
|
||||
impl Display for LoopKind {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
LoopKind::While => write!(f,"while"),
|
||||
LoopKind::Until => write!(f,"until")
|
||||
@@ -247,22 +295,22 @@ pub enum AssignKind {
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub enum NdRule<'t> {
|
||||
IfNode { cond_nodes: Vec<CondNode<'t>>, else_block: Vec<Node<'t>> },
|
||||
LoopNode { kind: LoopKind, cond_node: CondNode<'t> },
|
||||
ForNode { vars: Vec<Tk<'t>>, arr: Vec<Tk<'t>>, body: Vec<Node<'t>> },
|
||||
CaseNode { pattern: Tk<'t>, case_blocks: Vec<CaseNode<'t>> },
|
||||
Command { assignments: Vec<Node<'t>>, argv: Vec<Tk<'t>> },
|
||||
Pipeline { cmds: Vec<Node<'t>>, pipe_err: bool },
|
||||
Conjunction { elements: Vec<ConjunctNode<'t>> },
|
||||
Assignment { kind: AssignKind, var: Tk<'t>, val: Tk<'t> },
|
||||
BraceGrp { body: Vec<Node<'t>> },
|
||||
FuncDef { name: Tk<'t>, body: Box<Node<'t>> }
|
||||
pub enum NdRule {
|
||||
IfNode { cond_nodes: Vec<CondNode>, else_block: Vec<Node> },
|
||||
LoopNode { kind: LoopKind, cond_node: CondNode },
|
||||
ForNode { vars: Vec<Tk>, arr: Vec<Tk>, body: Vec<Node> },
|
||||
CaseNode { pattern: Tk, case_blocks: Vec<CaseNode> },
|
||||
Command { assignments: Vec<Node>, argv: Vec<Tk> },
|
||||
Pipeline { cmds: Vec<Node>, pipe_err: bool },
|
||||
Conjunction { elements: Vec<ConjunctNode> },
|
||||
Assignment { kind: AssignKind, var: Tk, val: Tk },
|
||||
BraceGrp { body: Vec<Node> },
|
||||
FuncDef { name: Tk, body: Box<Node> }
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ParseStream<'t> {
|
||||
pub tokens: Vec<Tk<'t>>,
|
||||
pub struct ParseStream {
|
||||
pub tokens: Vec<Tk>,
|
||||
pub flags: ParseFlags
|
||||
}
|
||||
|
||||
@@ -273,8 +321,8 @@ bitflags! {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'t> ParseStream<'t> {
|
||||
pub fn new(tokens: Vec<Tk<'t>>) -> Self {
|
||||
impl ParseStream {
|
||||
pub fn new(tokens: Vec<Tk>) -> Self {
|
||||
Self { tokens, flags: ParseFlags::empty() }
|
||||
}
|
||||
fn next_tk_class(&self) -> &TkRule {
|
||||
@@ -284,10 +332,10 @@ impl<'t> ParseStream<'t> {
|
||||
&TkRule::Null
|
||||
}
|
||||
}
|
||||
fn peek_tk(&self) -> Option<&Tk<'t>> {
|
||||
fn peek_tk(&self) -> Option<&Tk> {
|
||||
self.tokens.first()
|
||||
}
|
||||
fn next_tk(&mut self) -> Option<Tk<'t>> {
|
||||
fn next_tk(&mut self) -> Option<Tk> {
|
||||
if !self.tokens.is_empty() {
|
||||
if *self.next_tk_class() == TkRule::EOI {
|
||||
return None
|
||||
@@ -306,20 +354,20 @@ impl<'t> ParseStream<'t> {
|
||||
/// fi
|
||||
/// ```
|
||||
/// are valid syntax
|
||||
fn catch_separator(&mut self, node_tks: &mut Vec<Tk<'t>>) {
|
||||
fn catch_separator(&mut self, node_tks: &mut Vec<Tk>) {
|
||||
if *self.next_tk_class() == TkRule::Sep {
|
||||
node_tks.push(self.next_tk().unwrap());
|
||||
}
|
||||
}
|
||||
fn assert_separator(&mut self, node_tks: &mut Vec<Tk<'t>>) -> ShResult<()> {
|
||||
fn assert_separator(&mut self, node_tks: &mut Vec<Tk>) -> ShResult<()> {
|
||||
let next_class = self.next_tk_class();
|
||||
match next_class {
|
||||
TkRule::EOI |
|
||||
TkRule::Or |
|
||||
TkRule::Bg |
|
||||
TkRule::And |
|
||||
TkRule::BraceGrpEnd |
|
||||
TkRule::Pipe => Ok(()),
|
||||
TkRule::Or |
|
||||
TkRule::Bg |
|
||||
TkRule::And |
|
||||
TkRule::BraceGrpEnd |
|
||||
TkRule::Pipe => Ok(()),
|
||||
|
||||
TkRule::Sep => {
|
||||
if let Some(tk) = self.next_tk() {
|
||||
@@ -352,7 +400,7 @@ impl<'t> ParseStream<'t> {
|
||||
assert!(num_consumed <= self.tokens.len());
|
||||
self.tokens = self.tokens[num_consumed..].to_vec();
|
||||
}
|
||||
fn parse_cmd_list(&mut self) -> ShResult<Option<Node<'t>>> {
|
||||
fn parse_cmd_list(&mut self) -> ShResult<Option<Node>> {
|
||||
let mut elements = vec![];
|
||||
let mut node_tks = vec![];
|
||||
|
||||
@@ -390,7 +438,7 @@ impl<'t> ParseStream<'t> {
|
||||
/// Matches shell commands like if-then-fi, pipelines, etc.
|
||||
/// 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_pipeline()
|
||||
fn parse_block(&mut self, check_pipelines: bool) -> ShResult<Option<Node<'t>>> {
|
||||
fn parse_block(&mut self, check_pipelines: bool) -> ShResult<Option<Node>> {
|
||||
try_match!(self.parse_func_def()?);
|
||||
try_match!(self.parse_brc_grp(false /* from_func_def */)?);
|
||||
try_match!(self.parse_loop()?);
|
||||
@@ -402,7 +450,7 @@ impl<'t> ParseStream<'t> {
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
fn parse_func_def(&mut self) -> ShResult<Option<Node<'t>>> {
|
||||
fn parse_func_def(&mut self) -> ShResult<Option<Node>> {
|
||||
let mut node_tks: Vec<Tk> = vec![];
|
||||
let name;
|
||||
let body;
|
||||
@@ -418,7 +466,7 @@ impl<'t> ParseStream<'t> {
|
||||
return Err(parse_err_full(
|
||||
"Expected a brace group after function name",
|
||||
&node_tks.get_span().unwrap()
|
||||
)
|
||||
)
|
||||
)
|
||||
};
|
||||
body = Box::new(brc_grp);
|
||||
@@ -429,11 +477,10 @@ impl<'t> ParseStream<'t> {
|
||||
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>>> {
|
||||
fn parse_brc_grp(&mut self, from_func_def: bool) -> ShResult<Option<Node>> {
|
||||
let mut node_tks: Vec<Tk> = vec![];
|
||||
let mut body: Vec<Node> = vec![];
|
||||
let mut redirs: Vec<Redir> = vec![];
|
||||
@@ -456,7 +503,7 @@ impl<'t> ParseStream<'t> {
|
||||
return Err(parse_err_full(
|
||||
"Expected a closing brace for this brace group",
|
||||
&node_tks.get_span().unwrap()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -507,10 +554,9 @@ impl<'t> ParseStream<'t> {
|
||||
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>> {
|
||||
// Needs at last one 'if-then',
|
||||
// Any number of 'elif-then',
|
||||
// Zero or one 'else'
|
||||
@@ -532,16 +578,16 @@ impl<'t> ParseStream<'t> {
|
||||
};
|
||||
let Some(cond) = self.parse_block(true)? else {
|
||||
return Err(parse_err_full(
|
||||
&format!("Expected an expression after '{prefix_keywrd}'"),
|
||||
&node_tks.get_span().unwrap()
|
||||
&format!("Expected an expression after '{prefix_keywrd}'"),
|
||||
&node_tks.get_span().unwrap()
|
||||
));
|
||||
};
|
||||
node_tks.extend(cond.tokens.clone());
|
||||
|
||||
if !self.check_keyword("then") || !self.next_tk_is_some() {
|
||||
return Err(parse_err_full(
|
||||
&format!("Expected 'then' after '{prefix_keywrd}' condition"),
|
||||
&node_tks.get_span().unwrap()
|
||||
&format!("Expected 'then' after '{prefix_keywrd}' condition"),
|
||||
&node_tks.get_span().unwrap()
|
||||
));
|
||||
}
|
||||
node_tks.push(self.next_tk().unwrap());
|
||||
@@ -554,8 +600,8 @@ impl<'t> ParseStream<'t> {
|
||||
}
|
||||
if body_blocks.is_empty() {
|
||||
return Err(parse_err_full(
|
||||
"Expected an expression after 'then'",
|
||||
&node_tks.get_span().unwrap()
|
||||
"Expected an expression after 'then'",
|
||||
&node_tks.get_span().unwrap()
|
||||
));
|
||||
};
|
||||
let cond_node = CondNode { cond: Box::new(cond), body: body_blocks };
|
||||
@@ -577,8 +623,8 @@ impl<'t> ParseStream<'t> {
|
||||
}
|
||||
if else_block.is_empty() {
|
||||
return Err(parse_err_full(
|
||||
"Expected an expression after 'else'",
|
||||
&node_tks.get_span().unwrap()
|
||||
"Expected an expression after 'else'",
|
||||
&node_tks.get_span().unwrap()
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -639,10 +685,10 @@ impl<'t> ParseStream<'t> {
|
||||
};
|
||||
Ok(Some(node))
|
||||
}
|
||||
fn parse_loop(&mut self) -> ShResult<Option<Node<'t>>> {
|
||||
fn parse_loop(&mut self) -> ShResult<Option<Node>> {
|
||||
// Requires a single CondNode and a LoopKind
|
||||
let loop_kind: LoopKind;
|
||||
let cond_node: CondNode<'t>;
|
||||
let cond_node: CondNode;
|
||||
let mut node_tks = vec![];
|
||||
|
||||
if (!self.check_keyword("while") && !self.check_keyword("until")) || !self.next_tk_is_some() {
|
||||
@@ -653,57 +699,57 @@ impl<'t> ParseStream<'t> {
|
||||
.as_str()
|
||||
.parse() // LoopKind implements FromStr
|
||||
.unwrap();
|
||||
node_tks.push(loop_tk);
|
||||
self.catch_separator(&mut node_tks);
|
||||
node_tks.push(loop_tk);
|
||||
self.catch_separator(&mut node_tks);
|
||||
|
||||
let Some(cond) = self.parse_block(true)? else {
|
||||
return Err(parse_err_full(
|
||||
&format!("Expected an expression after '{loop_kind}'"), // It also implements Display
|
||||
&node_tks.get_span().unwrap()
|
||||
))
|
||||
};
|
||||
node_tks.extend(cond.tokens.clone());
|
||||
let Some(cond) = self.parse_block(true)? else {
|
||||
return Err(parse_err_full(
|
||||
&format!("Expected an expression after '{loop_kind}'"), // It also implements Display
|
||||
&node_tks.get_span().unwrap()
|
||||
))
|
||||
};
|
||||
node_tks.extend(cond.tokens.clone());
|
||||
|
||||
if !self.check_keyword("do") || !self.next_tk_is_some() {
|
||||
return Err(parse_err_full(
|
||||
"Expected 'do' after loop condition",
|
||||
&node_tks.get_span().unwrap()
|
||||
))
|
||||
}
|
||||
node_tks.push(self.next_tk().unwrap());
|
||||
self.catch_separator(&mut node_tks);
|
||||
if !self.check_keyword("do") || !self.next_tk_is_some() {
|
||||
return Err(parse_err_full(
|
||||
"Expected 'do' after loop condition",
|
||||
&node_tks.get_span().unwrap()
|
||||
))
|
||||
}
|
||||
node_tks.push(self.next_tk().unwrap());
|
||||
self.catch_separator(&mut node_tks);
|
||||
|
||||
let mut body = vec![];
|
||||
while let Some(block) = self.parse_block(true)? {
|
||||
node_tks.extend(block.tokens.clone());
|
||||
body.push(block);
|
||||
}
|
||||
if body.is_empty() {
|
||||
return Err(parse_err_full(
|
||||
"Expected an expression after 'do'",
|
||||
&node_tks.get_span().unwrap()
|
||||
))
|
||||
};
|
||||
let mut body = vec![];
|
||||
while let Some(block) = self.parse_block(true)? {
|
||||
node_tks.extend(block.tokens.clone());
|
||||
body.push(block);
|
||||
}
|
||||
if body.is_empty() {
|
||||
return Err(parse_err_full(
|
||||
"Expected an expression after 'do'",
|
||||
&node_tks.get_span().unwrap()
|
||||
))
|
||||
};
|
||||
|
||||
if !self.check_keyword("done") || !self.next_tk_is_some() {
|
||||
return Err(parse_err_full(
|
||||
"Expected 'done' after loop body",
|
||||
&node_tks.get_span().unwrap()
|
||||
))
|
||||
}
|
||||
node_tks.push(self.next_tk().unwrap());
|
||||
self.assert_separator(&mut node_tks)?;
|
||||
if !self.check_keyword("done") || !self.next_tk_is_some() {
|
||||
return Err(parse_err_full(
|
||||
"Expected 'done' after loop body",
|
||||
&node_tks.get_span().unwrap()
|
||||
))
|
||||
}
|
||||
node_tks.push(self.next_tk().unwrap());
|
||||
self.assert_separator(&mut node_tks)?;
|
||||
|
||||
cond_node = CondNode { cond: Box::new(cond), body };
|
||||
let loop_node = Node {
|
||||
class: NdRule::LoopNode { kind: loop_kind, cond_node },
|
||||
flags: NdFlags::empty(),
|
||||
redirs: vec![],
|
||||
tokens: node_tks
|
||||
};
|
||||
Ok(Some(loop_node))
|
||||
cond_node = CondNode { cond: Box::new(cond), body };
|
||||
let loop_node = Node {
|
||||
class: NdRule::LoopNode { kind: loop_kind, cond_node },
|
||||
flags: NdFlags::empty(),
|
||||
redirs: vec![],
|
||||
tokens: node_tks
|
||||
};
|
||||
Ok(Some(loop_node))
|
||||
}
|
||||
fn parse_pipeline(&mut self) -> ShResult<Option<Node<'t>>> {
|
||||
fn parse_pipeline(&mut self) -> ShResult<Option<Node>> {
|
||||
let mut cmds = vec![];
|
||||
let mut node_tks = vec![];
|
||||
while let Some(cmd) = self.parse_block(false)? {
|
||||
@@ -732,7 +778,7 @@ impl<'t> ParseStream<'t> {
|
||||
}))
|
||||
}
|
||||
}
|
||||
fn parse_cmd(&mut self) -> ShResult<Option<Node<'t>>> {
|
||||
fn parse_cmd(&mut self) -> ShResult<Option<Node>> {
|
||||
let tk_slice = self.tokens.as_slice();
|
||||
let mut tk_iter = tk_slice.iter();
|
||||
let mut node_tks = vec![];
|
||||
@@ -804,8 +850,8 @@ impl<'t> ParseStream<'t> {
|
||||
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
|
||||
"Error opening file for redirection",
|
||||
&path_tk.span
|
||||
));
|
||||
};
|
||||
|
||||
@@ -827,7 +873,7 @@ impl<'t> ParseStream<'t> {
|
||||
redirs,
|
||||
}))
|
||||
}
|
||||
fn parse_assignment(&self, token: &Tk<'t>) -> Option<Node<'t>> {
|
||||
fn parse_assignment(&self, token: &Tk) -> Option<Node> {
|
||||
let mut chars = token.span.as_str().chars();
|
||||
let mut var_name = String::new();
|
||||
let mut name_range = token.span.start..token.span.start;
|
||||
@@ -930,8 +976,8 @@ impl<'t> ParseStream<'t> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'t> Iterator for ParseStream<'t> {
|
||||
type Item = ShResult<Node<'t>>;
|
||||
impl Iterator for ParseStream {
|
||||
type Item = ShResult<Node>;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
// Empty token vector or only SOI/EOI tokens, nothing to do
|
||||
if self.tokens.is_empty() || self.tokens.len() == 2 {
|
||||
@@ -960,7 +1006,7 @@ impl<'t> Iterator for ParseStream<'t> {
|
||||
}
|
||||
}
|
||||
|
||||
fn node_is_punctuated<'t>(tokens: &Vec<Tk>) -> bool {
|
||||
fn node_is_punctuated(tokens: &Vec<Tk>) -> bool {
|
||||
tokens.last().is_some_and(|tk| {
|
||||
matches!(tk.class, TkRule::Sep)
|
||||
})
|
||||
@@ -992,7 +1038,7 @@ fn get_redir_file(class: RedirType, path: PathBuf) -> ShResult<File> {
|
||||
Ok(result?)
|
||||
}
|
||||
|
||||
fn parse_err_full<'t>(reason: &str, blame: &Span<'t>) -> ShErr {
|
||||
fn parse_err_full(reason: &str, blame: &Span) -> ShErr {
|
||||
ShErr::full(
|
||||
ShErrKind::ParseErr,
|
||||
reason,
|
||||
@@ -1000,9 +1046,9 @@ fn parse_err_full<'t>(reason: &str, blame: &Span<'t>) -> ShErr {
|
||||
)
|
||||
}
|
||||
|
||||
fn is_func_name<'t>(tk: Option<&Tk<'t>>) -> bool {
|
||||
fn is_func_name(tk: Option<&Tk>) -> bool {
|
||||
tk.is_some_and(|tk| {
|
||||
tk.flags.contains(TkFlags::KEYWORD) &&
|
||||
(tk.span.as_str().ends_with("()") && !tk.span.as_str().ends_with("\\()"))
|
||||
(tk.span.as_str().ends_with("()") && !tk.span.as_str().ends_with("\\()"))
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user