// ::= If // | { } In // | // | TypeOp // // ::= Literal // | OpenParen CloseParen // // ::= Let Symbol Assign // // ::= Is { Comma } [ Comma ] // | Then Else // // ::= Aro use std::{mem::{Discriminant, discriminant}, collections::LinkedList}; use crate::{token::{Literal, Token}, ir::{self, Identifier, expr::{Operation, set_last_ident_name}, BindingScope, pattern::Pattern, IrError}, cons_ll, join}; use super::{Parsable, pattern::BindingPattern, infix::{Rank1Exp, GenIR}, ParseError, WrappedLexer, absorb_token_or_error, parse_delineated_vec}; #[derive(Debug)] pub enum Expr { If(Box, Box), Let(Box, Vec, Box), Infix(Rank1Exp), Type(Box), } impl Parsable for Expr { fn expected() -> (Vec>, bool) { let mut expected = Rank1Exp::expected().0; expected.extend_from_slice(&[ discriminant(&Token::Let), discriminant(&Token::If), discriminant(&Token::TypeOp), ]); (expected, false) } fn parse(l: &mut WrappedLexer) -> Result { let next = discriminant(l.curtok()); let infix_expected = Rank1Exp::expected().0; if infix_expected.iter().find(|t| **t == next).is_some() { Rank1Exp::parse(l).map(Expr::Infix) } else { match l.curtok() { Token::Let => { let initial_let = LetStmt::parse(l)?; let subsequent_lets = Vec::parse(l)?; absorb_token_or_error(l, discriminant(&Token::In))?; let in_expr = Expr::parse(l)?; Ok(Expr::Let(Box::new(initial_let), subsequent_lets, Box::new(in_expr))) } Token::TypeOp => { l.monch(); let value = Expr::parse(l)?; Ok(Expr::Type(Box::new(value))) } Token::If => { l.monch(); let cond = Expr::parse(l)?; let block = IfBlock::parse(l)?; Ok(Expr::If(Box::new(cond), Box::new(block))) }, _ => { Err(ParseError { location: l.span(), expected: Self::expected().0, }) } } } } } impl Expr { pub fn gen_ir( self, bindings: &BindingScope, parent: &Identifier, index: u32, type_context: bool ) -> ir::Result> { match self { Self::If(cond, if_block) => { let self_ident = parent.subid_generate_name("ifblk", index); let preceding_expressions = cond.gen_ir(bindings, &self_ident, 0, type_context)?; let conditional = Operation::Conditional(match *if_block { IfBlock::IfIs(case, more_cases) => { Some(case).into_iter() .chain(more_cases) .enumerate() .map(|(i, Case(pattern, expr))| { let (pattern_bindings, pattern) = pattern.gen_ir(bindings, &self_ident, 2 * i as u32)?; let new_scope = bindings.bind(pattern_bindings); let new_exprs = expr.gen_ir(&new_scope, &self_ident, 2*i as u32 + 1, type_context)?; Ok((pattern, new_exprs)) }) .collect::>>() } IfBlock::IfThen(positive, negative) => { vec![negative, positive] .into_iter() .enumerate() .map(|(v, expr)| expr.gen_ir(bindings, &self_ident, v as u32, type_context).map(|expr| (Pattern::Literal(Literal::Int(v as u64)), expr) ) ) .collect() } }?); let new_expr = ir::expr::Expr { identifier: self_ident, operation: conditional, formals: vec![preceding_expressions.back() .ok_or(IrError::MissingExpression)? .identifier .clone()] }; Ok(cons_ll(preceding_expressions, new_expr)) } Self::Let(first_let, more_lets, finally) => { let self_ident = parent.subid_generate_name("letmany", index); let n_lets = more_lets.len() + 1; let (bindings, prior_exprs) = [*first_let].into_iter() .chain(more_lets) .enumerate() .try_fold( (bindings.bind(Vec::with_capacity(n_lets)), LinkedList::new()), |(bindings, exprs), (i, LetStmt(name, parsetree))| { let new_exprs = set_last_ident_name( parsetree.gen_ir(&bindings, &self_ident, i as u32, type_context)?, name ); let new_bindings = bindings.bind_single( new_exprs.back() .ok_or(IrError::MissingExpression)? .identifier .clone() ); Ok((new_bindings, join(exprs, new_exprs))) } )?; let exprs = finally.gen_ir(&bindings, &self_ident, n_lets as u32, type_context)?; Ok(join(prior_exprs, exprs)) } Self::Infix(infix) => infix.gen_ir(bindings, parent, index, type_context), Self::Type(expr) => if type_context { Err(IrError::NestedTypeDeclarations) } else { expr.gen_ir(bindings, parent, index, true) } } } } // todo: refactor this to infix #[derive(Debug)] pub enum TightExpr { Literal(Literal), Grouped(Expr), Symbol(String), } impl Parsable for TightExpr { fn expected() -> (Vec>, bool) { ( vec![ discriminant(&Token::Literal(Literal::Int(0))), discriminant(&Token::OpenParen), discriminant(&Token::Symbol(String::new())), ], false ) } fn parse(l: &mut WrappedLexer) -> Result { let span = l.span(); match l.monch() { Token::Literal(literal) => Ok(TightExpr::Literal(literal)), Token::OpenParen => { let expr = Expr::parse(l)?; absorb_token_or_error(l, discriminant(&Token::CloseParen))?; Ok(TightExpr::Grouped(expr)) } Token::Symbol(name) => Ok(TightExpr::Symbol(name)), _ => Err(ParseError { location: span, expected: Self::expected().0, }) } } } #[derive(Debug)] pub enum IfBlock { IfIs(Case, Vec), IfThen(Expr, Expr), } impl Parsable for IfBlock { fn expected() -> (Vec>, bool) { ( vec![ discriminant(&Token::Is), discriminant(&Token::Then), ], false ) } fn parse(l: &mut WrappedLexer) -> Result { let span = l.span(); match l.monch() { Token::Is => { let initial_case = Case::parse(l)?; let other_cases = if let Token::Comma = l.curtok() { parse_delineated_vec(l, discriminant(&Token::Comma))? } else { Vec::new() }; Ok(IfBlock::IfIs(initial_case, other_cases)) } Token::Then => { let positive = Expr::parse(l)?; absorb_token_or_error(l, discriminant(&Token::Else))?; let negative = Expr::parse(l)?; Ok(IfBlock::IfThen(positive, negative)) } _ => Err(ParseError { location: span, expected: Self::expected().0, }) } } } #[derive(Debug)] pub struct LetStmt(String, Expr); impl Parsable for LetStmt { fn expected() -> (Vec>, bool) { (vec![discriminant(&Token::Let)], false) } fn parse(l: &mut WrappedLexer) -> Result { absorb_token_or_error(l, discriminant(&Token::Let))?; let symbol = String::parse(l)?; absorb_token_or_error(l, discriminant(&Token::Assign))?; let value = Expr::parse(l)?; Ok(LetStmt(symbol, value)) } } #[derive(Debug)] pub struct Case(BindingPattern, Expr); impl Parsable for Case { fn expected() -> (Vec>, bool) { BindingPattern::expected() } fn parse(l: &mut WrappedLexer) -> Result { let pattern = BindingPattern::parse(l)?; absorb_token_or_error(l, discriminant(&Token::DubAro))?; let expr = Expr::parse(l)?; Ok(Case(pattern, expr)) } }