1use crate::dna::hel::error::HlxError;
7use crate::ops::eval::{run_program as eval_run_program, Env};
8use crate::dna::atp::value::Value;
11use anyhow;
12use std::collections::HashMap;
13use async_trait::async_trait;
14
15#[derive(Debug, Clone, PartialEq, Eq)]
17pub enum Expr {
18 Number(i64),
20 Var(String),
22 Mul(Box<Expr>, Box<Expr>),
24 Add(Box<Expr>, Box<Expr>),
26 Sub(Box<Expr>, Box<Expr>),
28 Ref {
30 var: String,
32 modifier: Option<i64>,
34 },
35}
36
37#[derive(Debug, Clone, PartialEq, Eq)]
39pub struct Assign {
40 pub name: String,
42 pub value: Expr,
44}
45
46pub struct MathOperators {
50 calculator: Calculator,
51}
52
53impl MathOperators {
54 pub async fn new() -> Result<Self, HlxError> {
55 Ok(Self {
56 calculator: Calculator::new(),
57 })
58 }
59
60 pub async fn execute(&self, operator: &str, params: &str) -> Result<Value, HlxError> {
61 self.execute_impl(operator, params).await
62 }
63
64 async fn execute_impl(&self, operator: &str, params: &str) -> Result<Value, HlxError> {
65 match operator {
66 "calc" => {
67 let parsed_params = crate::ops::utils::parse_params(params)?;
68 let source = parsed_params.get("source")
69 .ok_or_else(|| HlxError::invalid_input("Missing 'source' parameter", "Check the source parameter"))?
70 .to_string();
71
72 let result = self.calculator.evaluate(&source)
73 .map_err(|e| HlxError::execution_error(format!("Calculator error: {}", e), "Check calculator syntax"))?;
74
75 let mut result_obj = HashMap::new();
77 for (key, value) in result.env {
78 result_obj.insert(key, Value::Number(value as f64));
79 }
80 Ok(Value::Object(result_obj))
81 }
82 "eval" => {
83 let parsed_params = crate::ops::utils::parse_params(params)?;
84 let expression = parsed_params.get("expression")
85 .ok_or_else(|| HlxError::invalid_input("Missing 'expression' parameter", "Check the expression parameter"))?
86 .to_string();
87
88 let result = self.calculator.evaluate(&format!("reproducibility {{ result = {} }}", expression))
90 .map_err(|e| HlxError::execution_error(format!("Evaluation error: {}", e), "Check expression syntax"))?;
91
92 if let Some(value) = result.env.get("result") {
93 Ok(Value::Number(*value as f64))
94 } else {
95 Ok(Value::Number(0.0))
96 }
97 }
98 _ => Err(HlxError::invalid_input(format!("Unknown math operator: {}", operator), "Check the operator name"))
99 }
100 }
101}
102
103#[async_trait]
104impl crate::ops::OperatorTrait for MathOperators {
105 async fn execute(&self, operator: &str, params: &str) -> Result<Value, HlxError> {
106 self.execute_impl(operator, params).await
107 }
108}
109
110pub struct Calculator;
112
113pub struct CalcResult {
115 pub env: Env,
117}
118
119impl Calculator {
120 pub fn new() -> Self {
122 Self
123 }
124
125 pub fn evaluate(&self, source: &str) -> anyhow::Result<CalcResult> {
148 let assignments = parse_program(source)?;
150
151 let env = eval_run_program(&assignments)?;
153
154 Ok(CalcResult { env })
155 }
156
157 pub fn parse_only(&self, source: &str) -> anyhow::Result<Vec<Assign>> {
159 parse_program(source)
160 }
161}
162
163pub fn parse_program(source: &str) -> anyhow::Result<Vec<Assign>> {
165 let mut assignments = Vec::new();
167
168 for line in source.lines() {
169 let line = line.trim();
170 if line.is_empty() || line.starts_with("//") {
171 continue;
172 }
173
174 if let Some((name, expr_str)) = line.split_once('=') {
175 let name = name.trim().to_string();
176 let expr_str = expr_str.trim();
177
178 let expr = parse_expression(expr_str)?;
179 assignments.push(Assign {
180 name,
181 value: expr,
182 });
183 }
184 }
185
186 Ok(assignments)
187}
188
189fn parse_expression(expr_str: &str) -> anyhow::Result<Expr> {
191 parse_add_sub(expr_str)
192}
193
194fn parse_add_sub(expr_str: &str) -> anyhow::Result<Expr> {
196 let mut result = parse_mul_div(expr_str)?;
197
198 let expr_chars: Vec<char> = expr_str.chars().collect();
199 let mut i = 0;
200
201 while i < expr_chars.len() {
202 match expr_chars[i] {
203 '+' => {
204 i += 1;
205 let right = parse_mul_div(&expr_str[i..])?;
206 result = Expr::Add(Box::new(result), Box::new(right));
207 }
208 '-' => {
209 i += 1;
210 let right = parse_mul_div(&expr_str[i..])?;
211 result = Expr::Sub(Box::new(result), Box::new(right));
212 }
213 _ => break,
214 }
215 }
216
217 Ok(result)
218}
219
220fn parse_mul_div(expr_str: &str) -> anyhow::Result<Expr> {
222 let mut result = parse_factor(expr_str)?;
223
224 let expr_chars: Vec<char> = expr_str.chars().collect();
225 let mut i = 0;
226
227 while i < expr_chars.len() {
228 match expr_chars[i] {
229 'x' | '*' => {
230 i += 1;
231 let right = parse_factor(&expr_str[i..])?;
232 result = Expr::Mul(Box::new(result), Box::new(right));
233 }
234 _ => break,
235 }
236 }
237
238 Ok(result)
239}
240
241fn parse_factor(expr_str: &str) -> anyhow::Result<Expr> {
243 let expr_str = expr_str.trim();
244
245 if expr_str.starts_with('(') && expr_str.ends_with(')') {
247 let inner = &expr_str[1..expr_str.len()-1];
248 return parse_add_sub(inner);
249 }
250
251 if expr_str.starts_with('@') {
253 if let Some(hash_pos) = expr_str.find('#') {
254 let var = expr_str[1..hash_pos].to_string();
255 let modifier_str = &expr_str[hash_pos+1..];
256 let modifier = modifier_str.parse::<i64>()?;
257 return Ok(Expr::Ref {
258 var,
259 modifier: Some(modifier),
260 });
261 } else {
262 let var = expr_str[1..].to_string();
263 return Ok(Expr::Ref {
264 var,
265 modifier: None,
266 });
267 }
268 }
269
270 if let Ok(num) = expr_str.parse::<i64>() {
272 return Ok(Expr::Number(num));
273 }
274
275 Ok(Expr::Var(expr_str.to_string()))
277}
278
279
280pub fn eval_expr(expr: &Expr, env: &Env) -> i64 {
282 match expr {
283 Expr::Number(n) => *n,
284 Expr::Var(name) => env.get(name).copied().unwrap_or(0),
285 Expr::Add(left, right) => eval_expr(left, env) + eval_expr(right, env),
286 Expr::Sub(left, right) => eval_expr(left, env) - eval_expr(right, env),
287 Expr::Mul(left, right) => eval_expr(left, env) * eval_expr(right, env),
288 Expr::Ref { var, modifier } => {
289 let value = env.get(var).copied().unwrap_or(0);
290 match modifier {
291 Some(mod_val) => value % mod_val,
292 None => value,
293 }
294 }
295 }
296}
297
298pub fn run_program(assignments: &[Assign]) -> anyhow::Result<Env> {
300 let mut env = Env::new();
301
302 for assignment in assignments {
303 let value = eval_expr(&assignment.value, &env);
304 env.insert(assignment.name.clone(), value);
305 }
306
307 Ok(env)
308}
309
310#[cfg(test)]
315mod tests {
316 use super::*;
317
318 #[test]
319 fn test_basic_arithmetic() {
320 let calc = Calculator::new();
321 let src = r#"
322 reproducibility {
323 a = 2
324 b = 3
325 c = a x b
326 }
327 "#;
328
329 let result = calc.evaluate(src).unwrap();
330 assert_eq!(result.env["a"], 2);
331 assert_eq!(result.env["b"], 3);
332 assert_eq!(result.env["c"], 6);
333 }
334
335 #[test]
336 fn test_reference_with_modifier() {
337 let calc = Calculator::new();
338 let src = r#"
339 reproducibility {
340 a = 10
341 b = 3
342 c = a x b
343 d = @c #4
344 }
345 "#;
346
347 let result = calc.evaluate(src).unwrap();
348 assert_eq!(result.env["a"], 10);
349 assert_eq!(result.env["b"], 3);
350 assert_eq!(result.env["c"], 30);
351 assert_eq!(result.env["d"], 2); }
353
354 #[test]
355 fn test_complex_expression() {
356 let calc = Calculator::new();
357 let src = r#"
358 reproducibility {
359 x = 5
360 y = 3
361 z = (x + y) x (x - y)
362 }
363 "#;
364
365 let result = calc.evaluate(src).unwrap();
366 assert_eq!(result.env["x"], 5);
367 assert_eq!(result.env["y"], 3);
368 assert_eq!(result.env["z"], 16); }
370}