From f42a8d3da1e1dca9098175b005855c9b0318d69f Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sat, 29 Jan 2022 18:09:21 +0100 Subject: [PATCH] uh --- Cargo.lock | 16 ++++++ Cargo.toml | 1 + src/lib.rs | 139 ++++++++++++++++++++++++++++++++++++++++++---------- src/main.rs | 5 +- 4 files changed, 133 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 72280c6..a75ef2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,15 @@ dependencies = [ "const-random", ] +[[package]] +name = "ariadne" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7080ae01b2f0c312065d4914cd0f0de045eb8832e9415b355106a6cff3073cb4" +dependencies = [ + "yansi", +] + [[package]] name = "beef" version = "0.5.1" @@ -81,6 +90,7 @@ dependencies = [ name = "lambda-calculus" version = "0.1.0" dependencies = [ + "ariadne", "chumsky", "logos", ] @@ -188,3 +198,9 @@ name = "wasi" version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "yansi" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71" diff --git a/Cargo.toml b/Cargo.toml index 40f1065..8777113 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,5 +6,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +ariadne = "0.1.3" chumsky = "0.7.0" logos = "0.12.0" diff --git a/src/lib.rs b/src/lib.rs index 4e2245c..2ad8704 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,12 @@ +use ariadne::{Color, Fmt, Label, Report, ReportKind, Source}; +use chumsky::{Parser, Stream}; use logos::Logos; mod lexer { use logos::Logos; + use std::fmt::Formatter; - #[derive(Logos, Clone, Eq, PartialEq, Hash)] + #[derive(Logos, Debug, Clone, Eq, PartialEq, Hash)] pub enum Token<'a> { #[token("λ")] Lambda, @@ -27,56 +30,138 @@ mod lexer { #[regex(r"[ \t\r\n]+", logos::skip)] Error, } + + impl std::fmt::Display for Token<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Token::Lambda => write!(f, "λ"), + Token::Dot => write!(f, "."), + Token::Binding => write!(f, ":="), + Token::ParenO => write!(f, "("), + Token::ParenC => write!(f, ")"), + Token::Ident(ident) => write!(f, "{}", ident), + Token::Error => write!(f, "[error]"), + } + } + } } mod parser { use crate::lexer::Token; use chumsky::prelude::*; - enum Expr { + #[derive(Debug)] + pub enum Expr { Name(String), Application { function: Box, argument: Box, }, Abstraction { - args: Vec, - body: Expr, + args: Vec, + body: Box, }, } - fn expr_parser<'a>() -> impl Parser, Spanned, Error = Simple>> + Clone - { + pub fn expr_parser<'a>() -> impl Parser, Expr, Error = Simple>> + Clone { recursive(|expr| { - let variable = filter_map(|span, token| match token { - Token::Ident(name) => Ok(Expr::Name(name.to_string())), + let ident = filter_map(|span, token| match token { + Token::Ident(ident) => Ok(ident.to_string()), _ => Err(Simple::expected_input_found(span, [], Some(token))), }) - .labelled("variable"); + .labelled("ident"); let abstraction = just(Token::Lambda) - .ignore_then(variable) - .then(Token::Dot) - .then(expr.clone()); + .ignore_then(ident) + .then_ignore(just(Token::Dot)) + .then(expr) + .map(|(args, body)| Expr::Abstraction { + args: args.chars().collect(), + body: Box::new(body), + }) + .labelled("abstraction"); - let application = just(expr).then(expr.clone()); - - let atom = variable - .or(expr.delimited_by(Token::ParenO, Token::ParenC)) - .or(abstraction) - .or(application); - - let binding = just(variable) - .then_ignore(Token::Binding) - .then(expr.clone()); - - let statement = just(expr.clone()).or(binding); - - todo!() + abstraction }) } } pub fn run(input: &str) { - let mut lex = lexer::Token::lexer(input); + let mut lexer = lexer::Token::lexer(input); + let length = lexer.source().len(); + + match parser::expr_parser().parse(Stream::from_iter(length..length + 1, lexer.spanned())) { + Ok(ast) => println!("parsed: {ast:#?}"), + Err(errs) => errs + .into_iter() + .map(|e| e.map(|c| c.to_string())) + .for_each(|e| { + let report = Report::build(ReportKind::Error, (), e.span().start); + + let report = match e.reason() { + chumsky::error::SimpleReason::Unclosed { span, delimiter } => report + .with_message(format!( + "Unclosed delimiter {}", + delimiter.fg(Color::Yellow) + )) + .with_label( + Label::new(span.clone()) + .with_message(format!( + "Unclosed delimiter {}", + delimiter.fg(Color::Yellow) + )) + .with_color(Color::Yellow), + ) + .with_label( + Label::new(e.span()) + .with_message(format!( + "Must be closed before this {}", + e.found() + .unwrap_or(&"end of file".to_string()) + .fg(Color::Red) + )) + .with_color(Color::Red), + ), + chumsky::error::SimpleReason::Unexpected => report + .with_message(format!( + "{}, expected {}", + if e.found().is_some() { + "Unexpected token in input" + } else { + "Unexpected end of input" + }, + if e.expected().len() == 0 { + "something else".to_string() + } else { + e.expected() + .map(|expected| match expected { + Some(expected) => expected.to_string(), + None => "end of input".to_string(), + }) + .collect::>() + .join(", ") + } + )) + .with_label( + Label::new(e.span()) + .with_message(format!( + "Unexpected token {}", + e.found() + .unwrap_or(&"end of file".to_string()) + .fg(Color::Red) + )) + .with_color(Color::Red), + ), + chumsky::error::SimpleReason::Custom(msg) => { + report.with_message(msg).with_label( + Label::new(e.span()) + .with_message(format!("{}", msg.fg(Color::Red))) + .with_color(Color::Red), + ) + } + }; + + report.finish().print(Source::from(input)).unwrap(); + }), + } } diff --git a/src/main.rs b/src/main.rs index 4021117..9047038 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,6 @@ fn main() { - lambda_calculus::run("hello"); + lambda_calculus::run("λa.a"); + // lambda_calculus::run("λa.a"); + // lambda_calculus::run("λab.a"); + // lambda_calculus::run("U := λab.a"); }