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}