Error Messaging Redesign
This commit contains the following: * New data types to support full tracebacks * New traceback data type used across stl and ast * Updates to tests * fixes for error messaging in sym and some stl functions
This commit is contained in:
parent
91ad4eed12
commit
789349df48
24 changed files with 837 additions and 374 deletions
177
src/stl/posix.rs
177
src/stl/posix.rs
|
|
@ -24,6 +24,9 @@ use {
|
|||
SymTable, ValueType,
|
||||
Symbol, Args,
|
||||
},
|
||||
error::{
|
||||
Traceback, start_trace,
|
||||
},
|
||||
eval::eval,
|
||||
run,
|
||||
},
|
||||
|
|
@ -284,16 +287,20 @@ examples:
|
|||
(l ping -c ping-count google.com))
|
||||
(l emacs -nw (concat HOME '/.relishrc'))
|
||||
";
|
||||
fn load_callback(ast: &Seg, syms: &mut SymTable, state: &mut ShellState) -> Result<Ctr, String> {
|
||||
fn load_callback(ast: &Seg, syms: &mut SymTable, state: &mut ShellState) -> Result<Ctr, Traceback> {
|
||||
if ast.is_empty() {
|
||||
Err("need at least one argument".to_string())
|
||||
Err(start_trace(
|
||||
("load", "expected at least one input")
|
||||
.into()))
|
||||
} else {
|
||||
let mut args = VecDeque::from(args_from_ast(ast, syms));
|
||||
if args.is_empty() {
|
||||
Err("empty command".to_string())
|
||||
Err(start_trace(
|
||||
("load", "empty command")
|
||||
.into()))
|
||||
} else {
|
||||
if let Some(filepath) = run::find_on_path(args.pop_front().unwrap()) {
|
||||
launch_command(
|
||||
if let Err(e) = launch_command(
|
||||
filepath,
|
||||
&Vec::from(args.make_contiguous()),
|
||||
Stdio::inherit(),
|
||||
|
|
@ -301,10 +308,17 @@ fn load_callback(ast: &Seg, syms: &mut SymTable, state: &mut ShellState) -> Resu
|
|||
Stdio::inherit(),
|
||||
false,
|
||||
state,
|
||||
)?;
|
||||
Ok(Ctr::Integer(state.last_exit_code.into()))
|
||||
) {
|
||||
Err(start_trace(
|
||||
("load", e)
|
||||
.into()))
|
||||
} else {
|
||||
Ok(Ctr::Integer(state.last_exit_code.into()))
|
||||
}
|
||||
} else {
|
||||
Err("file not found".to_string())
|
||||
Err(start_trace(
|
||||
("load", "binary not found on PATH")
|
||||
.into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -318,9 +332,11 @@ Example:
|
|||
(ls -la)
|
||||
(grep '.rs')
|
||||
(tr -d '.rs'))";
|
||||
fn pipe_callback(ast: &Seg, syms: &mut SymTable, state: &mut ShellState) -> Result<Ctr, String> {
|
||||
fn pipe_callback(ast: &Seg, syms: &mut SymTable, state: &mut ShellState) -> Result<Ctr, Traceback> {
|
||||
if ast.is_empty() {
|
||||
return Err("need at least one argument".to_string())
|
||||
return Err(start_trace(
|
||||
("pipe", "expected at least one input")
|
||||
.into()))
|
||||
}
|
||||
|
||||
let mut err: String = String::new();
|
||||
|
|
@ -366,7 +382,9 @@ fn pipe_callback(ast: &Seg, syms: &mut SymTable, state: &mut ShellState) -> Resu
|
|||
false
|
||||
}
|
||||
}) {
|
||||
Err(err)
|
||||
Err(start_trace(
|
||||
("pipe", err)
|
||||
.into()))
|
||||
} else {
|
||||
if lastpid > 0 {
|
||||
if let Some(pos) = state.children
|
||||
|
|
@ -384,10 +402,14 @@ fn pipe_callback(ast: &Seg, syms: &mut SymTable, state: &mut ShellState) -> Resu
|
|||
println!("{}", String::from_utf8_lossy(&exit.stdout));
|
||||
Ok(Ctr::Integer(state.last_exit_code.into()))
|
||||
} else {
|
||||
Err(format!("lost last child (pid {})", lastpid))
|
||||
Err(start_trace(
|
||||
("pipe", format!("lost last child (pid {})", lastpid))
|
||||
.into()))
|
||||
}
|
||||
} else {
|
||||
Err("impossible error state".to_string())
|
||||
Err(start_trace(
|
||||
("pipe", "impossible error state".to_string())
|
||||
.into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -404,13 +426,17 @@ examples:
|
|||
|
||||
Unlike with the normal load function, the load to string function collects stdout output and returns it as a string.
|
||||
";
|
||||
fn load_to_string_callback(ast: &Seg, syms: &mut SymTable, state: &mut ShellState) -> Result<Ctr, String> {
|
||||
fn load_to_string_callback(ast: &Seg, syms: &mut SymTable, state: &mut ShellState) -> Result<Ctr, Traceback> {
|
||||
if ast.is_empty() {
|
||||
Err("need at least one argument".to_string())
|
||||
Err(start_trace(
|
||||
("load-to-string", "expected at least one input")
|
||||
.into()))
|
||||
} else {
|
||||
let mut args = VecDeque::from(args_from_ast(ast, syms));
|
||||
if args.is_empty() {
|
||||
Err("empty command".to_string())
|
||||
Err(start_trace(
|
||||
("load-to-string", "command is empty")
|
||||
.into()))
|
||||
} else {
|
||||
if let Some(filepath) = run::find_on_path(args.pop_front().unwrap()) {
|
||||
let res = Command::new(filepath).args(args).output();
|
||||
|
|
@ -421,13 +447,19 @@ fn load_to_string_callback(ast: &Seg, syms: &mut SymTable, state: &mut ShellStat
|
|||
if let Ok(string) = String::from_utf8(output.stdout) {
|
||||
Ok(Ctr::String(string.trim_end().to_string()))
|
||||
} else {
|
||||
Err(format!("couldn't marshall utf-8 command output into a string"))
|
||||
Err(start_trace(
|
||||
("load-to-string", format!("couldn't marshall utf-8 command output into a string"))
|
||||
.into()))
|
||||
}
|
||||
} else {
|
||||
Err(format!("{}", res.err().unwrap()))
|
||||
Err(start_trace(
|
||||
("load-to-string", format!("{}", res.err().unwrap()))
|
||||
.into()))
|
||||
}
|
||||
} else {
|
||||
Err("file not found".to_string())
|
||||
Err(start_trace(
|
||||
("load-to-string", "binary not found on PATH")
|
||||
.into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -453,9 +485,11 @@ const LOAD_WITH_DOCSTRING: &str = "Takes two arguments.
|
|||
Example invocation:
|
||||
(load-with (('stdout' '/dev/null'))
|
||||
(ping -c 4 google.com))";
|
||||
fn load_with_callback(ast: &Seg, syms: &mut SymTable, state: &mut ShellState) -> Result<Ctr, String> {
|
||||
fn load_with_callback(ast: &Seg, syms: &mut SymTable, state: &mut ShellState) -> Result<Ctr, Traceback> {
|
||||
if ast.len() != 2 {
|
||||
Err("exactly two arguments needed".to_string())
|
||||
Err(start_trace(
|
||||
("load-with", "expected two inputs")
|
||||
.into()))
|
||||
} else {
|
||||
if let Ctr::Seg(ref fd_redirect_forms) = *ast.car {
|
||||
let mut stdout: Option<Stdio> = None;
|
||||
|
|
@ -538,7 +572,9 @@ fn load_with_callback(ast: &Seg, syms: &mut SymTable, state: &mut ShellState) ->
|
|||
false
|
||||
}
|
||||
}) {
|
||||
return Err(e.unwrap())
|
||||
return Err(start_trace(
|
||||
("load-with", e.unwrap())
|
||||
.into()))
|
||||
}
|
||||
|
||||
// now deal with the actual command
|
||||
|
|
@ -547,14 +583,18 @@ fn load_with_callback(ast: &Seg, syms: &mut SymTable, state: &mut ShellState) ->
|
|||
if let Ctr::Seg(ref command) = *command_forms.car {
|
||||
args = VecDeque::from(args_from_ast(command, syms));
|
||||
} else {
|
||||
return Err("command not list".to_string())
|
||||
return Err(start_trace(
|
||||
("load-with", "expected command input to be a list")
|
||||
.into()))
|
||||
}
|
||||
|
||||
if args.is_empty() {
|
||||
Err("empty command".to_string())
|
||||
Err(start_trace(
|
||||
("load-with", "empty command")
|
||||
.into()))
|
||||
} else {
|
||||
if let Some(filepath) = run::find_on_path(args.pop_front().unwrap()) {
|
||||
launch_command(
|
||||
if let Err(e) = launch_command(
|
||||
filepath,
|
||||
&Vec::from(args.make_contiguous()),
|
||||
stdin.or(Some(Stdio::inherit())).unwrap(),
|
||||
|
|
@ -562,23 +602,34 @@ fn load_with_callback(ast: &Seg, syms: &mut SymTable, state: &mut ShellState) ->
|
|||
stderr.or(Some(Stdio::inherit())).unwrap(),
|
||||
false,
|
||||
state,
|
||||
)?;
|
||||
Ok(Ctr::Integer(state.last_exit_code.into()))
|
||||
) {
|
||||
Err(start_trace(
|
||||
("load-with", e)
|
||||
.into()))
|
||||
} else {
|
||||
Ok(Ctr::Integer(state.last_exit_code.into()))
|
||||
}
|
||||
} else {
|
||||
Err("file not found".to_string())
|
||||
Err(start_trace(
|
||||
("load-with", "binary not found on PATH")
|
||||
.into()))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err("second argument expected to be a list of command elements".to_string())
|
||||
Err(start_trace(
|
||||
("load-with", "expected second input to be list of command elements")
|
||||
.into()))
|
||||
}
|
||||
} else {
|
||||
Err("first argument expected to be a list of lists".to_string())
|
||||
Err(start_trace(
|
||||
("load-with", "expected first input to be a list of lists")
|
||||
.into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Q_DOCSTRING: &str = "returns exit code of last process to be run in posix layer";
|
||||
fn q_callback(_ast: &Seg, _syms: &SymTable, state: &mut ShellState) -> Result<Ctr, String> {
|
||||
fn q_callback(_ast: &Seg, _syms: &SymTable, state: &mut ShellState) -> Result<Ctr, Traceback> {
|
||||
Ok(Ctr::Integer(state.last_exit_code.into()))
|
||||
}
|
||||
|
||||
|
|
@ -593,16 +644,20 @@ examples:
|
|||
(bg ping -c4 google.com)
|
||||
(bg vim) ;; vim waits patiently for you to foreground the process with 'fg'
|
||||
";
|
||||
fn bg_callback(ast: &Seg, syms: &mut SymTable, state: &mut ShellState) -> Result<Ctr, String> {
|
||||
fn bg_callback(ast: &Seg, syms: &mut SymTable, state: &mut ShellState) -> Result<Ctr, Traceback> {
|
||||
if ast.is_empty() {
|
||||
Err("need at least one argument".to_string())
|
||||
Err(start_trace(
|
||||
("bg", "expected at least one input")
|
||||
.into()))
|
||||
} else {
|
||||
let mut args = VecDeque::from(args_from_ast(ast, syms));
|
||||
if args.is_empty() {
|
||||
Err("empty command".to_string())
|
||||
Err(start_trace(
|
||||
("bg", "empty command")
|
||||
.into()))
|
||||
} else {
|
||||
if let Some(filepath) = run::find_on_path(args.pop_front().unwrap()) {
|
||||
launch_command(
|
||||
if let Err(e) = launch_command(
|
||||
filepath,
|
||||
&Vec::from(args.make_contiguous()),
|
||||
Stdio::inherit(),
|
||||
|
|
@ -610,10 +665,17 @@ fn bg_callback(ast: &Seg, syms: &mut SymTable, state: &mut ShellState) -> Result
|
|||
Stdio::inherit(),
|
||||
true,
|
||||
state,
|
||||
)?;
|
||||
Ok(Ctr::Integer(state.last_exit_code.into()))
|
||||
) {
|
||||
Err(start_trace(
|
||||
("bg", e)
|
||||
.into()))
|
||||
} else {
|
||||
Ok(Ctr::Integer(state.last_exit_code.into()))
|
||||
}
|
||||
} else {
|
||||
Err("file not found".to_string())
|
||||
Err(start_trace(
|
||||
("bg", "binary not found on PATH")
|
||||
.into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -631,27 +693,38 @@ pub fn fg_callback(
|
|||
ast: &Seg,
|
||||
_syms: &mut SymTable,
|
||||
shell_state: &mut ShellState
|
||||
) -> Result <Ctr, String> {
|
||||
) -> Result <Ctr, Traceback> {
|
||||
if let Ctr::Integer(i) = *ast.car {
|
||||
make_foreground(i as u32, shell_state)?;
|
||||
Ok(Ctr::None)
|
||||
if let Err(e) = make_foreground(i as u32, shell_state) {
|
||||
Err(start_trace(
|
||||
("fg", e)
|
||||
.into()))
|
||||
} else {
|
||||
Ok(Ctr::None)
|
||||
}
|
||||
} else {
|
||||
Err(format!("illegal args to fg"))
|
||||
Err(start_trace(
|
||||
("fg", "expected input to be an integer")
|
||||
.into()))
|
||||
}
|
||||
}
|
||||
|
||||
pub const CD_DOCSTRING: &str =
|
||||
"Expects 1 argument (a string). Changes to a new directory";
|
||||
pub fn cd_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, String> {
|
||||
pub fn cd_callback(ast: &Seg, _syms: &mut SymTable) -> Result<Ctr, Traceback> {
|
||||
if let Ctr::String(ref dir) = *ast.car {
|
||||
let dirp = Path::new(dir);
|
||||
if let Err(s) = set_current_dir(&dirp) {
|
||||
Err(format!("{}", s))
|
||||
Err(start_trace(
|
||||
("cd", s.to_string())
|
||||
.into()))
|
||||
} else {
|
||||
Ok(Ctr::None)
|
||||
}
|
||||
} else {
|
||||
Err(format!("impossible err: arg not a string"))
|
||||
Err(start_trace(
|
||||
("cd", "expected input to be a string")
|
||||
.into()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -721,7 +794,7 @@ pub fn load_posix_shell(syms: &mut SymTable, shell_state: Rc<RefCell<ShellState>
|
|||
args: Args::Infinite,
|
||||
conditional_branches: true,
|
||||
docs: String::from(LOAD_DOCSTRING),
|
||||
value: ValueType::Internal(Rc::new(move |ast: &Seg, symtable: &mut SymTable| -> Result<Ctr, String> {
|
||||
value: ValueType::Internal(Rc::new(move |ast: &Seg, symtable: &mut SymTable| -> Result<Ctr, Traceback> {
|
||||
load_callback(ast, symtable, &mut shell_state.borrow_mut())
|
||||
})),
|
||||
..Default::default()
|
||||
|
|
@ -735,7 +808,7 @@ pub fn load_posix_shell(syms: &mut SymTable, shell_state: Rc<RefCell<ShellState>
|
|||
args: Args::Infinite,
|
||||
conditional_branches: true,
|
||||
docs: String::from(LOAD_DOCSTRING),
|
||||
value: ValueType::Internal(Rc::new(move |ast: &Seg, symtable: &mut SymTable| -> Result<Ctr, String> {
|
||||
value: ValueType::Internal(Rc::new(move |ast: &Seg, symtable: &mut SymTable| -> Result<Ctr, Traceback> {
|
||||
load_callback(ast, symtable, &mut load_ss.borrow_mut())
|
||||
})),
|
||||
..Default::default()
|
||||
|
|
@ -749,7 +822,7 @@ pub fn load_posix_shell(syms: &mut SymTable, shell_state: Rc<RefCell<ShellState>
|
|||
args: Args::Infinite,
|
||||
conditional_branches: true,
|
||||
docs: String::from(BG_DOCSTRING),
|
||||
value: ValueType::Internal(Rc::new(move |ast: &Seg, symtable: &mut SymTable| -> Result<Ctr, String> {
|
||||
value: ValueType::Internal(Rc::new(move |ast: &Seg, symtable: &mut SymTable| -> Result<Ctr, Traceback> {
|
||||
bg_callback(ast, symtable, &mut bg_ss.borrow_mut())
|
||||
})),
|
||||
..Default::default()
|
||||
|
|
@ -763,7 +836,7 @@ pub fn load_posix_shell(syms: &mut SymTable, shell_state: Rc<RefCell<ShellState>
|
|||
args: Args::Strict(vec![Type::Integer]),
|
||||
conditional_branches: true,
|
||||
docs: String::from(FG_DOCSTRING),
|
||||
value: ValueType::Internal(Rc::new(move |ast: &Seg, symtable: &mut SymTable| -> Result<Ctr, String> {
|
||||
value: ValueType::Internal(Rc::new(move |ast: &Seg, symtable: &mut SymTable| -> Result<Ctr, Traceback> {
|
||||
fg_callback(ast, symtable, &mut fg_ss.borrow_mut())
|
||||
})),
|
||||
..Default::default()
|
||||
|
|
@ -777,7 +850,7 @@ pub fn load_posix_shell(syms: &mut SymTable, shell_state: Rc<RefCell<ShellState>
|
|||
args: Args::None,
|
||||
conditional_branches: false,
|
||||
docs: String::from(Q_DOCSTRING),
|
||||
value: ValueType::Internal(Rc::new(move |ast: &Seg, symtable: &mut SymTable| -> Result<Ctr, String> {
|
||||
value: ValueType::Internal(Rc::new(move |ast: &Seg, symtable: &mut SymTable| -> Result<Ctr, Traceback> {
|
||||
q_callback(ast, symtable, &mut q_ss.borrow_mut())
|
||||
})),
|
||||
..Default::default()
|
||||
|
|
@ -791,7 +864,7 @@ pub fn load_posix_shell(syms: &mut SymTable, shell_state: Rc<RefCell<ShellState>
|
|||
args: Args::Lazy(2),
|
||||
conditional_branches: true,
|
||||
docs: String::from(LOAD_WITH_DOCSTRING),
|
||||
value: ValueType::Internal(Rc::new(move |ast: &Seg, symtable: &mut SymTable| -> Result<Ctr, String> {
|
||||
value: ValueType::Internal(Rc::new(move |ast: &Seg, symtable: &mut SymTable| -> Result<Ctr, Traceback> {
|
||||
load_with_callback(ast, symtable, &mut lw_ss.borrow_mut())
|
||||
})),
|
||||
..Default::default()
|
||||
|
|
@ -805,7 +878,7 @@ pub fn load_posix_shell(syms: &mut SymTable, shell_state: Rc<RefCell<ShellState>
|
|||
args: Args::Infinite,
|
||||
conditional_branches: true,
|
||||
docs: String::from(LOAD_TO_STRING_DOCSTRING),
|
||||
value: ValueType::Internal(Rc::new(move |ast: &Seg, symtable: &mut SymTable| -> Result<Ctr, String> {
|
||||
value: ValueType::Internal(Rc::new(move |ast: &Seg, symtable: &mut SymTable| -> Result<Ctr, Traceback> {
|
||||
// extra nonsense needed to allow nested calls
|
||||
load_to_string_callback(
|
||||
ast,
|
||||
|
|
@ -834,7 +907,7 @@ pub fn load_posix_shell(syms: &mut SymTable, shell_state: Rc<RefCell<ShellState>
|
|||
args: Args::Infinite,
|
||||
conditional_branches: true,
|
||||
docs: String::from(PIPE_DOCSTRING),
|
||||
value: ValueType::Internal(Rc::new(move |ast: &Seg, symtable: &mut SymTable| -> Result<Ctr, String> {
|
||||
value: ValueType::Internal(Rc::new(move |ast: &Seg, symtable: &mut SymTable| -> Result<Ctr, Traceback> {
|
||||
pipe_callback(ast, symtable, &mut p_ss.borrow_mut())
|
||||
})),
|
||||
..Default::default()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue