This commit adds a parser, complete with tests. The parser implements
an iterator which returns Datum. It wraps around a Lexer and uses the
Lexer's iterator interfact to consume lexemes. It may return an error
which may wrap around a LexError or a fully lexed lexeme.

In the implementation of the Parser bugs were found in the lexer
package. This resulted in the lexing tests being extended as well as
several small logic updates.

The number package has had slight tweaks to make number representations
less cumbersome.

Finally, the Datum display logic in the sexpr package has also been updated.

Signed-off-by: Ava Affine <ava@sunnypup.io>
This commit is contained in:
Ava Apples Affine 2025-05-19 14:38:11 -07:00
parent a48fc52fab
commit 86f905ba1d
5 changed files with 632 additions and 29 deletions

View file

@ -16,6 +16,7 @@
*/
use core::fmt::{self, Formatter};
use alloc::format;
use alloc::rc::Rc;
use alloc::vec::Vec;
use alloc::string::String;
@ -26,33 +27,51 @@ use crate::number::Number;
pub enum Datum {
Number(Number),
Bool(bool),
List(Ast),
List(Rc<Ast>),
Symbol(String),
Char(u8),
String(Vec<u8>),
Vector(Vec<Datum>),
Vector(Vec<Rc<Datum>>),
ByteVector(Vec<u8>),
#[default]
None,
}
fn byte_to_escaped_char(b: u8) -> String {
unimplemented!()
// alarm, backspace, delete
match b {
_ if b > 31 && b < 127 => String::from(b as char),
_ => format!("x{:x}", b),
}
}
fn fmt_vec<T: fmt::Display>(v: &Vec<T>) -> String {
if v.len() == 0 {
return String::new()
}
let mut s = format!("{}", v[0]);
let mut i = v.iter();
i.next(); // discard
i.for_each(|e| {
s = format!("{} {}", s, e);
});
s
}
impl fmt::Display for Datum {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Datum::Number(n) => write!(f, "{}", Into::<String>::into(*n)),
Datum::Bool(n) => write!(f, "{n}"),
Datum::Bool(n) => write!(f, "{}", if *n {"#t"} else {"#f"}),
Datum::List(n) => write!(f, "{n}"),
Datum::Symbol(n) => write!(f, "{n}"),
Datum::Char(n) => write!(f, "{}",
Datum::Char(n) => write!(f, "#\\{}",
byte_to_escaped_char(*n)),
Datum::String(n) =>
write!(f, "\"{}\"", String::from_utf8_lossy(&*n)),
Datum::Vector(n) => write!(f, "#({n:?})"),
Datum::ByteVector(n) => write!(f, "#u8({n:?})"),
Datum::Vector(n) => write!(f, "#({})", fmt_vec(n)),
Datum::ByteVector(n) => write!(f, "#u8({})", fmt_vec(n)),
Datum::None => Ok(())
}
}
@ -68,7 +87,7 @@ impl fmt::Debug for Datum {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Datum::Number(n) => write!(f, "{}", Into::<String>::into(*n)),
Datum::Bool(n) => write!(f, "{n}"),
Datum::Bool(n) => write!(f, "{}", if *n {"#t"} else {"#f"}),
Datum::List(n) => write!(f, "{n}"),
Datum::Char(n) => write!(f, "{}",
byte_to_escaped_char(*n)),
@ -84,7 +103,7 @@ impl fmt::Debug for Datum {
#[derive(Default, Clone)]
pub struct Ast(Rc<Datum>, Rc<Datum>);
pub struct Ast(pub Rc<Datum>, pub Rc<Datum>);
impl Iterator for Ast {
type Item = Rc<Datum>;
@ -120,7 +139,7 @@ impl fmt::Display for Ast {
if let Datum::None = &*cur.1 {
write!(f, ")")
} else {
write!(f, " {})", cur.1)
write!(f, " . {})", cur.1)
}
}
}