diff options
Diffstat (limited to 'crates/rebel-parse/src/ast/mod.rs')
-rw-r--r-- | crates/rebel-parse/src/ast/mod.rs | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/crates/rebel-parse/src/ast/mod.rs b/crates/rebel-parse/src/ast/mod.rs new file mode 100644 index 0000000..0cdc808 --- /dev/null +++ b/crates/rebel-parse/src/ast/mod.rs @@ -0,0 +1,187 @@ +use std::borrow::Cow; + +use derive_into_owned::{Borrowed, IntoOwned}; +use rustc_hash::FxHashSet; + +pub mod expr; +pub mod pat; +pub mod typ; + +use expr::{Expr, StructField}; +use pat::{DestrPat, Pat}; +use typ::Type; + +pub type Recipe<'a> = Vec<RecipeStmt<'a>>; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum RecipeStmt<'a> { + BlockStmt(BlockStmt<'a>), + Fetch { + name: Ident<'a>, + entries: Vec<StructField<'a>>, + }, + Task { + name: Ident<'a>, + params: Vec<FuncParam<'a>>, + block: Block<'a>, + }, +} + +impl<'a> RecipeStmt<'a> { + pub fn validate(&self) -> Result<(), ValidationError> { + match self { + RecipeStmt::BlockStmt(stmt) => stmt.validate(), + RecipeStmt::Fetch { name: _, entries } => { + let mut fields = FxHashSet::default(); + for StructField { name, value } in entries { + if !fields.insert(name) { + return Err(ValidationError::DuplicateKey); + } + value.validate()?; + } + Ok(()) + } + RecipeStmt::Task { + name: _, + params: _, + block, + } => { + // TODO: Validate params? + block.validate() + } + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, IntoOwned, Borrowed)] +pub struct Block<'a>(pub Vec<BlockStmt<'a>>); + +impl<'a> Block<'a> { + pub fn validate(&self) -> Result<(), ValidationError> { + for stmt in &self.0 { + stmt.validate()?; + } + Ok(()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, IntoOwned, Borrowed)] +pub enum BlockStmt<'a> { + Let { + dest: Box<TypedPat<'a>>, + expr: Option<Box<Expr<'a>>>, + }, + Assign { + dest: Box<DestrPat<'a>>, + expr: Box<Expr<'a>>, + }, + Fn { + ident: Ident<'a>, + params: Vec<FuncParam<'a>>, + ret: Option<Box<Type<'a>>>, + block: Block<'a>, + }, + Expr { + expr: Box<Expr<'a>>, + }, + Empty, +} + +impl<'a> BlockStmt<'a> { + pub(crate) fn let_assign(dest: TypedPat<'a>, expr: Option<Expr<'a>>) -> Self { + BlockStmt::Let { + dest: Box::new(dest), + expr: expr.map(Box::new), + } + } + + pub(crate) fn assign( + dest: DestrPat<'a>, + op: Option<expr::OpBinary>, + swapped: bool, + expr: Expr<'a>, + ) -> Self { + let expr = match op { + Some(op) => { + let dest_expr = Expr::from(dest.clone()); + if swapped { + Expr::binary(expr, op, dest_expr) + } else { + Expr::binary(dest_expr, op, expr) + } + } + None => expr, + }; + BlockStmt::Assign { + dest: Box::new(dest), + expr: Box::new(expr), + } + } + + pub fn validate(&self) -> Result<(), ValidationError> { + match self { + BlockStmt::Let { dest, expr } => { + let TypedPat { pat, typ: _ } = dest.as_ref(); + pat.validate()?; + if let Some(expr) = expr { + expr.validate()?; + } + Ok(()) + } + BlockStmt::Assign { dest, expr } => { + dest.validate()?; + expr.validate()?; + Ok(()) + } + BlockStmt::Fn { + ident: _, + params: _, + ret: _, + block, + } => { + // TODO: Validate params? + block.validate() + } + BlockStmt::Expr { expr } => expr.validate(), + BlockStmt::Empty => Ok(()), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, IntoOwned, Borrowed)] +pub struct TypedPat<'a> { + pub pat: Pat<'a>, + pub typ: Option<Type<'a>>, +} + +#[derive(Debug, Clone, PartialEq, Eq, IntoOwned, Borrowed)] +pub struct FuncParam<'a> { + pub name: Ident<'a>, + pub typ: Type<'a>, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PathRoot { + Absolute, + Relative, + Recipe, + Task, +} + +#[derive(Debug, Clone, PartialEq, Eq, IntoOwned, Borrowed)] +pub struct Path<'a> { + pub root: PathRoot, + pub components: Vec<Ident<'a>>, +} + +#[derive(Debug, Clone, PartialEq, Eq, IntoOwned, Borrowed)] +pub struct Ident<'a> { + pub name: Cow<'a, str>, +} + +#[derive(Debug, Clone, Copy)] +pub enum ValidationError { + DuplicateKey, + NeedsParens, + InvalidLet, +} |