diff --git a/crates/frontend/src/resolve.rs b/crates/frontend/src/resolve.rs index 36f6ac7..13eaae4 100644 --- a/crates/frontend/src/resolve.rs +++ b/crates/frontend/src/resolve.rs @@ -22,7 +22,9 @@ use std::{collections::BTreeMap, convert::Infallible, marker::PhantomData, sync::Arc}; use indexmap::IndexSet; +use kerolox_ir::EnumRepr; use salsa::Update; +use strum::EnumIter; use crate::prelude::*; @@ -206,7 +208,8 @@ pub fn expr<'db>( relation: loaded.insert_full(relation_name(db, apply.head.clone())).0, }))), - ast::ExprExtra::Aggregate(aggregate) => todo!(), + // aggregate resolution validates the specific operation + ast::ExprExtra::Aggregate(agg) => todo!(), }, // TODO: handle unreachability better? @@ -322,3 +325,88 @@ pub struct Apply<'db> { /// The parameters to the function or query. pub body: Expr<'db>, } + +/// A resolved aggregate operator. +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct Aggregate<'db> { + /// The resolved kind of aggregate operator. + pub kind: AggregateKind, + + /// The list of witnesses (variable indices) to the body. + pub witnesses: Vec>, + + /// The body of the aggregate. + pub body: AggregateBody<'db>, +} + +/// A resolved aggregate operator kind. +#[derive(Clone, PartialEq, Eq, Hash)] +pub enum AggregateKind { + /// A "bound" aggregate: selects a variable to perform the aggregate over. + Bound { + /// The kind of aggregate operator. + kind: WithAst, + + /// The index of the variable to bind over. + variable: WithAst, + }, + + /// An "unbound" aggregate: no variable needs to be selected to perform the operation. + Unbound { + /// The kind of aggregate operator. + kind: WithAst, + }, + + /// The aggregate name could not be resolved. + Unknown(WithAst), +} + +/// A bound aggregate operator. +#[derive(Copy, Clone, PartialEq, Eq, Hash, EnumIter)] +pub enum BoundAggregateKind { + Sum, + Average, + Min, + Max, +} + +impl EnumRepr for BoundAggregateKind { + fn repr(&self) -> &'static str { + use BoundAggregateKind::*; + match self { + Sum => "sum", + Average => "average", + Min => "min", + Max => "max", + } + } +} + +/// An unbound aggregate operator. +#[derive(Copy, Clone, PartialEq, Eq, Hash, EnumIter)] +pub enum UnboundAggregateKind { + Count, + Any, + All, +} + +impl EnumRepr for UnboundAggregateKind { + fn repr(&self) -> &'static str { + use UnboundAggregateKind::*; + match self { + Count => "count", + Any => "any", + All => "all", + } + } +} + +/// An aggregate operator's body. +#[derive(Clone, PartialEq, Eq, Hash)] +pub enum AggregateBody<'db> { + /// A single application operation. + Apply(Apply<'db>), + + /// A list of clauses, like in a [RuleBody]. + Body(Vec>), +}