added another test for the parser

This commit is contained in:
2025-03-19 15:16:43 -04:00
parent bbcbbe0ada
commit 3b0e576d29
11 changed files with 20179 additions and 17 deletions

39
src/builtin/flowctl.rs Normal file
View File

@@ -0,0 +1,39 @@
use crate::{builtin::setup_builtin, jobs::JobBldr, libsh::error::{ShErr, ShErrKind, ShResult}, parse::{execute::prepare_argv, NdRule, Node}, prelude::*, procio::IoStack, state};
pub fn flowctl(node: Node, kind: ShErrKind) -> ShResult<()> {
use ShErrKind::*;
let NdRule::Command { assignments: _, argv } = node.class else {
unreachable!()
};
let mut code = 0;
let mut argv = prepare_argv(argv);
let cmd = argv.remove(0).0;
if !argv.is_empty() {
let (arg,span) = argv
.into_iter()
.next()
.unwrap();
let Ok(status) = arg.parse::<i32>() else {
return Err(
ShErr::full(ShErrKind::SyntaxErr, format!("{cmd}: Expected a number"), span)
)
};
code = status;
}
flog!(DEBUG,code);
let kind = match kind {
LoopContinue(_) => LoopContinue(code),
LoopBreak(_) => LoopBreak(code),
FuncReturn(_) => FuncReturn(code),
CleanExit(_) => CleanExit(code),
_ => unreachable!()
};
Err(ShErr::simple(kind, ""))
}

View File

