use crate::parser::VecDspT; pub use super::*; pub enum Expr { Block(Id), Group(Id), Ident(Id), Lit(Id), Negate(Id), Call { target: Id, args: Vec>, }, Assign { target: Id, val: Id, }, If { cond: Id, body: Id, }, Loop { body: Id, }, While { cond: Id, body: Id, }, Fn(Id), } impl Parsable for Expr { fn parse(ctx: &mut ParseCtx) -> Result { let mut res = Self::unit(ctx)?; while let Some(next) = ctx.peek() { res = match next { Token::Equal => { let target = ctx.push_adv(res); let val = ctx.parse_with(Self::unit)?; Expr::Assign { target, val } } Token::OpenParen => { let target = ctx.push_adv(res); let args = ctx.list(Token::Comma, Token::CloseParen)?; Expr::Call { target, args } } _ => break, } } Ok(res) } } impl Expr { fn unit(ctx: &mut ParseCtx) -> Result { Ok(match ctx.expect_next()? { Token::Dash => Self::Negate(ctx.parse()?), Token::Ident(s) => Self::Ident(ctx.ident(s)), Token::Lit(l) => Self::Lit(ctx.lit(l)), Token::Fn => Self::Fn(ctx.parse()?), Token::If => { let cond = ctx.parse()?; let body = cond_body(ctx)?; Self::If { cond, body } } Token::While => { let cond = ctx.parse()?; let body = cond_body(ctx)?; Self::While { cond, body } } Token::Loop => { let body = ctx.parse()?; Self::Loop { body } } Token::OpenParen => { if ctx.next_if(Token::CloseParen) { Self::Lit(ctx.push(Lit::Unit)) } else { let inner = ctx.parse()?; ctx.expect(Token::CloseParen)?; Self::Group(inner) } } Token::OpenCurly => { let body = ctx.parse()?; ctx.expect(Token::CloseCurly)?; Self::Block(body) } other => return ctx.unexpected(&other, "an expression"), }) } pub fn is_group(&self) -> bool { matches!(self, Expr::Group(_)) } pub fn is_block(&self) -> bool { matches!(self, Expr::Block(_)) } pub fn block(ctx: &mut ParseCtx) -> Result { ctx.expect(Token::OpenCurly)?; let id = ctx.parse()?; ctx.expect(Token::CloseCurly)?; Ok(Expr::Block(id)) } } fn cond_body(ctx: &mut ParseCtx) -> Result, CompilerMsg> { if ctx.next_if(Token::Do) { ctx.parse() } else { ctx.parse_with(Expr::block) } } impl Id { pub fn ends_with_block(&self, nodes: &Nodes) -> bool { match nodes[self] { Expr::Block(..) => true, Expr::Loop { body } | Expr::While { body, .. } | Expr::If { body, .. } | Expr::Negate(body) | Expr::Assign { val: body, .. } => body.ends_with_block(nodes), Expr::Fn(f) => f.ends_with_block(nodes), _ => false, } } } impl FmtNode for Expr { fn fmt(&self, f: &mut std::fmt::Formatter, mut ctx: DisplayCtx) -> std::fmt::Result { let do_ = |id: Id| if ctx.nodes[id].is_block() { "" } else { "do " }; match *self { Self::Ident(id) => id.fmt(f, ctx), Self::Group(id) => write!(f, "({})", id.dsp(ctx)), Self::Fn(id) => id.fmt(f, ctx), Self::Lit(id) => id.fmt(f, ctx), Self::Negate(id) => { write!(f, "-{}", id.dsp(ctx)) } Self::Call { target, ref args } => { write!(f, "{}({})", target.dsp(ctx), args.dsp(ctx)) } Self::Assign { target, val } => { write!(f, "{} = {}", target.dsp(ctx), val.dsp(ctx)) } Self::If { cond, body } => { write!(f, "if {} {}{}", cond.dsp(ctx), do_(body), body.dsp(ctx)) } Self::While { cond, body } => { write!(f, "while {} {}{}", cond.dsp(ctx), do_(body), body.dsp(ctx)) } Self::Loop { body } => { write!(f, "loop {}", body.dsp(ctx)) } Self::Block(body) => { write!(f, "{{")?; if !ctx.nodes[body].items.is_empty() { writeln!(f)?; ctx.indent += 3; body.fmt(f, ctx)?; } write!(f, "}}")?; Ok(()) } } } }