diff --git a/Cargo.lock b/Cargo.lock index ae3bf6f..8621c4b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -76,9 +76,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.147" +version = "0.2.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "logos" @@ -120,9 +120,9 @@ checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" dependencies = [ "unicode-ident", ] @@ -176,9 +176,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.29" +version = "2.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +checksum = "9caece70c63bfba29ec2fed841a09851b14a235c60010fa4de58089b6c025668" dependencies = [ "proc-macro2", "quote", @@ -187,9 +187,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "version_check" diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index 45fb1cf..fd54f81 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -26,22 +26,30 @@ pub enum Expr<'a> { Call(ExprRef<'a>, ExprList<'a>), Binary(Spanned, ExprRef<'a>, ExprRef<'a>), Unary(Spanned, ExprRef<'a>), - BindLocal(ExprRef<'a>, Spanned, Option>), + BindLocal(Spanned, ExprRef<'a>, Option>), BindIn( - ExprRef<'a>, Spanned, + ExprRef<'a>, ExprList<'a>, Option>, ), - Match(ExprRef<'a>, &'a [(Spanned, SpanExpr<'a>)]), Set(ExprRef<'a>, ExprRef<'a>), - Func(Ident, &'a [Spanned], ExprRef<'a>), + Match(ExprRef<'a>, &'a [(Spanned, SpanExpr<'a>)]), + Func(&'a [(Spanned, Spanned)], Spanned, ExprRef<'a>), Block(ExprList<'a>), + Unit, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Type { + Ident(Ident), + Unit, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Pattern { Ident(Ident), + Literal(Literal), None, } diff --git a/src/syntax/parser.rs b/src/syntax/parser.rs index 78b7b31..3dd8aef 100644 --- a/src/syntax/parser.rs +++ b/src/syntax/parser.rs @@ -1,8 +1,8 @@ -use super::ast::{BinaryOperator, Literal, SpanExpr, UnaryOperator}; +use super::ast::Type; use { super::{ - ast::{Expr, Spanned}, + ast::{BinaryOperator, Expr, Literal, Pattern, SpanExpr, Spanned, UnaryOperator}, token::Token, }, crate::utils::Pipe, @@ -28,39 +28,119 @@ where I: Input<'a, Token = Token, Span = SimpleSpan> + ValueInput<'a>, { recursive(|expr| { - let ident = select!(Token::Ident(id) => Expr::Ident(id)); + let ident = select!(Token::Ident(id) => id); let literal = select! { Token::Int(a) => Literal::Integer(a), Token::String(a) => Literal::String(a) + }; + + let pattern = select! { + Token::Ident(id) => Pattern::Ident(id), + Token::Underscore => Pattern::None, } - .map(Expr::Literal); + .or(literal.map(Pattern::Literal)) + .map_with_span(Spanned::new); - let atom = literal.or(ident).map_with_span(Spanned::new).or(expr - .clone() - .delimited_by(just(Token::LeftParen), just(Token::RightParen))); + let type_ = just([Token::LeftParen, Token::RightParen]) + .to(Type::Unit) + .or(ident.map(Type::Ident)) + .map_with_span(Spanned::new); - let call = atom + let block = expr .clone() - .foldl_with_state( - expr.clone() + .separated_by(just(Token::Semicolon)) + .allow_trailing() + .pipe(arena_collect) + .delimited_by(just(Token::LeftCurly), just(Token::RightCurly)); + + let func = just(Token::Func) + .ignore_then( + pattern + .then_ignore(just(Token::Colon)) + .then(type_) .separated_by(just(Token::Comma)) .allow_trailing() .pipe(arena_collect) - .delimited_by(just(Token::LeftParen), just(Token::RightParen)) - .map_with_span(Spanned::new) - .repeated(), - |expr, paramlist, state: &mut State| { - Spanned::new( - Expr::Call(state.arena.alloc(expr), paramlist.item), - merge_spans(expr.span, paramlist.span), - ) + .delimited_by(just(Token::LeftParen), just(Token::RightParen)), + ) + .then_ignore(just(Token::Colon)) + .then(type_) + .then( + just(Token::Equ) + .ignore_then(expr.clone()) + .or(block.clone().map(Expr::Block).map_with_span(Spanned::new)), + ) + .map_with_state(|((params, ret), expr), _, state| { + Expr::Func(params, ret, state.arena.alloc(expr)) + }); + + let atom = literal + .map(Expr::Literal) + .or(just([Token::LeftParen, Token::RightParen]).to(Expr::Unit)) + .or(ident.map(Expr::Ident)) + .or(func) + .map_with_span(Spanned::new) + .or(expr + .clone() + .delimited_by(just(Token::LeftParen), just(Token::RightParen))); + + // (expr1, expr2, …) + let call = atom.clone().foldl_with_state( + expr.clone() + .separated_by(just(Token::Comma)) + .allow_trailing() + .pipe(arena_collect) + .delimited_by(just(Token::LeftParen), just(Token::RightParen)) + .map_with_span(Spanned::new) + .repeated(), + |expr, paramlist, state: &mut State| { + Spanned::new( + Expr::Call(state.arena.alloc(expr), paramlist.item), + merge_spans(expr.span, paramlist.span), + ) + }, + ); + + let path = call + .clone() + .map_with_state(|item, _, state| bumpalo::vec![in state.arena; item]) + .foldl( + just(Token::Dot).ignore_then(call).repeated(), + |mut v, expr| { + v.push(expr); + v }, ) - .boxed(); + .map(|v| Expr::Path(v.into_bump_slice())) + .map_with_span(Spanned::new); - let unary = call; + /* let unary = equivmap!(Token, UnaryOperator, [Minus, Tilde]) + .map_with_span(Spanned::new) + .repeated() + .foldr_with_state(call, |op, expr, state| { + Spanned::new( + Expr::Unary(op, state.arena.alloc(expr)), + merge_spans(op.span, expr.span), + ) + }); + */ + let unary = path.foldl_with_state( + just([Token::Dot, Token::Star]) + .to(UnaryOperator::Star) + .or(just(Token::Tilde).to(UnaryOperator::Tilde)) + .map_with_span(Spanned::new) + .repeated(), + |expr, op, state| { + Spanned::new( + Expr::Unary(op, state.arena.alloc(expr)), + merge_spans(expr.span, op.span), + ) + }, + ); + + // OP let binary = unary.clone().foldl_with_state( equivmap!( Token, @@ -78,31 +158,92 @@ where }, ); - binary.or(atom) + let bind = { + let start = pattern.then_ignore(just(Token::Colon)).then(expr.clone()); // := + let else_ = just(Token::Else).ignore_then(block.clone()).or_not(); // else {…} + + // := [else {…}] + let local = start.clone().then(else_.clone()).map_with_state( + |((pat, expr), else_), _, state| { + Expr::BindLocal(pat, &*state.arena.alloc(expr), else_) + }, + ); + + // := {…} else {…} + let in_ = start.then(block.clone()).then(else_).map_with_state( + |(((pat, expr), block), else_), _, state| { + Expr::BindIn(pat, &*state.arena.alloc(expr), block, else_) + }, + ); + + in_.or(local) + }; + + // + let set = atom + .clone() + .then_ignore(just(Token::LArrow)) + .then(expr.clone()) + .map_with_state(|(place, expr), _, state| { + Expr::Set(state.arena.alloc(place), state.arena.alloc(expr)) + }); + + // .match { , … } + let match_ = atom + .clone() + .then_ignore(just([Token::Dot, Token::Match])) + .then( + pattern + .then_ignore(just(Token::RArrow)) + .then(expr) + .separated_by(just(Token::Comma)) + .allow_trailing() + .pipe(arena_collect) + .delimited_by(just(Token::LeftCurly), just(Token::RightCurly)), + ) + .map_with_state(|(expr, branches), _, state| { + Expr::Match(state.arena.alloc(expr), branches) + }); + + bind.or(set) + .or(match_) + .or(block.map(Expr::Block)) + .map_with_span(Spanned::new) + .or(binary) + .or(atom) }) } pub struct State<'a> { pub arena: &'a Bump, } -pub type Extra<'a> = Full, State<'a>, ()>; + +type Extra<'a> = Full, State<'a>, ()>; +type ParseResult = (); 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 })); +) -> ParseResult { + println!( + "{:?}", + expr() + .separated_by(just(Token::Semicolon)) + .allow_trailing() + .pipe(arena_collect) + .parse_with_state(input, &mut State { arena }) + ); } pub fn parse_iter( input: impl Iterator, eoi: impl Into, arena: &Bump, -) { +) -> ParseResult { parse_input(Stream::from_iter(input).spanned(eoi.into()), arena) } -pub fn parse_lexer(input: Lexer, arena: &Bump) { +pub fn parse_lexer(input: Lexer, arena: &Bump) -> ParseResult { let end = input.span().end; parse_iter( input diff --git a/src/syntax/token.rs b/src/syntax/token.rs index 6ce9a45..2651e09 100644 --- a/src/syntax/token.rs +++ b/src/syntax/token.rs @@ -1,4 +1,5 @@ use lasso::Spur; +use logos::Lexer; use {lasso::Rodeo, logos::Logos}; @@ -25,12 +26,14 @@ pub enum Token { #[token("}")] RightCurly, #[token(".")] Dot, #[token(",")] Comma, + #[token(":")] Colon, #[token(";")] Semicolon, - #[token(":=")] //__ - #[token("≔") ] Bind, + #[token("_")] Underscore, - #[token("←")] //_ - #[token("<-")] Set, + #[token("←")] //____ + #[token("<-")] LArrow, + #[token("→")] //____ + #[token("->")] RArrow, #[token(":>")] Pipe, @@ -66,6 +69,7 @@ pub enum Token { |l| l.extras.interner.get_or_intern(l.slice()) )] Ident(Spur), + #[token("»", better_string)] #[regex( "\"[^\"]*\"", |l| { @@ -87,3 +91,22 @@ pub enum Token { Invalid, } + +// For Evy, with love. +fn better_string(lexer: &mut Lexer) -> Option { + let mut count = 1; + for (ix, chr) in lexer.remainder().char_indices() { + match chr { + '«' => count -= 1, + '»' => count += 1, + _ => (), + } + + if count == 0 { + let slice = &lexer.remainder()[..ix]; + lexer.bump(ix + '«'.len_utf8()); + return Some(lexer.extras.interner.get_or_intern(slice)); + } + } + None +}