rhea/src/syntax/parser.rs

285 lines
9.0 KiB
Rust

use super::ast::Type;
use {
super::{
ast::{BinaryOperator, Expr, Literal, Pattern, SpanExpr, Spanned, UnaryOperator},
token::Token,
},
crate::utils::Pipe,
bumpalo::Bump,
chumsky::{
extra::Full,
input::{Stream, ValueInput},
prelude::*,
},
logos::Lexer,
};
/// Equivalently-named unit variant mapping
macro_rules! equivmap {
($src:ident, $target:ident, [$variant0:ident $(, $variant:ident)* $(,)?] $(,)?) => {
just($src::$variant0).to($target::$variant0)
$(.or(just($src::$variant).to($target::$variant)))*
};
}
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) => 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,
}
.or(literal.map(Pattern::Literal))
.map_with_span(Spanned::new);
let type_ = just([Token::LeftParen, Token::RightParen])
.to(Type::Unit)
.or(ident.map(Type::Ident))
.map_with_span(Spanned::new);
let block = 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)),
)
.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)));
// <expr>(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
},
)
.map(|v| Expr::Path(v.into_bump_slice()))
.map_with_span(Spanned::new);
/* 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),
)
},
);
// <exprL> OP <exprR>
let binary = unary.clone().foldl_with_state(
equivmap!(
Token,
BinaryOperator,
[Plus, Minus, Star, Slash, And, VLine, Lt, Gt, Equ, Nequ, LtEqu, GtEqu],
)
.map_with_span(Spanned::new)
.then(unary)
.repeated(),
|l, (op, r), state: &mut State| {
Spanned::new(
Expr::Binary(op, state.arena.alloc(l), state.arena.alloc(r)),
merge_spans(l.span, r.span),
)
},
);
let bind = {
let start = pattern.then_ignore(just(Token::Colon)).then(expr.clone()); // <pat> := <expr>
let else_ = just(Token::Else).ignore_then(block.clone()).or_not(); // else {…}
// <pat> := <expr> [else {…}]
let local = start.clone().then(else_.clone()).map_with_state(
|((pat, expr), else_), _, state| {
Expr::BindLocal(pat, &*state.arena.alloc(expr), else_)
},
);
// <pat> := <expr> {…} 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)
};
// <atom> ← <expr>
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))
});
// <expr>.match { <pat> → <expr>, … }
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,
}
type Extra<'a> = Full<Rich<'a, Token>, State<'a>, ()>;
type ParseResult = ();
pub fn parse_input<'a>(
input: impl ValueInput<'a, Token = Token, Span = SimpleSpan>,
arena: &'a Bump,
) -> 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<Item = (Token, SimpleSpan)>,
eoi: impl Into<SimpleSpan>,
arena: &Bump,
) -> ParseResult {
parse_input(Stream::from_iter(input).spanned(eoi.into()), arena)
}
pub fn parse_lexer(input: Lexer<Token>, arena: &Bump) -> ParseResult {
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))
}
#[inline]
fn merge_spans(start: SimpleSpan, end: SimpleSpan) -> SimpleSpan {
SimpleSpan::new(start.start, end.end)
}