datex_core/runtime/
execution.rs

1use super::stack::{Scope, ScopeStack};
2use crate::compiler::ast_parser::{BinaryOperator, UnaryOperator};
3use crate::compiler::compile_value;
4use crate::compiler::error::CompilerError;
5use crate::global::binary_codes::{InstructionCode, InternalSlot};
6use crate::global::protocol_structures::instructions::*;
7use crate::network::com_hub::ResponseError;
8use crate::parser::body;
9use crate::parser::body::DXBParserError;
10use crate::utils::buffers::append_u32;
11use crate::values::core_value::CoreValue;
12use crate::values::core_values::array::Array;
13use crate::values::core_values::decimal::decimal::Decimal;
14use crate::values::core_values::decimal::typed_decimal::TypedDecimal;
15use crate::values::core_values::integer::integer::Integer;
16use crate::values::core_values::object::Object;
17use crate::values::core_values::tuple::Tuple;
18use crate::values::reference::Reference;
19use crate::values::traits::identity::Identity;
20use crate::values::traits::structural_eq::StructuralEq;
21use crate::values::traits::value_eq::ValueEq;
22use crate::values::value::Value;
23use crate::values::value_container::{ValueContainer, ValueError};
24use std::cell::RefCell;
25use std::collections::HashMap;
26use std::fmt::Display;
27use std::rc::Rc;
28use log::info;
29use num_enum::TryFromPrimitive;
30use crate::runtime::execution_context::RemoteExecutionContext;
31use crate::runtime::RuntimeInternal;
32
33#[derive(Debug, Clone, Default)]
34pub struct ExecutionOptions {
35    pub verbose: bool,
36}
37
38#[derive(Debug, Clone)]
39pub struct ExecutionInput<'a> {
40    pub options: ExecutionOptions,
41    pub dxb_body: &'a [u8],
42    pub end_execution: bool,
43    pub context: Rc<RefCell<RuntimeExecutionContext>>,
44}
45
46// TODO #229: do we want a DatexProgram input enum like this for execution?
47// #[derive(Debug, Clone)]
48// pub enum DatexProgram {
49//     Dxb(Vec<u8>),
50//     Script(String),
51// }
52
53// impl From<Vec<u8>> for DatexProgram {
54//     fn from(dxb: Vec<u8>) -> Self {
55//         DatexProgram::Dxb(dxb)
56//     }
57// }
58// impl From<String> for DatexProgram {
59//     fn from(script: String) -> Self {
60//         DatexProgram::Script(script)
61//     }
62// }
63
64impl Default for ExecutionInput<'_> {
65    fn default() -> Self {
66        Self {
67            options: ExecutionOptions::default(),
68            dxb_body: &[],
69            context: Rc::new(RefCell::new(RuntimeExecutionContext::default())),
70            end_execution: true,
71        }
72    }
73}
74
75impl<'a> ExecutionInput<'a> {
76    pub fn new_with_dxb_and_options(
77        dxb_body: &'a [u8],
78        options: ExecutionOptions,
79    ) -> Self {
80        Self {
81            options,
82            dxb_body,
83            context: Rc::new(RefCell::new(RuntimeExecutionContext::default())),
84            end_execution: true,
85        }
86    }
87}
88
89#[derive(Debug, Clone, Default)]
90pub struct RuntimeExecutionContext {
91    index: usize,
92    scope_stack: ScopeStack,
93    slots: RefCell<HashMap<u32, Option<ValueContainer>>>,
94    // if set to true, the execution loop will pop the current scope before continuing with the next instruction
95    pop_next_scope: bool,
96    runtime_internal: Option<Rc<RuntimeInternal>>,
97}
98
99impl RuntimeExecutionContext {
100
101    pub fn new(runtime_internal: Rc<RuntimeInternal>) -> Self {
102        Self {
103            runtime_internal: Some(runtime_internal),
104            ..Default::default()
105        }
106    }
107
108    pub fn reset_index(&mut self) {
109        self.index = 0;
110    }
111
112    pub fn runtime_internal(&self) -> &Option<Rc<RuntimeInternal>> {
113        &self.runtime_internal
114    }
115    
116    pub fn set_runtime_internal(
117        &mut self,
118        runtime_internal: Rc<RuntimeInternal>,
119    ) {
120        self.runtime_internal = Some(runtime_internal);
121    }
122
123    /// Allocates a new slot with the given slot address.
124    fn allocate_slot(&self, address: u32, value: Option<ValueContainer>) {
125        self.slots.borrow_mut().insert(address, value);
126    }
127
128    /// Drops a slot by its address, returning the value if it existed.
129    /// If the slot is not allocated, it returns an error.
130    fn drop_slot(
131        &self,
132        address: u32,
133    ) -> Result<Option<ValueContainer>, ExecutionError> {
134        self.slots
135            .borrow_mut()
136            .remove(&address)
137            .ok_or(())
138            .map_err(|_| ExecutionError::SlotNotAllocated(address))
139    }
140
141    /// Sets the value of a slot, returning the previous value if it existed.
142    /// If the slot is not allocated, it returns an error.
143    fn set_slot_value(
144        &self,
145        address: u32,
146        value: ValueContainer,
147    ) -> Result<Option<ValueContainer>, ExecutionError> {
148        self.slots
149            .borrow_mut()
150            .insert(address, Some(value))
151            .ok_or(())
152            .map_err(|_| ExecutionError::SlotNotAllocated(address))
153    }
154
155    /// Retrieves the value of a slot by its address.
156    /// If the slot is not allocated, it returns an error.
157    fn get_slot_value(
158        &self,
159        address: u32,
160    ) -> Result<Option<ValueContainer>, ExecutionError> {
161        self.slots
162            .borrow_mut()
163            .get(&address)
164            .cloned()
165            .ok_or(())
166            .map_err(|_| ExecutionError::SlotNotAllocated(address))
167    }
168}
169
170pub fn execute_dxb_sync(
171    input: ExecutionInput,
172) -> Result<Option<ValueContainer>, ExecutionError> {
173    let interrupt_provider = Rc::new(RefCell::new(None));
174    let runtime_internal = input.context.borrow_mut().runtime_internal().clone();
175
176    for output in execute_loop(input, interrupt_provider.clone()) {
177        match output? {
178            ExecutionStep::Return(result) => return Ok(result),
179            ExecutionStep::ResolvePointer(_pointer_id) => {
180                *interrupt_provider.borrow_mut() =
181                    Some(InterruptProvider::Result(Some(ValueContainer::from(42))));
182            }
183            ExecutionStep::GetInternalSlot(slot) => {
184                *interrupt_provider.borrow_mut() =
185                    Some(InterruptProvider::Result(get_internal_slot_value(&runtime_internal, slot)?));
186            }
187            _ => return Err(ExecutionError::RequiresAsyncExecution),
188        }
189    }
190
191    Err(ExecutionError::RequiresAsyncExecution)
192}
193
194fn get_internal_slot_value(runtime_internal: &Option<Rc<RuntimeInternal>>, slot: u32) -> Result<Option<ValueContainer>, ExecutionError> {
195    if let Some(runtime) = &runtime_internal {
196        // convert slot to InternalSlot enum
197        let slot = InternalSlot::try_from_primitive(slot).map_err(|_| {
198            ExecutionError::SlotNotAllocated(slot)
199        })?;
200        let res = match slot {
201            InternalSlot::ENDPOINT => {
202                Some(ValueContainer::from(runtime.endpoint.clone()))
203            }
204        };
205        Ok(res)
206    }
207    else {
208        Err(ExecutionError::RequiresRuntime)
209    }
210}
211
212pub async fn get_pointer_test() {}
213
214pub async fn execute_dxb(
215    input: ExecutionInput<'_>,
216) -> Result<Option<ValueContainer>, ExecutionError> {
217    let interrupt_provider = Rc::new(RefCell::new(None));
218    let runtime_internal = input.context.borrow_mut().runtime_internal().clone();
219
220    for output in execute_loop(input, interrupt_provider.clone()) {
221        match output? {
222            ExecutionStep::Return(result) => return Ok(result),
223            ExecutionStep::ResolvePointer(_pointer_id) => {
224                get_pointer_test().await;
225                *interrupt_provider.borrow_mut() =
226                    Some(InterruptProvider::Result(Some(ValueContainer::from(42))));
227            }
228            ExecutionStep::RemoteExecution(receivers, body) => {
229                if let Some(runtime) = &runtime_internal {
230                    // assert that receivers is a single endpoint
231                    // TODO #230: support advanced receivers
232                    let receiver_endpoint = receivers.to_value().borrow().cast_to_endpoint().unwrap();
233                    let mut remote_execution_context = RemoteExecutionContext::new(
234                        receiver_endpoint,
235                        true
236                    );
237                    let res = RuntimeInternal::execute_remote(runtime.clone(), &mut remote_execution_context, body).await?;
238                    *interrupt_provider.borrow_mut() =
239                        Some(InterruptProvider::Result(res));
240                }
241                else {
242                    return Err(ExecutionError::RequiresRuntime);
243                }
244            }
245            ExecutionStep::GetInternalSlot(slot) => {
246                *interrupt_provider.borrow_mut() =
247                    Some(InterruptProvider::Result(get_internal_slot_value(&runtime_internal, slot)?));
248            }
249            _ => todo!("#99 Undescribed by author."),
250        }
251    }
252
253    unreachable!("Execution loop should always return a result");
254}
255
256#[derive(Debug, Clone, PartialEq, Eq)]
257pub enum InvalidProgramError {
258    InvalidScopeClose,
259    InvalidKeyValuePair,
260    // any unterminated sequence, e.g. missing key in key-value pair
261    UnterminatedSequence,
262    MissingRemoteExecutionReceiver,
263}
264
265impl Display for InvalidProgramError {
266    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
267        match self {
268            InvalidProgramError::InvalidScopeClose => {
269                write!(f, "Invalid scope close")
270            }
271            InvalidProgramError::InvalidKeyValuePair => {
272                write!(f, "Invalid key-value pair")
273            }
274            InvalidProgramError::UnterminatedSequence => {
275                write!(f, "Unterminated sequence")
276            }
277            InvalidProgramError::MissingRemoteExecutionReceiver => {
278                write!(f, "Missing remote execution receiver")
279            }
280        }
281    }
282}
283
284#[derive(Debug)]
285pub enum ExecutionError {
286    ParserError(DXBParserError),
287    ValueError(ValueError),
288    InvalidProgram(InvalidProgramError),
289    Unknown,
290    NotImplemented(String),
291    SlotNotAllocated(u32),
292    SlotNotInitialized(u32),
293    RequiresAsyncExecution,
294    RequiresRuntime,
295    ResponseError(ResponseError),
296    CompilerError(CompilerError),
297}
298
299impl From<DXBParserError> for ExecutionError {
300    fn from(error: DXBParserError) -> Self {
301        ExecutionError::ParserError(error)
302    }
303}
304
305impl From<ValueError> for ExecutionError {
306    fn from(error: ValueError) -> Self {
307        ExecutionError::ValueError(error)
308    }
309}
310
311impl From<InvalidProgramError> for ExecutionError {
312    fn from(error: InvalidProgramError) -> Self {
313        ExecutionError::InvalidProgram(error)
314    }
315}
316
317impl From<ResponseError> for ExecutionError {
318    fn from(error: ResponseError) -> Self {
319        ExecutionError::ResponseError(error)
320    }
321}
322
323impl From<CompilerError> for ExecutionError {
324    fn from(error: CompilerError) -> Self {
325        ExecutionError::CompilerError(error)
326    }
327}
328
329impl Display for ExecutionError {
330    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
331        match self {
332            ExecutionError::CompilerError(err) => {
333                write!(f, "Compiler error: {err}")
334            }
335            ExecutionError::ParserError(err) => {
336                write!(f, "Parser error: {err}")
337            }
338            ExecutionError::Unknown => write!(f, "Unknown execution error"),
339            ExecutionError::ValueError(err) => write!(f, "Value error: {err}"),
340            ExecutionError::InvalidProgram(err) => {
341                write!(f, "Invalid program error: {err}")
342            }
343            ExecutionError::NotImplemented(msg) => {
344                write!(f, "Not implemented: {msg}")
345            }
346            ExecutionError::SlotNotAllocated(address) => {
347                write!(
348                    f,
349                    "Tried to access unallocated slot at address {address}"
350                )
351            }
352            ExecutionError::SlotNotInitialized(address) => {
353                write!(
354                    f,
355                    "Tried to access uninitialized slot at address {address}"
356                )
357            }
358            ExecutionError::RequiresAsyncExecution => {
359                write!(f, "Program must be executed asynchronously")
360            }
361            ExecutionError::RequiresRuntime => {
362                write!(f, "Execution requires a runtime to be set")
363            }
364            ExecutionError::ResponseError(err) => {
365                write!(f, "Response error: {err}")
366            }
367        }
368    }
369}
370
371#[derive(Debug)]
372pub enum ExecutionStep {
373    InternalReturn(Option<ValueContainer>),
374    Return(Option<ValueContainer>),
375    ResolvePointer(u64),
376    GetInternalSlot(u32),
377    RemoteExecution(ValueContainer, Vec<u8>),
378    Pause,
379}
380
381#[derive(Debug)]
382pub enum InterruptProvider {
383    Result(Option<ValueContainer>),
384}
385
386#[macro_export]
387macro_rules! interrupt {
388    ($input:expr, $arg:expr) => {{
389        yield Ok($arg);
390        $input.take().unwrap()
391    }};
392}
393
394#[macro_export]
395macro_rules! interrupt_with_result {
396    ($input:expr, $arg:expr) => {{
397        yield Ok($arg);
398        let res = $input.take().unwrap();
399        match res {
400            InterruptProvider::Result(value) => value,
401        }
402    }};
403}
404
405#[macro_export]
406macro_rules! yield_unwrap {
407    ($e:expr) => {{
408        let res = $e;
409        if let Ok(res) = res {
410            res
411        } else {
412            return yield Err(res.unwrap_err().into());
413        }
414    }};
415}
416
417pub fn execute_loop(
418    input: ExecutionInput,
419    interrupt_provider: Rc<RefCell<Option<InterruptProvider>>>,
420) -> impl Iterator<Item = Result<ExecutionStep, ExecutionError>> {
421    gen move {
422        let dxb_body = input.dxb_body;
423        let end_execution = input.end_execution;
424        let context = input.context;
425
426        let instruction_iterator = body::iterate_instructions(dxb_body);
427
428        for instruction in instruction_iterator {
429            // TODO #100: use ? operator instead of yield_unwrap once supported in gen blocks
430            let instruction = yield_unwrap!(instruction);
431            if input.options.verbose {
432                info!("[Exec]: {instruction}");
433            }
434
435            // get initial value from instruction
436            let mut result_value = None;
437
438            for output in get_result_value_from_instruction(
439                context.clone(),
440                instruction,
441                interrupt_provider.clone(),
442            ) {
443                match yield_unwrap!(output) {
444                    ExecutionStep::InternalReturn(result) => {
445                        result_value = result;
446                    }
447                    step => {
448                        *interrupt_provider.borrow_mut() =
449                            Some(interrupt!(interrupt_provider, step));
450                    }
451                }
452            }
453
454            // 1. if value is Some, handle it
455            // 2. while pop_next_scope is true: pop current scope and repeat
456            loop {
457                let mut context_mut = context.borrow_mut();
458                context_mut.pop_next_scope = false;
459                if let Some(value) = result_value {
460                    let res = handle_value(&mut context_mut, value);
461                    drop(context_mut);
462                    yield_unwrap!(res);
463                } else {
464                    drop(context_mut);
465                }
466
467                let mut context_mut = context.borrow_mut();
468
469                if context_mut.pop_next_scope {
470                    let res = context_mut.scope_stack.pop();
471                    drop(context_mut);
472                    result_value = yield_unwrap!(res);
473                } else {
474                    break;
475                }
476            }
477        }
478
479        if end_execution {
480            // cleanup...
481            // TODO #101: check for other unclosed stacks
482            // if we have an active key here, this is invalid and leads to an error
483            // if context.scope_stack.get_active_key().is_some() {
484            //     return Err(ExecutionError::InvalidProgram(
485            //         InvalidProgramError::UnterminatedSequence,
486            //     ));
487            // }
488
489            // removes the current active value from the scope stack
490            let res = match context.borrow_mut().scope_stack.pop_active_value()
491            {
492                None => ExecutionStep::Return(None),
493                Some(val) => ExecutionStep::Return(Some(val)),
494            };
495            yield Ok(res);
496        } else {
497            yield Ok(ExecutionStep::Pause);
498        }
499    }
500}
501
502#[inline]
503fn get_result_value_from_instruction(
504    context: Rc<RefCell<RuntimeExecutionContext>>,
505    instruction: Instruction,
506    interrupt_provider: Rc<RefCell<Option<InterruptProvider>>>,
507) -> impl Iterator<Item = Result<ExecutionStep, ExecutionError>> {
508    gen move {
509        yield Ok(ExecutionStep::InternalReturn(match instruction {
510            // boolean
511            Instruction::True => Some(true.into()),
512            Instruction::False => Some(false.into()),
513
514            // integers
515            Instruction::Int8(integer) => Some(Integer::from(integer.0).into()),
516            Instruction::Int16(integer) => {
517                Some(Integer::from(integer.0).into())
518            }
519            Instruction::Int32(integer) => {
520                Some(Integer::from(integer.0).into())
521            }
522            Instruction::Int64(integer) => {
523                Some(Integer::from(integer.0).into())
524            }
525            Instruction::Int128(integer) => {
526                Some(Integer::from(integer.0).into())
527            }
528
529            // unsigned integers
530            Instruction::UInt128(integer) => {
531                Some(Integer::from(integer.0).into())
532            }
533
534            // specific floats
535            Instruction::DecimalF32(Float32Data(f32)) => {
536                Some(TypedDecimal::from(f32).into())
537            }
538            Instruction::DecimalF64(Float64Data(f64)) => {
539                Some(TypedDecimal::from(f64).into())
540            }
541
542            // default decimals (big decimals)
543            Instruction::DecimalAsInt16(FloatAsInt16Data(i16)) => {
544                Some(Decimal::from(i16 as f32).into())
545            }
546            Instruction::DecimalAsInt32(FloatAsInt32Data(i32)) => {
547                Some(Decimal::from(i32 as f32).into())
548            }
549            Instruction::Decimal(DecimalData(big_decimal)) => {
550                Some(big_decimal.into())
551            }
552
553            // endpoint
554            Instruction::Endpoint(endpoint) => Some(endpoint.into()),
555
556            // null
557            Instruction::Null => Some(Value::null().into()),
558
559            // text
560            Instruction::ShortText(ShortTextData(text)) => Some(text.into()),
561            Instruction::Text(TextData(text)) => Some(text.into()),
562
563            // operations
564            Instruction::Add
565            | Instruction::Subtract
566            | Instruction::Multiply
567            | Instruction::Divide
568            | Instruction::Is
569            | Instruction::StructuralEqual
570            | Instruction::Equal
571            | Instruction::NotStructuralEqual
572            | Instruction::NotEqual => {
573                context.borrow_mut().scope_stack.create_scope(
574                    Scope::BinaryOperation {
575                        operator: BinaryOperator::from(instruction),
576                    },
577                );
578                None
579            }
580
581            Instruction::ExecutionBlock(block) => {
582                // build dxb
583
584                let mut buffer = Vec::with_capacity(256);
585                for (addr, local_slot) in
586                    block.injected_slots.into_iter().enumerate()
587                {
588                    buffer.push(InstructionCode::ALLOCATE_SLOT as u8);
589                    append_u32(&mut buffer, addr as u32);
590
591                    if let Some(vc) = yield_unwrap!(
592                        context.borrow().get_slot_value(local_slot).map_err(
593                            |_| ExecutionError::SlotNotAllocated(local_slot),
594                        )
595                    ) {
596                        buffer.extend_from_slice(&yield_unwrap!(
597                            compile_value(&vc)
598                        ));
599                    } else {
600                        return yield Err(ExecutionError::SlotNotInitialized(
601                            local_slot,
602                        ));
603                    }
604                }
605                buffer.extend_from_slice(&block.body);
606
607                let maybe_receivers =
608                    context.borrow_mut().scope_stack.pop_active_value();
609
610                if let Some(receivers) = maybe_receivers {
611                    interrupt_with_result!(
612                        interrupt_provider,
613                        ExecutionStep::RemoteExecution(receivers, buffer)
614                    )
615                } else {
616                    // should not happen, receivers must be set
617                    yield Err(ExecutionError::InvalidProgram(
618                        InvalidProgramError::MissingRemoteExecutionReceiver,
619                    ));
620                    None
621                }
622            }
623
624            Instruction::CloseAndStore => {
625                let _ = context.borrow_mut().scope_stack.pop_active_value();
626                None
627            }
628
629            Instruction::ScopeStart => {
630                context
631                    .borrow_mut()
632                    .scope_stack
633                    .create_scope(Scope::Default);
634                None
635            }
636
637            Instruction::ArrayStart => {
638                context
639                    .borrow_mut()
640                    .scope_stack
641                    .create_scope_with_active_value(
642                        Scope::Collection,
643                        Array::default().into(),
644                    );
645                None
646            }
647
648            Instruction::ObjectStart => {
649                context
650                    .borrow_mut()
651                    .scope_stack
652                    .create_scope_with_active_value(
653                        Scope::Collection,
654                        Object::default().into(),
655                    );
656                None
657            }
658
659            Instruction::TupleStart => {
660                context
661                    .borrow_mut()
662                    .scope_stack
663                    .create_scope_with_active_value(
664                        Scope::Collection,
665                        Tuple::default().into(),
666                    );
667                None
668            }
669
670            Instruction::KeyValueShortText(ShortTextData(key)) => {
671                context
672                    .borrow_mut()
673                    .scope_stack
674                    .create_scope_with_active_value(
675                        Scope::KeyValuePair,
676                        key.into(),
677                    );
678                None
679            }
680
681            Instruction::KeyValueDynamic => {
682                context
683                    .borrow_mut()
684                    .scope_stack
685                    .create_scope(Scope::KeyValuePair);
686                None
687            }
688
689            Instruction::ScopeEnd => {
690                // pop scope and return value
691                yield_unwrap!(context.borrow_mut().scope_stack.pop())
692            }
693
694            // slots
695            Instruction::AllocateSlot(SlotAddress(address)) => {
696                let mut context = context.borrow_mut();
697                context.allocate_slot(address, None);
698                context
699                    .scope_stack
700                    .create_scope(Scope::SlotAssignment { address });
701                None
702            }
703            Instruction::GetSlot(SlotAddress(address)) => {
704
705                // if address is >= 0xffffff00, resolve internal slot
706                if address >= 0xffffff00 {
707                    interrupt_with_result!(
708                        interrupt_provider,
709                        ExecutionStep::GetInternalSlot(address)
710                    )
711                }
712
713                // else handle normal slot
714                else {
715                    let res = context.borrow_mut().get_slot_value(address);
716                    // get value from slot
717                    let slot_value = yield_unwrap!(res);
718                    if slot_value.is_none() {
719                        return yield Err(ExecutionError::SlotNotInitialized(
720                            address,
721                        ));
722                    }
723                    slot_value
724                }
725            }
726            Instruction::SetSlot(SlotAddress(address)) => {
727                context
728                    .borrow_mut()
729                    .scope_stack
730                    .create_scope(Scope::SlotAssignment { address });
731                None
732            }
733
734            // refs
735            Instruction::CreateRef => {
736                context.borrow_mut().scope_stack.create_scope(
737                    Scope::UnaryOperation {
738                        operator: UnaryOperator::CreateRef,
739                    },
740                );
741                None
742            }
743
744            // remote execution
745            Instruction::RemoteExecution => {
746                context
747                    .borrow_mut()
748                    .scope_stack
749                    .create_scope(Scope::RemoteExecution);
750                None
751            }
752
753            Instruction::DropSlot(SlotAddress(address)) => {
754                // remove slot from slots
755                let res = context.borrow_mut().drop_slot(address);
756                yield_unwrap!(res);
757                None
758            }
759
760            i => {
761                return yield Err(ExecutionError::NotImplemented(
762                    format!("Instruction {i}").to_string(),
763                ));
764            }
765        }))
766    }
767}
768
769/// Takes a produced value and handles it according to the current scope
770fn handle_value(
771    context: &mut RuntimeExecutionContext,
772    value_container: ValueContainer,
773) -> Result<(), ExecutionError> {
774    let scope_container = context.scope_stack.get_current_scope_mut();
775
776    let result_value = match &scope_container.scope {
777        Scope::KeyValuePair => {
778            let key = &scope_container.active_value;
779            match key {
780                // set key as active_value for key-value pair (for dynamic keys)
781                None => Some(value_container),
782
783                // set value for key-value pair
784                Some(_) => {
785                    let key = context.scope_stack.pop()?.unwrap();
786                    match context.scope_stack.get_active_value_mut() {
787                        Some(collector) => {
788                            // handle active value collector
789                            handle_key_value_pair(
790                                collector,
791                                key,
792                                value_container,
793                            )?;
794                        }
795                        None => unreachable!(
796                            "Expected active value for key-value pair, but got None"
797                        ),
798                    }
799                    None
800                }
801            }
802        }
803
804        Scope::SlotAssignment { address } => {
805            // set value for slot
806            let address = *address;
807            context.set_slot_value(address, value_container.clone())?;
808            Some(value_container)
809        }
810
811        Scope::UnaryOperation { operator } => {
812            let operator = *operator;
813            context.pop_next_scope = true;
814            Some(handle_unary_operation(operator, value_container))
815        }
816
817        Scope::BinaryOperation { operator } => {
818            let active_value = &scope_container.active_value;
819            match active_value {
820                Some(active_value_container) => {
821                    let res = handle_binary_operation(
822                        active_value_container,
823                        value_container,
824                        *operator,
825                    );
826                    if let Ok(val) = res {
827                        // set val as active value
828                        context.pop_next_scope = true;
829                        Some(val)
830                    } else {
831                        // handle error
832                        return Err(res.unwrap_err());
833                    }
834                }
835                None => Some(value_container),
836            }
837        }
838
839        Scope::Collection => {
840            let active_value = &mut scope_container.active_value;
841            match active_value {
842                Some(active_value_container) => {
843                    // handle active value collector
844                    handle_collector(active_value_container, value_container);
845                    None
846                }
847                None => {
848                    unreachable!(
849                        "Expected active value for collection scope, but got None"
850                    );
851                }
852            }
853        }
854
855        _ => Some(value_container),
856    };
857
858    if let Some(result_value) = result_value {
859        context.scope_stack.set_active_value_container(result_value);
860    }
861
862    Ok(())
863}
864
865fn handle_collector(collector: &mut ValueContainer, value: ValueContainer) {
866    match collector {
867        ValueContainer::Value(Value {
868            inner: CoreValue::Array(array),
869            ..
870        }) => {
871            // append value to array
872            array.push(value);
873        }
874        ValueContainer::Value(Value {
875            inner: CoreValue::Tuple(tuple),
876            ..
877        }) => {
878            // automatic tuple keys are always default integer values
879            let index = CoreValue::Integer(Integer::from(tuple.next_int_key()));
880            tuple.set(index, value);
881        }
882        _ => {
883            unreachable!(
884                "Expected active value in array scope to be an array, but got: {}",
885                collector
886            );
887        }
888    }
889}
890
891fn handle_key_value_pair(
892    active_container: &mut ValueContainer,
893    key: ValueContainer,
894    value: ValueContainer,
895) -> Result<(), ExecutionError> {
896    // insert key value pair into active object/tuple
897    match active_container {
898        // object
899        ValueContainer::Value(Value {
900            inner: CoreValue::Object(object),
901            ..
902        }) => {
903            // make sure key is a string
904            match key {
905                ValueContainer::Value(Value {
906                    inner: CoreValue::Text(key_str),
907                    ..
908                }) => {
909                    object.set(&key_str.0, value);
910                }
911                _ => {
912                    return Err(ExecutionError::InvalidProgram(
913                        InvalidProgramError::InvalidKeyValuePair,
914                    ));
915                }
916            }
917        }
918        // tuple
919        ValueContainer::Value(Value {
920            inner: CoreValue::Tuple(tuple),
921            ..
922        }) => {
923            // set key-value pair in tuple
924            tuple.set(key, value);
925        }
926        _ => {
927            unreachable!(
928                "Expected active value object or tuple to collect key value pairs, but got: {}",
929                active_container
930            );
931        }
932    }
933
934    Ok(())
935}
936
937fn handle_unary_operation(
938    operator: UnaryOperator,
939    value_container: ValueContainer,
940) -> ValueContainer {
941    match operator {
942        UnaryOperator::CreateRef => {
943            ValueContainer::Reference(Reference::from(value_container))
944        }
945        _ => todo!("#102 Unary instruction not implemented: {operator:?}"),
946    }
947}
948
949fn handle_binary_operation(
950    active_value_container: &ValueContainer,
951    value_container: ValueContainer,
952    operator: BinaryOperator,
953) -> Result<ValueContainer, ExecutionError> {
954    // apply operation to active value
955    match operator {
956        BinaryOperator::Add => Ok((active_value_container + &value_container)?),
957        BinaryOperator::Subtract => {
958            Ok((active_value_container - &value_container)?)
959        }
960        BinaryOperator::StructuralEqual => {
961            let val = active_value_container.structural_eq(&value_container);
962            Ok(ValueContainer::from(val))
963        }
964        BinaryOperator::Equal => {
965            let val = active_value_container.value_eq(&value_container);
966            Ok(ValueContainer::from(val))
967        }
968        BinaryOperator::NotStructuralEqual => {
969            let val = !active_value_container.structural_eq(&value_container);
970            Ok(ValueContainer::from(val))
971        }
972        BinaryOperator::NotEqual => {
973            let val = !active_value_container.value_eq(&value_container);
974            Ok(ValueContainer::from(val))
975        }
976        BinaryOperator::Is => {
977            // TODO #103 we should throw a runtime error when one of lhs or rhs is a value
978            // instead of a ref. Identity checks using the is operator shall be only allowed
979            // for references.
980            // @benstre: or keep as always false ? - maybe a compiler check would be better
981            let val = active_value_container.identical(&value_container);
982            Ok(ValueContainer::from(val))
983        }
984        _ => {
985            unreachable!("Instruction {:?} is not a valid operation", operator);
986        }
987    }
988}
989
990#[cfg(test)]
991mod tests {
992    use std::assert_matches::assert_matches;
993    use std::vec;
994
995    use log::debug;
996
997    use super::*;
998    use crate::compiler::{CompileOptions, compile_script};
999    use crate::global::binary_codes::InstructionCode;
1000    use crate::logger::init_logger_debug;
1001    use crate::values::traits::structural_eq::StructuralEq;
1002    use crate::{assert_structural_eq, assert_value_eq, datex_array};
1003
1004    fn execute_datex_script_debug(
1005        datex_script: &str,
1006    ) -> Option<ValueContainer> {
1007        let (dxb, _) =
1008            compile_script(datex_script, CompileOptions::default()).unwrap();
1009        let context = ExecutionInput::new_with_dxb_and_options(
1010            &dxb,
1011            ExecutionOptions { verbose: true },
1012        );
1013        execute_dxb_sync(context).unwrap_or_else(|err| {
1014            panic!("Execution failed: {err}");
1015        })
1016    }
1017
1018    fn execute_datex_script_debug_with_error(
1019        datex_script: &str,
1020    ) -> Result<Option<ValueContainer>, ExecutionError> {
1021        let (dxb, _) =
1022            compile_script(datex_script, CompileOptions::default()).unwrap();
1023        let context = ExecutionInput::new_with_dxb_and_options(
1024            &dxb,
1025            ExecutionOptions { verbose: true },
1026        );
1027        execute_dxb_sync(context)
1028    }
1029
1030    fn execute_datex_script_debug_with_result(
1031        datex_script: &str,
1032    ) -> ValueContainer {
1033        execute_datex_script_debug(datex_script).unwrap()
1034    }
1035
1036    fn execute_dxb_debug(
1037        dxb_body: &[u8],
1038    ) -> Result<Option<ValueContainer>, ExecutionError> {
1039        let context = ExecutionInput::new_with_dxb_and_options(
1040            dxb_body,
1041            ExecutionOptions { verbose: true },
1042        );
1043        execute_dxb_sync(context)
1044    }
1045
1046    #[test]
1047    fn test_empty_script() {
1048        assert_eq!(execute_datex_script_debug(""), None);
1049    }
1050
1051    #[test]
1052    fn test_empty_script_semicolon() {
1053        assert_eq!(execute_datex_script_debug(";;;"), None);
1054    }
1055
1056    #[test]
1057    fn test_single_value() {
1058        assert_eq!(
1059            execute_datex_script_debug_with_result("42"),
1060            Integer::from(42).into()
1061        );
1062    }
1063
1064    #[test]
1065    fn test_single_value_semicolon() {
1066        assert_eq!(execute_datex_script_debug("42;"), None)
1067    }
1068
1069    #[test]
1070    fn test_is() {
1071        let result = execute_datex_script_debug_with_result("1 is 1");
1072        assert_eq!(result, false.into());
1073        assert_structural_eq!(result, ValueContainer::from(false));
1074    }
1075
1076    #[test]
1077    fn test_equality() {
1078        let result = execute_datex_script_debug_with_result("1 == 1");
1079        assert_eq!(result, true.into());
1080        assert_structural_eq!(result, ValueContainer::from(true));
1081
1082        let result = execute_datex_script_debug_with_result("1 == 2");
1083        assert_eq!(result, false.into());
1084        assert_structural_eq!(result, ValueContainer::from(false));
1085
1086        let result = execute_datex_script_debug_with_result("1 != 2");
1087        assert_eq!(result, true.into());
1088        assert_structural_eq!(result, ValueContainer::from(true));
1089
1090        let result = execute_datex_script_debug_with_result("1 != 1");
1091        assert_eq!(result, false.into());
1092        assert_structural_eq!(result, ValueContainer::from(false));
1093        let result = execute_datex_script_debug_with_result("1 === 1");
1094        assert_eq!(result, true.into());
1095
1096        assert_structural_eq!(result, ValueContainer::from(true));
1097        let result = execute_datex_script_debug_with_result("1 !== 2");
1098        assert_eq!(result, true.into());
1099        assert_structural_eq!(result, ValueContainer::from(true));
1100
1101        let result = execute_datex_script_debug_with_result("1 !== 1");
1102        assert_eq!(result, false.into());
1103        assert_structural_eq!(result, ValueContainer::from(false));
1104    }
1105
1106    #[test]
1107    fn test_single_value_scope() {
1108        let result = execute_datex_script_debug_with_result("(42)");
1109        assert_eq!(result, Integer::from(42).into());
1110        assert_structural_eq!(result, ValueContainer::from(42_u128));
1111    }
1112
1113    #[test]
1114    fn test_add() {
1115        let result = execute_datex_script_debug_with_result("1 + 2");
1116        assert_structural_eq!(result, ValueContainer::from(3_u128));
1117        assert_eq!(result, Integer::from(3).into());
1118    }
1119
1120    #[test]
1121    fn test_nested_scope() {
1122        let result = execute_datex_script_debug_with_result("1 + (2 + 3)");
1123        assert_eq!(result, Integer::from(6).into());
1124    }
1125
1126    #[test]
1127    fn test_invalid_scope_close() {
1128        let result = execute_dxb_debug(&[
1129            InstructionCode::SCOPE_START.into(),
1130            InstructionCode::SCOPE_END.into(),
1131            InstructionCode::SCOPE_END.into(),
1132        ]);
1133        assert!(matches!(
1134            result,
1135            Err(ExecutionError::InvalidProgram(
1136                InvalidProgramError::InvalidScopeClose
1137            ))
1138        ));
1139    }
1140
1141    #[test]
1142    fn test_empty_array() {
1143        let result = execute_datex_script_debug_with_result("[]");
1144        let array: Array = result.to_value().borrow().cast_to_array().unwrap();
1145        assert_eq!(array.len(), 0);
1146        assert_eq!(result, Vec::<ValueContainer>::new().into());
1147        assert_eq!(result, ValueContainer::from(Vec::<ValueContainer>::new()));
1148    }
1149
1150    #[test]
1151    fn test_array() {
1152        let result = execute_datex_script_debug_with_result("[1, 2, 3]");
1153        let array: Array = result.to_value().borrow().cast_to_array().unwrap();
1154        let expected =
1155            datex_array![Integer::from(1), Integer::from(2), Integer::from(3)];
1156        assert_eq!(array.len(), 3);
1157        assert_eq!(result, expected.into());
1158        assert_ne!(result, ValueContainer::from(vec![1, 2, 3]));
1159        assert_structural_eq!(result, ValueContainer::from(vec![1, 2, 3]));
1160    }
1161
1162    #[test]
1163    fn test_array_with_nested_scope() {
1164        init_logger_debug();
1165        let result = execute_datex_script_debug_with_result("[1, (2 + 3), 4]");
1166        let expected =
1167            datex_array![Integer::from(1), Integer::from(5), Integer::from(4)];
1168
1169        assert_eq!(result, expected.into());
1170        assert_ne!(result, ValueContainer::from(vec![1_u8, 5_u8, 4_u8]));
1171        assert_structural_eq!(
1172            result,
1173            ValueContainer::from(vec![1_u8, 5_u8, 4_u8])
1174        );
1175    }
1176
1177    #[test]
1178    fn test_boolean() {
1179        let result = execute_datex_script_debug_with_result("true");
1180        assert_eq!(result, true.into());
1181        assert_structural_eq!(result, ValueContainer::from(true));
1182
1183        let result = execute_datex_script_debug_with_result("false");
1184        assert_eq!(result, false.into());
1185        assert_structural_eq!(result, ValueContainer::from(false));
1186    }
1187
1188    #[test]
1189    fn test_decimal() {
1190        let result = execute_datex_script_debug_with_result("1.5");
1191        assert_eq!(result, Decimal::from_string("1.5").into());
1192        assert_structural_eq!(result, ValueContainer::from(1.5));
1193    }
1194
1195    #[test]
1196    fn test_decimal_and_integer() {
1197        let result = execute_datex_script_debug_with_result("-2341324.0");
1198        assert_eq!(result, Decimal::from_string("-2341324").into());
1199        assert!(!result.structural_eq(&ValueContainer::from(-2341324)));
1200    }
1201
1202    #[test]
1203    fn test_integer_2() {
1204        init_logger_debug();
1205        let result = execute_datex_script_debug_with_result("2");
1206        assert_eq!(result, Integer::from(2).into());
1207        assert_ne!(result, 2_u8.into());
1208        assert_structural_eq!(result, ValueContainer::from(2_u8));
1209    }
1210
1211    #[test]
1212    fn test_null() {
1213        let result = execute_datex_script_debug_with_result("null");
1214        assert_eq!(result, ValueContainer::from(CoreValue::Null));
1215        assert_eq!(result, CoreValue::Null.into());
1216        assert_structural_eq!(result, ValueContainer::from(CoreValue::Null));
1217    }
1218
1219    #[test]
1220    fn test_tuple() {
1221        init_logger_debug();
1222        let result = execute_datex_script_debug_with_result("(x: 1, 2, 42)");
1223        let tuple: CoreValue = result.clone().to_value().borrow().clone().inner;
1224        let tuple: Tuple = tuple.try_into().unwrap();
1225
1226        // form and size
1227        assert_eq!(tuple.to_string(), "(\"x\": 1, 0: 2, 1: 42)");
1228        assert_eq!(tuple.size(), 3);
1229
1230        // access by key
1231        assert_eq!(tuple.get(&"x".into()), Some(&Integer::from(1).into()));
1232        assert_eq!(
1233            tuple.get(&Integer::from(0_u32).into()),
1234            Some(&Integer::from(2).into())
1235        );
1236        assert_eq!(
1237            tuple.get(&Integer::from(1_u32).into()),
1238            Some(&Integer::from(42).into())
1239        );
1240
1241        // structural equality checks
1242        let expected_se: Tuple = Tuple::from(vec![
1243            ("x".into(), 1.into()),
1244            (0.into(), 2.into()),
1245            (1.into(), 42.into()),
1246        ]);
1247        assert_structural_eq!(tuple, expected_se);
1248
1249        // strict equality checks
1250        let expected_strict: Tuple = Tuple::from(vec![
1251            ("x".into(), Integer::from(1_u32).into()),
1252            (0_u32.into(), Integer::from(2_u32).into()),
1253            (1_u32.into(), Integer::from(42_u32).into()),
1254        ]);
1255        debug!("Expected tuple: {expected_strict}");
1256        debug!("Tuple result: {tuple}");
1257        // FIXME #104 type information gets lost on compile
1258        // assert_eq!(result, expected.into());
1259    }
1260
1261    #[test]
1262    fn test_val_assignment() {
1263        init_logger_debug();
1264        let result = execute_datex_script_debug_with_result("const x = 42; x");
1265        assert_eq!(result, Integer::from(42).into());
1266    }
1267
1268    #[test]
1269    fn test_val_assignment_with_addition() {
1270        init_logger_debug();
1271        let result = execute_datex_script_debug_with_result("const x = 1 + 2; x");
1272        assert_eq!(result, Integer::from(3).into());
1273    }
1274
1275    #[test]
1276    fn test_val_assignment_inside_scope() {
1277        init_logger_debug();
1278        let result =
1279            execute_datex_script_debug_with_result("[const x = 42, 2, x]");
1280        let expected = datex_array![
1281            Integer::from(42),
1282            Integer::from(2),
1283            Integer::from(42)
1284        ];
1285        assert_eq!(result, expected.into());
1286    }
1287
1288    #[test]
1289    fn test_ref_assignment() {
1290        init_logger_debug();
1291        let result = execute_datex_script_debug_with_result("const mut x = 42; x");
1292        assert_matches!(result, ValueContainer::Reference(..));
1293        assert_value_eq!(result, ValueContainer::from(Integer::from(42)));
1294    }
1295
1296    #[test]
1297    fn test_endpoint_slot() {
1298        init_logger_debug();
1299        let result = execute_datex_script_debug_with_error("#endpoint");
1300        assert_matches!(result.unwrap_err(), ExecutionError::RequiresRuntime);
1301    }
1302
1303    #[test]
1304    fn test_shebang() {
1305        init_logger_debug();
1306        let result = execute_datex_script_debug_with_result("#!datex\n42");
1307        assert_eq!(result, Integer::from(42).into());
1308    }
1309
1310    #[test]
1311    fn test_single_line_comment() {
1312        init_logger_debug();
1313        let result =
1314            execute_datex_script_debug_with_result("// this is a comment\n42");
1315        assert_eq!(result, Integer::from(42).into());
1316
1317        let result = execute_datex_script_debug_with_result(
1318            "// this is a comment\n// another comment\n42",
1319        );
1320        assert_eq!(result, Integer::from(42).into());
1321    }
1322
1323    #[test]
1324    fn test_multi_line_comment() {
1325        init_logger_debug();
1326        let result = execute_datex_script_debug_with_result(
1327            "/* this is a comment */\n42",
1328        );
1329        assert_eq!(result, Integer::from(42).into());
1330
1331        let result = execute_datex_script_debug_with_result(
1332            "/* this is a comment\n   with multiple lines */\n42",
1333        );
1334        assert_eq!(result, Integer::from(42).into());
1335
1336        let result = execute_datex_script_debug_with_result("[1, /* 2, */ 3]");
1337        let expected = datex_array![Integer::from(1), Integer::from(3)];
1338        assert_eq!(result, expected.into());
1339    }
1340}