egglog_ast/
generic_ast.rs

1use std::fmt::Display;
2use std::hash::Hash;
3
4use ordered_float::OrderedFloat;
5
6use crate::span::Span;
7
8#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
9pub enum Literal {
10    Int(i64),
11    Float(OrderedFloat<f64>),
12    String(String),
13    Bool(bool),
14    Unit,
15}
16
17#[derive(Clone, Debug, PartialEq, Eq, Hash)]
18pub enum GenericExpr<Head, Leaf> {
19    Var(Span, Leaf),
20    Call(Span, Head, Vec<GenericExpr<Head, Leaf>>),
21    Lit(Span, Literal),
22}
23
24/// Facts are the left-hand side of a [`Command::Rule`].
25/// They represent a part of a database query.
26/// Facts can be expressions or equality constraints between expressions.
27///
28/// Note that primitives such as  `!=` are partial.
29/// When two things are equal, it returns nothing and the query does not match.
30/// For example, the following egglog code runs:
31/// ```text
32/// (fail (check (!= 1 1)))
33/// ```
34#[derive(Clone, Debug, PartialEq, Eq, Hash)]
35pub enum GenericFact<Head, Leaf> {
36    Eq(Span, GenericExpr<Head, Leaf>, GenericExpr<Head, Leaf>),
37    Fact(GenericExpr<Head, Leaf>),
38}
39
40#[derive(Clone, Debug, PartialEq, Eq, Hash)]
41pub struct GenericActions<Head: Clone + Display, Leaf: Clone + PartialEq + Eq + Display + Hash>(
42    pub Vec<GenericAction<Head, Leaf>>,
43);
44
45#[derive(Clone, Debug, PartialEq, Eq, Hash)]
46pub enum GenericAction<Head, Leaf>
47where
48    Head: Clone + Display,
49    Leaf: Clone + PartialEq + Eq + Display + Hash,
50{
51    /// Bind a variable to a particular datatype or primitive.
52    /// At the top level (in a [`Command::Action`]), this defines a global variable.
53    /// In a [`Command::Rule`], this defines a local variable in the actions.
54    Let(Span, Leaf, GenericExpr<Head, Leaf>),
55    /// `set` a function to a particular result.
56    /// `set` should not be used on datatypes-
57    /// instead, use `union`.
58    Set(
59        Span,
60        Head,
61        Vec<GenericExpr<Head, Leaf>>,
62        GenericExpr<Head, Leaf>,
63    ),
64    /// Delete or subsume (mark as hidden from future rewrites and unextractable) an entry from a function.
65    Change(Span, Change, Head, Vec<GenericExpr<Head, Leaf>>),
66    /// `union` two datatypes, making them equal
67    /// in the implicit, global equality relation
68    /// of egglog.
69    /// All rules match modulo this equality relation.
70    ///
71    /// Example:
72    /// ```text
73    /// (datatype Math (Num i64))
74    /// (union (Num 1) (Num 2)); Define that Num 1 and Num 2 are equivalent
75    /// (extract (Num 1)); Extracts Num 1
76    /// (extract (Num 2)); Extracts Num 1
77    /// ```
78    Union(Span, GenericExpr<Head, Leaf>, GenericExpr<Head, Leaf>),
79    Panic(Span, String),
80    Expr(Span, GenericExpr<Head, Leaf>),
81}
82
83/// How a rule is evaluated. The three modes are mutually exclusive, so they
84/// share one field on [`GenericRule`].
85#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
86pub enum RuleEvalMode {
87    /// Default: seminaive (delta) evaluation with restrictive `Pure`/`Write`
88    /// primitive contexts (no database reads in the RHS).
89    #[default]
90    Seminaive,
91    /// `:naive`: match the whole database every iteration, with permissive
92    /// `Read`/`Full` contexts so the RHS may read the database.
93    Naive,
94    /// `:unsafe-seminaive`: like `:naive`'s `Read`/`Full` contexts (the RHS may
95    /// read the database) but keeps delta evaluation. **Unsafe**: an RHS read
96    /// observes the database mid-iteration and isn't re-evaluated if it changes.
97    UnsafeSeminaive,
98}
99
100impl RuleEvalMode {
101    /// `:naive` — disables seminaive evaluation (unlike `:unsafe-seminaive`).
102    pub fn is_naive(self) -> bool {
103        matches!(self, RuleEvalMode::Naive)
104    }
105}
106
107#[derive(Clone, Debug, PartialEq, Eq, Hash)]
108pub struct GenericRule<Head, Leaf>
109where
110    Head: Clone + Display,
111    Leaf: Clone + PartialEq + Eq + Display + Hash,
112{
113    pub span: Span,
114    pub head: GenericActions<Head, Leaf>,
115    pub body: Vec<GenericFact<Head, Leaf>>,
116    /// A globally unique name for this rule in the EGraph.
117    pub name: String,
118    /// The ruleset this rule belongs to. Defaults to `""`.
119    pub ruleset: String,
120    /// How this rule is evaluated; set by `:naive` / `:unsafe-seminaive`.
121    pub eval_mode: RuleEvalMode,
122    /// If `true`, this rule skips tree-decomposition during query
123    /// planning and evaluate rules as a single-bag (without decomposing
124    /// it into smaller queries). Set via the `:no-decomp` rule option.
125    pub no_decomp: bool,
126    /// If `true`, table atoms in this rule match subsumed rows as well as
127    /// live rows. This is intended for internal maintenance rules, not
128    /// ordinary user rewrites.
129    pub include_subsumed: bool,
130}
131
132/// Change a function entry.
133#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash)]
134pub enum Change {
135    /// `delete` this entry from a function.
136    /// Be wary! Only delete entries that are guaranteed to be not useful.
137    Delete,
138    /// `subsume` this entry so that it cannot be queried or extracted, but still can be checked.
139    /// Note that this is currently forbidden for functions with custom merges.
140    Subsume,
141}