datex_core/runtime/execution/
mod.rs

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