synthphonia/expr/ops/
op_impl.rs

1
2#[macro_export]
3/// Defines a macro for creating structures with basic functionality. 
4/// 
5/// This macro generates a structure that includes a single `usize` field, implements several traits, and provides methods related to configuration and naming. 
6/// For any given identifier and a name, the generated structure derives traits for debugging, cloning, copying, equality comparison, and hashing. 
7/// It includes a method to create an instance from a configuration, specifically fetching a "cost" value, defaulting to 1 if unspecified. 
8/// There’s also a method for obtaining the name of the structure as a static string. 
9/// Additionally, it implements the `Display` trait for formatted output of the structure's name, and provides a default implementation that creates an instance using a default configuration.
10/// 
11macro_rules! impl_basic {
12    ($s:ident, $name:expr) => {
13        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14        pub struct $s(pub usize);
15        impl $s {
16            pub fn from_config(config: &$crate::parser::config::Config) -> Self {
17                Self(config.get_usize("cost").unwrap_or(1))
18            }
19            pub fn name() -> &'static str {$name}
20        }
21        impl std::fmt::Display for $s {
22            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23                Self::name().fmt(f)
24            }
25        }
26        impl Default for $s {
27            fn default() -> Self {
28                Self::from_config(&Default::default())
29            }
30        }
31    };
32}
33
34#[macro_export]
35/// Expands to provide a default value for various data types. 
36/// 
37/// The macro accepts a type identifier—`Str`, `Int`, `Bool`, `Float`, `ListStr`, or `ListInt`—and emits a corresponding default value: an empty string, integer zero as a 64-bit signed integer, a boolean false, a float wrapped in the project's `F64` utility, a reference to an empty slice of string slices, or an empty array of integers, respectively. 
38/// This aids in initializing variables with predetermined standard values across different types within the synthesis framework.
39/// 
40macro_rules! default_value {
41    (Str) => { "" }; 
42    (Int) => { 0i64 }; 
43    (Bool) => { false }; 
44    (Float) => { $crate::utils::F64(0.0) }; 
45    (ListStr) => { &[] as &[&str] }; 
46    (ListInt) => { [] }; 
47}
48#[macro_export]
49/// Defines a macro that automates the implementation of several traits for a given type. 
50/// 
51/// This macro takes an identifier and a string literal as inputs, implementing functionalities related to the name of the type. 
52/// The macro allows the type to return a static string as its name and implements the `Display` trait to facilitate formatted printing, using the name as the displayed representation. 
53/// Additionally, it provides a default implementation for the type, initializing it using a configuration obtained from the `Default` trait. 
54/// This macro is designed to reduce repetitive code for types that require a static name representation and a standardized construction approach.
55/// 
56macro_rules! impl_name {
57    ($s:ident, $name:expr) => {
58        impl $s {
59            pub fn name() -> &'static str {$name}
60        }
61        impl std::fmt::Display for $s {
62            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63                Self::name().fmt(f)
64            }
65        }
66        impl Default for $s {
67            fn default() -> Self {
68                Self::from_config(&Default::default())
69            }
70        }
71    };
72}
73
74
75#[macro_export]
76/// Implements a macro to define unary operations for expression types. 
77/// 
78/// This macro provides a structured way to implement the `Op1` trait for a given type, enabling the definition of operation costs and evaluation methods. 
79/// Specifically, it defines the `cost` method to return a constant value associated with the operation type and the `try_eval` method to attempt evaluation of the operation on an input value.
80/// 
81/// Within the implementation, the evaluation is performed by matching on the input value type. 
82/// For each specified type conversion, it maps an expression over the input's iterable values, collecting results using `galloc_scollect`. 
83/// If the input type matches one of the specified conversions, the operation applies and returns `true` along with the resulting value; otherwise, it returns `false` and a `Null` value. 
84/// This macro thus facilitates efficient and reusable implementations of unary operations over various value types in the string synthesis framework.
85macro_rules! impl_op1 {
86    ($s:ident, $name:expr, $($t1:ident -> $rt:ident { $f:expr }),*) => {
87        impl $crate::expr::ops::Op1 for $s {
88            fn cost(&self) -> usize { self.0 }
89            fn try_eval(&self, a1 : $crate::value::Value) -> (bool, $crate::value::Value) {
90                match a1 {
91                    $(
92                        crate::value::Value::$t1(s) => (true, crate::value::Value::$rt(s.iter().map($f).galloc_scollect())),
93                    )*
94                    _ => (false, crate::value::Value::Null),
95                }
96            }
97        }
98    }
99}
100#[macro_export]
101/// Defines a macro to simplify the creation and implementation of unary operations in the synthesis framework. 
102/// 
103/// This macro automates the process of setting up a new unary operation by invoking several components. 
104/// It takes an identifier, a name, and a series of type transformation definitions, associating each with an expression used for transformation. 
105/// It uses the `impl_basic!` macro to provide basic trait implementations for the operation, sets up the operation as an implementation of `Enumerator1` from the `forward::enumeration` module, and then employs the `impl_op1!` macro to complete the detailed implementation needed for the operation including how it transforms various input types to the result type using the provided expression. 
106/// This macro reduces repetitive code and ensures consistency when creating new operations.
107/// 
108macro_rules! new_op1 {
109    ($s:ident, $name:expr, $($t1:ident -> $rt:ident { $f:expr }),*) => {
110        $crate::impl_basic!($s, $name);
111        impl $crate::forward::enumeration::Enumerator1 for $s {}
112        $crate::impl_op1!($s, $name, $($t1 -> $rt { $f }),*);
113    };
114}
115
116#[macro_export]
117/// Defines a macro to implement the `Op1` trait for a given type, focusing on optional transformations. 
118/// 
119/// 
120/// This macro, `impl_op1_opt`, takes a type identifier and a series of transformation patterns, generating an implementation of the `Op1` trait for the specified type. 
121/// The trait requires two key methods: `cost`, which returns the associated cost from the tuple stored within the type, and `try_eval`, which attempts to evaluate a unary operation on the provided `Value`. 
122/// In `try_eval`, the macro iterates over values of specific types, applying a transformation function that returns an optional result. 
123/// It collects results, tracking whether all transformations succeeded, and constructs a new `Value` of the result type, falling back to a default value if necessary. 
124/// If the transformation can't be applied to the input type, it returns a flag of `false` with a `Null` value. 
125/// This macro streamlines the implementation of the `Op1` trait for operations that may or may not succeed based on optional transformations. 
126/// 
127macro_rules! impl_op1_opt {
128    ($s:ident, $name:expr, $($t1:ident -> $rt:ident { $f:expr }),*) => {
129        impl $crate::expr::ops::Op1 for $s {
130            fn cost(&self) -> usize { self.0 }
131            fn try_eval(&self, a1 : $crate::value::Value) -> (bool, $crate::value::Value) {
132                match a1 {
133                    $(
134                        crate::value::Value::$t1(s1) => {
135                            let mut flag = true;
136                            let v = s1.iter().map($f).map(|f| { flag &= f.is_some(); f.unwrap_or($crate::default_value![$rt]) }).galloc_scollect();
137                            (flag, crate::value::Value::$rt(v))
138                        }
139                    )*
140                    _ => (false, crate::value::Value::Null),
141                }
142            }
143        }
144    };
145}
146#[macro_export]
147/// Defines a macro for creating a new unary operation. 
148/// 
149/// This macro simplifies the process of defining new unary operators by automating the implementation of necessary traits and logic. 
150/// It first abstracts over the implementation of basic traits and characteristics by invoking `impl_basic!` with the specified identifier and name. 
151/// Then, it ensures the new type implements `Enumerator1`, which likely facilitates iterating over or evaluating expressions. 
152/// Finally, the macro invokes `impl_op1_opt!`, passing along the type, operation name, and mapping of input types to result types along with associated function logic. 
153/// Through this structured approach, multiple unary operations with various input types and results can be efficiently defined and integrated into the synthesis framework.
154/// 
155macro_rules! new_op1_opt {
156    ($s:ident, $name:expr, $($t1:ident -> $rt:ident { $f:expr }),*) => {
157        $crate::impl_basic!($s, $name);
158        impl $crate::forward::enumeration::Enumerator1 for $s {}
159        $crate::impl_op1_opt!($s, $name, $($t1 -> $rt { $f }),*);
160    };
161}
162
163#[macro_export]
164/// Defines a macro for creating new binary operations within the expression framework. 
165/// 
166/// This macro, `new_op2`, takes as input a series of tokens to set up a binary operation type, specifically an identifier `$s`, a name `$name`, and multiple tuples of input types (`$t1` and `$t2`), return type `$rt`, and a functional expression `$f`. 
167/// 
168/// 
169/// The macro performs several tasks: it invokes an implementation macro, `impl_basic!`, for basic setup using the provided identifier and name. 
170/// It implements the `Enumerator2` trait for the operation using the `impl` syntax. 
171/// Finally, it calls another implementation macro, `impl_op2!`, which probably performs the core operation setup, defining how each type of binary operation works using the provided type and functional expression pairs. 
172/// This structure aids in generating consistent and concise definitions of new binary operations throughout the synthesis module.
173macro_rules! new_op2 {
174    ($s:ident, $name:expr, $(($t1:ident, $t2:ident) -> $rt:ident { $f:expr }),*) => {
175        $crate::impl_basic!($s, $name);
176        impl $crate::forward::enumeration::Enumerator2 for $s {}
177        $crate::impl_op2!($s, $name, $(($t1, $t2) -> $rt { $f }),*);
178    };
179}
180
181#[macro_export]
182/// This macro facilitates the creation of a new binary operation with optional behavior. 
183/// 
184/// It integrates a binary operation into the framework by implementing necessary traits and logic through various components. 
185/// Initially, it utilizes `$crate::impl_basic!` to establish a basic setup for a given operation `$s`, associating it with a specified `$name`. 
186/// The macro then implements the `Enumerator2` trait for `$s`, effectively enabling the operation to be part of the enumeration process utilized in forward synthesis tasks. 
187/// 
188/// 
189/// Following this, it calls `$crate::impl_op2_opt!` to define additional implementation details for the operation, identifying the input types `$t1` and `$t2`, the return type `$rt`, and the expression `$f` that dictates the operation's computation. 
190/// This approach ensures the cohesive integration of optional binary operations that can be used efficiently within the domain-specific synthesis framework, allowing for complex operations to be handled flexibly and dynamically.
191macro_rules! new_op2_opt {
192    ($s:ident, $name:expr, $(($t1:ident, $t2:ident) -> $rt:ident { $f:expr }),*) => {
193        $crate::impl_basic!($s, $name);
194        impl $crate::forward::enumeration::Enumerator2 for $s {}
195        $crate::impl_op2_opt!($s, $name, $(($t1, $t2) -> $rt { $f }),*);
196    };
197}
198
199#[macro_export]
200/// Creates a new ternary operation macro for use within the string synthesis framework. 
201/// 
202/// The macro accepts the name of the operation and a list of tuples, each representing a combination of three argument types and a return type, along with a closure that defines the operation's functionality. 
203/// It expands to implement basic operation utilities and enumeration traits for the specified operation, invoking helper macros `impl_basic!`, which likely sets up foundational properties, and `impl_op3!`, which embeds the provided logic into the operational infrastructure. 
204/// Overall, this macro streamlines the definition and integration of complex ternary operations within the module.
205/// 
206macro_rules! new_op3 {
207    ($s:ident, $name:expr, $(($t1:ident, $t2:ident, $t3:ident) -> $rt:ident { $f:expr }),*) => {
208        $crate::impl_basic!($s, $name);
209        impl $crate::forward::enumeration::Enumerator3 for $s {}
210        $crate::impl_op3!($s, $name, $(($t1, $t2, $t3) -> $rt { $f }),*);
211    };
212}
213
214#[macro_export]
215/// Defines a macro that simplifies the creation and implementation of three-input (ternary) operations with optional functionality.
216/// 
217/// This macro-rule is structured to declare and implement a new ternary operation by accepting an identifier, a name, and a sequence of type transformations that results in a function. 
218/// It leverages other internal macros, such as `impl_basic!` and `impl_op3_opt!`, to automatically generate and bind necessary implementations. 
219/// The `impl_basic!` macro assists in setting up basic structure and namespacing for the operation, while `impl_op3_opt!` is likely responsible for the implementation details and handling optional features across different inputs. 
220/// The macro also implements the `Enumerator3` trait from the `forward::enumeration` module for the specified identifier, providing enumeration capabilities for the new operation in a modular and reusable manner.
221macro_rules! new_op3_opt {
222    ($s:ident, $name:expr, $(($t1:ident, $t2:ident, $t3:ident) -> $rt:ident { $f:expr }),*) => {
223        $crate::impl_basic!($s, $name);
224        impl $crate::forward::enumeration::Enumerator3 for $s {}
225        $crate::impl_op3_opt!($s, $name, $(($t1, $t2, $t3) -> $rt { $f }),*);
226    };
227}
228#[macro_export]
229/// This macro facilitates the implementation of the `Op2` trait for a given struct, allowing it to define binary operations on values. 
230/// 
231/// It enables configuration of the operation by specifying patterns for value types, corresponding result types, and a closure for computation. 
232/// When invoked, the macro takes the struct name, operation name, and a series of type pattern mappings where each pair of input types maps to a result type and an associated function. 
233/// For each specified type combination, the generated implementation of the `try_eval` method attempts to evaluate the operation by zipping and transforming the input iterables using the provided closure function. 
234/// If the types match, it returns a successful evaluation with the transformed result; otherwise, it returns a failure with a null value. 
235/// This macro centralizes the repetitive logic for implementing binary operations across different value types, promoting code reuse and reducing boilerplate.
236/// 
237macro_rules! impl_op2 {
238    ($s:ident, $name:expr, $(($t1:ident, $t2:ident) -> $rt:ident { $f:expr }),*) => {
239
240        impl $crate::expr::ops::Op2 for $s {
241            fn cost(&self) -> usize { self.0 }
242            fn try_eval(&self, a1 : $crate::value::Value, a2 : $crate::value::Value) -> (bool, crate::value::Value) {
243                match (a1, a2) { 
244                    $(
245                        (crate::value::Value::$t1(s1), crate::value::Value::$t2(s2)) => (true, crate::value::Value::$rt(itertools::izip!(s1.iter(), s2.iter()).map($f).galloc_scollect())),
246                    )*
247                    _ => (false, crate::value::Value::Null),
248                }
249            }
250        }
251    };
252}
253
254#[macro_export]
255/// Defines a macro for implementing a two-operand operation trait with optional evaluation. 
256/// 
257/// 
258/// This macro generates implementations for the `Op2` trait for a specified type by providing a framework where specific pairs of input types can define conversion logic and evaluation. 
259/// For each tuple of input types and a resulting type provided within the macro invocation, the macro matches on the value types and applies a provided closure `f` to corresponding elements of the input values. 
260/// It utilizes the `itertools::izip!` macro to pair elements from both input lists, applying the transformation function `f`, and collecting the results into a new list. 
261/// If the operation fails or an element cannot be converted, it uses a default value for the resulting type. 
262/// The generated implementation returns a flag indicating whether the operation was successful for all elements and the new value. 
263/// If the inputs do not match any specified type combinations, the operation returns `false` with a `Null` value. 
264/// This approach facilitates the flexible application of operations over compatible pairings of types while maintaining robust default behavior.
265macro_rules! impl_op2_opt {
266    ($s:ident, $name:expr, $(($t1:ident, $t2:ident) -> $rt:ident { $f:expr }),*) => {
267
268        impl $crate::expr::ops::Op2 for $s {
269            fn cost(&self) -> usize { self.0 }
270            fn try_eval(&self, a1 : $crate::value::Value, a2 : $crate::value::Value) -> (bool, crate::value::Value) {
271                match (a1, a2) {
272                    $(
273                        (crate::value::Value::$t1(s1), crate::value::Value::$t2(s2)) => {
274                            let mut flag = true;
275                            let a = itertools::izip!(s1.iter(), s2.iter()).map($f).map(|f| { flag &= f.is_some(); f.unwrap_or($crate::default_value![$rt]) }).galloc_scollect();
276                            (flag, crate::value::Value::$rt(a))
277                        }
278                    )*
279                    _ => (false, crate::value::Value::Null),
280                }
281            }
282        }
283    };
284}
285
286#[macro_export]
287/// Defines a macro for implementing a ternary operation trait. 
288/// 
289/// This macro, when invoked, generates an implementation of the `Op3` trait for a given structure. 
290/// The trait includes two functions: `cost`, which returns a constant cost value associated with the operation, and `try_eval`, which attempts to evaluate the operation based on three input values. 
291/// 
292/// 
293/// The `try_eval` function utilizes Rust's pattern matching to handle specific combinations of value types, applying a provided function using `itertools::izip` to iterate over the input values in tandem, mapping them to a result that is collected using `galloc_scollect`. 
294/// If the provided input types do not match any of the specified patterns, the function returns a false success flag and a `Null` value, indicating an unsuccessful evaluation. 
295/// This macro simplifies the creation of complex operations by automating the repetitive parts of defining the `Op3` implementations.
296macro_rules! impl_op3 {
297    ($s:ident, $name:expr, $(($t1:ident, $t2:ident, $t3:ident) -> $rt:ident { $f:expr }),*) => {
298
299        impl $crate::expr::ops::Op3 for $s {
300            fn cost(&self) -> usize { self.0 }
301            fn try_eval(&self, a1 : $crate::value::Value, a2 : $crate::value::Value, a3 : crate::value::Value) -> (bool, crate::value::Value) {
302                match (a1, a2, a3) {
303                    $(
304                        (crate::value::Value::$t1(s1), crate::value::Value::$t2(s2), crate::value::Value::$t3(s3)) =>
305                            (true, crate::value::Value::$rt(itertools::izip!(s1.iter(), s2.iter(), s3.iter()).map($f).galloc_scollect())),
306                    )*
307                    _ => (false, crate::value::Value::Null),
308                }
309            }
310        }
311    };
312}
313
314#[macro_export]
315/// A macro for implementing ternary operations with optional characteristics. 
316/// 
317/// This macro, `impl_op3_opt!`, simplifies the process of defining implementations for the `Op3` trait on a specified type (designated by `$s`). 
318/// For each combination of argument types and return type specified, it generates the operation's logic by defining the `cost` and `try_eval` methods for the `Op3` trait. 
319/// 
320/// 
321/// In `try_eval`, the macro handles different matching patterns of input argument types. 
322/// It uses pattern matching to destructure given values into tuples of variant types defined in `$t1`, `$t2`, and `$t3`. 
323/// The evaluation iterates over combined elements of these tuples using the `itertools::izip!` macro, applying an expression `$f` on them. 
324/// This produces an output optionally handled via a `flag`, which determines success. 
325/// If any element evaluation yields `None`, the default value specified for the return type `$rt` is used instead. 
326/// The resulting values are collected using `galloc_scollect()` into the expected result type wrapped in `crate::value::Value::$rt`. 
327/// If none of the specified patterns match, the operation defaults to returning a `false` flag and a `Value::Null`.
328macro_rules! impl_op3_opt {
329    ($s:ident, $name:expr, $(($t1:ident, $t2:ident, $t3:ident) -> $rt:ident { $f:expr }),*) => {
330
331        impl $crate::expr::ops::Op3 for $s {
332            fn cost(&self) -> usize { self.0 }
333            fn try_eval(&self, a1 : $crate::value::Value, a2 : $crate::value::Value, a3 : crate::value::Value) -> (bool, crate::value::Value) {
334                match (a1, a2, a3) {
335                    $(
336                        (crate::value::Value::$t1(s1), crate::value::Value::$t2(s2), crate::value::Value::$t3(s3)) => {
337                            let mut flag = true
338                            let a = itertools::izip!(s1.iter(), s2.iter(), s3.iter()).map($f).map(|f| { flag &= f.is_some(); f.unwrap_or($crate::default_value![$rt]) }).galloc_scollect();
339                            (flag, crate::value::Value::$rt(a))
340                        }
341                    )*
342                    _ => (false, crate::value::Value::Null),
343                }
344            }
345        }
346    };
347}