egglog_bridge/
macros.rs

1use hashbrown::HashMap;
2
3use crate::{
4    ColumnTy, FunctionId, QueryEntry, RuleBuilder,
5    rule::{Variable, VariableId},
6};
7
8#[macro_export]
9#[doc(hidden)]
10macro_rules! parse_rhs_atom_args {
11    ($ebuilder:expr, $builder:expr, $table:ident, $v:expr, []) => { };
12    ($ebuilder:expr, $builder:expr, $table:ident, $v:expr, [{ $e:expr } $($args:tt)*]) => {
13
14
15        $v.push($e);
16        $crate::parse_rhs_atom_args!($ebuilder, $builder, $table, $v, [$($args)*]);
17    };
18    ($ebuilder:expr, $builder:expr, $table:ident, $v:expr, [$var:ident $($args:tt)*]) => {
19        let v = $ebuilder.lookup_var(stringify!($var)).unwrap_or_else(|| {
20            panic!("use of unbound variable {} on the right-hand side of a rule", stringify!($var))
21        });
22        $v.push(v);
23        $crate::parse_rhs_atom_args!($ebuilder, $builder, $table, $v, [$($args)*]);
24    };
25    ($ebuilder:expr, $builder:expr, $table:ident, $v:expr, [($func:tt $($fargs:tt)*) $($args:tt)*]) => {
26        let ret = $crate::parse_rhs_atom!($ebuilder, $builder, ($func $($fargs)*));
27        $v.push(ret.into());
28        $crate::parse_rhs_atom_args!($ebuilder, $builder, $table, $v, [$($args)*]);
29    }
30}
31
32#[macro_export]
33#[doc(hidden)]
34macro_rules! parse_rhs_atom {
35    ($ebuilder:expr, $builder:expr, $var:ident) => {{
36        $ebuilder.lookup_var(stringify!($var)).unwrap_or_else(|| {
37            panic!("use of unbound variable {} on the right-hand side of a rule", stringify!($var))
38        })
39    }};
40    ($ebuilder:expr, $builder:expr, ($func:tt $($args:tt)*)) => {{
41        #[allow(clippy::vec_init_then_push)]
42        {
43            let mut vec = Vec::<$crate::QueryEntry>::new();
44            $crate::parse_rhs_atom_args!($ebuilder, $builder, $func, vec, [$($args)*]);
45            $builder.lookup($func.into(), &vec, || stringify!($func ($($args)*)).to_string())
46        }
47    }};
48}
49
50#[macro_export]
51#[doc(hidden)]
52macro_rules! parse_rhs_command {
53    ($ebuilder:expr, $builder:expr, []) => { };
54    ($ebuilder:expr, $builder:expr, [(let $i:ident $($expr:tt)*) $($rest:tt)*]) => {
55        let res = $crate::parse_rhs_atom!($ebuilder, $builder, $($expr)*);
56        $ebuilder.bind_var(stringify!($i), res);
57        $crate::parse_rhs_command!($ebuilder, $builder, [$($rest)*]);
58    };
59    ($ebuilder:expr, $builder:expr, [(set ($func:tt $($args:tt)*) $res:tt) $($rest:tt)*]) => {
60        let mut vec = Vec::<$crate::QueryEntry>::new();
61        $crate::parse_rhs_atom_args!($ebuilder, $builder, $func, vec, [$($args)*]);
62        $crate::parse_rhs_atom_args!($ebuilder, $builder, $func, vec, [$res]);
63        $builder.set($func.into(), &vec);
64        $crate::parse_rhs_command!($ebuilder, $builder, [$($rest)*]);
65    };
66    ($ebuilder:expr, $builder:expr, [(union $l:tt $r:tt) $($rest:tt)*]) => {
67        let lqe = $crate::parse_rhs_atom!($ebuilder, $builder, $l);
68        let rqe = $crate::parse_rhs_atom!($ebuilder, $builder, $r);
69        $builder.union(lqe.into(), rqe.into());
70        $crate::parse_rhs_command!($ebuilder, $builder, [$($rest)*]);
71    };
72}
73
74// left-hand side parsing
75
76#[macro_export]
77#[doc(hidden)]
78macro_rules! parse_lhs_atom_args {
79    ($ebuilder:expr, $builder:expr, $table:ident, $v:expr, []) => {};
80    ($ebuilder:expr, $builder:expr, $table:ident, $v:expr, [{ $e:expr } $( $args:tt)*]) => {
81        $v.push($e);
82        $crate::parse_lhs_atom_args!($ebuilder, $builder, $table, $v, [$($args),*]);
83    };
84    ($ebuilder:expr, $builder:expr, $table:ident, $v:expr, [$var:ident $( $args:tt)*]) => {
85        let ret = $ebuilder.get_var(stringify!($var), $table, $v.len(), &mut $builder);
86        $v.push(ret.into());
87        $crate::parse_lhs_atom_args!($ebuilder, $builder, $table, $v, [$($args),*]);
88    };
89    ($ebuilder:expr, $builder:expr, $table:ident, $v:expr, [($func:tt $($fargs:tt)*) $($args:tt)*]) => {
90        let ret = $crate::parse_lhs_atom!($ebuilder, $builder, ($func $($fargs)*));
91        $v.push(ret.into());
92        $crate::parse_lhs_atom_args!($ebuilder, $builder, $table, $v, [$($args),*]);
93    };
94}
95#[macro_export]
96#[doc(hidden)]
97macro_rules! parse_lhs_atom {
98    ($ebuilder:expr, $builder:expr; $inferred_ty:expr, $var:ident) => {
99        $ebuilder.bind_or_lookup_var(stringify!($var), $inferred_ty, &mut $builder)
100    };
101    ($ebuilder:expr, $builder:expr; $inferred_ty:expr, ($func:tt $($args:tt)*)) => {
102        parse_lhs_atom!($ebuilder, $builder, ($func $($args)*))
103    };
104    ($ebuilder:expr, $builder:expr, ($func:tt $($args:tt)*)) => {{
105        let mut vec = Vec::<$crate::QueryEntry>::new();
106        $crate::parse_lhs_atom_args!($ebuilder, $builder, $func, vec, [$($args)*]);
107        // Now return the last argument
108        let ty = $ebuilder.infer_type($func.into(), vec.len(), &$builder);
109        let res = $builder.new_var_named(ty, stringify!($func ($($args)*)));
110        vec.push(res.clone());
111        $builder.query_table($func.into(), &vec, Some(false)).unwrap();
112
113        res
114    }};
115}
116
117#[macro_export]
118#[doc(hidden)]
119macro_rules! parse_lhs_atom_with_ret {
120    ($ebuilder:expr, $builder:expr, $ret:expr, ($func:tt $($args:tt)*)) => {{
121        #[allow(clippy::vec_init_then_push)]
122        {
123            let mut vec = Vec::<$crate::QueryEntry>::new();
124            $crate::parse_lhs_atom_args!($ebuilder, $builder, $func, vec, [$($args)*]);
125            vec.push($ret.into());
126            $builder.query_table($func.into(), &vec, Some(false)).unwrap();
127        }
128    }};
129}
130
131#[macro_export]
132#[doc(hidden)]
133macro_rules! parse_lhs {
134    ($ebuilder:expr, $builder:expr, [$((-> ($func:tt $($args:tt)*) $ret:tt))*]) => {
135        $(
136            // First, parse the return value, getting a variable out:
137            let ty = $ebuilder.infer_return_type($func.into(), &$builder);
138            let ret_var = $crate::parse_lhs_atom!($ebuilder, $builder; ty, $ret);
139            $crate::parse_lhs_atom_with_ret!($ebuilder, $builder, ret_var, ($func $($args)*));
140        )*
141    };
142}
143
144#[macro_export]
145macro_rules! define_rule {
146    ([$egraph:expr] ($($lhs:tt)*) => ($($rhs:tt)*))  => {{
147        let mut ebuilder = $crate::macros::ExpressionBuilder::default();
148        let mut builder = $egraph.new_rule(stringify!(($($lhs)* => $($rhs)*)), true);
149        $crate::parse_lhs!(ebuilder, builder, [ $($lhs)* ]);
150        $crate::parse_rhs_command!(ebuilder, builder, [ $($rhs)* ]);
151        builder.build()
152    }};
153}
154
155#[macro_export]
156#[doc(hidden)]
157macro_rules! add_expression_impl {
158    ([ $egraph:expr ] $x:ident) => { $x };
159    ([ $egraph:expr ] ($func:tt $($args:tt)*)) => {{
160        let inner = [
161            $($crate::add_expression_impl!([ $egraph ] $args),)*
162        ];
163        $egraph.add_term($func, &inner)
164    }};
165}
166
167#[macro_export]
168macro_rules! add_expressions {
169    ([ $egraph:expr ] $($expr:tt)*) => {{
170        $( $crate::add_expression_impl!([ $egraph ] $expr);)*
171    }};
172}
173
174/// A struct used specifically to make macro invocations easier to parse. Prefer
175/// using [`RuleBuilder`] for constructing rules directly.
176///
177/// [`RuleBuilder`]: crate::RuleBuilder
178#[doc(hidden)]
179#[derive(Default)]
180pub struct ExpressionBuilder {
181    vars: HashMap<&'static str, QueryEntry>,
182}
183
184impl ExpressionBuilder {
185    pub fn bind_var(&mut self, name: &'static str, var: VariableId) {
186        if self.vars.contains_key(name) {
187            return;
188        }
189        self.vars.insert(
190            name,
191            QueryEntry::Var(Variable {
192                id: var,
193                name: Some(name.into()),
194            }),
195        );
196    }
197    pub fn bind_or_lookup_var(
198        &mut self,
199        name: &'static str,
200        ty: ColumnTy,
201        builder: &mut RuleBuilder,
202    ) -> QueryEntry {
203        if let Some(var) = self.vars.get(name) {
204            return var.clone();
205        }
206        let var = builder.new_var_named(ty, name);
207        self.vars.insert(name, var.clone());
208        var
209    }
210    pub fn lookup_var(&mut self, name: &'static str) -> Option<QueryEntry> {
211        self.vars.get(name).cloned()
212    }
213
214    pub fn get_var(
215        &mut self,
216        name: &'static str,
217        func: FunctionId,
218        col: usize,
219        rb: &mut RuleBuilder,
220    ) -> QueryEntry {
221        if let Some(var) = self.vars.get(name) {
222            return var.clone();
223        }
224        let ty = self.infer_type(func, col, rb);
225        let var = rb.new_var_named(ty, name);
226        self.vars.insert(name, var.clone());
227        var
228    }
229
230    pub fn infer_return_type(&self, func: FunctionId, rb: &RuleBuilder) -> ColumnTy {
231        rb.egraph().funcs[func].ret_ty()
232    }
233
234    pub fn infer_type(&self, func: FunctionId, col: usize, rb: &RuleBuilder) -> ColumnTy {
235        rb.egraph().funcs[func].schema[col]
236    }
237}