bendn 2024-02-05
parent 2bee12f · commit 3f50c46
-rw-r--r--src/main.rs1
-rw-r--r--src/parser.rs34
-rw-r--r--src/parser/fun.rs54
-rw-r--r--src/parser/types.rs4
-rw-r--r--src/parser/util.rs2
-rw-r--r--src/ui.rs42
6 files changed, 101 insertions, 36 deletions
diff --git a/src/main.rs b/src/main.rs
index 18bd388..4499d9b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2,6 +2,7 @@
mod array;
mod lexer;
mod parser;
+mod ui;
fn main() {
println!("Hello, world!");
}
diff --git a/src/parser.rs b/src/parser.rs
index 8cee0ef..e90f11e 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -1,15 +1,17 @@
-mod types;
+pub mod types;
use crate::lexer::{Lexer, Token};
use chumsky::{
input::{SpannedInput, Stream},
prelude::*,
Parser,
};
-mod fun;
-mod util;
+pub mod fun;
+pub mod util;
use types::*;
use util::*;
+use self::fun::Function;
+
impl<'s> Value<'s> {
pub fn parse() -> parser![Self] {
select! {
@@ -35,11 +37,11 @@ impl<'s> Expr<'s> {
choice((t![ident].map(Expr::Ident), val)).boxed()
});
- let λ = Lambda::parse().map(Expr::Lambda);
+ let λ = Λ::parse(expr.clone());
let decl = t![ident]
.then_ignore(t![<-])
- .then(inline_expr.clone().or(λ.clone()))
+ .then(inline_expr.clone().or(λ.clone().map(Expr::Lambda)))
.map(|(name, body)| Expr::Let {
name,
rhs: Box::new(body),
@@ -50,7 +52,7 @@ impl<'s> Expr<'s> {
let r#if = t![if]
.ignore_then(
expr.clone()
- .then(t![else].or_not().ignore_then(expr.or_not()))
+ .then(t![else].or_not().ignore_then(expr.clone().or_not()))
.delimited_by(t!['('], t![')']),
)
.map(|(a, b)| Expr::If {
@@ -59,14 +61,22 @@ impl<'s> Expr<'s> {
})
.labelled("🐋")
.boxed();
- choice((decl, r#if, inline_expr, λ))
+ choice((
+ decl,
+ r#if,
+ inline_expr,
+ λ.clone().map(Expr::Lambda),
+ Function::parse(λ).map(Expr::Function),
+ ))
+ .labelled("expr")
})
}
}
#[test]
fn parse_expr() {
- dbg!(Expr::parse().parse(code("a ← λ ( +- 🍴 )")).unwrap());
+ parse_s("a ← λ ( +- 🍴 )", Expr::parse());
+ // dbg!(Expr::parse().parse(code("a ← λ ( +- 🍴 )")).unwrap());
}
pub fn stream(lexer: Lexer<'_>, len: usize) -> SpannedInput<Token<'_>, Span, Stream<Lexer<'_>>> {
@@ -78,6 +88,14 @@ pub fn code<'s>(x: &'s str) -> SpannedInput<Token<'s>, Span, Stream<Lexer<'s>>>
stream(crate::lexer::lex(x), x.len())
}
+#[cfg(test)]
+pub fn parse_s<'s, T: std::fmt::Debug>(x: &'s str, p: parser![T]) -> T {
+ match crate::ui::display(p.parse(code(x)).into_result(), x) {
+ Ok(x) => dbg!(x),
+ Err(()) => panic!(),
+ }
+}
+
pub fn parse(tokens: Lexer<'_>, len: usize) -> Result<Ast<'_>, Vec<Error<'_>>> {
parser().parse(stream(tokens, len)).into_result()
}
diff --git a/src/parser/fun.rs b/src/parser/fun.rs
index 9dfd561..3a4c01c 100644
--- a/src/parser/fun.rs
+++ b/src/parser/fun.rs
@@ -6,12 +6,12 @@ use chumsky::{prelude::*, Parser};
#[derive(Debug, Clone)]
pub enum Function<'s> {
Dup,
- Both(Lambda<'s>, Lambda<'s>),
- Fork(Lambda<'s>, Lambda<'s>),
- Gap(Lambda<'s>),
- Hold(Lambda<'s>),
+ Both(Λ<'s>, Λ<'s>),
+ Fork(Λ<'s>, Λ<'s>),
+ Gap(Λ<'s>),
+ Hold(Λ<'s>),
Flip,
- Duck(Lambda<'s>),
+ Duck(Λ<'s>),
Reverse,
Zap,
Add,
@@ -36,28 +36,28 @@ pub enum Function<'s> {
Split,
First,
Last,
- Each(Lambda<'s>),
- Reduce(Lambda<'s>),
- ReduceStack(Lambda<'s>),
+ Each(Λ<'s>),
+ Reduce(Λ<'s>),
+ ReduceStack(Λ<'s>),
Range,
Call,
}
-impl<'s> Lambda<'s> {
- pub fn parse() -> parser![Self] {
- t![λ]
- .ignore_then(
- Expr::parse()
- .repeated()
- .collect()
- .delimited_by(t!['('], t![')']),
- )
- .map(|x| Self(x))
+impl<'s> Λ<'s> {
+ pub fn parse(exp: parser![Expr<'s>]) -> parser![Self] {
+ let mut λ = Recursive::declare();
+ λ.define(choice((
+ t![λ]
+ .ignore_then(exp.repeated().collect().delimited_by(t!['('], t![')']))
+ .map(|x| Self(x)),
+ Function::parse(λ.clone()).map(|x| Λ(vec![Expr::Function(x)])),
+ )));
+ λ.labelled("λ")
}
}
impl<'s> Function<'s> {
- pub fn parse() -> parser![Self] {
+ pub fn parse(λ: parser![Λ<'s>]) -> parser![Self] {
use Function::*;
let basic = select! {
Token::Dup => Dup,
@@ -88,12 +88,16 @@ impl<'s> Function<'s> {
Token::Last => Last,
};
macro_rules! two {
- ($name:ident) => {
- Lambda::parse()
- .then(Lambda::parse())
- .then_ignore(just(Token::$name))
- .map(|(a, b)| $name(a, b))
- };
+ ($name:ident) => {{
+ let mut p = Recursive::declare();
+ p.define(
+ λ.clone()
+ .then(λ.clone())
+ .then_ignore(just(Token::$name))
+ .map(|(a, b)| $name(a, b)),
+ );
+ p
+ }};
}
choice((basic, two![Both], two![Fork]))
}
diff --git a/src/parser/types.rs b/src/parser/types.rs
index 2385564..e050890 100644
--- a/src/parser/types.rs
+++ b/src/parser/types.rs
@@ -16,7 +16,7 @@ pub enum Ast<'s> {
}
#[derive(Clone, Debug)]
-pub struct Lambda<'s>(pub Vec<Expr<'s>>);
+pub struct Λ<'s>(pub Vec<Expr<'s>>);
#[derive(Clone)]
pub enum Value<'s> {
@@ -43,7 +43,7 @@ pub enum Expr<'s> {
Function(super::fun::Function<'s>),
Value(Value<'s>),
Ident(&'s str),
- Lambda(Lambda<'s>),
+ Lambda(Λ<'s>),
Let {
name: &'s str,
rhs: Box<Expr<'s>>,
diff --git a/src/parser/util.rs b/src/parser/util.rs
index 60c242a..42ff5d2 100644
--- a/src/parser/util.rs
+++ b/src/parser/util.rs
@@ -52,7 +52,7 @@ macro_rules! t {
}
macro_rules! parser {
($t:ty) => {
- impl Parser<'s, crate::parser::types::Input<'s>, $t, extra::Err<Error<'s>>> + Clone
+ impl Parser<'s, crate::parser::types::Input<'s>, $t, extra::Err<Error<'s>>> + Clone + 's
}
}
diff --git a/src/ui.rs b/src/ui.rs
new file mode 100644
index 0000000..9c3dcd9
--- /dev/null
+++ b/src/ui.rs
@@ -0,0 +1,42 @@
+use crate::parser::types::Error;
+use chumsky::{error::RichReason, prelude::*};
+use comat::cformat as cmt;
+
+pub fn display<T>(result: Result<T, Vec<Error>>, code: &str) -> Result<T, ()> {
+ let e = match result {
+ Ok(x) => return Ok(x),
+ Err(e) => e,
+ };
+
+ for e in e.into_iter().map(|e| e.map_token(|c| c.to_string())) {
+ let mut o = lerr::Error::new(code);
+ o.label((e.span().into_range(), "here"));
+ match e.reason() {
+ RichReason::Custom(x) => {
+ o.message(cmt!("{red}error{reset}: {x}"));
+ }
+ RichReason::ExpectedFound { .. } => {
+ o.message(cmt!("{red}error{reset}: {e}"));
+ }
+ RichReason::Many(x) => {
+ match &x[..] {
+ [x, rest @ ..] => {
+ o.message(cmt!("{red}error{reset}: {x}"));
+ for elem in rest {
+ o.note(cmt!("{yellow}also{reset}: {elem}"));
+ }
+ }
+ _ => unreachable!(),
+ };
+ }
+ }
+ for (l, span) in e.contexts() {
+ o.label((
+ span.into_range(),
+ cmt!("{yellow}while parsing this{reset}: {l}"),
+ ));
+ }
+ eprintln!("{o}");
+ }
+ Err(())
+}