adana_script/
compute.rs

1use anyhow::{anyhow, Context, Error};
2use slab_tree::{NodeRef, Tree};
3use std::{
4    borrow::Borrow,
5    fs::read_to_string,
6    path::{Path, PathBuf},
7    sync::Arc,
8};
9
10use crate::{parser::parse_instructions, prelude::BTreeMap};
11
12use super::{ast::to_ast, require_dynamic_lib::require_dynamic_lib};
13
14use adana_script_core::{
15    primitive::{
16        Abs, Add, And, Array, BitShift, Cos, DisplayBinary, DisplayHex, Div,
17        Json, Logarithm, Mul, Neg, Not, Or, Pow, Primitive, RefPrimitive, Rem,
18        Round, Sin, Sqrt, StringManipulation, Sub, Tan, ToBool, ToNumber,
19        TypeOf, TYPE_ARRAY, TYPE_BOOL, TYPE_DOUBLE, TYPE_ERROR, TYPE_FUNCTION,
20        TYPE_I8, TYPE_INT, TYPE_STRUCT, TYPE_U8,
21    },
22    BuiltInFunctionType, KeyAccess, Operator, TreeNodeValue, Value,
23};
24
25/// copy existing functions in a new ctx
26fn scoped_ctx(
27    ctx: &mut BTreeMap<String, RefPrimitive>,
28) -> anyhow::Result<BTreeMap<String, RefPrimitive>> {
29    let mut scope_ctx = BTreeMap::new();
30
31    // copy also the function definition to the scoped ctx
32    for (k, p) in ctx.iter() {
33        let maybe_fn = p
34            .read()
35            .map_err(|e| anyhow::format_err!("could not acquire lock {e}"))?;
36        if matches!(
37            *maybe_fn,
38            Primitive::Function { parameters: _, exprs: _ }
39                | Primitive::NativeLibrary(_)
40        ) {
41            scope_ctx.insert(k.to_string(), p.clone());
42        }
43    }
44
45    Ok(scope_ctx)
46}
47
48fn compute_key_access(
49    key: &KeyAccess,
50    ctx: &mut BTreeMap<String, RefPrimitive>,
51    shared_lib: impl AsRef<Path> + Copy,
52) -> anyhow::Result<KeyAccess> {
53    fn compute_key_access_ref(key: &Primitive) -> anyhow::Result<KeyAccess> {
54        match key {
55            Primitive::U8(u) => Ok(KeyAccess::Index(Primitive::U8(*u))),
56            Primitive::I8(u) => Ok(KeyAccess::Index(Primitive::I8(*u))),
57            Primitive::Int(u) => Ok(KeyAccess::Index(Primitive::Int(*u))),
58            Primitive::Ref(r) => {
59                let r = r
60                    .read()
61                    .map_err(|e| anyhow!("could not acquire lock {e}"))?;
62                compute_key_access_ref(&r)
63            }
64            Primitive::String(s) => {
65                Ok(KeyAccess::Key(Primitive::String(s.to_string())))
66            }
67            _ => Err(anyhow!("illegal key access {key:?}")),
68        }
69    }
70    match key {
71        KeyAccess::Index(_)
72        | KeyAccess::Key(_)
73        | KeyAccess::FunctionCall { .. } => Ok(key.clone()),
74        KeyAccess::Variable(v) => {
75            compute_key_access_ref(&compute_lazy(v.clone(), ctx, shared_lib)?)
76        }
77    }
78}
79fn handle_function_call(
80    mut function: Primitive,
81    parameters: &Value,
82    ctx: &mut BTreeMap<String, RefPrimitive>,
83    shared_lib: impl AsRef<Path> + Copy,
84) -> anyhow::Result<Primitive> {
85    if let Value::BlockParen(param_values) = parameters {
86        // FIXME clone again
87        if let Primitive::Ref(r) = function {
88            function = r
89                .read()
90                .map_err(|e| {
91                    anyhow::format_err!("could not acquire lock in fn call{e}")
92                })?
93                .clone();
94        }
95        match function {
96            Primitive::Function { parameters: function_parameters, exprs } => {
97                let mut scope_ctx = scoped_ctx(ctx)?;
98                for (i, param) in function_parameters.iter().enumerate() {
99                    if let Some(value) = param_values.get(i) {
100                        if let Value::Variable(variable_from_fn_def) = param {
101                            let variable_from_fn_call =
102                                compute_lazy(value.clone(), ctx, shared_lib)?;
103                            scope_ctx.insert(
104                                variable_from_fn_def.clone(),
105                                variable_from_fn_call.ref_prim(),
106                            );
107                        }
108                    } else {
109                        return Ok(Primitive::Error(format!(
110                            "missing parameter {param:?}"
111                        )));
112                    }
113                }
114                // TODO remove this and replace Arc<Mutex<T>> by Arc<T>
115                // call function in a specific os thread with its own stack
116                // This was relative to a small stack allocated by musl
117                // But now it doesn't seem needed anymore
118                // let res = spawn(move || {}).join().map_err(|e| {
119                //     anyhow::Error::msg(format!(
120                //         "something wrong: {e:?}"
121                //     ))
122                // })??;
123                let res =
124                    compute_instructions(exprs, &mut scope_ctx, shared_lib)?;
125
126                if let Primitive::EarlyReturn(v) = res {
127                    return Ok(*v);
128                }
129                Ok(res)
130            }
131            _ => {
132                match function {
133                    Primitive::NativeLibrary(lib) => {
134                        if cfg!(test) {
135                            dbg!(&lib);
136                        }
137                        let mut parameters = vec![];
138                        for param in param_values.iter() {
139                            if let Value::Variable(_) = param {
140                                let variable_from_fn_call = compute_lazy(
141                                    param.clone(),
142                                    ctx,
143                                    shared_lib,
144                                )?;
145                                parameters.push(variable_from_fn_call);
146                            }
147                        }
148                        if cfg!(test) {
149                            dbg!(&parameters);
150                        }
151                        Ok(Primitive::Error("debug".into()))
152                        //Ok(function(vec![Primitive::String("s".into())]))
153                    }
154                    _ => match function {
155                        Primitive::NativeFunction(key, lib) => {
156                            #[cfg(not(target_arch = "wasm32"))]
157                            {
158                                if cfg!(test) {
159                                    dbg!(&key, &lib);
160                                }
161                                let mut parameters = vec![];
162
163                                for param in param_values.iter() {
164                                    let variable_from_fn_call = compute_lazy(
165                                        param.clone(),
166                                        ctx,
167                                        shared_lib,
168                                    )?;
169                                    parameters.push(variable_from_fn_call);
170                                }
171                                if cfg!(test) {
172                                    dbg!(&parameters);
173                                }
174
175                                let mut scope_ctx = scoped_ctx(ctx)?;
176
177                                let slb = shared_lib.as_ref().to_path_buf();
178                                let fun = move |v, extra_ctx| {
179                                    scope_ctx.extend(extra_ctx);
180                                    compute_lazy(v, &mut scope_ctx, &slb)
181                                };
182                                unsafe {
183                                    lib.call_function(
184                                        key.as_str(),
185                                        parameters,
186                                        Box::new(fun),
187                                    )
188                                }
189                            }
190                            #[cfg(target_arch = "wasm32")]
191                            {
192                                return Ok(Primitive::Error(format!("Loading native function {key} doesn't work in wasm context! {lib:?}")));
193                            }
194                        }
195                        _ => Ok(Primitive::Error(format!(
196                            " not a function: {function}"
197                        ))),
198                    },
199                }
200            }
201        }
202    } else {
203        Ok(Primitive::Error(format!(
204            "invalid function call: {parameters:?} => {function:?}"
205        )))
206    }
207}
208fn fold_multidepth(
209    root: &Value,
210    next_keys: &Vec<KeyAccess>,
211    mut new_value: Primitive,
212    ctx: &mut BTreeMap<String, RefPrimitive>,
213    shared_lib: impl AsRef<Path> + Copy,
214) -> anyhow::Result<Primitive> {
215    fn fold(
216        acc: &mut Primitive,
217        new_value: &mut Primitive,
218        mut next_keys: Vec<&KeyAccess>,
219        ctx: &mut BTreeMap<String, RefPrimitive>,
220        shared_lib: impl AsRef<Path> + Copy,
221    ) -> anyhow::Result<Primitive> {
222        if matches!(new_value, Primitive::Error(_)) {
223            return Ok(new_value.clone());
224        }
225        let k = next_keys.remove(0);
226        let k = compute_key_access(k, ctx, shared_lib)?;
227        match k {
228            KeyAccess::Index(key) | KeyAccess::Key(key) => {
229                if next_keys.is_empty() {
230                    if matches!(new_value, Primitive::Unit) {
231                        // handle drop as user cannot
232                        // construct a Primitive::Unit
233                        acc.remove(&key)?;
234                        return Ok(Primitive::Unit);
235                    }
236                    let res = acc.swap_mem(new_value, &key);
237                    if matches!(res, Primitive::Error(_)) {
238                        return Ok(res);
239                    }
240                } else {
241                    let mut new_value = fold(
242                        &mut acc.index_at(&key),
243                        new_value,
244                        next_keys,
245                        ctx,
246                        shared_lib,
247                    )?;
248                    if matches!(new_value, Primitive::Error(_)) {
249                        return Ok(new_value);
250                    }
251                    acc.swap_mem(&mut new_value, &key);
252                }
253            }
254            KeyAccess::FunctionCall { .. } | KeyAccess::Variable(_) => {
255                return Err(anyhow!("illegal assignement {next_keys:?} "))
256            }
257        }
258        Ok(acc.clone())
259    }
260
261    if next_keys.is_empty() {
262        return Err(anyhow!("not enough keys {next_keys:?}"));
263    }
264    match root {
265        Value::Variable(name) | Value::VariableRef(name) => {
266            let mut cloned_ctx = ctx.clone();
267            let mut acc = ctx
268                .get(name)
269                .context("array not found in context")?
270                .write()
271                .map_err(|e| {
272                    anyhow::format_err!("could not acquire lock {e}")
273                })?;
274            let res = fold(
275                &mut acc,
276                &mut new_value,
277                next_keys.iter().collect(),
278                &mut cloned_ctx,
279                shared_lib,
280            )?;
281
282            Ok(res)
283        }
284        _ => Ok(new_value),
285    }
286}
287fn compute_multidepth_access(
288    root: &Value,
289    keys: &[KeyAccess],
290    ctx: &mut BTreeMap<String, RefPrimitive>,
291    shared_lib: impl AsRef<Path> + Copy,
292) -> anyhow::Result<Primitive> {
293    fn compute_multidepth_access_primitive(
294        root: Primitive,
295        mut keys: Vec<&KeyAccess>,
296        ctx: &mut BTreeMap<String, RefPrimitive>,
297        shared_lib: impl AsRef<Path> + Copy,
298    ) -> anyhow::Result<Primitive> {
299        if keys.is_empty() {
300            return Err(anyhow::anyhow!(
301                "access error. not enough argument {keys:?}"
302            ));
303        }
304        match root {
305            Primitive::Ref(r) => {
306                let p = r.read().map_err(|e| {
307                    anyhow::anyhow!("could not acquire lock{e}")
308                })?;
309                compute_multidepth_access_primitive(
310                    p.clone(),
311                    keys,
312                    ctx,
313                    shared_lib,
314                )
315            }
316            v @ Primitive::String(_) => {
317                if keys.len() != 1 {
318                    return Err(anyhow::anyhow!(
319                        "string access error. too many argument {keys:?}"
320                    ));
321                }
322                let key = compute_key_access(keys.remove(0), ctx, shared_lib)?;
323                match key {
324                    KeyAccess::Index(i) => Ok(v.index_at(&i)),
325                    _ => Err(anyhow!(
326                        "cannot use that key in this context {key:?} {v:?}"
327                    )),
328                }
329            }
330            Primitive::NativeLibrary(lib) => {
331                match compute_key_access(keys.remove(0), ctx, shared_lib)? {
332                    KeyAccess::Key(idx) => {
333                        Ok(Primitive::NativeFunction(idx.to_string(), lib))
334                    }
335                    KeyAccess::FunctionCall { key, parameters } => {
336                        let key = compute_key_access(&key, ctx, shared_lib)?;
337                        let KeyAccess::Key(idx) = key else {
338                            return Err(anyhow!( "native lib can only be accessed with a key str {keys:?}"));
339                        };
340                        let root_p= handle_function_call(Primitive::NativeFunction(idx.to_string(), lib), &Box::new(parameters), ctx, shared_lib)?;
341                        if !keys.is_empty() {
342                            compute_multidepth_access_primitive(
343                                root_p, keys, ctx, shared_lib,
344                            )
345                        } else {
346                            Ok(root_p)
347                        }
348                    },
349                    _ => Err(anyhow!(
350                        "native lib can only be accessed with a key str {keys:?}"
351                    ))
352                }
353            }
354            v @ Primitive::Array(_) => {
355                let root_p = match compute_key_access(
356                    keys.remove(0),
357                    ctx,
358                    shared_lib,
359                )? {
360                    KeyAccess::Index(idx) => v.index_at(&idx),
361                    KeyAccess::FunctionCall { key, parameters } => {
362                        let key = compute_key_access(&key, ctx, shared_lib)?;
363                        let KeyAccess::Index(idx) = key else {
364                            return Err(anyhow!( "array can only be accessed with an idx  {keys:?}"));
365                        };
366
367                        handle_function_call(
368                            v.index_at(&idx),
369                            &Box::new(parameters),
370                            ctx,
371                            shared_lib,
372                        )?
373                    }
374                    _ => {
375                        return Err(anyhow!(
376                            "array can only be accessed with an idx  {keys:?}"
377                        ))
378                    }
379                };
380                if !keys.is_empty() {
381                    compute_multidepth_access_primitive(
382                        root_p, keys, ctx, shared_lib,
383                    )
384                } else {
385                    Ok(root_p)
386                }
387            }
388
389            v @ Primitive::Struct(_) => {
390                let root_p = match compute_key_access(
391                    keys.remove(0),
392                    ctx,
393                    shared_lib,
394                )? {
395                    KeyAccess::Key(idx) => v.index_at(&idx),
396                    KeyAccess::FunctionCall { key, parameters } => {
397                        let key = compute_key_access(&key, ctx, shared_lib)?;
398                        let KeyAccess::Key(idx) = key else {
399                            return Err(anyhow!( "struct can only be accessed with a key {keys:?}"));
400                        };
401
402                        handle_function_call(
403                            v.index_at(&idx),
404                            &Box::new(parameters),
405                            ctx,
406                            shared_lib,
407                        )?
408                    }
409                    _ => {
410                        return Err(anyhow!(
411                            "struct can only be accessed with a key {keys:?}"
412                        ))
413                    }
414                };
415                if !keys.is_empty() {
416                    compute_multidepth_access_primitive(
417                        root_p, keys, ctx, shared_lib,
418                    )
419                } else {
420                    Ok(root_p)
421                }
422            }
423            _ => Err(anyhow!(
424                "illegal usage of multidepth access {root:?} => {keys:?}"
425            )),
426        }
427    }
428    let root_primitive = match root {
429        Value::String(s) => Primitive::String(s.to_string()),
430        v @ Value::FString(_, _)
431        | v @ Value::Variable(_)
432        | v @ Value::Array(_)
433        | v @ Value::Struct(_)
434        | v @ Value::BuiltInFunction {
435            fn_type: BuiltInFunctionType::Require,
436            ..
437        }
438        | v @ Value::FunctionCall { .. }
439        | v @ Value::VariableRef(_) => {
440            compute_lazy(v.clone(), ctx, shared_lib)?
441        }
442        v => return Err(anyhow::anyhow!("illegal multidepth access {v:?}")),
443    };
444
445    compute_multidepth_access_primitive(
446        root_primitive,
447        keys.iter().collect(),
448        ctx,
449        shared_lib,
450    )
451}
452
453fn compute_recur(
454    node: Option<NodeRef<TreeNodeValue>>,
455    ctx: &mut BTreeMap<String, RefPrimitive>,
456    shared_lib: impl AsRef<Path> + Copy,
457) -> anyhow::Result<Primitive> {
458    if let Some(node) = node {
459        match node.data() {
460            TreeNodeValue::Ops(Operator::Not) => {
461                if node.children().count() != 1 {
462                    return Err(Error::msg(
463                        "only one value allowed, no '!' possible",
464                    ));
465                }
466                let left = compute_recur(node.first_child(), ctx, shared_lib)?;
467                Ok(left.not())
468            }
469            TreeNodeValue::Ops(Operator::BitwiseNot) => {
470                if node.children().count() != 1 {
471                    return Err(Error::msg(
472                        "only one value allowed, no '~' possible",
473                    ));
474                }
475                let left = compute_recur(node.first_child(), ctx, shared_lib)?;
476                Ok(left.bitwise_not())
477            }
478            TreeNodeValue::Ops(Operator::Add) => {
479                if node.children().count() == 1 {
480                    return compute_recur(node.first_child(), ctx, shared_lib);
481                }
482                let left = compute_recur(node.first_child(), ctx, shared_lib)?;
483                let right = compute_recur(node.last_child(), ctx, shared_lib)?;
484                Ok(left.add(&right))
485            }
486            TreeNodeValue::Ops(Operator::Mult) => {
487                if node.children().count() == 1 {
488                    return compute_recur(node.first_child(), ctx, shared_lib);
489                }
490                let left = compute_recur(node.first_child(), ctx, shared_lib)?;
491                let right = compute_recur(node.last_child(), ctx, shared_lib)?;
492                Ok(left.mul(&right))
493            }
494            TreeNodeValue::VariableRef(name) => {
495                let v = ctx
496                    .get(name)
497                    .cloned()
498                    .context(format!("ref {name} not found in context!"))?;
499                let lock = v.read().map_err(|e| {
500                    anyhow::format_err!("variable ref err: {e}")
501                })?;
502                let primitive: &Primitive = &lock;
503                match primitive {
504                    v @ &Primitive::Ref(_) => Ok(v.clone()),
505                    _ => Ok(Primitive::Ref(v.clone())),
506                }
507            }
508            TreeNodeValue::Ops(Operator::Mod) => {
509                if node.children().count() == 1 {
510                    return compute_recur(node.first_child(), ctx, shared_lib);
511                }
512                let left = compute_recur(node.first_child(), ctx, shared_lib)?;
513                let right = compute_recur(node.last_child(), ctx, shared_lib)?;
514                Ok(left.rem(&right))
515            }
516            TreeNodeValue::Ops(Operator::Subtr) => {
517                if node.children().count() == 1 {
518                    return Ok(compute_recur(
519                        node.first_child(),
520                        ctx,
521                        shared_lib,
522                    )?
523                    .neg());
524                }
525                let left = compute_recur(node.first_child(), ctx, shared_lib)?;
526                let right = compute_recur(node.last_child(), ctx, shared_lib)?;
527                Ok(left.sub(&right))
528            }
529            TreeNodeValue::Ops(Operator::Pow) => {
530                if node.children().count() == 1 {
531                    return compute_recur(node.first_child(), ctx, shared_lib);
532                }
533                let left = compute_recur(node.first_child(), ctx, shared_lib)?;
534                let right = compute_recur(node.last_child(), ctx, shared_lib)?;
535                Ok(left.pow(&right))
536            }
537            TreeNodeValue::Ops(Operator::Pow2) => {
538                Err(Error::msg("BUG: unreacheable pow2 in compute!"))
539            }
540            TreeNodeValue::Ops(Operator::Pow3) => {
541                Err(Error::msg("BUG: unreacheable pow3 in compute!"))
542            }
543            TreeNodeValue::Ops(Operator::Div) => {
544                if node.children().count() == 1 {
545                    return compute_recur(node.first_child(), ctx, shared_lib);
546                }
547                let left = compute_recur(node.first_child(), ctx, shared_lib)?;
548                let right = compute_recur(node.last_child(), ctx, shared_lib)?;
549                Ok(left.div(&right))
550            }
551            TreeNodeValue::Ops(Operator::And) => {
552                if node.children().count() == 1 {
553                    return Err(Error::msg(
554                        "only one value, no '&&' comparison possible",
555                    ));
556                }
557                let left = compute_recur(node.first_child(), ctx, shared_lib)?;
558                let right = compute_recur(node.last_child(), ctx, shared_lib)?;
559                Ok(left.and(&right))
560            }
561            TreeNodeValue::Ops(Operator::BitwiseAnd) => {
562                if node.children().count() == 1 {
563                    return Err(Error::msg(
564                        "only one value, no 'AND' comparison possible",
565                    ));
566                }
567                let left = compute_recur(node.first_child(), ctx, shared_lib)?;
568                let right = compute_recur(node.last_child(), ctx, shared_lib)?;
569                Ok(left.bitwise_and(&right))
570            }
571            TreeNodeValue::Ops(Operator::BitwiseLShift) => {
572                if node.children().count() == 1 {
573                    return Err(Error::msg("only one value for '<<' "));
574                }
575                let left = compute_recur(node.first_child(), ctx, shared_lib)?;
576                let right = compute_recur(node.last_child(), ctx, shared_lib)?;
577                Ok(left.left_shift(&right))
578            }
579            TreeNodeValue::Ops(Operator::BitwiseRShift) => {
580                if node.children().count() == 1 {
581                    return Err(Error::msg("only one value, for '>>'"));
582                }
583                let left = compute_recur(node.first_child(), ctx, shared_lib)?;
584                let right = compute_recur(node.last_child(), ctx, shared_lib)?;
585                Ok(left.right_shift(&right))
586            }
587            TreeNodeValue::VariableUnused => {
588                Err(Error::msg("forbidden usage of VariableUnused"))
589            }
590            TreeNodeValue::FString(p, parameters) => {
591                let mut s = String::from(p);
592                for (key, param) in parameters {
593                    let primitive =
594                        compute_lazy(param.clone(), ctx, shared_lib)?;
595                    if let err @ Primitive::Error(_) = primitive {
596                        return Ok(err);
597                    }
598                    let string_value = primitive.to_string();
599                    s = s.replacen(key, &string_value, 1);
600                }
601
602                Ok(Primitive::String(s))
603            }
604            TreeNodeValue::Ops(Operator::Or) => {
605                if node.children().count() == 1 {
606                    return Err(Error::msg(
607                        "only one value, no '||' comparison possible",
608                    ));
609                }
610                let left = compute_recur(node.first_child(), ctx, shared_lib)?;
611                let right = compute_recur(node.last_child(), ctx, shared_lib)?;
612                Ok(left.or(&right))
613            }
614
615            TreeNodeValue::Ops(Operator::BitwiseOr) => {
616                if node.children().count() == 1 {
617                    return Err(Error::msg(
618                        "only one value, no '|' comparison possible",
619                    ));
620                }
621                let left = compute_recur(node.first_child(), ctx, shared_lib)?;
622                let right = compute_recur(node.last_child(), ctx, shared_lib)?;
623                Ok(left.bitwise_or(&right))
624            }
625
626            TreeNodeValue::Ops(Operator::BitwiseXor) => {
627                if node.children().count() == 1 {
628                    return Err(Error::msg(
629                        "only one value, no 'XOR' comparison possible",
630                    ));
631                }
632                let left = compute_recur(node.first_child(), ctx, shared_lib)?;
633                let right = compute_recur(node.last_child(), ctx, shared_lib)?;
634                Ok(left.bitwise_xor(&right))
635            }
636            TreeNodeValue::Ops(Operator::Equal) => {
637                if node.children().count() == 1 {
638                    return Err(Error::msg(
639                        "only one value, no '==' comparison possible",
640                    ));
641                }
642                let left = compute_recur(node.first_child(), ctx, shared_lib)?;
643                let right = compute_recur(node.last_child(), ctx, shared_lib)?;
644                Ok(left.is_equal(&right))
645            }
646            TreeNodeValue::Ops(Operator::NotEqual) => {
647                if node.children().count() == 1 {
648                    return Err(Error::msg(
649                        "only one value, no '!=' comparison possible",
650                    ));
651                }
652                let left = compute_recur(node.first_child(), ctx, shared_lib)?;
653                let right = compute_recur(node.last_child(), ctx, shared_lib)?;
654                Ok(left.is_equal(&right).not())
655            }
656            TreeNodeValue::Ops(Operator::Less) => {
657                if node.children().count() == 1 {
658                    return Err(Error::msg(
659                        "only one value, no '<' comparison possible",
660                    ));
661                }
662                let left = compute_recur(node.first_child(), ctx, shared_lib)?;
663                let right = compute_recur(node.last_child(), ctx, shared_lib)?;
664                Ok(left.is_less_than(&right))
665            }
666            TreeNodeValue::Ops(Operator::Greater) => {
667                if node.children().count() == 1 {
668                    return Err(Error::msg(
669                        "only one value, no '>' comparison possible",
670                    ));
671                }
672                let left = compute_recur(node.first_child(), ctx, shared_lib)?;
673                let right = compute_recur(node.last_child(), ctx, shared_lib)?;
674                Ok(left.is_greater_than(&right))
675            }
676            TreeNodeValue::Ops(Operator::GreaterOrEqual) => {
677                if node.children().count() == 1 {
678                    return Err(Error::msg(
679                        "only one value, no '>=' comparison possible",
680                    ));
681                }
682                let left = compute_recur(node.first_child(), ctx, shared_lib)?;
683                let right = compute_recur(node.last_child(), ctx, shared_lib)?;
684                Ok(left.is_greater_or_equal(&right))
685            }
686            TreeNodeValue::Ops(Operator::LessOrEqual) => {
687                if node.children().count() == 1 {
688                    return Err(Error::msg(
689                        "only one value, no '<=' comparison possible",
690                    ));
691                }
692                let left = compute_recur(node.first_child(), ctx, shared_lib)?;
693                let right = compute_recur(node.last_child(), ctx, shared_lib)?;
694                Ok(left.is_less_or_equal(&right))
695            }
696            TreeNodeValue::Primitive(p) => Ok(p.clone()),
697            TreeNodeValue::VariableAssign(name) => {
698                let v = compute_recur(node.first_child(), ctx, shared_lib)?;
699                if !matches!(v, Primitive::Error(_)) {
700                    if let Some(name) = name {
701                        let old = ctx
702                            .entry(name.clone())
703                            .or_insert(Primitive::Unit.ref_prim());
704                        match &v {
705                            Primitive::Ref(v) if Arc::ptr_eq(old, v) => (),
706                            _ => {
707                                let mut old = old.write().map_err(|e| {
708                                    anyhow::format_err!(
709                                        "could not acquire lock {e}"
710                                    )
711                                })?;
712                                *old = v.clone();
713                            }
714                        }
715                    }
716                }
717                Ok(v)
718            }
719
720            TreeNodeValue::IfExpr(v) => {
721                let mut scoped_ctx = ctx.clone();
722                compute_instructions(
723                    vec![v.clone()],
724                    &mut scoped_ctx,
725                    shared_lib,
726                )
727            }
728            TreeNodeValue::WhileExpr(v) => {
729                let mut scoped_ctx = ctx.clone();
730                compute_instructions(
731                    vec![v.clone()],
732                    &mut scoped_ctx,
733                    shared_lib,
734                )
735            }
736            TreeNodeValue::Foreach(v) => {
737                let mut scoped_ctx = ctx.clone();
738                compute_instructions(
739                    vec![v.clone()],
740                    &mut scoped_ctx,
741                    shared_lib,
742                )
743            }
744            TreeNodeValue::Array(arr) => {
745                let mut primitives = vec![];
746                for v in arr {
747                    let primitive =
748                        compute_instructions(vec![v.clone()], ctx, shared_lib)?;
749                    match primitive {
750                        v @ Primitive::Error(_) => return Ok(v),
751                        Primitive::Unit => {
752                            return Ok(Primitive::Error(
753                                "cannot push unit () to array".to_string(),
754                            ))
755                        }
756                        _ => primitives.push(primitive),
757                    }
758                }
759                Ok(Primitive::Array(primitives))
760            }
761
762            TreeNodeValue::Struct(struc) => {
763                let mut primitives = BTreeMap::new();
764                for (k, v) in struc {
765                    if !k.starts_with('_') {
766                        let primitive = compute_instructions(
767                            vec![v.clone()],
768                            ctx,
769                            shared_lib,
770                        )?;
771                        match primitive {
772                            v @ Primitive::Error(_) => return Ok(v),
773                            Primitive::Unit => {
774                                return Ok(Primitive::Error(
775                                    "cannot push unit () to struct".to_string(),
776                                ))
777                            }
778                            _ => {
779                                primitives.insert(k.to_string(), primitive);
780                            }
781                        }
782                    }
783                }
784                Ok(Primitive::Struct(primitives))
785            }
786            TreeNodeValue::MultiDepthAccess { root, keys } => {
787                compute_multidepth_access(root, keys, ctx, shared_lib)
788            }
789            TreeNodeValue::MultiDepthVariableAssign { root, next_keys } => {
790                let new_value =
791                    compute_recur(node.first_child(), ctx, shared_lib)?;
792                fold_multidepth(root, next_keys, new_value, ctx, shared_lib)
793            }
794
795            TreeNodeValue::Function(Value::Function { parameters, exprs }) => {
796                if let Value::BlockParen(parameters) = parameters.borrow() {
797                    if !parameters.iter().all(|v| {
798                        matches!(v, Value::Variable(_))
799                         //   || matches!(v, Value::String(_))
800                            || matches!(v, Value::VariableUnused)
801                    }) {
802                        return Ok(Primitive::Error(format!(
803                            "not a valid parameter: {parameters:?}"
804                        )));
805                    }
806                    Ok(Primitive::Function {
807                        parameters: parameters.clone(),
808                        exprs: exprs.to_owned(),
809                    })
810                } else {
811                    Ok(Primitive::Error(format!(
812                        "not a valid function: {parameters:?}, {exprs:?}"
813                    )))
814                }
815            }
816            TreeNodeValue::BuiltInFunction { fn_type, params } => {
817                let v = compute_lazy(params.clone(), ctx, shared_lib)?;
818                match fn_type {
819                    adana_script_core::BuiltInFunctionType::Sqrt => {
820                        Ok(v.sqrt())
821                    }
822                    adana_script_core::BuiltInFunctionType::Abs => Ok(v.abs()),
823                    adana_script_core::BuiltInFunctionType::Log => Ok(v.log()),
824                    adana_script_core::BuiltInFunctionType::Ln => Ok(v.ln()),
825                    adana_script_core::BuiltInFunctionType::Sin => Ok(v.sin()),
826                    adana_script_core::BuiltInFunctionType::Cos => Ok(v.cos()),
827                    adana_script_core::BuiltInFunctionType::Eval => match v {
828                        Primitive::String(script) => {
829                            compute(&script, ctx, shared_lib)
830                        }
831                        _ => {
832                            Ok(Primitive::Error(format!("invalid script {v}")))
833                        }
834                    },
835                    adana_script_core::BuiltInFunctionType::Tan => Ok(v.tan()),
836                    adana_script_core::BuiltInFunctionType::ToInt => {
837                        Ok(v.to_int())
838                    }
839                    adana_script_core::BuiltInFunctionType::ToHex => {
840                        Ok(v.to_hex())
841                    }
842                    adana_script_core::BuiltInFunctionType::ToBinary => {
843                        Ok(v.to_binary())
844                    }
845
846                    adana_script_core::BuiltInFunctionType::ToDouble => {
847                        Ok(v.to_double())
848                    }
849                    adana_script_core::BuiltInFunctionType::ToBool => {
850                        Ok(v.to_bool())
851                    }
852                    adana_script_core::BuiltInFunctionType::ToString => {
853                        Ok(Primitive::String(v.to_string()))
854                    }
855                    adana_script_core::BuiltInFunctionType::Length => {
856                        Ok(v.len())
857                    }
858                    adana_script_core::BuiltInFunctionType::Println => {
859                        #[cfg(not(target_arch = "wasm32"))]
860                        {
861                            println!("{v}");
862                            Ok(Primitive::Unit)
863                        }
864                        #[cfg(target_arch = "wasm32")]
865                        {
866                            web_sys::console::log_1(
867                                &wasm_bindgen::JsValue::from_str(&format!(
868                                    "{v}\n"
869                                )),
870                            );
871                            Ok(Primitive::Unit)
872                        }
873                    }
874                    adana_script_core::BuiltInFunctionType::Print => {
875                        #[cfg(not(target_arch = "wasm32"))]
876                        {
877                            print!("{v}");
878                            Ok(Primitive::Unit)
879                        }
880                        #[cfg(target_arch = "wasm32")]
881                        {
882                            web_sys::console::log_1(
883                                &wasm_bindgen::JsValue::from_str(&format!(
884                                    "{v}"
885                                )),
886                            );
887                            Ok(Primitive::Unit)
888                        }
889                    }
890                    adana_script_core::BuiltInFunctionType::Require => {
891                        match v {
892                            Primitive::String(file_path) => {
893                                let native_lib = require_dynamic_lib(
894                                    file_path.as_str(),
895                                    shared_lib,
896                                )?;
897                                Ok(Primitive::NativeLibrary(Arc::new(
898                                    native_lib,
899                                )))
900                            }
901                            _ => Ok(Primitive::Error(
902                                "wrong include call".to_string(),
903                            )),
904                        }
905                    }
906                    adana_script_core::BuiltInFunctionType::Include => {
907                        match v {
908                            Primitive::String(file_path) => {
909                                let curr_path = std::env::current_dir()
910                                    .context(
911                                        "no current dir! wasn't expected",
912                                    )?;
913                                let temp_path = Path::new(&file_path);
914                                if temp_path.is_absolute() || temp_path.exists()
915                                {
916                                    let parent = temp_path
917                                        .parent()
918                                        .context("parent doesn't exist")?;
919
920                                    std::env::set_current_dir(PathBuf::from(
921                                        &parent,
922                                    ))?;
923                                }
924
925                                let res = temp_path
926                                    .file_name()
927                                    .context("file name not found")
928                                    .and_then(|p| {
929                                        read_to_string(p)
930                                            .map_err(anyhow::Error::new)
931                                    })
932                                    .and_then(move |file| {
933                                        compute(&file, ctx, shared_lib)
934                                    });
935                                std::env::set_current_dir(curr_path)?; // todo this might be quiet fragile
936                                res
937                            }
938                            _ => Ok(Primitive::Error(
939                                "wrong include call".to_string(),
940                            )),
941                        }
942                    }
943
944                    adana_script_core::BuiltInFunctionType::TypeOf => {
945                        Ok(v.type_of())
946                    }
947                    adana_script_core::BuiltInFunctionType::Floor => {
948                        Ok(v.floor())
949                    }
950                    adana_script_core::BuiltInFunctionType::Ceil => {
951                        Ok(v.ceil())
952                    }
953                    adana_script_core::BuiltInFunctionType::Round => match v {
954                        Primitive::Array(arr) => {
955                            if arr.is_empty() {
956                                return Ok(Primitive::Error(format!(
957                                    "Invalid argument len {}",
958                                    arr.len()
959                                )));
960                            }
961                            let s = &arr[0];
962                            let decimals = if arr.len() == 2 {
963                                &arr[1]
964                            } else {
965                                &Primitive::Int(2)
966                            };
967                            Ok(s.round(decimals))
968                        }
969                        _ => Ok(Primitive::Error(
970                            "invalid call to builtin fn match".to_string(),
971                        )),
972                    },
973                    adana_script_core::BuiltInFunctionType::ToUpper => {
974                        Ok(v.to_upper())
975                    }
976                    adana_script_core::BuiltInFunctionType::ToLower => {
977                        Ok(v.to_lower())
978                    }
979                    adana_script_core::BuiltInFunctionType::Capitalize => {
980                        Ok(v.capitalize())
981                    }
982
983                    adana_script_core::BuiltInFunctionType::Replace => {
984                        match v {
985                            Primitive::Array(arr) => {
986                                let [s, r, p] = &arr[0..=2] else {
987                                    return Ok(Primitive::Error(format!(
988                                        "Invalid argument len {}",
989                                        arr.len()
990                                    )));
991                                };
992                                Ok(s.replace(r, p))
993                            }
994                            _ => Ok(Primitive::Error(
995                                "invalid call to builtin fn replace"
996                                    .to_string(),
997                            )),
998                        }
999                    }
1000                    adana_script_core::BuiltInFunctionType::ReplaceAll => {
1001                        match v {
1002                            Primitive::Array(arr) => {
1003                                let [s, r, p] = &arr[0..=2] else {
1004                                    return Ok(Primitive::Error(format!(
1005                                        "Invalid argument len {}",
1006                                        arr.len()
1007                                    )));
1008                                };
1009                                Ok(s.replace_all(r, p))
1010                            }
1011                            _ => Ok(Primitive::Error(
1012                                "invalid call to builtin fn replace_all"
1013                                    .to_string(),
1014                            )),
1015                        }
1016                    }
1017                    adana_script_core::BuiltInFunctionType::Match => match v {
1018                        Primitive::Array(arr) => {
1019                            let [s, r] = &arr[0..=1] else {
1020                                return Ok(Primitive::Error(format!(
1021                                    "Invalid argument len {}",
1022                                    arr.len()
1023                                )));
1024                            };
1025                            Ok(s.match_regex(r))
1026                        }
1027                        _ => Ok(Primitive::Error(
1028                            "invalid call to builtin fn match".to_string(),
1029                        )),
1030                    },
1031                    adana_script_core::BuiltInFunctionType::IsMatch => {
1032                        match v {
1033                            Primitive::Array(arr) => {
1034                                let [s, r] = &arr[0..=1] else {
1035                                    return Ok(Primitive::Error(format!(
1036                                        "Invalid argument len {}",
1037                                        arr.len()
1038                                    )));
1039                                };
1040                                Ok(s.is_match(r))
1041                            }
1042                            _ => Ok(Primitive::Error(
1043                                "invalid call to builtin fn is_match"
1044                                    .to_string(),
1045                            )),
1046                        }
1047                    }
1048                    adana_script_core::BuiltInFunctionType::IsError => {
1049                        Ok(Primitive::Bool(v.type_of_str() == TYPE_ERROR))
1050                    }
1051                    adana_script_core::BuiltInFunctionType::IsU8 => {
1052                        Ok(Primitive::Bool(v.type_of_str() == TYPE_U8))
1053                    }
1054                    adana_script_core::BuiltInFunctionType::IsI8 => {
1055                        Ok(Primitive::Bool(v.type_of_str() == TYPE_I8))
1056                    }
1057                    adana_script_core::BuiltInFunctionType::IsStruct => {
1058                        Ok(Primitive::Bool(v.type_of_str() == TYPE_STRUCT))
1059                    }
1060                    adana_script_core::BuiltInFunctionType::IsBool => {
1061                        Ok(Primitive::Bool(v.type_of_str() == TYPE_BOOL))
1062                    }
1063                    adana_script_core::BuiltInFunctionType::IsInt => {
1064                        Ok(Primitive::Bool(v.type_of_str() == TYPE_INT))
1065                    }
1066                    adana_script_core::BuiltInFunctionType::IsDouble => {
1067                        Ok(Primitive::Bool(v.type_of_str() == TYPE_DOUBLE))
1068                    }
1069                    adana_script_core::BuiltInFunctionType::IsFunction => {
1070                        Ok(Primitive::Bool(v.type_of_str() == TYPE_FUNCTION))
1071                    }
1072                    adana_script_core::BuiltInFunctionType::IsArray => {
1073                        Ok(Primitive::Bool(v.type_of_str() == TYPE_ARRAY))
1074                    }
1075                    adana_script_core::BuiltInFunctionType::MakeError => {
1076                        Ok(Primitive::Error(v.to_string()))
1077                    }
1078                    adana_script_core::BuiltInFunctionType::Jsonify => {
1079                        Ok(Primitive::String(v.to_json()?))
1080                    }
1081                    adana_script_core::BuiltInFunctionType::ParseJson => {
1082                        Primitive::from_json(&v.to_string())
1083                    }
1084                }
1085            }
1086
1087            TreeNodeValue::FunctionCall(Value::FunctionCall {
1088                parameters,
1089                function,
1090            }) => {
1091                let function = compute_instructions(
1092                    vec![*function.clone()],
1093                    ctx,
1094                    shared_lib,
1095                )?;
1096
1097                handle_function_call(function, parameters, ctx, shared_lib)
1098            }
1099            TreeNodeValue::FunctionCall(v) => Ok(Primitive::Error(format!(
1100                "unexpected function call declaration: {v:?}"
1101            ))),
1102            TreeNodeValue::Function(v) => Ok(Primitive::Error(format!(
1103                "unexpected function declaration: {v:?}"
1104            ))),
1105            TreeNodeValue::Break => Ok(Primitive::NoReturn),
1106            TreeNodeValue::Null => Ok(Primitive::Null),
1107            TreeNodeValue::Drop(variables) => {
1108                pub use Value::Variable;
1109                for var in variables {
1110                    match var {
1111                        Variable(v) => {
1112                            ctx.remove(v);
1113                        }
1114                        Value::MultiDepthAccess { root, next_keys } => {
1115                            fold_multidepth(
1116                                root,
1117                                next_keys,
1118                                Primitive::Unit,
1119                                ctx,
1120                                shared_lib,
1121                            )?;
1122                        }
1123                        _ => {
1124                            return Err(Error::msg(format!(
1125                                "ERROR DROP: not a valid variable {var:?}"
1126                            )))
1127                        }
1128                    }
1129                }
1130                Ok(Primitive::Unit)
1131            }
1132            TreeNodeValue::EarlyReturn(v) => {
1133                if let Some(v) = v {
1134                    let p =
1135                        compute_instructions(vec![v.clone()], ctx, shared_lib)?;
1136                    Ok(Primitive::EarlyReturn(Box::new(p)))
1137                } else {
1138                    Ok(Primitive::EarlyReturn(Box::new(Primitive::Null)))
1139                }
1140            }
1141        }
1142    } else {
1143        Ok(Primitive::Unit)
1144    }
1145}
1146
1147fn value_to_tree(
1148    value: Value,
1149    ctx: &mut BTreeMap<String, RefPrimitive>,
1150) -> anyhow::Result<Tree<TreeNodeValue>> {
1151    let mut tree: Tree<TreeNodeValue> = Tree::new();
1152    to_ast(ctx, value, &mut tree, &None)?;
1153
1154    anyhow::ensure!(tree.root_id().is_some(), "Invalid expression!");
1155
1156    if cfg!(test) {
1157        let mut tree_fmt = String::new();
1158        tree.write_formatted(&mut tree_fmt)?;
1159        println!("===================DEBUG TREE==================");
1160        print!("{tree_fmt}");
1161        println!("===================DEBUG TREE==================");
1162    }
1163    Ok(tree)
1164}
1165
1166fn compute_lazy(
1167    instruction: Value,
1168    ctx: &mut BTreeMap<String, RefPrimitive>,
1169    shared_lib: impl AsRef<Path> + Copy,
1170) -> anyhow::Result<Primitive> {
1171    let tree = value_to_tree(instruction, ctx)?;
1172
1173    let root = tree.root();
1174
1175    compute_recur(root, ctx, shared_lib)
1176}
1177fn compute_instructions(
1178    instructions: Vec<Value>,
1179    ctx: &mut BTreeMap<String, RefPrimitive>,
1180    shared_lib: impl AsRef<Path> + Copy,
1181) -> anyhow::Result<Primitive> {
1182    let mut result = Primitive::Unit;
1183
1184    for instruction in instructions {
1185        match instruction {
1186            v @ Value::EarlyReturn(_) => {
1187                let res = compute_lazy(v, ctx, shared_lib)?;
1188                match res {
1189                    Primitive::EarlyReturn(r) => {
1190                        return Ok(*r);
1191                    }
1192                    _ => {
1193                        return Err(anyhow::Error::msg("bug! fixme"));
1194                    }
1195                }
1196            }
1197            Value::IfExpr { cond, exprs, else_expr } => {
1198                let cond = compute_lazy(*cond, ctx, shared_lib)?;
1199                if matches!(cond, Primitive::Error(_)) {
1200                    return Ok(cond);
1201                }
1202                if matches!(cond, Primitive::Bool(true)) {
1203                    let mut scoped_ctx = ctx.clone();
1204
1205                    for instruction in exprs {
1206                        match compute_lazy(
1207                            instruction.clone(),
1208                            &mut scoped_ctx,
1209                            shared_lib,
1210                        )? {
1211                            v @ Primitive::EarlyReturn(_)
1212                            | v @ Primitive::Error(_) => return Ok(v),
1213                            p => result = p,
1214                        }
1215                    }
1216                } else if let Some(else_expr) = else_expr {
1217                    let mut scoped_ctx = ctx.clone();
1218
1219                    for instruction in else_expr {
1220                        match compute_lazy(
1221                            instruction.clone(),
1222                            &mut scoped_ctx,
1223                            shared_lib,
1224                        )? {
1225                            v @ Primitive::EarlyReturn(_)
1226                            | v @ Primitive::Error(_) => return Ok(v),
1227                            p => result = p,
1228                        }
1229                    }
1230                }
1231            }
1232            Value::WhileExpr { cond, exprs } => {
1233                let mut scoped_ctx = ctx.clone();
1234
1235                'while_loop: while matches!(
1236                    compute_lazy(*cond.clone(), &mut scoped_ctx, shared_lib,)?,
1237                    Primitive::Bool(true)
1238                ) {
1239                    for instruction in &exprs {
1240                        match compute_lazy(
1241                            instruction.clone(),
1242                            &mut scoped_ctx,
1243                            shared_lib,
1244                        )? {
1245                            Primitive::NoReturn => break 'while_loop,
1246                            v @ Primitive::EarlyReturn(_)
1247                            | v @ Primitive::Error(_) => return Ok(v),
1248                            p => result = p,
1249                        }
1250                    }
1251                }
1252            }
1253            Value::ForeachExpr { var, index_var, iterator, exprs } => {
1254                let iterator = compute_lazy(*iterator, ctx, shared_lib)?;
1255
1256                let mut scoped_ctx = ctx.clone();
1257                let arr = match iterator {
1258                    Primitive::Array(arr) => arr,
1259                    Primitive::Struct(s) => s
1260                        .iter()
1261                        .map(|(k, v)| {
1262                            Primitive::Struct(BTreeMap::from([
1263                                ("key".into(), Primitive::String(k.clone())),
1264                                ("value".into(), v.clone()),
1265                            ]))
1266                        })
1267                        .collect(),
1268                    Primitive::String(s) => s
1269                        .chars()
1270                        .map(|c| Primitive::String(c.to_string()))
1271                        .collect(),
1272                    _ => {
1273                        return Ok(Primitive::Error(format!(
1274                            "not an iterable {iterator:?}"
1275                        )));
1276                    }
1277                };
1278                'foreach_loop: for (i, it) in arr.into_iter().enumerate() {
1279                    if !var.starts_with('_') {
1280                        scoped_ctx.insert(var.clone(), it.ref_prim());
1281                    }
1282                    match &index_var {
1283                        Some(index_var) if !index_var.starts_with('_') => {
1284                            scoped_ctx.insert(
1285                                index_var.clone(),
1286                                Primitive::Int(i as i128).ref_prim(),
1287                            );
1288                        }
1289                        _ => (),
1290                    };
1291                    for instruction in &exprs {
1292                        match compute_lazy(
1293                            instruction.clone(),
1294                            &mut scoped_ctx,
1295                            shared_lib,
1296                        )? {
1297                            Primitive::NoReturn => break 'foreach_loop,
1298                            v @ Primitive::EarlyReturn(_)
1299                            | v @ Primitive::Error(_) => return Ok(v),
1300                            p => result = p,
1301                        }
1302                    }
1303                }
1304            }
1305            _ => {
1306                result = compute_lazy(instruction, ctx, shared_lib)?;
1307            }
1308        }
1309        if let Primitive::EarlyReturn(p) = result {
1310            return Ok(*p);
1311        }
1312        if matches!(result, Primitive::Error(_)) {
1313            return Ok(result);
1314        }
1315    }
1316
1317    Ok(result)
1318}
1319// region: exposed api
1320pub fn compute(
1321    s: &str,
1322    ctx: &mut BTreeMap<String, RefPrimitive>,
1323    shared_lib: impl AsRef<Path> + Copy,
1324) -> anyhow::Result<Primitive> {
1325    let (rest, instructions) = parse_instructions(s).map_err(|e| {
1326        anyhow::Error::msg(format!(
1327            "PARSER ERROR: could not parse instructions. \n{e:?} => {e}",
1328        ))
1329    })?;
1330
1331    if cfg!(test) {
1332        dbg!(rest);
1333        dbg!(&instructions);
1334    }
1335
1336    anyhow::ensure!(
1337        rest.trim().is_empty(),
1338        format!("PARSING ERROR: rest is not empty! {instructions:?} => {rest}",)
1339    );
1340
1341    compute_instructions(instructions, ctx, shared_lib)
1342}