synthphonia/expr/ops/str/
replace.rs

1use std::cmp::min;
2
3use crate::{
4    expr::{ops::Op3, Expr}, forward::enumeration::Enumerator3, galloc::{AllocForExactSizeIter, AllocForStr}, new_op3, parser::config::Config, value::Value
5};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8/// A struct that represents a string replacement operation. 
9/// 
10/// It includes two public fields, both of type `usize`, which likely denote positions or lengths in a string where a replacement operation is intended to take place. 
11/// The straightforward structure of this item suggests it serves as a utility to encapsulate parameters for a replace-like task within a larger synthesis or transformation process.
12/// 
13pub struct Replace(pub usize, pub usize);
14
15impl Replace {
16    /// Creates a new instance by extracting configuration values. 
17    /// 
18    /// It fetches the "cost" and "enum_replace_cost" from the given `Config` object, setting them as the first and second elements respectively. 
19    /// If the configuration values are not present, it defaults to using 1 for "cost" and 3 for "enum_replace_cost". 
20    /// This approach allows the object to be instantiated with specific costs based on the provided configuration, facilitating customizability within the synthesis framework.
21    /// 
22    pub fn from_config(config: &Config) -> Self {
23        Self(config.get_usize("cost").unwrap_or(1), config.get_usize("enum_replace_cost").unwrap_or(3))
24    }
25    /// Returns the name associated with the `Replace` operation. 
26    /// 
27    /// This functionality provides a static method that outputs the string `"str.replace"`, serving as an identifier for the operation within the synthesis framework. 
28    /// This is useful for referencing the operation in logs, configuration, or other parts of the system where consistent naming is necessary.
29    /// 
30    pub fn name() -> &'static str {
31        "str.replace"
32    }
33}
34
35impl std::fmt::Display for Replace {
36    /// Formats the `Replace` instance for display purposes. 
37    /// 
38    /// This implementation of the `fmt` function, part of the `std::fmt` module, uses the `name` method of the `Replace` struct itself to provide a formatted representation. 
39    /// The formatted output is directed to a given formatter instance, which integrates the `Replace` instance into a formatted output stream.
40    /// 
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        Self::name().fmt(f)
43    }
44}
45
46impl Default for Replace {
47    /// Creates a new instance with default configuration. 
48    /// 
49    /// This method initializes the instance using the `from_config` function, passing a default configuration. 
50    /// The implementation implies the `Replace` structure can be configured with external settings, yet defaults allow creating a baseline instance without specifying particular attributes.
51    /// 
52    fn default() -> Self {
53        Self::from_config(&Default::default())
54    }
55}
56
57impl Enumerator3 for Replace {
58    /// Enumerates possible expressions in the context of the synthesis problem. 
59    /// 
60    /// This function first checks if the executor's available size meets the minimum cost requirement, terminating early if it does not. 
61    /// It calculates the total allowable size for enumeration and iterates over combinations of sub-expressions e1, e2, and e3 from the executor's data, constrained by the maximum specified size. 
62    /// Within these loops, it constructs a ternary operation expression with the provided `Op3Enum`. 
63    /// The function attempts to evaluate this new expression using given values, and if successful, it proceeds to enumerate the expression in the executor with its evaluated value. 
64    /// The process ensures that only valid expressions with feasible evaluations are considered, thus optimizing the string synthesis tasks.
65    /// 
66    fn enumerate(&self, this: &'static crate::expr::ops::Op3Enum, exec: &'static crate::forward::executor::Executor, nt: [usize; 3]) -> Result<(), ()> {
67        if exec.size() < self.cost() { return Ok(()); }
68        let total = exec.size() - self.cost();
69        for (i, (e2, v2)) in exec.data[nt[0]].size.get_all_under(min(total, self.1)) {
70            for (j, (e3, v3)) in exec.data[nt[1]].size.get_all_under(min(total - i, self.1)) {
71                for (e1, v1) in exec.data[nt[2]].size.get_all(total - i - j) {
72                    let expr = Expr::Op3(this, e1, e2, e3);
73                    if let (true, value) = self.try_eval(*v1, *v2, *v3) {
74                        exec.enum_expr(expr, value)?;
75                    }
76                }
77            } 
78        }
79        Ok(())
80    }
81}
82
83impl Op3 for Replace {
84    /// Provides functionality to calculate the cost of a `Replace` operation. 
85    /// 
86    /// The `cost` method, when called on an instance of `Replace`, returns the first element of the tuple. 
87    /// This represents the operational cost or significance of the replacement process defined by the instance.
88    /// 
89    fn cost(&self) -> usize {
90        self.0
91    }
92    /// Provides a method to attempt the evaluation of a replacement operation within a given context of string values. 
93    /// 
94    /// This method takes three `Value` parameters, `a1`, `a2`, and `a3`, assuming they are all strings. 
95    /// It performs a replacement operation using the Rust `replacen` string method, which replaces the first occurrence of a substring (from `a1` and `a2` combinations) with a new string (`a3`). 
96    /// The use of `itertools::izip!` allows iterating over the characters of the input strings in parallel, applying the replacement on each character triplet. 
97    /// If the inputs match the expected string types, the method returns a tuple indicating success and the resulting string; otherwise, it returns a tuple indicating failure with a `Value::Null`. 
98    /// The `galloc_str` and `galloc_scollect` methods are employed to efficiently handle memory allocation for the resulting strings.
99    /// 
100    fn try_eval(&self, a1: Value, a2: Value, a3: Value) -> (bool, Value) {
101        match (a1, a2, a3) {
102            (Value::Str(s1), Value::Str(s2), Value::Str(s3)) => (true, Value::Str(
103                itertools::izip!(s1.iter(), s2.iter(), s3.iter())
104                    .map(|(s1, s2, s3)| s1.replacen(*s2, s3, 1).galloc_str())
105                    .galloc_scollect(),
106            )),
107            _ => (false, Value::Null),
108        }
109    }
110}