@@ -10,8 +10,9 @@ pub mod source;
pub mod shift;
pub mod jobctl;
pub mod alias;
pub mod flowctl;
pub const BUILTINS: [&str;10] = [
pub const BUILTINS: [&str;14] = [
"echo",
"cd",
"export",
@@ -21,7 +22,11 @@ pub const BUILTINS: [&str;10] = [
"jobs",
"fg",
"bg",
"alias"
"alias",
"return",
"break",
"continue",
"exit"
];
/// Sets up a builtin command

View File

@@ -1,4 +1,4 @@
use crate::{libsh::{error::ShResult, term::{Style, Styled}}, prelude::*, procio::borrow_fd, state::{set_status, write_jobs}};
use crate::{libsh::{error::ShResult, term::{Style, Styled}}, prelude::*, procio::borrow_fd, state::{self, set_status, write_jobs}};
pub const SIG_EXIT_OFFSET: i32 = 128;
@@ -460,7 +460,9 @@ impl Job {
for child in self.children.iter_mut() {
if child.pid == Pid::this() {
// TODO: figure out some way to get the exit code of builtins
stats.push(WtStat::Exited(child.pid, 0));
let code = state::get_status();
flog!(DEBUG,code);
stats.push(WtStat::Exited(child.pid, code));
continue
}
let result = child.wait(Some(WtFlag::WSTOPPED));
@@ -619,7 +621,9 @@ pub fn wait_fg(job: Job) -> ShResult<()> {
attach_tty(job.pgid())?;
disable_reaping()?;
let statuses = write_jobs(|j| j.new_fg(job))?;
flog!(DEBUG,statuses);
for status in statuses {
flog!(DEBUG,status);
match status {
WtStat::Exited(_, exit_code) => {
code = exit_code;

View File

@@ -217,10 +217,10 @@ pub enum ShErrKind {
Errno,
FileNotFound(String),
CmdNotFound(String),
CleanExit,
FuncReturn,
LoopContinue,
LoopBreak,
CleanExit(i32),
FuncReturn(i32),
LoopContinue(i32),
LoopBreak(i32),
Null
}
@@ -237,10 +237,10 @@ impl Display for ShErrKind {
ShErrKind::Errno => "ERRNO",
ShErrKind::FileNotFound(file) => &format!("File not found: {file}"),
ShErrKind::CmdNotFound(cmd) => &format!("Command not found: {cmd}"),
ShErrKind::CleanExit => "",
ShErrKind::FuncReturn => "",
ShErrKind::LoopContinue => "",
ShErrKind::LoopBreak => "",
ShErrKind::CleanExit(_) => "",
ShErrKind::FuncReturn(_) => "",
ShErrKind::LoopContinue(_) => "",
ShErrKind::LoopBreak(_) => "",
ShErrKind::Null => "",
};
write!(f,"{output}")

View File

@@ -1,7 +1,7 @@
use std::collections::VecDeque;
use crate::{builtin::{alias::alias, 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 crate::{builtin::{alias::alias, cd::cd, echo::echo, export::export, flowctl::flowctl, 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, KEYWORDS}, AssignKind, CaseNode, CondNode, ConjunctNode, ConjunctOp, LoopKind, NdFlags, NdRule, Node, ParseStream, ParsedSrc, Redir, RedirType};
@@ -151,7 +151,14 @@ impl Dispatcher {
if let Err(e) = self.exec_brc_grp((*func).clone()) {
write_vars(|v| **v = scope_snapshot);
return Err(e.into())
match e.kind() {
ShErrKind::FuncReturn(code) => {
state::set_status(*code);
return Ok(())
}
_ => return Err(e.into())
}
}
// Return to the outer scope
@@ -233,7 +240,7 @@ impl Dispatcher {
body_frame.extend(out_redirs);
let CondNode { cond, body } = cond_node;
loop {
'outer: loop {
self.io_stack.push(cond_frame.clone());
if let Err(e) = self.dispatch_node(*cond.clone()) {
@@ -247,8 +254,14 @@ impl Dispatcher {
for node in &body {
if let Err(e) = self.dispatch_node(node.clone()) {
match e.kind() {
ShErrKind::LoopBreak => break,
ShErrKind::LoopContinue => continue,
ShErrKind::LoopBreak(code) => {
state::set_status(*code);
break 'outer
}
ShErrKind::LoopContinue(code) => {
state::set_status(*code);
continue 'outer
}
_ => return Err(e.into())
}
}
@@ -346,6 +359,10 @@ impl Dispatcher {
"bg" => continue_job(cmd, curr_job_mut, JobBehavior::Background),
"jobs" => jobs(cmd, io_stack_mut, curr_job_mut),
"alias" => alias(cmd, io_stack_mut, curr_job_mut),
"return" => flowctl(cmd, ShErrKind::FuncReturn(0)),
"break" => flowctl(cmd, ShErrKind::LoopBreak(0)),
"continue" => flowctl(cmd, ShErrKind::LoopContinue(0)),
"exit" => flowctl(cmd, ShErrKind::CleanExit(0)),
_ => unimplemented!("Have not yet added support for builtin '{}'", cmd_raw.span.as_str())
};

View File

@@ -615,6 +615,7 @@ impl ParseStream {
if self.check_keyword("esac") {
node_tks.push(self.next_tk().unwrap());
self.assert_separator(&mut node_tks)?;
break
}
@@ -623,6 +624,7 @@ impl ParseStream {
}
}
let node = Node {
class: NdRule::CaseNode { pattern, case_blocks },
flags: NdFlags::empty(),

View File

@@ -66,6 +66,7 @@ impl Highlighter for FernReadline {
impl Validator for FernReadline {
fn validate(&self, ctx: &mut rustyline::validate::ValidationContext) -> rustyline::Result<rustyline::validate::ValidationResult> {
return Ok(ValidationResult::Valid(None));
let mut tokens = vec![];
let tk_stream = LexStream::new(Rc::new(ctx.input().to_string()), LexFlags::empty());
for tk in tk_stream {

View File

@@ -315,7 +315,10 @@ pub fn write_logic<T, F: FnOnce(&mut RwLockWriteGuard<LogTab>) -> T>(f: F) -> T
pub fn get_status() -> i32 {
read_vars(|v| v.get_param('?')).parse::<i32>().unwrap()
}
#[track_caller]
pub fn set_status(code: i32) {
flog!(DEBUG,std::panic::Location::caller());
flog!(DEBUG,code);
write_vars(|v| v.set_param('?', &code.to_string()))
}

View File

@@ -184,3 +184,15 @@ esac";
insta::assert_debug_snapshot!(nodes)
}
#[test]
fn parse_cursed() {
let input = "if if if if case foo in foo) if true; then true; fi;; esac; then case foo in foo) until true; do true; done;; esac; fi; then until if case foo in foo) true;; esac; then if true; then true; fi; fi; do until until true; do true; done; do case foo in foo) true;; esac; done; done; fi; then until until case foo in foo) true;; esac; do if true; then true; fi; done; do until true; do true; done; done; fi; then until case foo in foo) case foo in foo) true;; esac;; esac; do if if true; then true; fi; then until true; do true; done; fi; done; elif until until case foo in foo) true;; esac; do if true; then true; fi; done; do case foo in foo) until true; do true; done;; esac; done; then case foo in foo) if case foo in foo) true;; esac; then if true; then true; fi; fi;; esac; else case foo in foo) until until true; do true; done; do case foo in foo) true;; esac; done;; esac; fi";
let tk_stream: Vec<_> = LexStream::new(Rc::new(input.to_string()), LexFlags::empty())
.map(|tk| tk.unwrap())
.collect();
let nodes: Vec<_> = ParseStream::new(tk_stream).collect();
// 15,000 line snapshot file btw
insta::assert_debug_snapshot!(nodes)
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff