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#[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 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 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#[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}