summaryrefslogtreecommitdiffstats
path: root/crates/rebel-parse/src/ast/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/rebel-parse/src/ast/mod.rs')
-rw-r--r--crates/rebel-parse/src/ast/mod.rs187
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,
+}