Skip to main content

egglog_bridge/
macros.rs

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