futilehdl/src/parser/block_expression.rs

84 lines
2.2 KiB
Rust

use nom::{
branch::alt,
combinator::map,
error::context,
multi::{many0, separated_list1},
sequence::{delimited, separated_pair, terminated, tuple},
};
use crate::parser::{
expression::{expression, Expression},
tokens::{token, Token, TokenKind as tk, TokenSpan},
IResult,
};
/// a block that is a single expression
#[derive(Debug, Clone)]
pub struct ExpressionBlock<'a> {
pub assignments: Vec<(Token<'a>, Expression<'a>)>,
pub value: Expression<'a>,
}
/// an expression that contains a block
#[derive(Debug, Clone)]
pub enum BlockExpr<'a> {
IfElse(IfElseBlock),
Match(MatchBlock<'a>),
Block(ExpressionBlock<'a>),
}
// TODO: postponed because annoying to implement
#[derive(Debug, Clone)]
pub struct IfElseBlock {}
#[derive(Debug, Clone)]
pub struct MatchBlock<'a> {
pub expr: Expression<'a>,
pub arms: Vec<(Expression<'a>, Expression<'a>)>,
}
fn match_arm(input: TokenSpan) -> IResult<TokenSpan, (Expression, Expression)> {
separated_pair(expression, token(tk::FatArrow), expression)(input)
}
fn match_block(input: TokenSpan) -> IResult<TokenSpan, MatchBlock> {
context(
"match block",
map(
tuple((
token(tk::Match),
delimited(token(tk::LParen), expression, token(tk::RParen)),
delimited(
token(tk::LBrace),
separated_list1(token(tk::Comma), match_arm),
token(tk::RBrace),
),
)),
|(_, expr, arms)| MatchBlock { expr, arms },
),
)(input)
}
fn expression_block(input: TokenSpan) -> IResult<TokenSpan, ExpressionBlock> {
map(
tuple((
many0(tuple((
delimited(token(tk::Let), token(tk::Ident), token(tk::EqAssign)),
terminated(expression, token(tk::Semicolon)),
))),
expression,
)),
|(assignments, value)| ExpressionBlock { assignments, value },
)(input)
}
pub fn block_expr(input: TokenSpan) -> IResult<TokenSpan, BlockExpr> {
alt((
map(match_block, BlockExpr::Match),
map(
delimited(token(tk::LBrace), expression_block, token(tk::RBrace)),
BlockExpr::Block,
),
))(input)
}