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
25fn scoped_ctx(
27 ctx: &mut BTreeMap<String, RefPrimitive>,
28) -> anyhow::Result<BTreeMap<String, RefPrimitive>> {
29 let mut scope_ctx = BTreeMap::new();
30
31 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 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 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!(¶meters);
150 }
151 Ok(Primitive::Error("debug".into()))
152 }
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!(¶meters);
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 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::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)?; 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}
1319pub 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}