pub mod expr; pub mod typ; use expr::*; use typ::Type; pub type Recipe<'a> = Vec>; #[derive(Debug, Clone, PartialEq, Eq)] pub enum RecipeStmt<'a> { BodyStmt(BodyStmt<'a>), Fetch { name: Ident<'a>, body: Body<'a>, }, Task { name: Ident<'a>, params: Vec>, body: Body<'a>, }, } impl<'a> RecipeStmt<'a> { pub fn validate(&self) -> Result<(), ValidationError> { match self { RecipeStmt::BodyStmt(stmt) => stmt.validate(), RecipeStmt::Fetch { name: _, body } => body.validate(), RecipeStmt::Task { name: _, params: _, body, } => { // TODO: Validate params? body.validate() } } } } #[derive(Debug, Clone, PartialEq, Eq)] pub struct Body<'a>(pub Vec>); impl<'a> Body<'a> { pub fn validate(&self) -> Result<(), ValidationError> { for stmt in &self.0 { stmt.validate()?; } Ok(()) } } #[derive(Debug, Clone, PartialEq, Eq)] pub enum BodyStmt<'a> { Assign { dest: Box>, expr: Box>, }, Expr { expr: Box>, }, Empty, } impl<'a> BodyStmt<'a> { pub(crate) fn assign( dest: TypedExpr<'a>, op: Option, swapped: bool, expr: Expr<'a>, ) -> Self { let expr = match op { Some(op) => { let dest_expr = dest.expr.clone(); if swapped { Expr::binary(expr, op, dest_expr) } else { Expr::binary(dest_expr, op, expr) } } None => expr, }; BodyStmt::Assign { dest: Box::new(dest), expr: Box::new(expr), } } pub fn validate(&self) -> Result<(), ValidationError> { match self { BodyStmt::Assign { dest, expr } => { // TODO: Extend destination validation dest.expr.validate()?; expr.validate() } BodyStmt::Expr { expr } => expr.validate(), BodyStmt::Empty => Ok(()), } } } #[derive(Debug, Clone, PartialEq, Eq)] pub struct TypedExpr<'a> { pub expr: Expr<'a>, pub typ: Option>, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct FuncParam<'a> { pub name: Ident<'a>, pub typ: Type<'a>, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct Path<'a> { pub components: Vec>, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Ident<'a> { pub name: &'a str, } #[derive(Debug, Clone, Copy)] pub enum ValidationError { DuplicateKey, NeedsParens, }