Work on integrating error reporting using the ariadne crate
This commit is contained in:
@@ -1,16 +1,17 @@
|
||||
use std::{
|
||||
collections::{HashSet, VecDeque},
|
||||
os::unix::fs::PermissionsExt,
|
||||
cell::Cell, collections::{HashSet, VecDeque}, os::unix::fs::PermissionsExt
|
||||
};
|
||||
|
||||
use ariadne::{Fmt, Label};
|
||||
|
||||
use crate::{
|
||||
builtin::{
|
||||
alias::{alias, unalias}, arrops::{arr_pop, arr_fpop, arr_push, arr_fpush, arr_rotate}, cd::cd, complete::{compgen_builtin, complete_builtin}, dirstack::{dirs, popd, pushd}, echo::echo, eval, exec, flowctl::flowctl, jobctl::{JobBehavior, continue_job, disown, jobs}, map, pwd::pwd, read::read_builtin, shift::shift, shopt::shopt, source::source, test::double_bracket_test, trap::{TrapTarget, trap}, varcmds::{export, local, readonly, unset}, zoltraak::zoltraak
|
||||
alias::{alias, unalias}, arrops::{arr_fpop, arr_fpush, arr_pop, arr_push, arr_rotate}, cd::cd, complete::{compgen_builtin, complete_builtin}, dirstack::{dirs, popd, pushd}, echo::echo, eval, exec, flowctl::flowctl, jobctl::{JobBehavior, continue_job, disown, jobs}, map, pwd::pwd, read::read_builtin, shift::shift, shopt::shopt, source::source, test::double_bracket_test, trap::{TrapTarget, trap}, varcmds::{export, local, readonly, unset}, zoltraak::zoltraak
|
||||
},
|
||||
expand::{expand_aliases, glob_to_regex},
|
||||
jobs::{ChildProc, JobStack, dispatch_job},
|
||||
libsh::{
|
||||
error::{ShErr, ShErrKind, ShResult, ShResultExt},
|
||||
error::{ShErr, ShErrKind, ShResult, ShResultExt, next_color},
|
||||
utils::RedirVecUtils,
|
||||
},
|
||||
prelude::*,
|
||||
@@ -27,7 +28,7 @@ use super::{
|
||||
};
|
||||
|
||||
thread_local! {
|
||||
static RECURSE_DEPTH: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
|
||||
static RECURSE_DEPTH: Cell<usize> = const { Cell::new(0) };
|
||||
}
|
||||
|
||||
pub fn is_in_path(name: &str) -> bool {
|
||||
@@ -160,7 +161,7 @@ pub fn exec_input(input: String, io_stack: Option<IoStack>, interactive: bool) -
|
||||
let mut parser = ParsedSrc::new(Arc::new(input)).with_lex_flags(lex_flags);
|
||||
if let Err(errors) = parser.parse_src() {
|
||||
for error in errors {
|
||||
eprintln!("{error}");
|
||||
error.print_error();
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
@@ -284,6 +285,7 @@ impl Dispatcher {
|
||||
}
|
||||
pub fn exec_func_def(&mut self, func_def: Node) -> ShResult<()> {
|
||||
let blame = func_def.get_span();
|
||||
let ctx = func_def.context.clone();
|
||||
let NdRule::FuncDef { name, body } = func_def.class else {
|
||||
unreachable!()
|
||||
};
|
||||
@@ -299,10 +301,10 @@ impl Dispatcher {
|
||||
));
|
||||
}
|
||||
|
||||
let mut func_parser = ParsedSrc::new(Arc::new(body));
|
||||
let mut func_parser = ParsedSrc::new(Arc::new(body)).with_context(ctx);
|
||||
if let Err(errors) = func_parser.parse_src() {
|
||||
for error in errors {
|
||||
eprintln!("{error}");
|
||||
error.print_error();
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
@@ -318,14 +320,14 @@ impl Dispatcher {
|
||||
|
||||
self.run_fork("anonymous_subshell", |s| {
|
||||
if let Err(e) = s.set_assignments(assignments, AssignBehavior::Export) {
|
||||
eprintln!("{e}");
|
||||
e.print_error();
|
||||
return;
|
||||
};
|
||||
s.io_stack.append_to_frame(subsh.redirs);
|
||||
let mut argv = match prepare_argv(argv) {
|
||||
Ok(argv) => argv,
|
||||
Err(e) => {
|
||||
eprintln!("{e}");
|
||||
e.print_error();
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -334,12 +336,12 @@ impl Dispatcher {
|
||||
let subsh_body = subsh.0.to_string();
|
||||
|
||||
if let Err(e) = exec_input(subsh_body, None, s.interactive) {
|
||||
eprintln!("{e}");
|
||||
e.print_error();
|
||||
};
|
||||
})
|
||||
}
|
||||
fn exec_func(&mut self, func: Node) -> ShResult<()> {
|
||||
let blame = func.get_span().clone();
|
||||
let mut blame = func.get_span().clone();
|
||||
let NdRule::Command {
|
||||
assignments,
|
||||
mut argv,
|
||||
@@ -369,10 +371,11 @@ impl Dispatcher {
|
||||
self.io_stack.append_to_frame(func.redirs);
|
||||
|
||||
let func_name = argv.remove(0).span.as_str().to_string();
|
||||
blame.rename(func_name.clone());
|
||||
|
||||
let argv = prepare_argv(argv)?;
|
||||
let result = if let Some(ref mut func_body) = read_logic(|l| l.get_func(&func_name)) {
|
||||
let _guard = ScopeGuard::exclusive_scope(Some(argv));
|
||||
|
||||
func_body.body_mut().flags = func.flags;
|
||||
|
||||
if let Err(e) = self.exec_brc_grp(func_body.body().clone()) {
|
||||
@@ -381,7 +384,7 @@ impl Dispatcher {
|
||||
state::set_status(*code);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(e).blame(blame),
|
||||
_ => Err(e),
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
@@ -418,7 +421,7 @@ impl Dispatcher {
|
||||
log::trace!("Forking brace group");
|
||||
self.run_fork("brace group", |s| {
|
||||
if let Err(e) = brc_grp_logic(s) {
|
||||
eprintln!("{e}");
|
||||
e.print_error();
|
||||
}
|
||||
})
|
||||
} else {
|
||||
@@ -472,7 +475,7 @@ impl Dispatcher {
|
||||
log::trace!("Forking builtin: case");
|
||||
self.run_fork("case", |s| {
|
||||
if let Err(e) = case_logic(s) {
|
||||
eprintln!("{e}");
|
||||
e.print_error();
|
||||
}
|
||||
})
|
||||
} else {
|
||||
@@ -535,7 +538,7 @@ impl Dispatcher {
|
||||
log::trace!("Forking builtin: loop");
|
||||
self.run_fork("loop", |s| {
|
||||
if let Err(e) = loop_logic(s) {
|
||||
eprintln!("{e}");
|
||||
e.print_error();
|
||||
}
|
||||
})
|
||||
} else {
|
||||
@@ -613,7 +616,7 @@ impl Dispatcher {
|
||||
log::trace!("Forking builtin: for");
|
||||
self.run_fork("for", |s| {
|
||||
if let Err(e) = for_logic(s) {
|
||||
eprintln!("{e}");
|
||||
e.print_error();
|
||||
}
|
||||
})
|
||||
} else {
|
||||
@@ -669,7 +672,7 @@ impl Dispatcher {
|
||||
log::trace!("Forking builtin: if");
|
||||
self.run_fork("if", |s| {
|
||||
if let Err(e) = if_logic(s) {
|
||||
eprintln!("{e}");
|
||||
e.print_error();
|
||||
state::set_status(1);
|
||||
}
|
||||
})
|
||||
@@ -726,7 +729,7 @@ impl Dispatcher {
|
||||
let _guard = self.io_stack.pop_frame().redirect()?;
|
||||
self.run_fork(&cmd_raw, |s| {
|
||||
if let Err(e) = s.dispatch_builtin(cmd) {
|
||||
eprintln!("{e}");
|
||||
e.print_error();
|
||||
}
|
||||
})
|
||||
} else {
|
||||
@@ -819,6 +822,7 @@ impl Dispatcher {
|
||||
}
|
||||
}
|
||||
fn exec_cmd(&mut self, cmd: Node) -> ShResult<()> {
|
||||
let context = cmd.context.clone();
|
||||
let NdRule::Command { assignments, argv } = cmd.class else {
|
||||
unreachable!()
|
||||
};
|
||||
@@ -855,12 +859,22 @@ impl Dispatcher {
|
||||
let cmd_str = cmd.to_str().unwrap().to_string();
|
||||
match e {
|
||||
Errno::ENOENT => {
|
||||
let err = ShErr::full(ShErrKind::CmdNotFound(cmd_str), "", span);
|
||||
eprintln!("{err}");
|
||||
let source = span.span_source().clone();
|
||||
let color = next_color();
|
||||
ShErr::full(ShErrKind::CmdNotFound, "", span.clone())
|
||||
.with_label(
|
||||
source,
|
||||
Label::new(span)
|
||||
.with_color(color)
|
||||
.with_message(format!("{}: command not found", cmd_str.fg(color)))
|
||||
)
|
||||
.with_context(context)
|
||||
.print_error();
|
||||
}
|
||||
_ => {
|
||||
let err = ShErr::full(ShErrKind::Errno(e), format!("{e}"), span);
|
||||
eprintln!("{err}");
|
||||
ShErr::full(ShErrKind::Errno(e), format!("{e}"), span)
|
||||
.with_context(context)
|
||||
.print_error();
|
||||
}
|
||||
}
|
||||
exit(e as i32)
|
||||
|
||||
@@ -64,24 +64,59 @@ impl QuoteState {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Default, Debug, Eq, Hash)]
|
||||
pub struct SpanSource {
|
||||
name: String,
|
||||
content: Arc<String>
|
||||
}
|
||||
|
||||
impl SpanSource {
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
pub fn content(&self) -> Arc<String> {
|
||||
self.content.clone()
|
||||
}
|
||||
pub fn rename(&mut self, name: String) {
|
||||
self.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for SpanSource {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
/// Span::new(10..20)
|
||||
#[derive(Clone, PartialEq, Default, Debug)]
|
||||
pub struct Span {
|
||||
range: Range<usize>,
|
||||
source: Arc<String>,
|
||||
source: SpanSource
|
||||
}
|
||||
|
||||
impl Span {
|
||||
/// New `Span`. Wraps a range and a string slice that it refers to.
|
||||
pub fn new(range: Range<usize>, source: Arc<String>) -> Self {
|
||||
let source = SpanSource { name: "<stdin>".into(), content: source };
|
||||
Span { range, source }
|
||||
}
|
||||
pub fn rename(&mut self, name: String) {
|
||||
self.source.name = name;
|
||||
}
|
||||
pub fn with_name(mut self, name: String) -> Self {
|
||||
self.source.name = name;
|
||||
self
|
||||
}
|
||||
/// Slice the source string at the wrapped range
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.source[self.start..self.end]
|
||||
&self.source.content[self.range().start..self.range().end]
|
||||
}
|
||||
pub fn get_source(&self) -> Arc<String> {
|
||||
self.source.clone()
|
||||
self.source.content.clone()
|
||||
}
|
||||
pub fn span_source(&self) -> &SpanSource {
|
||||
&self.source
|
||||
}
|
||||
pub fn range(&self) -> Range<usize> {
|
||||
self.range.clone()
|
||||
@@ -93,14 +128,23 @@ impl Span {
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows simple access to the underlying range wrapped by the span
|
||||
impl Deref for Span {
|
||||
type Target = Range<usize>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.range
|
||||
}
|
||||
impl ariadne::Span for Span {
|
||||
type SourceId = SpanSource;
|
||||
|
||||
fn source(&self) -> &Self::SourceId {
|
||||
&self.source
|
||||
}
|
||||
|
||||
fn start(&self) -> usize {
|
||||
self.range.start
|
||||
}
|
||||
|
||||
fn end(&self) -> usize {
|
||||
self.range.end
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows simple access to the underlying range wrapped by the span
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum TkRule {
|
||||
Null,
|
||||
@@ -148,7 +192,7 @@ impl Tk {
|
||||
self.span.as_str()
|
||||
}
|
||||
pub fn source(&self) -> Arc<String> {
|
||||
self.span.source.clone()
|
||||
self.span.source.content.clone()
|
||||
}
|
||||
pub fn mark(&mut self, flag: TkFlags) {
|
||||
self.flags |= flag;
|
||||
@@ -931,12 +975,12 @@ pub fn split_tk(tk: &Tk, pat: &str) -> Vec<Tk> {
|
||||
let mut cursor = 0;
|
||||
let mut splits = vec![];
|
||||
while let Some(split) = split_at_unescaped(&slice[cursor..], pat) {
|
||||
let before_span = Span::new(tk.span.start + cursor..tk.span.start + cursor + split.0.len(), tk.source().clone());
|
||||
let before_span = Span::new(tk.span.range().start + cursor..tk.span.range().start + cursor + split.0.len(), tk.source().clone());
|
||||
splits.push(Tk::new(tk.class.clone(), before_span));
|
||||
cursor += split.0.len() + pat.len();
|
||||
}
|
||||
if slice.get(cursor..).is_some_and(|s| !s.is_empty()) {
|
||||
let remaining_span = Span::new(tk.span.start + cursor..tk.span.end, tk.source().clone());
|
||||
let remaining_span = Span::new(tk.span.range().start + cursor..tk.span.range().end, tk.source().clone());
|
||||
splits.push(Tk::new(tk.class.clone(), remaining_span));
|
||||
}
|
||||
splits
|
||||
@@ -957,8 +1001,8 @@ pub fn split_tk_at(tk: &Tk, pat: &str) -> Option<(Tk, Tk)> {
|
||||
}
|
||||
|
||||
if slice[i..].starts_with(pat) {
|
||||
let before_span = Span::new(tk.span.start..tk.span.start + i, tk.source().clone());
|
||||
let after_span = Span::new(tk.span.start + i + pat.len()..tk.span.end, tk.source().clone());
|
||||
let before_span = Span::new(tk.span.range().start..tk.span.range().start + i, tk.source().clone());
|
||||
let after_span = Span::new(tk.span.range().start + i + pat.len()..tk.span.range().end, tk.source().clone());
|
||||
let before_tk = Tk::new(tk.class.clone(), before_span);
|
||||
let after_tk = Tk::new(tk.class.clone(), after_span);
|
||||
return Some((before_tk, after_tk));
|
||||
|
||||
133
src/parse/mod.rs
133
src/parse/mod.rs
@@ -1,12 +1,14 @@
|
||||
use std::str::FromStr;
|
||||
use std::{fmt::Debug, str::FromStr, sync::Arc};
|
||||
|
||||
use ariadne::{Fmt, Label};
|
||||
use bitflags::bitflags;
|
||||
use fmt::Display;
|
||||
use lex::{LexFlags, LexStream, Span, Tk, TkFlags, TkRule};
|
||||
use lex::{LexFlags, LexStream, Span, SpanSource, Tk, TkFlags, TkRule};
|
||||
use yansi::Color;
|
||||
|
||||
use crate::{
|
||||
libsh::{
|
||||
error::{Note, ShErr, ShErrKind, ShResult},
|
||||
error::{Note, ShErr, ShErrKind, ShResult, next_color},
|
||||
utils::TkVecUtils,
|
||||
},
|
||||
prelude::*,
|
||||
@@ -45,6 +47,7 @@ pub struct ParsedSrc {
|
||||
pub src: Arc<String>,
|
||||
pub ast: Ast,
|
||||
pub lex_flags: LexFlags,
|
||||
pub context: LabelCtx,
|
||||
}
|
||||
|
||||
impl ParsedSrc {
|
||||
@@ -53,12 +56,17 @@ impl ParsedSrc {
|
||||
src,
|
||||
ast: Ast::new(vec![]),
|
||||
lex_flags: LexFlags::empty(),
|
||||
context: vec![],
|
||||
}
|
||||
}
|
||||
pub fn with_lex_flags(mut self, flags: LexFlags) -> Self {
|
||||
self.lex_flags = flags;
|
||||
self
|
||||
}
|
||||
pub fn with_context(mut self, ctx: LabelCtx) -> Self {
|
||||
self.context = ctx;
|
||||
self
|
||||
}
|
||||
pub fn parse_src(&mut self) -> Result<(), Vec<ShErr>> {
|
||||
let mut tokens = vec![];
|
||||
for lex_result in LexStream::new(self.src.clone(), self.lex_flags) {
|
||||
@@ -70,7 +78,7 @@ impl ParsedSrc {
|
||||
|
||||
let mut errors = vec![];
|
||||
let mut nodes = vec![];
|
||||
for parse_result in ParseStream::new(tokens) {
|
||||
for parse_result in ParseStream::with_context(tokens, self.context.clone()) {
|
||||
match parse_result {
|
||||
Ok(node) => nodes.push(node),
|
||||
Err(error) => errors.push(error),
|
||||
@@ -104,12 +112,15 @@ impl Ast {
|
||||
}
|
||||
}
|
||||
|
||||
pub type LabelCtx = Vec<(SpanSource, Label<Span>)>;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Node {
|
||||
pub class: NdRule,
|
||||
pub flags: NdFlags,
|
||||
pub redirs: Vec<Redir>,
|
||||
pub tokens: Vec<Tk>,
|
||||
pub context: LabelCtx,
|
||||
}
|
||||
|
||||
impl Node {
|
||||
@@ -133,7 +144,7 @@ impl Node {
|
||||
};
|
||||
|
||||
Span::new(
|
||||
first_tk.span.start..last_tk.span.end,
|
||||
first_tk.span.range().start..last_tk.span.range().end,
|
||||
first_tk.span.get_source(),
|
||||
)
|
||||
}
|
||||
@@ -539,14 +550,25 @@ pub enum NdRule {
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ParseStream {
|
||||
pub tokens: Vec<Tk>,
|
||||
pub context: LabelCtx
|
||||
}
|
||||
|
||||
impl Debug for ParseStream {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("ParseStream")
|
||||
.field("tokens", &self.tokens)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl ParseStream {
|
||||
pub fn new(tokens: Vec<Tk>) -> Self {
|
||||
Self { tokens }
|
||||
Self { tokens, context: vec![] }
|
||||
}
|
||||
pub fn with_context(tokens: Vec<Tk>, context: LabelCtx) -> Self {
|
||||
Self { tokens, context }
|
||||
}
|
||||
fn next_tk_class(&self) -> &TkRule {
|
||||
if let Some(tk) = self.tokens.first() {
|
||||
@@ -684,6 +706,7 @@ impl ParseStream {
|
||||
class: NdRule::Conjunction { elements },
|
||||
flags: NdFlags::empty(),
|
||||
redirs: vec![],
|
||||
context: self.context.clone(),
|
||||
tokens: node_tks,
|
||||
}))
|
||||
}
|
||||
@@ -697,23 +720,47 @@ impl ParseStream {
|
||||
}
|
||||
let name_tk = self.next_tk().unwrap();
|
||||
node_tks.push(name_tk.clone());
|
||||
let name = name_tk;
|
||||
let name = name_tk.clone();
|
||||
let name_raw = name.to_string();
|
||||
let mut src = name_tk.span.span_source().clone();
|
||||
src.rename(name_raw.clone());
|
||||
let color = next_color();
|
||||
// Push a placeholder context so child nodes inherit it
|
||||
self.context.push((
|
||||
src.clone(),
|
||||
Label::new(name_tk.span.clone().with_name(name_raw.clone()))
|
||||
.with_message(format!("in function '{}' defined here", name_raw.clone().fg(color)))
|
||||
.with_color(color),
|
||||
));
|
||||
|
||||
let Some(brc_grp) = self.parse_brc_grp(true /* from_func_def */)? else {
|
||||
self.context.pop();
|
||||
return Err(parse_err_full(
|
||||
"Expected a brace group after function name",
|
||||
&node_tks.get_span().unwrap(),
|
||||
self.context.clone()
|
||||
));
|
||||
};
|
||||
body = Box::new(brc_grp);
|
||||
// Replace placeholder with full-span label
|
||||
self.context.pop();
|
||||
let full_span = body.get_span();
|
||||
self.context.push((
|
||||
src,
|
||||
Label::new(full_span.with_name(name_raw.clone()))
|
||||
.with_message(format!("in function '{}' called here", name_raw.fg(color)))
|
||||
.with_color(color),
|
||||
));
|
||||
|
||||
let node = Node {
|
||||
class: NdRule::FuncDef { name, body },
|
||||
flags: NdFlags::empty(),
|
||||
redirs: vec![],
|
||||
tokens: node_tks,
|
||||
context: self.context.clone()
|
||||
};
|
||||
|
||||
self.context.pop();
|
||||
Ok(Some(node))
|
||||
}
|
||||
fn panic_mode(&mut self, node_tks: &mut Vec<Tk>) {
|
||||
@@ -743,6 +790,7 @@ impl ParseStream {
|
||||
return Err(parse_err_full(
|
||||
"Malformed test call",
|
||||
&node_tks.get_span().unwrap(),
|
||||
self.context.clone()
|
||||
));
|
||||
} else {
|
||||
break;
|
||||
@@ -769,6 +817,7 @@ impl ParseStream {
|
||||
return Err(parse_err_full(
|
||||
"Invalid placement for logical operator in test",
|
||||
&node_tks.get_span().unwrap(),
|
||||
self.context.clone()
|
||||
));
|
||||
}
|
||||
let op = match tk.class {
|
||||
@@ -784,6 +833,7 @@ impl ParseStream {
|
||||
return Err(parse_err_full(
|
||||
"Invalid placement for logical operator in test",
|
||||
&node_tks.get_span().unwrap(),
|
||||
self.context.clone()
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -797,6 +847,7 @@ impl ParseStream {
|
||||
class: NdRule::Test { cases },
|
||||
flags: NdFlags::empty(),
|
||||
redirs: vec![],
|
||||
context: self.context.clone(),
|
||||
tokens: node_tks,
|
||||
};
|
||||
Ok(Some(node))
|
||||
@@ -828,6 +879,7 @@ impl ParseStream {
|
||||
return Err(parse_err_full(
|
||||
"Expected a closing brace for this brace group",
|
||||
&node_tks.get_span().unwrap(),
|
||||
self.context.clone()
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -840,6 +892,7 @@ impl ParseStream {
|
||||
class: NdRule::BraceGrp { body },
|
||||
flags: NdFlags::empty(),
|
||||
redirs,
|
||||
context: self.context.clone(),
|
||||
tokens: node_tks,
|
||||
};
|
||||
Ok(Some(node))
|
||||
@@ -893,12 +946,10 @@ impl ParseStream {
|
||||
let pat_err = parse_err_full(
|
||||
"Expected a pattern after 'case' keyword",
|
||||
&node_tks.get_span().unwrap(),
|
||||
self.context.clone()
|
||||
)
|
||||
.with_note(
|
||||
Note::new("Patterns can be raw text, or anything that gets substituted with raw text")
|
||||
.with_sub_notes(vec![
|
||||
"This includes variables like '$foo' or command substitutions like '$(echo foo)'",
|
||||
]),
|
||||
"Patterns can be raw text, or anything that gets substituted with raw text"
|
||||
);
|
||||
|
||||
let Some(pat_tk) = self.next_tk() else {
|
||||
@@ -919,6 +970,7 @@ impl ParseStream {
|
||||
return Err(parse_err_full(
|
||||
"Expected 'in' after case variable name",
|
||||
&node_tks.get_span().unwrap(),
|
||||
self.context.clone()
|
||||
));
|
||||
}
|
||||
node_tks.push(self.next_tk().unwrap());
|
||||
@@ -931,6 +983,7 @@ impl ParseStream {
|
||||
return Err(parse_err_full(
|
||||
"Expected a case pattern here",
|
||||
&node_tks.get_span().unwrap(),
|
||||
self.context.clone()
|
||||
));
|
||||
}
|
||||
let case_pat_tk = self.next_tk().unwrap();
|
||||
@@ -967,6 +1020,7 @@ impl ParseStream {
|
||||
return Err(parse_err_full(
|
||||
"Expected 'esac' after case block",
|
||||
&node_tks.get_span().unwrap(),
|
||||
self.context.clone()
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -978,10 +1032,17 @@ impl ParseStream {
|
||||
},
|
||||
flags: NdFlags::empty(),
|
||||
redirs,
|
||||
context: self.context.clone(),
|
||||
tokens: node_tks,
|
||||
};
|
||||
Ok(Some(node))
|
||||
}
|
||||
fn make_err(&self, span: lex::Span, label: Label<lex::Span>) -> ShErr {
|
||||
let src = span.span_source().clone();
|
||||
ShErr::new(ShErrKind::ParseErr, span)
|
||||
.with_label(src, label)
|
||||
.with_context(self.context.clone())
|
||||
}
|
||||
fn parse_if(&mut self) -> ShResult<Option<Node>> {
|
||||
// Needs at last one 'if-then',
|
||||
// Any number of 'elif-then',
|
||||
@@ -1000,10 +1061,14 @@ impl ParseStream {
|
||||
let prefix_keywrd = if cond_nodes.is_empty() { "if" } else { "elif" };
|
||||
let Some(cond) = self.parse_cmd_list()? else {
|
||||
self.panic_mode(&mut node_tks);
|
||||
return Err(parse_err_full(
|
||||
&format!("Expected an expression after '{prefix_keywrd}'"),
|
||||
&node_tks.get_span().unwrap(),
|
||||
));
|
||||
let span = node_tks.get_span().unwrap();
|
||||
let color = next_color();
|
||||
return Err(self.make_err(span.clone(),
|
||||
Label::new(span)
|
||||
.with_message(format!("Expected an expression after '{}'", prefix_keywrd.fg(color)))
|
||||
.with_color(color)
|
||||
));
|
||||
|
||||
};
|
||||
node_tks.extend(cond.tokens.clone());
|
||||
|
||||
@@ -1012,6 +1077,7 @@ impl ParseStream {
|
||||
return Err(parse_err_full(
|
||||
&format!("Expected 'then' after '{prefix_keywrd}' condition"),
|
||||
&node_tks.get_span().unwrap(),
|
||||
self.context.clone()
|
||||
));
|
||||
}
|
||||
node_tks.push(self.next_tk().unwrap());
|
||||
@@ -1027,6 +1093,7 @@ impl ParseStream {
|
||||
return Err(parse_err_full(
|
||||
"Expected an expression after 'then'",
|
||||
&node_tks.get_span().unwrap(),
|
||||
self.context.clone()
|
||||
));
|
||||
};
|
||||
let cond_node = CondNode {
|
||||
@@ -1056,6 +1123,7 @@ impl ParseStream {
|
||||
return Err(parse_err_full(
|
||||
"Expected an expression after 'else'",
|
||||
&node_tks.get_span().unwrap(),
|
||||
self.context.clone()
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -1066,6 +1134,7 @@ impl ParseStream {
|
||||
return Err(parse_err_full(
|
||||
"Expected 'fi' after if statement",
|
||||
&node_tks.get_span().unwrap(),
|
||||
self.context.clone()
|
||||
));
|
||||
}
|
||||
node_tks.push(self.next_tk().unwrap());
|
||||
@@ -1081,6 +1150,7 @@ impl ParseStream {
|
||||
},
|
||||
flags: NdFlags::empty(),
|
||||
redirs,
|
||||
context: self.context.clone(),
|
||||
tokens: node_tks,
|
||||
};
|
||||
Ok(Some(node))
|
||||
@@ -1120,6 +1190,7 @@ impl ParseStream {
|
||||
return Err(parse_err_full(
|
||||
"This for loop is missing a variable",
|
||||
&node_tks.get_span().unwrap(),
|
||||
self.context.clone()
|
||||
));
|
||||
}
|
||||
if arr.is_empty() {
|
||||
@@ -1127,6 +1198,7 @@ impl ParseStream {
|
||||
return Err(parse_err_full(
|
||||
"This for loop is missing an array",
|
||||
&node_tks.get_span().unwrap(),
|
||||
self.context.clone()
|
||||
));
|
||||
}
|
||||
if !self.check_keyword("do") || !self.next_tk_is_some() {
|
||||
@@ -1134,6 +1206,7 @@ impl ParseStream {
|
||||
return Err(parse_err_full(
|
||||
"Missing a 'do' for this for loop",
|
||||
&node_tks.get_span().unwrap(),
|
||||
self.context.clone()
|
||||
));
|
||||
}
|
||||
node_tks.push(self.next_tk().unwrap());
|
||||
@@ -1149,6 +1222,7 @@ impl ParseStream {
|
||||
return Err(parse_err_full(
|
||||
"Missing a 'done' after this for loop",
|
||||
&node_tks.get_span().unwrap(),
|
||||
self.context.clone()
|
||||
));
|
||||
}
|
||||
node_tks.push(self.next_tk().unwrap());
|
||||
@@ -1159,6 +1233,7 @@ impl ParseStream {
|
||||
class: NdRule::ForNode { vars, arr, body },
|
||||
flags: NdFlags::empty(),
|
||||
redirs,
|
||||
context: self.context.clone(),
|
||||
tokens: node_tks,
|
||||
};
|
||||
Ok(Some(node))
|
||||
@@ -1188,6 +1263,7 @@ impl ParseStream {
|
||||
return Err(parse_err_full(
|
||||
&format!("Expected an expression after '{loop_kind}'"), // It also implements Display
|
||||
&node_tks.get_span().unwrap(),
|
||||
self.context.clone()
|
||||
));
|
||||
};
|
||||
node_tks.extend(cond.tokens.clone());
|
||||
@@ -1197,6 +1273,7 @@ impl ParseStream {
|
||||
return Err(parse_err_full(
|
||||
"Expected 'do' after loop condition",
|
||||
&node_tks.get_span().unwrap(),
|
||||
self.context.clone()
|
||||
));
|
||||
}
|
||||
node_tks.push(self.next_tk().unwrap());
|
||||
@@ -1212,6 +1289,7 @@ impl ParseStream {
|
||||
return Err(parse_err_full(
|
||||
"Expected an expression after 'do'",
|
||||
&node_tks.get_span().unwrap(),
|
||||
self.context.clone()
|
||||
));
|
||||
};
|
||||
|
||||
@@ -1221,6 +1299,7 @@ impl ParseStream {
|
||||
return Err(parse_err_full(
|
||||
"Expected 'done' after loop body",
|
||||
&node_tks.get_span().unwrap(),
|
||||
self.context.clone()
|
||||
));
|
||||
}
|
||||
node_tks.push(self.next_tk().unwrap());
|
||||
@@ -1240,6 +1319,7 @@ impl ParseStream {
|
||||
},
|
||||
flags: NdFlags::empty(),
|
||||
redirs,
|
||||
context: self.context.clone(),
|
||||
tokens: node_tks,
|
||||
};
|
||||
Ok(Some(loop_node))
|
||||
@@ -1277,6 +1357,7 @@ impl ParseStream {
|
||||
},
|
||||
flags,
|
||||
redirs: vec![],
|
||||
context: self.context.clone(),
|
||||
tokens: node_tks,
|
||||
}))
|
||||
}
|
||||
@@ -1295,6 +1376,7 @@ impl ParseStream {
|
||||
return Err(parse_err_full(
|
||||
"Found case pattern in command",
|
||||
&prefix_tk.span,
|
||||
self.context.clone()
|
||||
));
|
||||
}
|
||||
let is_cmd = prefix_tk.flags.contains(TkFlags::IS_CMD);
|
||||
@@ -1335,6 +1417,7 @@ impl ParseStream {
|
||||
tokens: node_tks,
|
||||
flags,
|
||||
redirs,
|
||||
context: self.context.clone(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -1398,16 +1481,17 @@ impl ParseStream {
|
||||
tokens: node_tks,
|
||||
flags,
|
||||
redirs,
|
||||
context: self.context.clone(),
|
||||
}))
|
||||
}
|
||||
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;
|
||||
let mut name_range = token.span.range().start..token.span.range().start;
|
||||
let mut var_val = String::new();
|
||||
let mut val_range = token.span.end..token.span.end;
|
||||
let mut val_range = token.span.range().end..token.span.range().end;
|
||||
let mut assign_kind = None;
|
||||
let mut pos = token.span.start;
|
||||
let mut pos = token.span.range().start;
|
||||
|
||||
while let Some(ch) = chars.next() {
|
||||
if assign_kind.is_some() {
|
||||
@@ -1500,6 +1584,7 @@ impl ParseStream {
|
||||
tokens: vec![token.clone()],
|
||||
flags,
|
||||
redirs: vec![],
|
||||
context: self.context.clone(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
@@ -1556,8 +1641,14 @@ pub fn get_redir_file(class: RedirType, path: PathBuf) -> ShResult<File> {
|
||||
Ok(result?)
|
||||
}
|
||||
|
||||
fn parse_err_full(reason: &str, blame: &Span) -> ShErr {
|
||||
ShErr::full(ShErrKind::ParseErr, reason, blame.clone())
|
||||
fn parse_err_full(reason: &str, blame: &Span, context: LabelCtx) -> ShErr {
|
||||
let color = next_color();
|
||||
ShErr::new(ShErrKind::ParseErr, blame.clone())
|
||||
.with_label(
|
||||
blame.span_source().clone(),
|
||||
Label::new(blame.clone()).with_message(reason).with_color(color)
|
||||
)
|
||||
.with_context(context)
|
||||
}
|
||||
|
||||
fn is_func_name(tk: Option<&Tk>) -> bool {
|
||||
|
||||
Reference in New Issue
Block a user