This commit is contained in:
nora 2022-01-29 18:09:21 +01:00
parent 731974ef45
commit f42a8d3da1
4 changed files with 133 additions and 28 deletions

16
Cargo.lock generated
View file

@ -11,6 +11,15 @@ dependencies = [
"const-random", "const-random",
] ]
[[package]]
name = "ariadne"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7080ae01b2f0c312065d4914cd0f0de045eb8832e9415b355106a6cff3073cb4"
dependencies = [
"yansi",
]
[[package]] [[package]]
name = "beef" name = "beef"
version = "0.5.1" version = "0.5.1"
@ -81,6 +90,7 @@ dependencies = [
name = "lambda-calculus" name = "lambda-calculus"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"ariadne",
"chumsky", "chumsky",
"logos", "logos",
] ]
@ -188,3 +198,9 @@ name = "wasi"
version = "0.10.2+wasi-snapshot-preview1" version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "yansi"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71"

View file

@ -6,5 +6,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
ariadne = "0.1.3"
chumsky = "0.7.0" chumsky = "0.7.0"
logos = "0.12.0" logos = "0.12.0"

View file

@ -1,9 +1,12 @@
use ariadne::{Color, Fmt, Label, Report, ReportKind, Source};
use chumsky::{Parser, Stream};
use logos::Logos; use logos::Logos;
mod lexer { mod lexer {
use logos::Logos; use logos::Logos;
use std::fmt::Formatter;
#[derive(Logos, Clone, Eq, PartialEq, Hash)] #[derive(Logos, Debug, Clone, Eq, PartialEq, Hash)]
pub enum Token<'a> { pub enum Token<'a> {
#[token("λ")] #[token("λ")]
Lambda, Lambda,
@ -27,56 +30,138 @@ mod lexer {
#[regex(r"[ \t\r\n]+", logos::skip)] #[regex(r"[ \t\r\n]+", logos::skip)]
Error, 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 { mod parser {
use crate::lexer::Token; use crate::lexer::Token;
use chumsky::prelude::*; use chumsky::prelude::*;
enum Expr { #[derive(Debug)]
pub enum Expr {
Name(String), Name(String),
Application { Application {
function: Box<Expr>, function: Box<Expr>,
argument: Box<Expr>, argument: Box<Expr>,
}, },
Abstraction { Abstraction {
args: Vec<String>, args: Vec<char>,
body: Expr, body: Box<Expr>,
}, },
} }
fn expr_parser<'a>() -> impl Parser<Token<'a>, Spanned<Expr>, Error = Simple<Token<'a>>> + Clone pub fn expr_parser<'a>() -> impl Parser<Token<'a>, Expr, Error = Simple<Token<'a>>> + Clone {
{
recursive(|expr| { recursive(|expr| {
let variable = filter_map(|span, token| match token { let ident = filter_map(|span, token| match token {
Token::Ident(name) => Ok(Expr::Name(name.to_string())), Token::Ident(ident) => Ok(ident.to_string()),
_ => Err(Simple::expected_input_found(span, [], Some(token))), _ => Err(Simple::expected_input_found(span, [], Some(token))),
}) })
.labelled("variable"); .labelled("ident");
let abstraction = just(Token::Lambda) let abstraction = just(Token::Lambda)
.ignore_then(variable) .ignore_then(ident)
.then(Token::Dot) .then_ignore(just(Token::Dot))
.then(expr.clone()); .then(expr)
.map(|(args, body)| Expr::Abstraction {
args: args.chars().collect(),
body: Box::new(body),
})
.labelled("abstraction");
let application = just(expr).then(expr.clone()); abstraction
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!()
}) })
} }
} }
pub fn run(input: &str) { 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::<Vec<_>>()
.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();
}),
}
} }

View file

@ -1,3 +1,6 @@
fn main() { 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");
} }