error printing is less bad

main
Jordan Orelli 8 months ago
parent 5108e4457f
commit 27ebbf7c97

@ -10,7 +10,7 @@ thiserror = "1.0"
macros = { path = "macros" }
dirs = "4"
log = { version = "0.4", features = [ "max_level_off", "release_max_level_off" ] }
log = { version = "0.4", features = [ "max_level_debug", "release_max_level_off" ] }
[dependencies.windows]
version = "0.44.0"

@ -65,11 +65,20 @@ pub enum ParseError {
#[derive(Debug, Error)]
pub enum ExecError {
#[error("failed waiting on child process")]
ProcessWaitError(#[source] io::Error),
#[error("failed to spawn child process")]
ProcessSpawnError(#[source] io::Error),
#[error("failed waiting on child process '{name}' ({pid})")]
ProcessWaitError {
name: String,
pid: u32,
#[source]
source: io::Error,
},
#[error("failed to spawn '{name}' as a child process")]
ProcessSpawnError {
name: String,
#[source]
source: io::Error,
},
#[error("value type error: {0}")]
ValueTypeError(String),

@ -9,15 +9,16 @@ pub fn lex(source: &str) -> Result<Vec<Token>, LexError> {
Lexer::new(source).collect()
}
/// A Lexeme is the text of a given Token, without respect to that Token's type, but with respect
/// to where the text appears relative to some source code. This is, simply, a string that contains
/// the addresses of each of its characters with respect to some source text.
/// A Lexeme is the text of a given [Token]. The lexeme contains no information about the Token's
/// meaning or type; it is just a fancy string with position information.
#[derive(PartialEq, Clone)]
pub struct Lexeme {
elems: Vec<Glyph>,
}
impl Lexeme {
/// The area of the input text covered by this lexeme. This might return none of the Lexeme is
/// an empty string.
fn span(&self) -> Option<Range<Position>> {
if self.elems.is_empty() {
return None;
@ -75,6 +76,10 @@ pub enum Token {
}
impl Token {
/// Checks whether or not another token has the same contents as this Token. The eq
/// implementation will also consider positional information, whereas the `same` function does
/// not.
#[allow(unused)]
fn same(&self, other: &Self) -> bool {
use Token::*;
@ -86,17 +91,20 @@ impl Token {
}
}
/// Tokenizer splits some input [Glyphs] into [Token] values
pub struct Tokenizer<'text> {
source: Glyphs<'text>,
}
impl<'text> Tokenizer<'text> {
/// creates a new tokenizer for some given input source text
pub fn new(text: &'text str) -> Self {
Self {
source: Glyphs::new(text),
}
}
/// produces the next token in our input source text. If we've hit EOF, this will return None.
fn next_token(&mut self) -> Option<Result<Token, LexError>> {
self.source.yeet_whitespace();
let next = self.source.next()?;
@ -235,7 +243,7 @@ mod tests {
.chars()
.map(|c| Glyph {
glyph: c,
position: Position::start(),
position: Position::origin(),
bytes: 0..0,
})
.collect();

@ -1,16 +1,38 @@
/// hi
/// builtin functions
mod builtins;
/// all of the errors for the clyde project live in this module
mod error;
mod ext;
/// handles input from terminals
mod input;
/// key presses, independent from input sources
mod key;
/// lexical analysis
mod lex;
/// a real primitive line editor
mod line;
mod log;
mod output;
/// turns our tokens into parse trees
mod parse;
mod prompt;
mod shell;
/// syntax and semantic analysis
mod syntax;
/// topoglyph is a real word, i promise
mod topo;
use crate::log::*;
@ -68,12 +90,12 @@ fn main() -> Result<()> {
let mut state = syntax::State::new();
if let Err(e) = tree.eval(&mut state) {
error!("{e:?}");
println!("Error: {e:?}");
shell.render_error(e);
}
}
Err(e) => {
error!("{e:?}");
println!("Error: {e:?}");
shell.render_error(e);
}
}
// shell.exec(tree.into())?;
@ -202,7 +224,3 @@ fn main() -> Result<()> {
}
}
}
/*
*/

@ -103,7 +103,7 @@ impl Writer {
unsafe {
let handle = Console::GetStdHandle(Console::STD_OUTPUT_HANDLE)
.context("unable to get stdout handle")?;
let mut stdout = Self{output: handle};
let mut stdout = Self { output: handle };
stdout.reset()?;
Ok(stdout)
}
@ -146,11 +146,6 @@ impl Writer {
self.write(b"\x1b[2J\x1b[0;0H")?;
Ok(())
}
// pub fn hide_cursor(&mut self) -> Result<()> {
// self.write(b"\x1b[?25l")?;
// Ok(())
// }
}
impl Write for Writer {

@ -6,7 +6,12 @@ use crate::{
output, syntax,
};
use std::path::{Path, PathBuf};
use std::{
error::Error,
fmt,
io::{self, Write},
path::{Path, PathBuf},
};
use anyhow::Result;
use dirs;
@ -153,4 +158,21 @@ impl Shell {
}
}
}
pub fn render_error<E: Error>(&mut self, e: E) -> io::Result<()> {
self.render_error_helper(e, 0)?;
Ok(())
}
fn render_error_helper<E: Error>(&mut self, e: E, depth: u8) -> io::Result<()> {
if depth > 0 {
println!(" {e}");
} else {
println!("{e}:");
}
if let Some(cause) = e.source() {
self.render_error_helper(cause, depth + 1)?;
}
Ok(())
}
}

@ -103,7 +103,7 @@ pub struct Command {
impl Eval for Command {
fn eval(&self, ctx: &mut State) -> Result<Value, ExecError> {
let name = self.name.eval(ctx)?.try_to_string()?;
let mut proc = process::Command::new(name);
let mut proc = process::Command::new(&name);
if self.args.len() > 0 {
let args = self
@ -113,8 +113,16 @@ impl Eval for Command {
.collect::<Result<Vec<String>, ExecError>>()?;
proc.args(args);
}
let mut child = proc.spawn().map_err(|e| ExecError::ProcessSpawnError(e))?;
let status = child.wait().map_err(|e| ExecError::ProcessWaitError(e))?;
let mut child = proc.spawn().map_err(|e| ExecError::ProcessSpawnError {
name: name.clone(),
source: e,
})?;
let pid = child.id();
let status = child.wait().map_err(|e| ExecError::ProcessWaitError {
name,
pid,
source: e,
})?;
return Ok(Value::ExitStatus(status));
}
}

@ -1,9 +1,9 @@
use crate::error::LexError;
use std::{collections::VecDeque, fmt, ops::Range, str::Chars};
/// The position of a specific glyph within a corpus of text. We use this for rendering error
/// messages and communicating to the user the location of errors.
#[derive(Debug, PartialEq, Clone, Copy)]
/// A position a some two dimensional space defined by lines and columns, specifically concerned
/// with positions of characters within text
#[derive(PartialEq, Clone, Copy)]
pub struct Position {
/// The visual line in which this glyph appears in the source text
pub line: u64,
@ -13,7 +13,8 @@ pub struct Position {
}
impl Position {
pub fn start() -> Self {
/// The 0,0 position: the very first character of a document is at the 0,0 position
pub fn origin() -> Self {
Self { line: 0, column: 0 }
}
@ -35,6 +36,18 @@ impl Position {
}
}
impl fmt::Debug for Position {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(
f,
"({line}, {column})",
line = self.line,
column = self.column
)
}
}
/// A Glyph represents a character occurring at some specific position in some input text.
#[derive(PartialEq, Clone)]
pub struct Glyph {
/// the unicode code point of the glyph
@ -50,10 +63,12 @@ pub struct Glyph {
}
impl Glyph {
/// checks to see whether or not the character for this glyph is a word character
pub fn is_word(&self) -> bool {
self.glyph.is_alphanumeric() || self.glyph == '.'
}
/// checks to see whether or not the character for this glyph is a glob character
pub fn is_glob(&self) -> bool {
self.is_word() || self.glyph == '*'
}
@ -75,9 +90,16 @@ impl fmt::Debug for Glyph {
/// iteration of [tokens](crate::lex::Token), Glyphs is responsible for the creation and iteration
/// of glyphs.
pub struct Glyphs<'text> {
/// the characters of the input text that we're interested in reading
source: Chars<'text>,
/// the position of the next character in the source text
next_position: Position,
/// the number of bytes we've read from our input document so far
bytes_read: u64,
/// lookahead buffer
lookahead: VecDeque<Glyph>,
}
@ -86,7 +108,7 @@ impl<'text> Glyphs<'text> {
// neat
Self {
source: source.chars(),
next_position: Position::start(),
next_position: Position::origin(),
bytes_read: 0,
lookahead: VecDeque::new(),
}
@ -157,6 +179,9 @@ impl<'text> Glyphs<'text> {
}
}
/// discards all of the upcoming whitespace characters. After calling yeet_whitespace, the next
/// Glyp is guaranteed to be either non-whitespace or None (None would be because we've hit
/// EOF)
pub fn yeet_whitespace(&mut self) {
self.yeet_while(|tg| tg.glyph.is_whitespace());
}

Loading…
Cancel
Save