Skip to main content

datex_core/runtime/execution/
mod.rs

1use crate::{
2    global::protocol_structures::instructions::*,
3    libs::core::{CoreLibPointerId, get_core_lib_value},
4    runtime::{
5        RuntimeInternal,
6        execution::{
7            context::{ExecutionMode, RemoteExecutionContext},
8            execution_loop::interrupts::{
9                ExternalExecutionInterrupt, InterruptResult,
10            },
11        },
12    },
13    traits::apply::Apply,
14    values::value_container::ValueContainer,
15};
16
17use crate::{
18    prelude::*,
19    shared_values::{
20        pointer::PointerReferenceMutability,
21        pointer_address::{PointerAddress, ReferencedPointerAddress},
22    },
23};
24use core::{result::Result, unreachable};
25pub use errors::*;
26pub use execution_input::{ExecutionInput, ExecutionOptions};
27pub use memory_dump::*;
28
29pub mod context;
30mod errors;
31mod execution_input;
32pub mod execution_loop;
33pub mod macros;
34mod memory_dump;
35
36#[cfg(all(test, feature = "std"))]
37mod test_remote_execution;
38
39pub fn execute_dxb_sync(
40    input: ExecutionInput,
41) -> Result<Option<ValueContainer>, ExecutionError> {
42    let runtime_internal = input.runtime.clone();
43    let (interrupt_provider, execution_loop) = input.execution_loop();
44
45    for output in execution_loop {
46        match output? {
47            ExternalExecutionInterrupt::Result(result) => return Ok(result),
48            ExternalExecutionInterrupt::GetReferenceToRemotePointer(
49                address,
50                mutability,
51            ) => interrupt_provider.provide_result(
52                InterruptResult::ResolvedValue(get_remote_pointer_value(
53                    &runtime_internal,
54                    address,
55                    mutability,
56                )?),
57            ),
58            ExternalExecutionInterrupt::GetReferenceToLocalPointer(address) => {
59                // TODO #401: in the future, local pointer addresses should be relative to the block sender, not the local runtime
60                interrupt_provider.provide_result(
61                    InterruptResult::ResolvedValue(get_local_pointer_value(
62                        &runtime_internal,
63                        address,
64                    )?),
65                );
66            }
67            ExternalExecutionInterrupt::GetReferenceInternalPointer(
68                address,
69            ) => {
70                interrupt_provider.provide_result(
71                    InterruptResult::ResolvedValue(Some(
72                        get_internal_pointer_value(&runtime_internal, address)?,
73                    )),
74                );
75            }
76            ExternalExecutionInterrupt::Apply(callee, args) => {
77                let res = handle_apply(&callee, &args)?;
78                interrupt_provider
79                    .provide_result(InterruptResult::ResolvedValue(res));
80            }
81            _ => return Err(ExecutionError::RequiresAsyncExecution),
82        }
83    }
84
85    Err(ExecutionError::RequiresAsyncExecution)
86}
87
88pub async fn execute_dxb(
89    input: ExecutionInput<'_>,
90) -> Result<Option<ValueContainer>, ExecutionError> {
91    let runtime_internal = input.runtime.clone();
92    let (interrupt_provider, execution_loop) = input.execution_loop();
93
94    for output in execution_loop {
95        match output? {
96            ExternalExecutionInterrupt::Result(result) => return Ok(result),
97            ExternalExecutionInterrupt::GetReferenceToRemotePointer(
98                address,
99                mutability,
100            ) => {
101                interrupt_provider.provide_result(
102                    InterruptResult::ResolvedValue(get_remote_pointer_value(
103                        &runtime_internal,
104                        address,
105                        mutability,
106                    )?),
107                );
108            }
109            ExternalExecutionInterrupt::GetReferenceToLocalPointer(address) => {
110                // TODO #402: in the future, local pointer addresses should be relative to the block sender, not the local runtime
111                interrupt_provider.provide_result(
112                    InterruptResult::ResolvedValue(get_local_pointer_value(
113                        &runtime_internal,
114                        address,
115                    )?),
116                );
117            }
118            ExternalExecutionInterrupt::GetReferenceInternalPointer(
119                address,
120            ) => {
121                interrupt_provider.provide_result(
122                    InterruptResult::ResolvedValue(Some(
123                        get_internal_pointer_value(&runtime_internal, address)?,
124                    )),
125                );
126            }
127            ExternalExecutionInterrupt::RemoteExecution(receivers, body) => {
128                // assert that receivers is a single endpoint
129                // TODO #230: support advanced receivers
130                let receiver_endpoint =
131                    receivers.to_value().borrow().cast_to_endpoint().unwrap();
132                let mut remote_execution_context = RemoteExecutionContext::new(
133                    receiver_endpoint,
134                    ExecutionMode::Static,
135                );
136                let res = RuntimeInternal::execute_remote(
137                    runtime_internal.clone(),
138                    &mut remote_execution_context,
139                    body,
140                )
141                .await?;
142                interrupt_provider
143                    .provide_result(InterruptResult::ResolvedValue(res));
144            }
145            ExternalExecutionInterrupt::Apply(callee, args) => {
146                let res = handle_apply(&callee, &args)?;
147                interrupt_provider
148                    .provide_result(InterruptResult::ResolvedValue(res));
149            }
150        }
151    }
152
153    unreachable!("Execution loop should always return a result");
154}
155
156fn handle_apply(
157    callee: &ValueContainer,
158    args: &[ValueContainer],
159) -> Result<Option<ValueContainer>, ExecutionError> {
160    // callee is guaranteed to be Some here
161    // apply_single if one arg, apply otherwise
162    Ok(if args.len() == 1 {
163        callee.apply_single(&args[0])?
164    } else {
165        callee.apply(args)?
166    })
167}
168
169fn get_remote_pointer_value(
170    runtime_internal: &Rc<RuntimeInternal>,
171    address: RawRemotePointerAddress,
172    _mutability: PointerReferenceMutability,
173) -> Result<Option<ValueContainer>, ExecutionError> {
174    let memory = runtime_internal.memory.borrow();
175    let resolved_address =
176        memory.get_pointer_address_from_raw_full_address(address);
177    // convert slot to InternalSlot enum
178    // TODO: resolve from remote, handle mutability
179    Ok(memory
180        .get_reference(&resolved_address)
181        .map(|r| ValueContainer::Shared(r.clone())))
182}
183
184fn get_internal_pointer_value(
185    runtime_internal: &Rc<RuntimeInternal>,
186    address: RawInternalPointerAddress,
187) -> Result<ValueContainer, ExecutionError> {
188    // first try to get from memory
189    if let Ok(core_lib_id) =
190        get_internal_pointer_value_from_memory(runtime_internal, &address)
191    {
192        return Ok(core_lib_id);
193    }
194
195    let core_lib_id = CoreLibPointerId::try_from(&PointerAddress::Referenced(
196        ReferencedPointerAddress::Internal(address.id),
197    ));
198    core_lib_id
199        .map_err(|_| ExecutionError::ReferenceNotFound)
200        .map(|id| {
201            get_core_lib_value(id).ok_or(ExecutionError::ReferenceNotFound)
202        })?
203}
204
205fn get_internal_pointer_value_from_memory(
206    runtime_internal: &Rc<RuntimeInternal>,
207    address: &RawInternalPointerAddress,
208) -> Result<ValueContainer, ExecutionError> {
209    let pointer_address = PointerAddress::Referenced(
210        ReferencedPointerAddress::Internal(address.id),
211    );
212    let memory = runtime_internal.memory.borrow();
213    if let Some(reference) = memory.get_reference(&pointer_address) {
214        Ok(ValueContainer::Shared(reference.clone()))
215    } else {
216        Err(ExecutionError::ReferenceNotFound)
217    }
218}
219
220fn get_local_pointer_value(
221    runtime_internal: &Rc<RuntimeInternal>,
222    address: RawLocalPointerAddress,
223) -> Result<Option<ValueContainer>, ExecutionError> {
224    // convert slot to InternalSlot enum
225    Ok(runtime_internal
226        .memory
227        .borrow()
228        .get_reference(&PointerAddress::owned(address.id))
229        .map(|r| ValueContainer::Shared(r.clone())))
230}
231
232#[cfg(test)]
233#[cfg(feature = "compiler")]
234mod tests {
235    use super::*;
236    use crate::{
237        assert_structural_eq, assert_value_eq,
238        compiler::{CompileOptions, compile_script, scope::CompilationScope},
239        datex_list,
240        global::instruction_codes::InstructionCode,
241        libs::core::get_core_lib_type_reference,
242        runtime::{
243            RuntimeConfig, RuntimeRunner,
244            execution::{
245                context::{ExecutionContext, LocalExecutionContext},
246                execution_input::ExecutionOptions,
247            },
248        },
249        shared_values::shared_container::{
250            SharedContainer, SharedContainerMutability,
251        },
252        traits::{structural_eq::StructuralEq, value_eq::ValueEq},
253        values::{
254            core_value::CoreValue,
255            core_values::{
256                decimal::Decimal,
257                integer::{Integer, typed_integer::TypedInteger},
258                list::List,
259                map::Map,
260            },
261        },
262    };
263    use binrw::meta::EndianKind::Runtime;
264    use core::assert_matches;
265    use log::{debug, info};
266
267    fn execute_datex_script_debug(
268        datex_script: &str,
269    ) -> Option<ValueContainer> {
270        let (dxb, _) =
271            compile_script(datex_script, CompileOptions::default()).unwrap();
272        let context = ExecutionInput::new(
273            &dxb,
274            ExecutionOptions { verbose: true },
275            Rc::new(RuntimeInternal::stub()),
276        );
277        execute_dxb_sync(context).unwrap_or_else(|err| {
278            core::panic!("Execution failed: {err}");
279        })
280    }
281
282    fn execute_datex_script_debug_unbounded(
283        datex_script_parts: impl Iterator<Item = &'static str>,
284    ) -> impl Iterator<Item = Result<Option<ValueContainer>, ExecutionError>>
285    {
286        gen move {
287            let datex_script_parts = datex_script_parts.collect::<Vec<_>>();
288            let mut execution_context =
289                ExecutionContext::Local(LocalExecutionContext::new(
290                    ExecutionMode::unbounded(),
291                    Rc::new(RuntimeInternal::stub()),
292                ));
293            let mut compilation_scope =
294                CompilationScope::new(ExecutionMode::unbounded());
295
296            let len = datex_script_parts.len();
297            for (index, script_part) in
298                datex_script_parts.into_iter().enumerate()
299            {
300                // if last part, compile and return static value if possible
301                if index == len - 1 {
302                    compilation_scope.mark_as_last_execution();
303                }
304
305                let (dxb, new_compilation_scope) = compile_script(
306                    script_part,
307                    CompileOptions::new_with_scope(compilation_scope),
308                )
309                .unwrap();
310                compilation_scope = new_compilation_scope;
311                yield execution_context.execute_dxb_sync(&dxb)
312            }
313        }
314    }
315
316    fn assert_unbounded_input_matches_output(
317        input: Vec<&'static str>,
318        expected_output: Vec<Option<ValueContainer>>,
319    ) {
320        let input = input.into_iter();
321        let expected_output = expected_output.into_iter();
322        for (result, expected) in
323            execute_datex_script_debug_unbounded(input.into_iter())
324                .zip(expected_output.into_iter())
325        {
326            let result = result.unwrap();
327            assert_eq!(result, expected);
328        }
329    }
330
331    fn execute_datex_script_debug_with_error(
332        datex_script: &str,
333    ) -> Result<Option<ValueContainer>, ExecutionError> {
334        let (dxb, _) =
335            compile_script(datex_script, CompileOptions::default()).unwrap();
336        let context = ExecutionInput::new(
337            &dxb,
338            ExecutionOptions { verbose: true },
339            Rc::new(RuntimeInternal::stub()),
340        );
341        execute_dxb_sync(context)
342    }
343
344    fn execute_datex_script_debug_with_result(
345        datex_script: &str,
346    ) -> ValueContainer {
347        execute_datex_script_debug(datex_script).unwrap()
348    }
349
350    fn execute_dxb_debug(
351        dxb_body: &[u8],
352    ) -> Result<Option<ValueContainer>, ExecutionError> {
353        let context = ExecutionInput::new(
354            dxb_body,
355            ExecutionOptions { verbose: true },
356            Rc::new(RuntimeInternal::stub()),
357        );
358        execute_dxb_sync(context)
359    }
360
361    async fn execute_datex_script_with_runtime(
362        config: RuntimeConfig,
363        datex_script: &str,
364    ) -> Result<Option<ValueContainer>, ExecutionError> {
365        RuntimeRunner::new(config)
366            .run(async |runtime| {
367                let (dxb, _) =
368                    compile_script(datex_script, CompileOptions::default())
369                        .unwrap();
370                let context = ExecutionInput::new(
371                    &dxb,
372                    ExecutionOptions { verbose: true },
373                    runtime.internal,
374                );
375                execute_dxb(context).await
376            })
377            .await
378    }
379
380    #[test]
381    fn empty_script() {
382        assert_eq!(execute_datex_script_debug(""), None);
383    }
384
385    #[test]
386    fn empty_script_semicolon() {
387        assert_eq!(execute_datex_script_debug(";;;"), None);
388    }
389
390    #[test]
391    fn single_value() {
392        assert_eq!(
393            execute_datex_script_debug_with_result("42"),
394            Integer::from(42i8).into()
395        );
396    }
397
398    #[test]
399    fn single_value_semicolon() {
400        assert_eq!(execute_datex_script_debug("42;"), None)
401    }
402
403    #[test]
404    fn is() {
405        let result = execute_datex_script_debug_with_result("1 is 1");
406        assert_eq!(result, false.into());
407        assert_structural_eq!(result, ValueContainer::from(false));
408    }
409
410    #[test]
411    fn equality() {
412        let result = execute_datex_script_debug_with_result("1 == 1");
413        assert_eq!(result, true.into());
414        assert_structural_eq!(result, ValueContainer::from(true));
415
416        let result = execute_datex_script_debug_with_result("1 == 2");
417        assert_eq!(result, false.into());
418        assert_structural_eq!(result, ValueContainer::from(false));
419
420        let result = execute_datex_script_debug_with_result("1 != 2");
421        assert_eq!(result, true.into());
422        assert_structural_eq!(result, ValueContainer::from(true));
423
424        let result = execute_datex_script_debug_with_result("1 != 1");
425        assert_eq!(result, false.into());
426        assert_structural_eq!(result, ValueContainer::from(false));
427        let result = execute_datex_script_debug_with_result("1 === 1");
428        assert_eq!(result, true.into());
429
430        assert_structural_eq!(result, ValueContainer::from(true));
431        let result = execute_datex_script_debug_with_result("1 !== 2");
432        assert_eq!(result, true.into());
433        assert_structural_eq!(result, ValueContainer::from(true));
434
435        let result = execute_datex_script_debug_with_result("1 !== 1");
436        assert_eq!(result, false.into());
437        assert_structural_eq!(result, ValueContainer::from(false));
438    }
439
440    #[test]
441    fn single_value_scope() {
442        let result = execute_datex_script_debug_with_result("(42)");
443        assert_eq!(result, Integer::from(42i8).into());
444        assert_structural_eq!(result, ValueContainer::from(42_u128));
445    }
446
447    #[test]
448    fn add() {
449        let result = execute_datex_script_debug_with_result("1 + 2");
450        assert_eq!(result, Integer::from(3i8).into());
451        assert_structural_eq!(result, ValueContainer::from(3i8));
452    }
453
454    #[test]
455    fn nested_scope() {
456        let result = execute_datex_script_debug_with_result("1 + (2 + 3)");
457        assert_eq!(result, Integer::from(6i8).into());
458    }
459
460    #[test]
461    fn empty_list() {
462        let result = execute_datex_script_debug_with_result("[]");
463        let list: List = result.to_value().borrow().cast_to_list().unwrap();
464        assert_eq!(list.len(), 0);
465        assert_eq!(result, Vec::<ValueContainer>::new().into());
466        assert_eq!(result, ValueContainer::from(Vec::<ValueContainer>::new()));
467    }
468
469    #[test]
470    fn list() {
471        let result = execute_datex_script_debug_with_result("[1, 2, 3]");
472        let list: List = result.to_value().borrow().cast_to_list().unwrap();
473        let expected = datex_list![
474            Integer::from(1i8),
475            Integer::from(2i8),
476            Integer::from(3i8)
477        ];
478        assert_eq!(list.len(), 3);
479        assert_eq!(result, expected.into());
480        assert_ne!(result, ValueContainer::from(vec![1, 2, 3]));
481        assert_structural_eq!(result, ValueContainer::from(vec![1, 2, 3]));
482    }
483
484    #[test]
485    fn list_with_nested_scope() {
486        let result = execute_datex_script_debug_with_result("[1, (2 + 3), 4]");
487        let expected = datex_list![
488            Integer::from(1i8),
489            Integer::from(5i8),
490            Integer::from(4i8)
491        ];
492
493        assert_eq!(result, expected.into());
494        assert_ne!(result, ValueContainer::from(vec![1_u8, 5_u8, 4_u8]));
495        assert_structural_eq!(
496            result,
497            ValueContainer::from(vec![1_u8, 5_u8, 4_u8])
498        );
499    }
500
501    #[test]
502    fn boolean() {
503        let result = execute_datex_script_debug_with_result("true");
504        assert_eq!(result, true.into());
505        assert_structural_eq!(result, ValueContainer::from(true));
506
507        let result = execute_datex_script_debug_with_result("false");
508        assert_eq!(result, false.into());
509        assert_structural_eq!(result, ValueContainer::from(false));
510    }
511
512    #[test]
513    fn decimal() {
514        let result = execute_datex_script_debug_with_result("1.5");
515        assert_eq!(result, Decimal::from_string("1.5").unwrap().into());
516        assert_structural_eq!(result, ValueContainer::from(1.5));
517    }
518
519    #[test]
520    fn decimal_and_integer() {
521        let result = execute_datex_script_debug_with_result("-2341324.0");
522        assert_eq!(result, Decimal::from_string("-2341324").unwrap().into());
523        assert!(!result.structural_eq(&ValueContainer::from(-2341324)));
524    }
525
526    #[test]
527    fn integer() {
528        let result = execute_datex_script_debug_with_result("2");
529        assert_eq!(result, Integer::from(2).into());
530        assert_ne!(result, 2_u8.into());
531        assert_structural_eq!(result, ValueContainer::from(2_i8));
532    }
533
534    #[test]
535    fn typed_integer() {
536        let result = execute_datex_script_debug_with_result("-2i16");
537        assert_eq!(result, TypedInteger::from(-2i16).into());
538        assert_structural_eq!(result, ValueContainer::from(-2_i16));
539
540        let result = execute_datex_script_debug_with_result("2i32");
541        assert_eq!(result, TypedInteger::from(2i32).into());
542        assert_structural_eq!(result, ValueContainer::from(2_i32));
543
544        let result = execute_datex_script_debug_with_result("-2i64");
545        assert_eq!(result, TypedInteger::from(-2i64).into());
546        assert_structural_eq!(result, ValueContainer::from(-2_i64));
547
548        let result = execute_datex_script_debug_with_result("2i128");
549        assert_eq!(result, TypedInteger::from(2i128).into());
550        assert_structural_eq!(result, ValueContainer::from(2_i128));
551
552        let result = execute_datex_script_debug_with_result("2u8");
553        assert_eq!(result, TypedInteger::from(2_u8).into());
554        assert_structural_eq!(result, ValueContainer::from(2_u8));
555
556        let result = execute_datex_script_debug_with_result("2u16");
557        assert_eq!(result, TypedInteger::from(2_u16).into());
558        assert_structural_eq!(result, ValueContainer::from(2_u16));
559
560        let result = execute_datex_script_debug_with_result("2u32");
561        assert_eq!(result, TypedInteger::from(2_u32).into());
562        assert_structural_eq!(result, ValueContainer::from(2_u32));
563
564        let result = execute_datex_script_debug_with_result("2u64");
565        assert_eq!(result, TypedInteger::from(2_u64).into());
566        assert_structural_eq!(result, ValueContainer::from(2_u64));
567
568        let result = execute_datex_script_debug_with_result("2u128");
569        assert_eq!(result, TypedInteger::from(2_u128).into());
570        assert_structural_eq!(result, ValueContainer::from(2_u128));
571
572        let result = execute_datex_script_debug_with_result("2ibig");
573        assert_eq!(result, TypedInteger::IBig(Integer::from(2)).into());
574        assert_structural_eq!(result, ValueContainer::from(2));
575    }
576
577    #[test]
578    fn null() {
579        let result = execute_datex_script_debug_with_result("null");
580        assert_eq!(result, ValueContainer::from(CoreValue::Null));
581        assert_eq!(result, CoreValue::Null.into());
582        assert_structural_eq!(result, ValueContainer::from(CoreValue::Null));
583    }
584
585    #[test]
586    fn map() {
587        let result =
588            execute_datex_script_debug_with_result("{x: 1, y: 2, z: 42}");
589        let map: CoreValue = result.clone().to_value().borrow().clone().inner;
590        let map: Map = map.try_into().unwrap();
591
592        // form and size
593        assert_eq!(map.to_string(), "{\"x\": 1, \"y\": 2, \"z\": 42}");
594        assert_eq!(map.size(), 3);
595
596        info!("Map: {:?}", map);
597
598        // access by key
599        assert_eq!(map.get("x"), Ok(&Integer::from(1).into()));
600        assert_eq!(map.get("y"), Ok(&Integer::from(2).into()));
601        assert_eq!(map.get("z"), Ok(&Integer::from(42).into()));
602
603        // structural equality checks
604        let expected_se: Map = Map::from(vec![
605            ("x".to_string(), 1.into()),
606            ("y".to_string(), 2.into()),
607            ("z".to_string(), 42.into()),
608        ]);
609        assert_structural_eq!(map, expected_se);
610
611        // strict equality checks
612        let expected_strict: Map = Map::from(vec![
613            ("x".to_string(), Integer::from(1).into()),
614            ("y".to_string(), Integer::from(2).into()),
615            ("z".to_string(), Integer::from(42).into()),
616        ]);
617        debug!("Expected map: {expected_strict}");
618        debug!("Map result: {map}");
619        // FIXME #104 type information gets lost on compile
620        // assert_eq!(result, expected.into());
621    }
622
623    #[test]
624    fn empty_map() {
625        let result = execute_datex_script_debug_with_result("{}");
626        let map: CoreValue = result.clone().to_value().borrow().clone().inner;
627        let map: Map = map.try_into().unwrap();
628
629        // form and size
630        assert_eq!(map.to_string(), "{}");
631        assert_eq!(map.size(), 0);
632
633        info!("Map: {:?}", map);
634    }
635
636    #[test]
637    fn statements() {
638        let result = execute_datex_script_debug_with_result("1; 2; 3");
639        assert_eq!(result, Integer::from(3).into());
640    }
641
642    #[test]
643    fn single_terminated_statement() {
644        let result = execute_datex_script_debug("1;");
645        assert_eq!(result, None);
646    }
647
648    #[test]
649    fn const_declaration() {
650        let result = execute_datex_script_debug_with_result("const x = 42; x");
651        assert_eq!(result, Integer::from(42).into());
652    }
653
654    #[test]
655    fn const_declaration_with_addition() {
656        let result =
657            execute_datex_script_debug_with_result("const x = 1 + 2; x");
658        assert_eq!(result, Integer::from(3).into());
659    }
660
661    #[test]
662    fn unbox_shared() {
663        let result =
664            execute_datex_script_debug_with_result("const x = shared 42; *x");
665        assert_eq!(result, ValueContainer::from(Integer::from(42)));
666    }
667
668    #[test]
669    fn shared_assignment() {
670        let result = execute_datex_script_debug_with_result(
671            "const x = 'mut shared 42; x",
672        );
673        assert_matches!(result, ValueContainer::Shared(..));
674        assert_value_eq!(result, ValueContainer::from(Integer::from(42)));
675    }
676
677    #[test]
678    fn shared_value_add_assignment() {
679        let result = execute_datex_script_debug_with_result(
680            "var x = shared mut 42; *x += 1; x",
681        );
682
683        assert_value_eq!(result, ValueContainer::from(Integer::from(43)));
684        assert_matches!(result, ValueContainer::Shared(..));
685        if let ValueContainer::Shared(shared) = &result {
686            assert_eq!(shared.mutability(), SharedContainerMutability::Mutable);
687        } else {
688            panic!("Expected shared value");
689        }
690    }
691
692    #[test]
693    fn shared_value_sub_assignment() {
694        let result = execute_datex_script_debug_with_result(
695            "const x = 'mut shared mut 42; *x -= 1",
696        );
697        assert_value_eq!(result, ValueContainer::from(Integer::from(41)));
698
699        let result = execute_datex_script_debug_with_result(
700            "const x = 'mut shared mut 42; *x -= 1; x",
701        );
702
703        // FIXME #414 due to addition the resulting value container of the slot
704        // is no longer a reference but a value what is incorrect.
705        // assert_matches!(result, ValueContainer::Reference(..));
706        assert_value_eq!(result, ValueContainer::from(Integer::from(41)));
707    }
708
709    #[tokio::test]
710    async fn env_slot() {
711        let res = execute_datex_script_with_runtime(
712            RuntimeConfig {
713                env: Some(HashMap::from([(
714                    "TEST_ENV_VAR".to_string(),
715                    "test_value".to_string(),
716                )])),
717                ..Default::default()
718            },
719            "#env",
720        )
721        .await
722        .unwrap();
723        assert!(res.is_some());
724        let env = res.unwrap().to_value().borrow().cast_to_map().unwrap();
725        assert_eq!(env.get("TEST_ENV_VAR"), Ok(&"test_value".into()));
726    }
727
728    #[test]
729    fn shebang() {
730        let result = execute_datex_script_debug_with_result("#!datex\n42");
731        assert_eq!(result, Integer::from(42).into());
732    }
733
734    #[test]
735    fn single_line_comment() {
736        let result =
737            execute_datex_script_debug_with_result("// this is a comment\n42");
738        assert_eq!(result, Integer::from(42).into());
739
740        let result = execute_datex_script_debug_with_result(
741            "// this is a comment\n// another comment\n42",
742        );
743        assert_eq!(result, Integer::from(42).into());
744    }
745
746    #[test]
747    fn multi_line_comment() {
748        let result = execute_datex_script_debug_with_result(
749            "/* this is a comment */\n42",
750        );
751        assert_eq!(result, Integer::from(42).into());
752
753        let result = execute_datex_script_debug_with_result(
754            "/* this is a comment\n   with multiple lines */\n42",
755        );
756        assert_eq!(result, Integer::from(42).into());
757
758        let result = execute_datex_script_debug_with_result("[1, /* 2, */ 3]");
759        let expected = datex_list![Integer::from(1), Integer::from(3)];
760        assert_eq!(result, expected.into());
761    }
762
763    #[test]
764    fn continuous_execution() {
765        assert_unbounded_input_matches_output(
766            vec!["1", "2"],
767            vec![Some(Integer::from(1).into()), Some(Integer::from(2).into())],
768        )
769    }
770
771    #[test]
772    fn continuous_execution_multiple_external_interrupts() {
773        assert_unbounded_input_matches_output(
774            vec!["1", "integer", "integer"],
775            vec![
776                Some(Integer::from(1).into()),
777                Some(ValueContainer::Shared(SharedContainer::Type(
778                    get_core_lib_type_reference(CoreLibPointerId::Integer(
779                        None,
780                    )),
781                ))),
782                Some(ValueContainer::Shared(SharedContainer::Type(
783                    get_core_lib_type_reference(CoreLibPointerId::Integer(
784                        None,
785                    )),
786                ))),
787            ],
788        )
789    }
790}