diff --git a/Cargo.toml b/Cargo.toml index 992ec19..a11643b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -bumpalo = "3" +bumpalo = { version = "3", features = ["collections"] } chumsky = "1.0.0-alpha" lasso = "0.7" logos = "0.13" diff --git a/src/main.rs b/src/main.rs index e073dd7..b4f78c5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,12 +14,7 @@ fn main() -> Result<(), Box> { let lexer = syntax::token::Token::lexer_with_extras(&buf, default()); let arena = Bump::new(); - syntax::parser::parse( - lexer - .spanned() - .map(|(item, span)| (item.unwrap_or(syntax::token::Token::Invalid), span.into())), - &arena, - ); + syntax::parser::parse_lexer(lexer, &arena); Ok(()) } diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index 4e1fa1c..e351646 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -1,12 +1,47 @@ -use lasso::Spur; +use {super::token::IntLit, chumsky::span::SimpleSpan, lasso::Spur}; -use super::token::IntLit; +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Spanned { + pub item: T, + pub span: SimpleSpan, +} + +impl Spanned { + #[inline] + pub fn new(item: T, span: SimpleSpan) -> Self { + Self { item, span } + } +} + +pub type SpanExpr<'a> = Spanned>; +pub type ExprRef<'a> = &'a SpanExpr<'a>; +pub type ExprList<'a> = &'a [SpanExpr<'a>]; +pub type Ident = Spur; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Expr<'a> { - Binary(BinaryOperator, &'a Self, &'a Self), - Unary(UnaryOperator, &'a Self), + Ident(Ident), + Path(ExprList<'a>), Literal(Literal), + Call(ExprRef<'a>, ExprList<'a>), + Binary(Spanned, ExprRef<'a>, ExprRef<'a>), + Unary(Spanned, ExprRef<'a>), + BindLocal(ExprRef<'a>, Spanned, Option>), + BindIn( + ExprRef<'a>, + Spanned, + ExprList<'a>, + Option>, + ), + Match(ExprRef<'a>, &'a [(Spanned, SpanExpr<'a>)]), + Set(ExprRef<'a>, ExprRef<'a>), + Func(Ident, &'a [Spanned], ExprRef<'a>), +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Pattern { + Ident(Ident), + None, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] diff --git a/src/syntax/parser.rs b/src/syntax/parser.rs index 92adc53..5266491 100644 --- a/src/syntax/parser.rs +++ b/src/syntax/parser.rs @@ -1,18 +1,107 @@ -use super::{ast::Expr, token::Token}; -use bumpalo::Bump; -use chumsky::{extra::Full, input::Stream, prelude::*}; +use super::ast::{Literal, SpanExpr}; -type Item = (Token, SimpleSpan); -type Extra<'s> = Full, State<'s>, ()>; -struct State<'a> { +use { + super::{ + ast::{Expr, Spanned}, + token::Token, + }, + crate::utils::Pipe, + bumpalo::Bump, + chumsky::{ + extra::Full, + input::{Stream, ValueInput}, + prelude::*, + }, + logos::Lexer, +}; + +fn expr<'a, I>() -> impl Parser<'a, I, SpanExpr<'a>, Extra<'a>> + Clone +where + I: Input<'a, Token = Token, Span = SimpleSpan> + ValueInput<'a>, +{ + recursive(|expr| { + let ident = select!(Token::Ident(id) => Expr::Ident(id)); + let atom = { + // Literal + select! { + Token::Int(a) => Literal::Integer(a), + Token::String(a) => Literal::String(a) + } + .map(Expr::Literal) + } + .or(ident) + .map_with_span(Spanned::new) + .or(expr + .clone() + .delimited_by(just(Token::LeftParen), just(Token::RightParen))); + + let call = atom + .clone() + .pipe(arena_box) + .then( + expr.clone() + .separated_by(just(Token::Comma)) + .allow_trailing() + .pipe(arena_collect) + .delimited_by(just(Token::LeftParen), just(Token::RightParen)), + ) + .map(|(expr, params)| Expr::Call(expr, params)); + + call.map_with_span(Spanned::new).or(atom) + }) +} + +pub struct State<'a> { + pub arena: &'a Bump, +} +pub type Extra<'a> = Full, State<'a>, ()>; + +pub fn parse_input<'a>( + input: impl ValueInput<'a, Token = Token, Span = SimpleSpan>, arena: &'a Bump, +) { + println!("{:?}", expr().parse_with_state(input, &mut State { arena })); } -pub fn parse(input: impl Iterator, arena: &Bump) { - expr().parse_with_state(Stream::from_iter(input), &mut State { arena }); +pub fn parse_iter( + input: impl Iterator, + eoi: impl Into, + arena: &Bump, +) { + parse_input(Stream::from_iter(input).spanned(eoi.into()), arena) } -fn expr<'s, 'a, I: Iterator + 's>( -) -> impl Parser<'s, Stream, Expr<'a>, Extra<'s>> { - todo() +pub fn parse_lexer(input: Lexer, arena: &Bump) { + let end = input.span().end; + parse_iter( + input + .spanned() + .map(|(token, span)| (token.unwrap_or(Token::Invalid), span.into())), + end..end + 1, + arena, + ) +} + +fn arena_collect<'a, I, O: 'a>( + parser: impl IterParser<'a, I, O, Extra<'a>> + Clone, +) -> impl Parser<'a, I, &'a [O], Extra<'a>> + Clone +where + I: Input<'a, Span = SimpleSpan, Token = Token>, +{ + empty() + .map_with_state(|_, _, state: &mut State| bumpalo::vec![in state.arena]) + .foldl(parser, |mut v, o| { + v.push(o); + v + }) + .map(bumpalo::collections::Vec::into_bump_slice) +} + +fn arena_box<'a, I, O: 'a>( + parser: impl Parser<'a, I, O, Extra<'a>> + Clone, +) -> impl Parser<'a, I, &'a O, Extra<'a>> + Clone +where + I: Input<'a, Span = SimpleSpan, Token = Token>, +{ + parser.map_with_state(|item, _, state| &*state.arena.alloc(item)) } diff --git a/src/utils.rs b/src/utils.rs index 31a900e..6b4c784 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -2,3 +2,14 @@ pub fn default() -> T { Default::default() } + +pub trait Pipe { + fn pipe(self, mut f: impl FnMut(Self) -> R) -> R + where + Self: Sized, + { + f(self) + } +} + +impl Pipe for T {}