diff --git a/src/repl.rs b/src/repl.rs index b8d2100..3c7423f 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -1,4 +1,4 @@ -use crate::{error::Error, syntax::parser::read}; +use crate::{error::Error, syntax::parser::parse}; use rustyline::{error::ReadlineError, Editor}; pub fn repl() -> rustyline::Result<()> { @@ -6,7 +6,7 @@ pub fn repl() -> rustyline::Result<()> { let mut rl = Editor::<()>::new()?; loop { match rl.readline(prompt) { - Ok(line) => match read(&line) { + Ok(line) => match parse(&line) { Ok(values) => values.iter().for_each(|e| println!("{e}")), Err(e) => e.into_iter().map(Error::Parse).for_each(|e| { if let Err(e) = e.report(&line) { diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index 58a9edb..937b290 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -2,14 +2,48 @@ use ordered_float::OrderedFloat; use std::{ borrow::Cow, fmt::{Display, Write}, + hash::Hash, }; +pub type Span = std::ops::Range; + +/// A spanned item +#[derive(Clone, Debug)] +pub struct Spanned { + pub item: T, + pub span: Span, +} + +impl Spanned { + pub fn new(item: T, span: Span) -> Self { + Self { item, span } + } +} + +impl Display for Spanned { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} \x1B[2m@ {:?}\x1B[22m", self.item, self.span) + } +} + +impl PartialEq for Spanned { + fn eq(&self, other: &Self) -> bool { + self.item == other.item + } +} +impl Hash for Spanned { + fn hash(&self, state: &mut H) { + self.item.hash(state); + } +} + /// A Wisp AST -#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, Clone, Hash, PartialEq)] pub enum Expr<'a> { - List(Vec), - Vector(Vec), - Map(Vec<(Self, Self)>), + List(Vec>), + Vector(Vec>), + Map(Vec<(Spanned, Spanned)>), + Quote(Box>), Symbol(Cow<'a, str>), Keyword(Cow<'a, str>), Number(OrderedFloat), @@ -22,6 +56,7 @@ impl<'a> Display for Expr<'a> { Self::List(list) => write_seq(f, list, "(", ")"), Self::Vector(vec) => write_seq(f, vec, "[", "]"), Self::Map(map) => write_seq(f, map.iter().map(|(k, v)| format!("{k} {v}")), "{", "}"), + Self::Quote(expr) => write!(f, "'{expr}"), Self::Symbol(sym) => write!(f, "{sym}"), Self::Keyword(kw) => write!(f, ":{kw}"), Self::Number(n) => write!(f, "{n}"), diff --git a/src/syntax/parser.rs b/src/syntax/parser.rs index 0cfd7b1..e4dd914 100644 --- a/src/syntax/parser.rs +++ b/src/syntax/parser.rs @@ -1,15 +1,18 @@ -use super::{ast::Expr, lexer::Token}; +use super::{ + ast::{Expr, Spanned}, + lexer::Token, +}; use chumsky::{prelude::*, Stream}; use logos::Logos; /// Parse source string into a Expr -pub fn read(src: &str) -> Result, Vec>>> { +pub fn parse(src: &str) -> Result>, Vec>>> { let lexer = Token::lexer(src); let len = lexer.source().len(); parser().parse(Stream::from_iter(len..len + 1, lexer.spanned())) } -fn parser<'a>() -> impl Parser, Vec>, Error = Simple>> { +fn parser<'a>() -> impl Parser, Vec>>, Error = Simple>> { recursive(|expr| { let atom = select! { Token::Symbol(s) => Expr::Symbol(s.into()), @@ -40,9 +43,14 @@ fn parser<'a>() -> impl Parser, Vec>, Error = Simple() -> impl Parser, Vec>, Error = Simple(src: &'a str, expected: &'a [Expr<'a>]) { - assert_eq!(read(src).unwrap(), expected); + fn assert_parse<'a>(src: &'a str, expected: &'a [Spanned>]) { + assert_eq!(parse(src).unwrap(), expected); } }