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