1use regex::Regex;
3use std::collections::HashSet;
4
5pub struct ScriptParser {
6 math_operators: Vec<(Regex, &'static str)>,
7 number_re: Regex,
8 variable_re: Regex,
9}
10
11impl ScriptParser {
12 pub fn new() -> Self {
13 let math_operators = vec![
14 (Regex::new(r"\(\s*\+\s+").unwrap(), "(decimal-add "),
16 (Regex::new(r"\(\s*-\s+").unwrap(), "(decimal-sub "),
17 (Regex::new(r"\(\s*\*\s+").unwrap(), "(decimal-mul "),
18 (Regex::new(r"\(\s*/\s+").unwrap(), "(decimal-div "),
19
20 (Regex::new(r"\(\s*\^\s+").unwrap(), "(decimal-pow "),
22 (Regex::new(r"\(\s*\*\*\s+").unwrap(), "(decimal-pow "),
23 (Regex::new(r"\(\s*pow\s+").unwrap(), "(decimal-pow "),
24 (Regex::new(r"\(\s*sqrt\s+").unwrap(), "(decimal-sqrt "),
25
26 (Regex::new(r"\(\s*ln\s+").unwrap(), "(decimal-ln "),
28 (Regex::new(r"\(\s*log\s+").unwrap(), "(decimal-ln "),
29 (Regex::new(r"\(\s*log10\s+").unwrap(), "(decimal-log10 "),
30 (Regex::new(r"\(\s*exp\s+").unwrap(), "(decimal-exp "),
31
32 (Regex::new(r"\(\s*sin\s+").unwrap(), "(decimal-sin "),
34 (Regex::new(r"\(\s*cos\s+").unwrap(), "(decimal-cos "),
35 (Regex::new(r"\(\s*tan\s+").unwrap(), "(decimal-tan "),
36
37 (Regex::new(r"\(\s*>\s+").unwrap(), "(decimal-gt "),
39 (Regex::new(r"\(\s*<\s+").unwrap(), "(decimal-lt "),
40 (Regex::new(r"\(\s*=\s+").unwrap(), "(decimal-eq "),
41 (Regex::new(r"\(\s*>=\s+").unwrap(), "(decimal-gte "),
42 (Regex::new(r"\(\s*<=\s+").unwrap(), "(decimal-lte "),
43
44 (Regex::new(r"\(\s*abs\s+").unwrap(), "(decimal-abs "),
46 (Regex::new(r"\(\s*min\s+").unwrap(), "(decimal-min "),
47 (Regex::new(r"\(\s*max\s+").unwrap(), "(decimal-max "),
48 (Regex::new(r"\(\s*round\s+").unwrap(), "(decimal-round "),
49 ];
50
51 ScriptParser {
52 math_operators,
53 number_re: Regex::new(r"(^|[\s\(])(-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)").unwrap(),
57 variable_re: Regex::new(r"\$([^\s)]+)").unwrap(),
58 }
59 }
60
61 pub fn transform(&self, script: &str) -> String {
63 let mut transformed = script.to_string();
64
65 transformed = self.replace_math_functions(&transformed);
67 transformed = self.replace_variable_references(&transformed);
68 transformed = self.convert_numbers_to_strings(&transformed);
69
70 transformed
71 }
72
73 fn convert_numbers_to_strings(&self, script: &str) -> String {
75 let parts: Vec<&str> = script.split('"').collect();
76 let mut result = String::new();
77
78 for (i, part) in parts.iter().enumerate() {
79 if i % 2 == 0 {
80 let processed = self.number_re.replace_all(part, |caps: ®ex::Captures| {
82 format!("{}\"{}\"", &caps[1], &caps[2])
87 });
88 result.push_str(&processed);
89 } else {
90 result.push_str(part);
92 }
93
94 if i < parts.len() - 1 {
95 result.push('"');
96 }
97 }
98
99 result
100 }
101
102 fn replace_math_functions(&self, script: &str) -> String {
104 let mut result = script.to_string();
105
106 for (pattern, replacement) in &self.math_operators {
107 result = pattern.replace_all(&result, *replacement).to_string();
108 }
109
110 result
111 }
112
113 fn replace_variable_references(&self, script: &str) -> String {
115 self.variable_re.replace_all(script, |caps: ®ex::Captures| {
116 format!("(get-var \"{}\")", &caps[1])
117 }).to_string()
118 }
119
120 pub fn extract_dependencies(&self, script: &str) -> HashSet<String> {
122 let mut dependencies = HashSet::new();
123
124 for cap in self.variable_re.captures_iter(script) {
125 dependencies.insert(cap[1].to_string());
126 }
127
128 dependencies
129 }
130}
131
132impl Default for ScriptParser {
133 fn default() -> Self {
134 Self::new()
135 }
136}