datex_core/compiler/
mod.rs

1use crate::compiler::error::CompilerError;
2use crate::global::dxb_block::DXBBlock;
3use crate::global::protocol_structures::block_header::BlockHeader;
4use crate::global::protocol_structures::encrypted_header::EncryptedHeader;
5use crate::global::protocol_structures::routing_header;
6use crate::global::protocol_structures::routing_header::RoutingHeader;
7
8use crate::compiler::ast_parser::{DatexExpression, DatexScriptParser, TupleEntry, VariableType, parse, VariableMutType, VariableId};
9use crate::compiler::context::{CompilationContext, VirtualSlot};
10use crate::compiler::metadata::CompileMetadata;
11use crate::compiler::scope::CompilationScope;
12use crate::global::binary_codes::{InstructionCode, InternalSlot};
13use crate::values::core_values::decimal::decimal::Decimal;
14use crate::values::core_values::endpoint::Endpoint;
15use crate::values::value_container::ValueContainer;
16use std::cell::RefCell;
17use std::rc::Rc;
18use log::info;
19use datex_core::compiler::ast_parser::Slot;
20use crate::compiler::precompiler::{precompile_ast, AstMetadata, AstWithMetadata, VariableMetadata};
21
22pub mod ast_parser;
23pub mod context;
24pub mod error;
25mod lexer;
26pub mod metadata;
27pub mod scope;
28mod precompiler;
29
30#[derive(Clone, Default)]
31pub struct CompileOptions<'a> {
32    pub parser: Option<&'a DatexScriptParser<'a>>,
33    pub compile_scope: CompilationScope,
34}
35
36impl CompileOptions<'_> {
37    pub fn new_with_scope(compile_scope: CompilationScope) -> Self {
38        CompileOptions {
39            parser: None,
40            compile_scope,
41        }
42    }
43}
44
45#[derive(Debug, Clone, PartialEq, Eq)]
46pub enum StaticValueOrDXB {
47    StaticValue(Option<ValueContainer>),
48    Dxb(Vec<u8>),
49}
50
51impl From<Vec<u8>> for StaticValueOrDXB {
52    fn from(dxb: Vec<u8>) -> Self {
53        StaticValueOrDXB::Dxb(dxb)
54    }
55}
56
57
58#[derive(Debug, Clone, PartialEq, Eq, Copy)]
59pub enum VariableModel {
60    /// A variable that is declared once and never reassigned afterward
61    /// e.g. `const a = 42;`
62    Constant,
63    /// A variable that can be reassigned by updating the slot value
64    /// e.g. `var a = 42; a = 69;`
65    VariableSlot,
66    /// A variable that can be reassigned by updating a reference value. The slot always point to this reference.
67    /// When variables are transferred across realms, `VariableReference` is used for `var` variables instead of `VariableSlot`.
68    /// e.g. `var a = 42; x :: (a)
69    VariableReference,
70}
71
72impl From<VariableRepresentation> for VariableModel {
73    fn from(value: VariableRepresentation) -> Self {
74        match value {
75            VariableRepresentation::Constant(_) => VariableModel::Constant,
76            VariableRepresentation::VariableSlot(_) => VariableModel::VariableSlot,
77            VariableRepresentation::VariableReference { .. } => VariableModel::VariableReference,
78        }
79    }
80}
81
82impl VariableModel {
83    /// Determines the variable model based on the variable type and metadata.
84    pub fn infer(
85        variable_type: VariableType,
86        variable_metadata: Option<VariableMetadata>,
87        is_end_of_source_text: bool,
88    ) -> Self {
89        // const variables are always constant
90        if variable_type == VariableType::Const {
91            VariableModel::Constant
92        }
93        // for cross-realm variables, we always use VariableReference
94        // if we don't know the full source text yet (e.g. in a repl), we
95        // must fall back to VariableReference, because we cannot determine if
96        // the variable will be transferred across realms later
97        else if variable_metadata.is_none() || variable_metadata.unwrap().is_cross_realm || !is_end_of_source_text {
98            VariableModel::VariableReference
99        }
100        // otherwise, we use VariableSlot (default for `var` variables)
101        else {
102            VariableModel::VariableSlot
103        }
104    }
105
106    pub fn infer_from_ast_metadata_and_type(
107        ast_metadata: &AstMetadata,
108        variable_id: Option<VariableId>,
109        variable_type: VariableType,
110        is_end_of_source_text: bool,
111    ) -> Self {
112        let variable_metadata = variable_id.and_then(|id| {
113            ast_metadata.variable_metadata(id)
114        });
115        Self::infer(variable_type, variable_metadata.cloned(), is_end_of_source_text)
116    }
117}
118
119
120#[derive(Debug, Clone, PartialEq, Eq, Copy)]
121pub enum VariableRepresentation {
122    Constant(VirtualSlot),
123    VariableSlot(VirtualSlot),
124    VariableReference {
125        /// The slot that contains the reference that is used as the variable
126        variable_slot: VirtualSlot,
127        /// The slot that contains the actual value container used in the script (Note: the value container may also be a reference)
128        container_slot: VirtualSlot,
129    },
130}
131
132
133/// Represents a variable in the DATEX script.
134#[derive(Debug, Clone)]
135pub struct Variable {
136    pub name: String,
137    pub var_type: VariableType,
138    pub mut_type: VariableMutType,
139    pub representation: VariableRepresentation,
140}
141
142
143impl Variable {
144    pub fn new_const(
145        name: String,
146        var_type: VariableType,
147        mut_type: VariableMutType,
148        slot: VirtualSlot,
149    ) -> Self {
150        Variable {
151            name,
152            var_type,
153            mut_type,
154            representation: VariableRepresentation::Constant(slot),
155        }
156    }
157
158    pub fn new_variable_slot(
159        name: String,
160        var_type: VariableType,
161        mut_type: VariableMutType,
162        slot: VirtualSlot,
163    ) -> Self {
164        Variable {
165            name,
166            var_type,
167            mut_type,
168            representation: VariableRepresentation::VariableSlot(slot),
169        }
170    }
171
172    pub fn new_variable_reference(
173        name: String,
174        var_type: VariableType,
175        mut_type: VariableMutType,
176        variable_slot: VirtualSlot,
177        container_slot: VirtualSlot,
178    ) -> Self {
179        Variable {
180            name,
181            var_type,
182            mut_type,
183            representation: VariableRepresentation::VariableReference {
184                variable_slot,
185                container_slot,
186            },
187        }
188    }
189
190    pub fn slots(&self) -> Vec<VirtualSlot> {
191        match &self.representation {
192            VariableRepresentation::Constant(slot) => vec![*slot],
193            VariableRepresentation::VariableSlot(slot) => vec![*slot],
194            VariableRepresentation::VariableReference {
195                variable_slot,
196                container_slot,
197            } => {
198                vec![*variable_slot, *container_slot]
199            }
200        }
201    }
202}
203
204
205
206/// Compiles a DATEX script text into a single DXB block including routing and block headers.
207/// This function is used to create a block that can be sent over the network.
208pub fn compile_block(datex_script: &str) -> Result<Vec<u8>, CompilerError> {
209    let (body, _) = compile_script(datex_script, CompileOptions::default())?;
210
211    let routing_header = RoutingHeader {
212        version: 2,
213        flags: routing_header::Flags::new(),
214        block_size_u16: Some(0),
215        block_size_u32: None,
216        sender: Endpoint::LOCAL,
217        receivers: routing_header::Receivers {
218            flags: routing_header::ReceiverFlags::new()
219                .with_has_endpoints(false)
220                .with_has_pointer_id(false)
221                .with_has_endpoint_keys(false),
222            pointer_id: None,
223            endpoints: None,
224            endpoints_with_keys: None,
225        },
226        ..RoutingHeader::default()
227    };
228
229    let block_header = BlockHeader::default();
230    let encrypted_header = EncryptedHeader::default();
231
232    let block =
233        DXBBlock::new(routing_header, block_header, encrypted_header, body);
234
235    let bytes = block
236        .to_bytes()
237        .map_err(CompilerError::SerializationError)?;
238    Ok(bytes)
239}
240
241/// Compiles a DATEX script text into a DXB body
242pub fn compile_script<'a>(
243    datex_script: &'a str,
244    options: CompileOptions<'a>,
245) -> Result<(Vec<u8>, CompilationScope), CompilerError> {
246    compile_template(datex_script, &[], options)
247}
248
249/// Directly extracts a static value from a DATEX script as a `ValueContainer`.
250/// This only works if the script does not contain any dynamic values or operations.
251/// All JSON-files can be compiled to static values, but not all DATEX scripts.
252pub fn extract_static_value_from_script(
253    datex_script: &str,
254) -> Result<Option<ValueContainer>, CompilerError> {
255    let res = parse(datex_script)?;
256    extract_static_value_from_ast(res).map(Some)
257}
258
259/// Compiles a DATEX script template text with inserted values into a DXB body
260/// The value containers are passed by reference
261pub fn compile_template_with_refs<'a>(
262    datex_script: &'a str,
263    inserted_values: &[&ValueContainer],
264    options: CompileOptions<'a>,
265) -> Result<(Vec<u8>, CompilationScope), CompilerError> {
266    compile_template_or_return_static_value_with_refs(
267        datex_script,
268        inserted_values,
269        false,
270        options,
271    )
272    .map(|result| match result.0 {
273        StaticValueOrDXB::StaticValue(_) => unreachable!(),
274        StaticValueOrDXB::Dxb(dxb) => (dxb, result.1),
275    })
276}
277
278/// Compiles a DATEX script template text with inserted values into a DXB body
279/// If the script does not contain any dynamic values or operations, the static result value is
280/// directly returned instead of the DXB body.
281pub fn compile_script_or_return_static_value<'a>(
282    datex_script: &'a str,
283    options: CompileOptions<'a>,
284) -> Result<(StaticValueOrDXB, CompilationScope), CompilerError> {
285    compile_template_or_return_static_value_with_refs(
286        datex_script,
287        &[],
288        true,
289        options,
290    )
291}
292/// Compiles a DATEX script template text with inserted values into a DXB body
293pub fn compile_template_or_return_static_value_with_refs<'a>(
294    datex_script: &'a str,
295    inserted_values: &[&ValueContainer],
296    return_static_value: bool,
297    options: CompileOptions<'a>,
298) -> Result<(StaticValueOrDXB, CompilationScope), CompilerError> {
299    // shortcut if datex_script is "?" - call compile_value directly
300    if datex_script == "?" {
301        if inserted_values.len() != 1 {
302            return Err(CompilerError::InvalidPlaceholderCount);
303        }
304        let result =
305            compile_value(inserted_values[0]).map(StaticValueOrDXB::from)?;
306        return Ok((result, options.compile_scope));
307    }
308
309    let ast = parse(datex_script)?;
310
311    let buffer = RefCell::new(Vec::with_capacity(256));
312    let compilation_context = CompilationContext::new(buffer, inserted_values, options.compile_scope.once);
313
314    if return_static_value {
315        let scope = compile_ast(
316            &compilation_context,
317            ast.clone(),
318            options.compile_scope,
319        )?;
320
321        if !*compilation_context.has_non_static_value.borrow() {
322            if let Ok(value) = ValueContainer::try_from(ast) {
323                return Ok((
324                    StaticValueOrDXB::StaticValue(Some(value.clone())),
325                    scope,
326                ));
327            }
328            Ok((StaticValueOrDXB::StaticValue(None), scope))
329        } else {
330            // return DXB body
331            Ok((
332                StaticValueOrDXB::Dxb(compilation_context.buffer.take()),
333                scope,
334            ))
335        }
336    } else {
337        let scope =
338            compile_ast(&compilation_context, ast, options.compile_scope)?;
339        // return DXB body
340        Ok((
341            StaticValueOrDXB::Dxb(compilation_context.buffer.take()),
342            scope,
343        ))
344    }
345}
346
347/// Compiles a DATEX script template text with inserted values into a DXB body
348pub fn compile_template<'a>(
349    datex_script: &'a str,
350    inserted_values: &[ValueContainer],
351    options: CompileOptions<'a>,
352) -> Result<(Vec<u8>, CompilationScope), CompilerError> {
353    compile_template_with_refs(
354        datex_script,
355        &inserted_values.iter().collect::<Vec<_>>(),
356        options,
357    )
358}
359
360pub fn compile_value(value: &ValueContainer) -> Result<Vec<u8>, CompilerError> {
361    let buffer = RefCell::new(Vec::with_capacity(256));
362    let compilation_scope = CompilationContext::new(buffer, &[], true);
363
364    compilation_scope.insert_value_container(value);
365
366    Ok(compilation_scope.buffer.take())
367}
368
369/// Tries to extract a static value from a DATEX expression AST.
370/// If the expression is not a static value (e.g., contains a placeholder or dynamic operation),
371/// it returns an error.
372fn extract_static_value_from_ast(
373    ast: DatexExpression,
374) -> Result<ValueContainer, CompilerError> {
375    if let DatexExpression::Placeholder = ast {
376        return Err(CompilerError::NonStaticValue);
377    }
378    ValueContainer::try_from(ast).map_err(|_| CompilerError::NonStaticValue)
379}
380
381/// Macro for compiling a DATEX script template text with inserted values into a DXB body,
382/// behaves like the format! macro.
383/// Example:
384/// ```
385/// use datex_core::compile;
386/// compile!("4 + ?", 42);
387/// compile!("? + ?", 1, 2);
388#[macro_export]
389macro_rules! compile {
390    ($fmt:literal $(, $arg:expr )* $(,)?) => {
391        {
392            let script: &str = $fmt.into();
393            let values: &[$crate::values::value_container::ValueContainer] = &[$($arg.into()),*];
394
395            $crate::compiler::compile_template(&script, values, $crate::compiler::CompileOptions::default())
396        }
397    }
398}
399
400pub fn compile_ast(
401    compilation_context: &CompilationContext,
402    ast: DatexExpression,
403    mut scope: CompilationScope,
404) -> Result<CompilationScope, CompilerError> {
405    // if once is set to true in already used, return error
406    if scope.once {
407        if scope.was_used {
408            return Err(CompilerError::OnceScopeUsedMultipleTimes);
409        }
410        // set was_used to true
411        scope.was_used = true;
412    }
413    let ast_with_metadata = if let Some(precompiler_data) = &scope.precompiler_data {
414        // precompile the AST, adding metadata for variables etc.
415        precompile_ast(
416            ast,
417            precompiler_data.ast_metadata.clone(),
418            &mut precompiler_data.precompiler_scope_stack.borrow_mut(),
419        )?
420    }
421    else {
422        // if no precompiler data, just use the AST with default metadata
423        AstWithMetadata::new_without_metadata(ast)
424    };
425
426    compile_ast_with_metadata(
427        compilation_context,
428        ast_with_metadata,
429        scope,
430    )
431}
432
433pub fn compile_ast_with_metadata(
434    compilation_context: &CompilationContext,
435    ast_with_metadata: AstWithMetadata,
436    scope: CompilationScope,
437) -> Result<CompilationScope, CompilerError> {
438    let scope = compile_expression(
439        compilation_context,
440        ast_with_metadata,
441        CompileMetadata::outer(),
442        scope,
443    )?;
444
445    // handle scope virtual addr mapping
446    compilation_context.remap_virtual_slots();
447    Ok(scope)
448}
449
450fn compile_expression(
451    compilation_context: &CompilationContext,
452    ast_with_metadata: AstWithMetadata,
453    meta: CompileMetadata,
454    mut scope: CompilationScope,
455) -> Result<CompilationScope, CompilerError> {
456    let metadata = ast_with_metadata.metadata;
457    match ast_with_metadata.ast {
458        DatexExpression::Integer(int) => {
459            compilation_context.insert_int(int.0.as_i64().unwrap());
460        }
461        DatexExpression::Decimal(decimal) => match &decimal {
462            Decimal::Finite(big_decimal) if big_decimal.is_integer() => {
463                if let Some(int) = big_decimal.to_i16() {
464                    compilation_context.insert_float_as_i16(int);
465                } else if let Some(int) = big_decimal.to_i32() {
466                    compilation_context.insert_float_as_i32(int);
467                } else {
468                    compilation_context.insert_decimal(&decimal);
469                }
470            }
471            _ => {
472                compilation_context.insert_decimal(&decimal);
473            }
474        },
475        DatexExpression::Text(text) => {
476            compilation_context.insert_text(&text);
477        }
478        DatexExpression::Boolean(boolean) => {
479            compilation_context.insert_boolean(boolean);
480        }
481        DatexExpression::Endpoint(endpoint) => {
482            compilation_context.insert_endpoint(&endpoint);
483        }
484        DatexExpression::Null => {
485            compilation_context.append_binary_code(InstructionCode::NULL);
486        }
487        DatexExpression::Array(array) => {
488            compilation_context
489                .append_binary_code(InstructionCode::ARRAY_START);
490            for item in array {
491                scope = compile_expression(
492                    compilation_context,
493                    AstWithMetadata::new(item, &metadata),
494                    CompileMetadata::default(),
495                    scope,
496                )?;
497            }
498            compilation_context.append_binary_code(InstructionCode::SCOPE_END);
499        }
500        DatexExpression::Tuple(tuple) => {
501            compilation_context
502                .append_binary_code(InstructionCode::TUPLE_START);
503            for entry in tuple {
504                match entry {
505                    TupleEntry::KeyValue(key, value) => {
506                        scope = compile_key_value_entry(
507                            compilation_context,
508                            key,
509                            value,
510                            &metadata,
511                            scope,
512                        )?;
513                    }
514                    TupleEntry::Value(value) => {
515                        scope = compile_expression(
516                            compilation_context,
517                            AstWithMetadata::new(value, &metadata),
518                            CompileMetadata::default(),
519                            scope,
520                        )?;
521                    }
522                }
523            }
524            compilation_context.append_binary_code(InstructionCode::SCOPE_END);
525        }
526        DatexExpression::Object(object) => {
527            compilation_context
528                .append_binary_code(InstructionCode::OBJECT_START);
529            for (key, value) in object {
530                // compile key and value
531                scope = compile_key_value_entry(
532                    compilation_context,
533                    key,
534                    value,
535                    &metadata,
536                    scope,
537                )?;
538            }
539            compilation_context.append_binary_code(InstructionCode::SCOPE_END);
540        }
541
542        DatexExpression::Placeholder => {
543            compilation_context.insert_value_container(
544                compilation_context
545                    .inserted_values
546                    .borrow()
547                    .get(compilation_context.inserted_value_index.get())
548                    .unwrap(),
549            );
550            compilation_context.inserted_value_index.update(|x| x + 1);
551        }
552
553        // statements
554        DatexExpression::Statements(mut statements) => {
555            compilation_context.mark_has_non_static_value();
556            // if single statement and not terminated, just compile the expression
557            if statements.len() == 1 && !statements[0].is_terminated {
558                scope = compile_expression(
559                    compilation_context,
560                    AstWithMetadata::new(
561                        statements.remove(0).expression,
562                        &metadata
563                    ),
564                    CompileMetadata::default(),
565                    scope,
566                )?;
567            } else {
568                // if not outer context, new scope
569                let mut child_scope = if !meta.is_outer_context() {
570                    compilation_context
571                        .append_binary_code(InstructionCode::SCOPE_START);
572                    scope.push()
573                } else {
574                    scope
575                };
576                for statement in statements {
577                    child_scope = compile_expression(
578                        compilation_context,
579                        AstWithMetadata::new(statement.expression, &metadata),
580                        CompileMetadata::default(),
581                        child_scope,
582                    )?;
583                    // if statement is terminated, append close and store
584                    if statement.is_terminated {
585                        compilation_context.append_binary_code(
586                            InstructionCode::CLOSE_AND_STORE,
587                        );
588                    }
589                }
590                if !meta.is_outer_context() {
591                    let scope_data = child_scope
592                        .pop()
593                        .ok_or(CompilerError::ScopePopError)?;
594                    scope = scope_data.0; // set parent scope
595                    // drop all slot addresses that were allocated in this scope
596                    for slot_address in scope_data.1 {
597                        compilation_context
598                            .append_binary_code(InstructionCode::DROP_SLOT);
599                        // insert virtual slot address for dropping
600                        compilation_context
601                            .insert_virtual_slot_address(slot_address);
602                    }
603                    compilation_context
604                        .append_binary_code(InstructionCode::SCOPE_END);
605                } else {
606                    scope = child_scope;
607                }
608            }
609        }
610
611        // operations (add, subtract, multiply, divide, etc.)
612        DatexExpression::BinaryOperation(operator, a, b) => {
613            compilation_context.mark_has_non_static_value();
614            // append binary code for operation if not already current binary operator
615            compilation_context
616                .append_binary_code(InstructionCode::from(&operator));
617            scope = compile_expression(
618                compilation_context,
619                AstWithMetadata::new(*a, &metadata),
620                CompileMetadata::default(),
621                scope,
622            )?;
623            scope = compile_expression(
624                compilation_context,
625                AstWithMetadata::new(*b, &metadata),
626                CompileMetadata::default(),
627                scope,
628            )?;
629        }
630
631        // apply
632        DatexExpression::ApplyChain(val, operands) => {
633            compilation_context.mark_has_non_static_value();
634            // TODO #150
635        }
636
637        // variables
638        // declaration
639        DatexExpression::VariableDeclaration(id, var_type, mut_type, name, expression) => {
640            compilation_context.mark_has_non_static_value();
641
642            // allocate new slot for variable
643            let virtual_slot_addr = scope.get_next_virtual_slot();
644            compilation_context
645                .append_binary_code(InstructionCode::ALLOCATE_SLOT);
646            compilation_context.insert_virtual_slot_address(
647                VirtualSlot::local(virtual_slot_addr),
648            );
649            // create reference if internally mutable
650            if mut_type == VariableMutType::Mutable {
651                compilation_context
652                    .append_binary_code(InstructionCode::CREATE_REF);
653            }
654            // compile expression
655            scope = compile_expression(
656                compilation_context,
657                AstWithMetadata::new(*expression, &metadata),
658                CompileMetadata::default(),
659                scope,
660            )?;
661
662            let variable_model = VariableModel::infer_from_ast_metadata_and_type(
663                &metadata.borrow(),
664                id,
665                var_type,
666                compilation_context.is_end_of_source_text,
667            );
668            info!("variable model for {name}: {variable_model:?}");
669
670            // create new variable depending on the model
671            let variable = match variable_model {
672                VariableModel::VariableReference => {
673                    // scope end
674                    compilation_context
675                        .append_binary_code(InstructionCode::SCOPE_END);
676                    // allocate an additional slot with a reference to the variable
677                    let virtual_slot_addr_for_var = scope.get_next_virtual_slot();
678                    compilation_context
679                        .append_binary_code(InstructionCode::ALLOCATE_SLOT);
680                    compilation_context.insert_virtual_slot_address(
681                        VirtualSlot::local(virtual_slot_addr_for_var),
682                    );
683                    // indirect reference to the variable
684                    compilation_context
685                        .append_binary_code(InstructionCode::CREATE_REF);
686                    // append binary code to load variable
687                    compilation_context
688                        .append_binary_code(InstructionCode::GET_SLOT);
689                    compilation_context.insert_virtual_slot_address(
690                        VirtualSlot::local(virtual_slot_addr),
691                    );
692
693                    Variable::new_variable_reference(
694                        name.clone(),
695                        var_type,
696                        mut_type,
697                        VirtualSlot::local(virtual_slot_addr_for_var),
698                        VirtualSlot::local(virtual_slot_addr),
699                    )
700                }
701                VariableModel::Constant => {
702                    Variable::new_const(
703                        name.clone(),
704                        var_type,
705                        mut_type,
706                        VirtualSlot::local(virtual_slot_addr),
707                    )
708                }
709                VariableModel::VariableSlot => {
710                    Variable::new_variable_slot(
711                        name.clone(),
712                        var_type,
713                        mut_type,
714                        VirtualSlot::local(virtual_slot_addr),
715                    )
716                }
717            };
718
719            scope.register_variable_slot(variable);
720
721            compilation_context.append_binary_code(InstructionCode::SCOPE_END);
722        }
723
724        // assignment
725        DatexExpression::VariableAssignment(id, name, expression) => {
726            compilation_context.mark_has_non_static_value();
727            // get variable slot address
728            let (virtual_slot, var_type, mut_type) = scope
729                .resolve_variable_name_to_virtual_slot(&name)
730                .ok_or_else(|| {
731                    CompilerError::UndeclaredVariable(name.clone())
732                })?;
733
734            // if const, return error
735            if var_type == VariableType::Const {
736                return Err(CompilerError::AssignmentToConst(name.clone()));
737            }
738
739            // append binary code to load variable
740            info!("append variable virtual slot: {virtual_slot:?}, name: {name}");
741            compilation_context
742                .append_binary_code(InstructionCode::SET_SLOT);
743            compilation_context.insert_virtual_slot_address(virtual_slot);
744            // compile expression
745            scope = compile_expression(
746                compilation_context,
747                AstWithMetadata::new(*expression, &metadata),
748                CompileMetadata::default(),
749                scope,
750            )?;
751            // close assignment scope
752            compilation_context.append_binary_code(InstructionCode::SCOPE_END);
753        }
754
755        // variable access
756        DatexExpression::Variable(id, name) => {
757            compilation_context.mark_has_non_static_value();
758            // get variable slot address
759            let (virtual_slot, ..) = scope
760                .resolve_variable_name_to_virtual_slot(&name)
761                .ok_or_else(|| {
762                    CompilerError::UndeclaredVariable(name.clone())
763                })?;
764            // append binary code to load variable
765            compilation_context.append_binary_code(InstructionCode::GET_SLOT);
766            compilation_context.insert_virtual_slot_address(virtual_slot);
767        }
768
769        // remote execution
770        DatexExpression::RemoteExecution(caller, script) => {
771            compilation_context.mark_has_non_static_value();
772
773            // insert remote execution code
774            compilation_context
775                .append_binary_code(InstructionCode::REMOTE_EXECUTION);
776            // insert compiled caller expression
777            scope = compile_expression(
778                compilation_context,
779                AstWithMetadata::new(*caller, &metadata),
780                CompileMetadata::default(),
781                scope,
782            )?;
783
784            // compile remote execution block
785            let execution_block_ctx =
786                CompilationContext::new(RefCell::new(Vec::with_capacity(256)), &[], true);
787            let external_scope = compile_ast_with_metadata(
788                &execution_block_ctx,
789                AstWithMetadata::new(
790                    *script,
791                    &metadata,
792                ),
793                CompilationScope::new_with_external_parent_scope(scope),
794            )?;
795            // reset to current scope
796            scope = external_scope.pop_external().ok_or_else(|| {
797                CompilerError::ScopePopError
798            })?;
799
800            let external_slots = execution_block_ctx.external_slots();
801            // start block
802            compilation_context
803                .append_binary_code(InstructionCode::EXECUTION_BLOCK);
804            // set block size (len of compilation_context.buffer)
805            compilation_context
806                .append_u32(execution_block_ctx.buffer.borrow().len() as u32);
807            // set injected slot count
808            compilation_context.append_u32(external_slots.len() as u32);
809            for slot in external_slots {
810                compilation_context.insert_virtual_slot_address(slot.upgrade());
811            }
812
813            // insert block body (compilation_context.buffer)
814            compilation_context.append_buffer(
815                &execution_block_ctx.buffer.borrow(),
816            )
817        }
818
819        // named slot
820        DatexExpression::Slot(Slot::Named(name)) => {
821            match name.as_str() {
822                "endpoint" => {
823                    compilation_context.append_binary_code(
824                        InstructionCode::GET_SLOT,
825                    );
826                    compilation_context.append_u32(InternalSlot::ENDPOINT as u32);
827                }
828                _ => {
829                    // invalid slot name
830                    return Err(CompilerError::InvalidSlotName(name.clone()));
831                }
832            }
833        }
834
835        _ => return Err(CompilerError::UnexpectedTerm(ast_with_metadata.ast)),
836    }
837
838    Ok(scope)
839}
840
841fn compile_key_value_entry(
842    compilation_scope: &CompilationContext,
843    key: DatexExpression,
844    value: DatexExpression,
845    metadata: &Rc<RefCell<AstMetadata>>,
846    mut scope: CompilationScope,
847) -> Result<CompilationScope, CompilerError> {
848    match key {
849        // text -> insert key string
850        DatexExpression::Text(text) => {
851            compilation_scope.insert_key_string(&text);
852        }
853        // other -> insert key as dynamic
854        _ => {
855            compilation_scope
856                .append_binary_code(InstructionCode::KEY_VALUE_DYNAMIC);
857            scope = compile_expression(
858                compilation_scope,
859                AstWithMetadata::new(key, metadata),
860                CompileMetadata::default(),
861                scope,
862            )?;
863        }
864    };
865    // insert value
866    scope = compile_expression(
867        compilation_scope,
868        AstWithMetadata::new(value, metadata),
869        CompileMetadata::default(),
870        scope,
871    )?;
872    Ok(scope)
873}
874
875#[cfg(test)]
876pub mod tests {
877    use std::assert_matches::assert_matches;
878    use super::{
879        CompileOptions, CompilationContext, CompilationScope, StaticValueOrDXB, compile_ast,
880        compile_script, compile_script_or_return_static_value,
881        compile_template,
882    };
883    use std::cell::RefCell;
884    use std::io::Read;
885    use std::vec;
886
887    use crate::{global::binary_codes::InstructionCode, logger::init_logger_debug};
888    use log::*;
889    use datex_core::compiler::error::CompilerError;
890    use crate::compiler::ast_parser::parse;
891    use crate::values::core_values::integer::integer::Integer;
892
893    fn compile_and_log(datex_script: &str) -> Vec<u8> {
894        init_logger_debug();
895        let (result, _) =
896            compile_script(datex_script, CompileOptions::default()).unwrap();
897        info!(
898            "{:?}",
899            result
900                .iter()
901                .map(|x| InstructionCode::try_from(*x).map(|x| x.to_string()))
902                .map(|x| x.unwrap_or_else(|_| "Unknown".to_string()))
903                .collect::<Vec<_>>()
904        );
905        result
906    }
907
908    fn get_compilation_scope(script: &str) -> CompilationContext {
909        let ast = parse(script);
910        let ast = ast.unwrap();
911        let buffer = RefCell::new(Vec::with_capacity(256));
912        let compilation_scope = CompilationContext::new(buffer, &[], true);
913        compile_ast(&compilation_scope, ast, CompilationScope::default()).unwrap();
914        compilation_scope
915    }
916
917    #[test]
918    fn test_simple_multiplication() {
919        init_logger_debug();
920
921        let lhs: u8 = 1;
922        let rhs: u8 = 2;
923        let datex_script = format!("{lhs} * {rhs}"); // 1 * 2
924        let result = compile_and_log(&datex_script);
925        assert_eq!(
926            result,
927            vec![
928                InstructionCode::MULTIPLY.into(),
929                InstructionCode::INT_8.into(),
930                lhs,
931                InstructionCode::INT_8.into(),
932                rhs,
933            ]
934        );
935    }
936
937    #[test]
938    fn test_simple_multiplication_close() {
939        init_logger_debug();
940
941        let lhs: u8 = 1;
942        let rhs: u8 = 2;
943        let datex_script = format!("{lhs} * {rhs};"); // 1 * 2
944        let result = compile_and_log(&datex_script);
945        assert_eq!(
946            result,
947            vec![
948                InstructionCode::MULTIPLY.into(),
949                InstructionCode::INT_8.into(),
950                lhs,
951                InstructionCode::INT_8.into(),
952                rhs,
953                InstructionCode::CLOSE_AND_STORE.into()
954            ]
955        );
956    }
957
958    #[test]
959    fn test_is_operator() {
960        init_logger_debug();
961
962        // TODO #151: compare refs
963        let datex_script = "1 is 2".to_string();
964        let result = compile_and_log(&datex_script);
965        assert_eq!(
966            result,
967            vec![
968                InstructionCode::IS.into(),
969                InstructionCode::INT_8.into(),
970                1,
971                InstructionCode::INT_8.into(),
972                2
973            ]
974        );
975
976        let datex_script = "const mut a = 42; const mut b = 69; a is b".to_string(); // a is b
977        let result = compile_and_log(&datex_script);
978        assert_eq!(
979            result,
980            vec![
981                // val a = 42;
982                InstructionCode::ALLOCATE_SLOT.into(),
983                0,
984                0,
985                0,
986                0,
987                InstructionCode::CREATE_REF.into(),
988                InstructionCode::INT_8.into(),
989                42,
990                InstructionCode::SCOPE_END.into(),
991                InstructionCode::CLOSE_AND_STORE.into(),
992                // val b = 69;
993                InstructionCode::ALLOCATE_SLOT.into(),
994                1,
995                0,
996                0,
997                0,
998                InstructionCode::CREATE_REF.into(),
999                InstructionCode::INT_8.into(),
1000                69,
1001                InstructionCode::SCOPE_END.into(),
1002                InstructionCode::CLOSE_AND_STORE.into(),
1003                // a is b
1004                InstructionCode::IS.into(),
1005                InstructionCode::GET_SLOT.into(),
1006                0,
1007                0,
1008                0,
1009                0, // slot address for a
1010                InstructionCode::GET_SLOT.into(),
1011                1,
1012                0,
1013                0,
1014                0, // slot address for b
1015            ]
1016        );
1017    }
1018
1019    #[test]
1020    fn test_equality_operator() {
1021        init_logger_debug();
1022
1023        let lhs: u8 = 1;
1024        let rhs: u8 = 2;
1025        let datex_script = format!("{lhs} == {rhs}"); // 1 == 2
1026        let result = compile_and_log(&datex_script);
1027        assert_eq!(
1028            result,
1029            vec![
1030                InstructionCode::STRUCTURAL_EQUAL.into(),
1031                InstructionCode::INT_8.into(),
1032                lhs,
1033                InstructionCode::INT_8.into(),
1034                rhs,
1035            ]
1036        );
1037
1038        let datex_script = format!("{lhs} === {rhs}"); // 1 === 2
1039        let result = compile_and_log(&datex_script);
1040        assert_eq!(
1041            result,
1042            vec![
1043                InstructionCode::EQUAL.into(),
1044                InstructionCode::INT_8.into(),
1045                lhs,
1046                InstructionCode::INT_8.into(),
1047                rhs,
1048            ]
1049        );
1050
1051        let datex_script = format!("{lhs} != {rhs}"); // 1 != 2
1052        let result = compile_and_log(&datex_script);
1053        assert_eq!(
1054            result,
1055            vec![
1056                InstructionCode::NOT_STRUCTURAL_EQUAL.into(),
1057                InstructionCode::INT_8.into(),
1058                lhs,
1059                InstructionCode::INT_8.into(),
1060                rhs,
1061            ]
1062        );
1063        let datex_script = format!("{lhs} !== {rhs}"); // 1 !== 2
1064        let result = compile_and_log(&datex_script);
1065        assert_eq!(
1066            result,
1067            vec![
1068                InstructionCode::NOT_EQUAL.into(),
1069                InstructionCode::INT_8.into(),
1070                lhs,
1071                InstructionCode::INT_8.into(),
1072                rhs,
1073            ]
1074        );
1075    }
1076
1077    #[test]
1078    fn test_simple_addition() {
1079        init_logger_debug();
1080
1081        let lhs: u8 = 1;
1082        let rhs: u8 = 2;
1083        let datex_script = format!("{lhs} + {rhs}"); // 1 + 2
1084        let result = compile_and_log(&datex_script);
1085        assert_eq!(
1086            result,
1087            vec![
1088                InstructionCode::ADD.into(),
1089                InstructionCode::INT_8.into(),
1090                lhs,
1091                InstructionCode::INT_8.into(),
1092                rhs
1093            ]
1094        );
1095
1096        let datex_script = format!("{lhs} + {rhs};"); // 1 + 2;
1097        let result = compile_and_log(&datex_script);
1098        assert_eq!(
1099            result,
1100            vec![
1101                InstructionCode::ADD.into(),
1102                InstructionCode::INT_8.into(),
1103                lhs,
1104                InstructionCode::INT_8.into(),
1105                rhs,
1106                InstructionCode::CLOSE_AND_STORE.into()
1107            ]
1108        );
1109    }
1110
1111    #[test]
1112    fn test_multi_addition() {
1113        init_logger_debug();
1114
1115        let op1: u8 = 1;
1116        let op2: u8 = 2;
1117        let op3: u8 = 3;
1118        let op4: u8 = 4;
1119
1120        let datex_script = format!("{op1} + {op2} + {op3} + {op4}"); // 1 + 2 + 3 + 4
1121        let result = compile_and_log(&datex_script);
1122        assert_eq!(
1123            result,
1124            vec![
1125                InstructionCode::ADD.into(),
1126                InstructionCode::ADD.into(),
1127                InstructionCode::ADD.into(),
1128                InstructionCode::INT_8.into(),
1129                op1,
1130                InstructionCode::INT_8.into(),
1131                op2,
1132                InstructionCode::INT_8.into(),
1133                op3,
1134                InstructionCode::INT_8.into(),
1135                op4,
1136            ]
1137        );
1138    }
1139
1140    #[test]
1141    fn test_mixed_calculation() {
1142        init_logger_debug();
1143
1144        let op1: u8 = 1;
1145        let op2: u8 = 2;
1146        let op3: u8 = 3;
1147        let op4: u8 = 4;
1148
1149        let datex_script = format!("{op1} * {op2} + {op3} * {op4}"); // 1 + 2 + 3 + 4
1150        let result = compile_and_log(&datex_script);
1151        assert_eq!(
1152            result,
1153            vec![
1154                InstructionCode::ADD.into(),
1155                InstructionCode::MULTIPLY.into(),
1156                InstructionCode::INT_8.into(),
1157                op1,
1158                InstructionCode::INT_8.into(),
1159                op2,
1160                InstructionCode::MULTIPLY.into(),
1161                InstructionCode::INT_8.into(),
1162                op3,
1163                InstructionCode::INT_8.into(),
1164                op4,
1165            ]
1166        );
1167    }
1168
1169    #[test]
1170    fn test_complex_addition() {
1171        init_logger_debug();
1172
1173        let a: u8 = 1;
1174        let b: u8 = 2;
1175        let c: u8 = 3;
1176        let datex_script = format!("{a} + ({b} + {c})"); // 1 + (2 + 3)
1177        let result = compile_and_log(&datex_script);
1178
1179        // note: scope is automatically collapsed by the parser since this is all the same operation
1180        // TODO #152: we might need to change this to support nested additions, or maybe not if we only allow additions
1181        // of values of the same type?...
1182        assert_eq!(
1183            result,
1184            vec![
1185                InstructionCode::ADD.into(),
1186                InstructionCode::INT_8.into(),
1187                a,
1188                InstructionCode::ADD.into(),
1189                InstructionCode::INT_8.into(),
1190                b,
1191                InstructionCode::INT_8.into(),
1192                c,
1193            ]
1194        );
1195    }
1196
1197    #[test]
1198    fn test_complex_addition_and_subtraction() {
1199        init_logger_debug();
1200
1201        let a: u8 = 1;
1202        let b: u8 = 2;
1203        let c: u8 = 3;
1204        let datex_script = format!("{a} + ({b} - {c})"); // 1 + (2 - 3)
1205        let result = compile_and_log(&datex_script);
1206        assert_eq!(
1207            result,
1208            vec![
1209                InstructionCode::ADD.into(),
1210                InstructionCode::INT_8.into(),
1211                a,
1212                InstructionCode::SUBTRACT.into(),
1213                InstructionCode::INT_8.into(),
1214                b,
1215                InstructionCode::INT_8.into(),
1216                c,
1217            ]
1218        );
1219    }
1220
1221    // Test for integer/u8
1222    #[test]
1223    fn test_integer_u8() {
1224        init_logger_debug();
1225        let val: u8 = 42;
1226        let datex_script = format!("{val}"); // 42
1227        let result = compile_and_log(&datex_script);
1228        assert_eq!(result, vec![InstructionCode::INT_8.into(), val,]);
1229    }
1230
1231    // Test for decimal
1232    #[test]
1233    fn test_decimal() {
1234        init_logger_debug();
1235        let datex_script = "42.0";
1236        let result = compile_and_log(datex_script);
1237        let bytes = 42_i16.to_le_bytes();
1238
1239        let mut expected: Vec<u8> =
1240            vec![InstructionCode::DECIMAL_AS_INT_16.into()];
1241        expected.extend(bytes);
1242
1243        assert_eq!(result, expected);
1244    }
1245
1246    /// Test for test that is less than 256 characters
1247    #[test]
1248    fn test_short_text() {
1249        init_logger_debug();
1250        let val = "unyt";
1251        let datex_script = format!("\"{val}\""); // "42"
1252        let result = compile_and_log(&datex_script);
1253        let mut expected: Vec<u8> =
1254            vec![InstructionCode::SHORT_TEXT.into(), val.len() as u8];
1255        expected.extend(val.bytes());
1256        assert_eq!(result, expected);
1257    }
1258
1259    // Test empty array
1260    #[test]
1261    fn test_empty_array() {
1262        init_logger_debug();
1263        let datex_script = "[]";
1264        let result = compile_and_log(datex_script);
1265        let expected: Vec<u8> = vec![
1266            InstructionCode::ARRAY_START.into(),
1267            InstructionCode::SCOPE_END.into(),
1268        ];
1269        assert_eq!(result, expected);
1270    }
1271
1272    // Test array with single element
1273    #[test]
1274    fn test_single_element_array() {
1275        init_logger_debug();
1276        let datex_script = "[42]";
1277        let result = compile_and_log(datex_script);
1278        assert_eq!(
1279            result,
1280            vec![
1281                InstructionCode::ARRAY_START.into(),
1282                InstructionCode::INT_8.into(),
1283                42,
1284                InstructionCode::SCOPE_END.into(),
1285            ]
1286        );
1287    }
1288
1289    // Test array with multiple elements
1290    #[test]
1291    fn test_multi_element_array() {
1292        init_logger_debug();
1293        let datex_script = "[1, 2, 3]";
1294        let result = compile_and_log(datex_script);
1295        assert_eq!(
1296            result,
1297            vec![
1298                InstructionCode::ARRAY_START.into(),
1299                InstructionCode::INT_8.into(),
1300                1,
1301                InstructionCode::INT_8.into(),
1302                2,
1303                InstructionCode::INT_8.into(),
1304                3,
1305                InstructionCode::SCOPE_END.into(),
1306            ]
1307        );
1308    }
1309
1310    // Test nested arrays
1311    #[test]
1312    fn test_nested_arrays() {
1313        init_logger_debug();
1314        let datex_script = "[1, [2, 3], 4]";
1315        let result = compile_and_log(datex_script);
1316        assert_eq!(
1317            result,
1318            vec![
1319                InstructionCode::ARRAY_START.into(),
1320                InstructionCode::INT_8.into(),
1321                1,
1322                InstructionCode::ARRAY_START.into(),
1323                InstructionCode::INT_8.into(),
1324                2,
1325                InstructionCode::INT_8.into(),
1326                3,
1327                InstructionCode::SCOPE_END.into(),
1328                InstructionCode::INT_8.into(),
1329                4,
1330                InstructionCode::SCOPE_END.into(),
1331            ]
1332        );
1333    }
1334
1335    // Test array with expressions inside
1336    #[test]
1337    fn test_array_with_expressions() {
1338        init_logger_debug();
1339        let datex_script = "[1 + 2, 3 * 4]";
1340        let result = compile_and_log(datex_script);
1341        assert_eq!(
1342            result,
1343            vec![
1344                InstructionCode::ARRAY_START.into(),
1345                InstructionCode::ADD.into(),
1346                InstructionCode::INT_8.into(),
1347                1,
1348                InstructionCode::INT_8.into(),
1349                2,
1350                InstructionCode::MULTIPLY.into(),
1351                InstructionCode::INT_8.into(),
1352                3,
1353                InstructionCode::INT_8.into(),
1354                4,
1355                InstructionCode::SCOPE_END.into(),
1356            ]
1357        );
1358    }
1359
1360    // Test array with mixed expressions
1361    #[test]
1362    fn test_array_with_mixed_expressions() {
1363        init_logger_debug();
1364        let datex_script = "[1, 2, 3 + 4]";
1365        let result = compile_and_log(datex_script);
1366        assert_eq!(
1367            result,
1368            vec![
1369                InstructionCode::ARRAY_START.into(),
1370                InstructionCode::INT_8.into(),
1371                1,
1372                InstructionCode::INT_8.into(),
1373                2,
1374                InstructionCode::ADD.into(),
1375                InstructionCode::INT_8.into(),
1376                3,
1377                InstructionCode::INT_8.into(),
1378                4,
1379                InstructionCode::SCOPE_END.into(),
1380            ]
1381        );
1382    }
1383
1384    // Test tuple
1385    #[test]
1386    fn test_tuple() {
1387        init_logger_debug();
1388        let datex_script = "(1, 2, 3)";
1389        let result = compile_and_log(datex_script);
1390        assert_eq!(
1391            result,
1392            vec![
1393                InstructionCode::TUPLE_START.into(),
1394                InstructionCode::INT_8.into(),
1395                1,
1396                InstructionCode::INT_8.into(),
1397                2,
1398                InstructionCode::INT_8.into(),
1399                3,
1400                InstructionCode::SCOPE_END.into(),
1401            ]
1402        );
1403    }
1404
1405    // Nested tuple
1406    #[test]
1407    fn test_nested_tuple() {
1408        init_logger_debug();
1409        let datex_script = "(1, (2, 3), 4)";
1410        let result = compile_and_log(datex_script);
1411        assert_eq!(
1412            result,
1413            vec![
1414                InstructionCode::TUPLE_START.into(),
1415                InstructionCode::INT_8.into(),
1416                1,
1417                InstructionCode::TUPLE_START.into(),
1418                InstructionCode::INT_8.into(),
1419                2,
1420                InstructionCode::INT_8.into(),
1421                3,
1422                InstructionCode::SCOPE_END.into(),
1423                InstructionCode::INT_8.into(),
1424                4,
1425                InstructionCode::SCOPE_END.into(),
1426            ]
1427        );
1428    }
1429
1430    // Tuple without parentheses
1431    #[test]
1432    fn test_tuple_without_parentheses() {
1433        init_logger_debug();
1434        let datex_script = "1, 2, 3";
1435        let result = compile_and_log(datex_script);
1436        assert_eq!(
1437            result,
1438            vec![
1439                InstructionCode::TUPLE_START.into(),
1440                InstructionCode::INT_8.into(),
1441                1,
1442                InstructionCode::INT_8.into(),
1443                2,
1444                InstructionCode::INT_8.into(),
1445                3,
1446                InstructionCode::SCOPE_END.into(),
1447            ]
1448        );
1449    }
1450
1451    // key-value pair
1452    #[test]
1453    fn test_key_value_tuple() {
1454        init_logger_debug();
1455        let datex_script = "key: 42";
1456        let result = compile_and_log(datex_script);
1457        let expected = vec![
1458            InstructionCode::TUPLE_START.into(),
1459            InstructionCode::KEY_VALUE_SHORT_TEXT.into(),
1460            3, // length of "key"
1461            b'k',
1462            b'e',
1463            b'y',
1464            InstructionCode::INT_8.into(),
1465            42,
1466            InstructionCode::SCOPE_END.into(),
1467        ];
1468        assert_eq!(result, expected);
1469    }
1470
1471    // key-value pair with string key
1472    #[test]
1473    fn test_key_value_string() {
1474        init_logger_debug();
1475        let datex_script = "\"key\": 42";
1476        let result = compile_and_log(datex_script);
1477        let expected = vec![
1478            InstructionCode::TUPLE_START.into(),
1479            InstructionCode::KEY_VALUE_SHORT_TEXT.into(),
1480            3, // length of "key"
1481            b'k',
1482            b'e',
1483            b'y',
1484            InstructionCode::INT_8.into(),
1485            42,
1486            InstructionCode::SCOPE_END.into(),
1487        ];
1488        assert_eq!(result, expected);
1489    }
1490
1491    // key-value pair with integer key
1492    #[test]
1493    fn test_key_value_integer() {
1494        init_logger_debug();
1495        let datex_script = "10: 42";
1496        let result = compile_and_log(datex_script);
1497        let expected = vec![
1498            InstructionCode::TUPLE_START.into(),
1499            InstructionCode::KEY_VALUE_DYNAMIC.into(),
1500            InstructionCode::INT_8.into(),
1501            10,
1502            InstructionCode::INT_8.into(),
1503            42,
1504            InstructionCode::SCOPE_END.into(),
1505        ];
1506        assert_eq!(result, expected);
1507    }
1508
1509    // key-value pair with long text key (>255 bytes)
1510    #[test]
1511    fn test_key_value_long_text() {
1512        init_logger_debug();
1513        let long_key = "a".repeat(300);
1514        let datex_script = format!("\"{long_key}\": 42");
1515        let result = compile_and_log(&datex_script);
1516        let mut expected: Vec<u8> = vec![
1517            InstructionCode::TUPLE_START.into(),
1518            InstructionCode::KEY_VALUE_DYNAMIC.into(),
1519            InstructionCode::TEXT.into(),
1520        ];
1521        expected.extend((long_key.len() as u32).to_le_bytes());
1522        expected.extend(long_key.as_bytes());
1523        expected.extend(vec![
1524            InstructionCode::INT_8.into(),
1525            42,
1526            InstructionCode::SCOPE_END.into(),
1527        ]);
1528        assert_eq!(result, expected);
1529    }
1530
1531    // dynamic key-value pair
1532    #[test]
1533    fn test_dynamic_key_value() {
1534        init_logger_debug();
1535        let datex_script = "(1 + 2): 42";
1536        let result = compile_and_log(datex_script);
1537        let expected = [
1538            InstructionCode::TUPLE_START.into(),
1539            InstructionCode::KEY_VALUE_DYNAMIC.into(),
1540            InstructionCode::ADD.into(),
1541            InstructionCode::INT_8.into(),
1542            1,
1543            InstructionCode::INT_8.into(),
1544            2,
1545            InstructionCode::INT_8.into(),
1546            42,
1547            InstructionCode::SCOPE_END.into(),
1548        ];
1549        assert_eq!(result, expected);
1550    }
1551
1552    // multiple key-value pairs
1553    #[test]
1554    fn test_multiple_key_value_pairs() {
1555        init_logger_debug();
1556        let datex_script = "key: 42, 4: 43, (1 + 2): 44";
1557        let result = compile_and_log(datex_script);
1558        let expected = vec![
1559            InstructionCode::TUPLE_START.into(),
1560            InstructionCode::KEY_VALUE_SHORT_TEXT.into(),
1561            3, // length of "key"
1562            b'k',
1563            b'e',
1564            b'y',
1565            InstructionCode::INT_8.into(),
1566            42,
1567            InstructionCode::KEY_VALUE_DYNAMIC.into(),
1568            InstructionCode::INT_8.into(),
1569            4,
1570            InstructionCode::INT_8.into(),
1571            43,
1572            InstructionCode::KEY_VALUE_DYNAMIC.into(),
1573            InstructionCode::ADD.into(),
1574            InstructionCode::INT_8.into(),
1575            1,
1576            InstructionCode::INT_8.into(),
1577            2,
1578            InstructionCode::INT_8.into(),
1579            44,
1580            InstructionCode::SCOPE_END.into(),
1581        ];
1582        assert_eq!(result, expected);
1583    }
1584
1585    // key value pair with parentheses
1586    #[test]
1587    fn test_key_value_with_parentheses() {
1588        init_logger_debug();
1589        let datex_script = "(key: 42)";
1590        let result = compile_and_log(datex_script);
1591        let expected = vec![
1592            InstructionCode::TUPLE_START.into(),
1593            InstructionCode::KEY_VALUE_SHORT_TEXT.into(),
1594            3, // length of "key"
1595            b'k',
1596            b'e',
1597            b'y',
1598            InstructionCode::INT_8.into(),
1599            42,
1600            InstructionCode::SCOPE_END.into(),
1601        ];
1602        assert_eq!(result, expected);
1603    }
1604
1605    // empty object
1606    #[test]
1607    fn test_empty_object() {
1608        init_logger_debug();
1609        let datex_script = "{}";
1610        let result = compile_and_log(datex_script);
1611        let expected: Vec<u8> = vec![
1612            InstructionCode::OBJECT_START.into(),
1613            InstructionCode::SCOPE_END.into(),
1614        ];
1615        assert_eq!(result, expected);
1616    }
1617
1618    // object with single key-value pair
1619    #[test]
1620    fn test_single_key_value_object() {
1621        init_logger_debug();
1622        let datex_script = "{key: 42}";
1623        let result = compile_and_log(datex_script);
1624        let expected = vec![
1625            InstructionCode::OBJECT_START.into(),
1626            InstructionCode::KEY_VALUE_SHORT_TEXT.into(),
1627            3, // length of "key"
1628            b'k',
1629            b'e',
1630            b'y',
1631            InstructionCode::INT_8.into(),
1632            42,
1633            InstructionCode::SCOPE_END.into(),
1634        ];
1635        assert_eq!(result, expected);
1636    }
1637
1638    // object with multiple key-value pairs
1639    #[test]
1640    fn test_multi_key_value_object() {
1641        init_logger_debug();
1642        let datex_script = "{key1: 42, \"key2\": 43, 'key3': 44}";
1643        let result = compile_and_log(datex_script);
1644        let expected = vec![
1645            InstructionCode::OBJECT_START.into(),
1646            InstructionCode::KEY_VALUE_SHORT_TEXT.into(),
1647            4, // length of "key1"
1648            b'k',
1649            b'e',
1650            b'y',
1651            b'1',
1652            InstructionCode::INT_8.into(),
1653            42,
1654            InstructionCode::KEY_VALUE_SHORT_TEXT.into(),
1655            4, // length of "key2"
1656            b'k',
1657            b'e',
1658            b'y',
1659            b'2',
1660            InstructionCode::INT_8.into(),
1661            43,
1662            InstructionCode::KEY_VALUE_SHORT_TEXT.into(),
1663            4, // length of "key3"
1664            b'k',
1665            b'e',
1666            b'y',
1667            b'3',
1668            InstructionCode::INT_8.into(),
1669            44,
1670            InstructionCode::SCOPE_END.into(),
1671        ];
1672        assert_eq!(result, expected);
1673    }
1674
1675    #[test]
1676    fn test_allocate_slot() {
1677        init_logger_debug();
1678        let script = "const a = 42";
1679        let result = compile_and_log(script);
1680        assert_eq!(
1681            result,
1682            vec![
1683                InstructionCode::ALLOCATE_SLOT.into(),
1684                // slot index as u32
1685                0,
1686                0,
1687                0,
1688                0,
1689                InstructionCode::INT_8.into(),
1690                42,
1691                InstructionCode::SCOPE_END.into(),
1692            ]
1693        );
1694    }
1695
1696    #[test]
1697    fn test_allocate_slot_with_value() {
1698        init_logger_debug();
1699        let script = "const a = 42; a + 1";
1700        let result = compile_and_log(script);
1701        assert_eq!(
1702            result,
1703            vec![
1704                InstructionCode::ALLOCATE_SLOT.into(),
1705                // slot index as u32
1706                0,
1707                0,
1708                0,
1709                0,
1710                InstructionCode::INT_8.into(),
1711                42,
1712                InstructionCode::SCOPE_END.into(),
1713                InstructionCode::CLOSE_AND_STORE.into(),
1714                InstructionCode::ADD.into(),
1715                InstructionCode::GET_SLOT.into(),
1716                // slot index as u32
1717                0,
1718                0,
1719                0,
1720                0,
1721                InstructionCode::INT_8.into(),
1722                1,
1723            ]
1724        );
1725    }
1726
1727    #[test]
1728    fn test_allocate_scoped_slots() {
1729        init_logger_debug();
1730        let script = "const a = 42; (const a = 43; a); a";
1731        let result = compile_and_log(script);
1732        assert_eq!(
1733            result,
1734            vec![
1735                InstructionCode::ALLOCATE_SLOT.into(),
1736                0,
1737                0,
1738                0,
1739                0,
1740                InstructionCode::INT_8.into(),
1741                42,
1742                InstructionCode::SCOPE_END.into(),
1743                InstructionCode::CLOSE_AND_STORE.into(),
1744                InstructionCode::SCOPE_START.into(),
1745                InstructionCode::ALLOCATE_SLOT.into(),
1746                1,
1747                0,
1748                0,
1749                0,
1750                InstructionCode::INT_8.into(),
1751                43,
1752                InstructionCode::SCOPE_END.into(),
1753                InstructionCode::CLOSE_AND_STORE.into(),
1754                InstructionCode::GET_SLOT.into(),
1755                1,
1756                0,
1757                0,
1758                0,
1759                InstructionCode::DROP_SLOT.into(),
1760                1,
1761                0,
1762                0,
1763                0,
1764                InstructionCode::SCOPE_END.into(),
1765                InstructionCode::CLOSE_AND_STORE.into(),
1766                InstructionCode::GET_SLOT.into(),
1767                // slot index as u32
1768                0,
1769                0,
1770                0,
1771                0,
1772            ]
1773        );
1774    }
1775
1776    #[test]
1777    fn test_allocate_scoped_slots_with_parent_variables() {
1778        init_logger_debug();
1779        let script = "const a = 42; const b = 41; (const a = 43; a; b); a";
1780        let result = compile_and_log(script);
1781        assert_eq!(
1782            result,
1783            vec![
1784                InstructionCode::ALLOCATE_SLOT.into(),
1785                0,
1786                0,
1787                0,
1788                0,
1789                InstructionCode::INT_8.into(),
1790                42,
1791                InstructionCode::SCOPE_END.into(),
1792                InstructionCode::CLOSE_AND_STORE.into(),
1793                InstructionCode::ALLOCATE_SLOT.into(),
1794                1,
1795                0,
1796                0,
1797                0,
1798                InstructionCode::INT_8.into(),
1799                41,
1800                InstructionCode::SCOPE_END.into(),
1801                InstructionCode::CLOSE_AND_STORE.into(),
1802                InstructionCode::SCOPE_START.into(),
1803                InstructionCode::ALLOCATE_SLOT.into(),
1804                2,
1805                0,
1806                0,
1807                0,
1808                InstructionCode::INT_8.into(),
1809                43,
1810                InstructionCode::SCOPE_END.into(),
1811                InstructionCode::CLOSE_AND_STORE.into(),
1812                InstructionCode::GET_SLOT.into(),
1813                2,
1814                0,
1815                0,
1816                0,
1817                InstructionCode::CLOSE_AND_STORE.into(),
1818                InstructionCode::GET_SLOT.into(),
1819                1,
1820                0,
1821                0,
1822                0,
1823                InstructionCode::DROP_SLOT.into(),
1824                2,
1825                0,
1826                0,
1827                0,
1828                InstructionCode::SCOPE_END.into(),
1829                InstructionCode::CLOSE_AND_STORE.into(),
1830                InstructionCode::GET_SLOT.into(),
1831                // slot index as u32
1832                0,
1833                0,
1834                0,
1835                0,
1836            ]
1837        );
1838    }
1839
1840    #[test]
1841    fn test_allocate_ref() {
1842        init_logger_debug();
1843        let script = "const mut a = 42";
1844        let result = compile_and_log(script);
1845        assert_eq!(
1846            result,
1847            vec![
1848                InstructionCode::ALLOCATE_SLOT.into(),
1849                // slot index as u32
1850                0,
1851                0,
1852                0,
1853                0,
1854                InstructionCode::CREATE_REF.into(),
1855                InstructionCode::INT_8.into(),
1856                42,
1857                InstructionCode::SCOPE_END.into(),
1858            ]
1859        );
1860    }
1861
1862    #[test]
1863    fn test_read_ref() {
1864        init_logger_debug();
1865        let script = "const mut a = 42; a";
1866        let result = compile_and_log(script);
1867        assert_eq!(
1868            result,
1869            vec![
1870                InstructionCode::ALLOCATE_SLOT.into(),
1871                // slot index as u32
1872                0,
1873                0,
1874                0,
1875                0,
1876                InstructionCode::CREATE_REF.into(),
1877                InstructionCode::INT_8.into(),
1878                42,
1879                InstructionCode::SCOPE_END.into(),
1880                InstructionCode::CLOSE_AND_STORE.into(),
1881                InstructionCode::GET_SLOT.into(),
1882                // slot index as u32
1883                0,
1884                0,
1885                0,
1886                0,
1887            ]
1888        );
1889    }
1890
1891    #[test]
1892    fn test_compile() {
1893        init_logger_debug();
1894        let result = compile_template(
1895            "? + ?",
1896            &[1.into(), 2.into()],
1897            CompileOptions::default(),
1898        );
1899        assert_eq!(
1900            result.unwrap().0,
1901            vec![
1902                InstructionCode::ADD.into(),
1903                InstructionCode::INT_8.into(),
1904                1,
1905                InstructionCode::INT_8.into(),
1906                2
1907            ]
1908        );
1909    }
1910
1911    #[test]
1912    fn test_compile_macro() {
1913        init_logger_debug();
1914        let a = 1;
1915        let result = compile!("?", a);
1916        assert_eq!(result.unwrap().0, vec![InstructionCode::INT_8.into(), 1,]);
1917    }
1918
1919    #[test]
1920    fn test_compile_macro_multi() {
1921        init_logger_debug();
1922        let result = compile!("? + ?", 1, 2);
1923        assert_eq!(
1924            result.unwrap().0,
1925            vec![
1926                InstructionCode::ADD.into(),
1927                InstructionCode::INT_8.into(),
1928                1,
1929                InstructionCode::INT_8.into(),
1930                2
1931            ]
1932        );
1933    }
1934
1935    fn get_json_test_string(file_path: &str) -> String {
1936        // read json from test file
1937        let file_path = format!("benches/json/{file_path}");
1938        let file_path = std::path::Path::new(&file_path);
1939        let file =
1940            std::fs::File::open(file_path).expect("Failed to open test.json");
1941        let mut reader = std::io::BufReader::new(file);
1942        let mut json_string = String::new();
1943        reader
1944            .read_to_string(&mut json_string)
1945            .expect("Failed to read test.json");
1946        json_string
1947    }
1948
1949    #[test]
1950    fn test_json_to_dxb_large_file() {
1951        let json = get_json_test_string("test2.json");
1952        println!("JSON file read");
1953        let (dxb, _) = compile_script(&json, CompileOptions::default())
1954            .expect("Failed to parse JSON string");
1955        println!("DXB: {:?}", dxb.len());
1956    }
1957
1958    #[test]
1959    fn test_static_value_detection() {
1960        init_logger_debug();
1961
1962        // non-static
1963        let script = "1 + 2";
1964        let compilation_scope = get_compilation_scope(script);
1965        assert!(*compilation_scope.has_non_static_value.borrow());
1966
1967        let script = "1 2";
1968        let compilation_scope = get_compilation_scope(script);
1969        assert!(*compilation_scope.has_non_static_value.borrow());
1970
1971        let script = "1;2";
1972        let compilation_scope = get_compilation_scope(script);
1973        assert!(*compilation_scope.has_non_static_value.borrow());
1974
1975        let script = r#"{("x" + "y"): 1}"#;
1976        let compilation_scope = get_compilation_scope(script);
1977        assert!(*compilation_scope.has_non_static_value.borrow());
1978
1979        // static
1980        let script = "1";
1981        let compilation_scope = get_compilation_scope(script);
1982        assert!(!*compilation_scope.has_non_static_value.borrow());
1983
1984        let script = "[]";
1985        let compilation_scope = get_compilation_scope(script);
1986        assert!(!*compilation_scope.has_non_static_value.borrow());
1987
1988        let script = "{}";
1989        let compilation_scope = get_compilation_scope(script);
1990        assert!(!*compilation_scope.has_non_static_value.borrow());
1991
1992        let script = "[1,2,3]";
1993        let compilation_scope = get_compilation_scope(script);
1994        assert!(!*compilation_scope.has_non_static_value.borrow());
1995
1996        let script = "{a: 2}";
1997        let compilation_scope = get_compilation_scope(script);
1998        assert!(!*compilation_scope.has_non_static_value.borrow());
1999    }
2000
2001    #[test]
2002    fn test_compile_auto_static_value_detection() {
2003        let script = "1";
2004        let (res, _) = compile_script_or_return_static_value(
2005            script,
2006            CompileOptions::default(),
2007        )
2008        .unwrap();
2009        assert_eq!(
2010            res,
2011            StaticValueOrDXB::StaticValue(Some(Integer::from(1).into()))
2012        );
2013
2014        let script = "1 + 2";
2015        let (res, _) = compile_script_or_return_static_value(
2016            script,
2017            CompileOptions::default(),
2018        )
2019        .unwrap();
2020        assert_eq!(
2021            res,
2022            StaticValueOrDXB::Dxb(vec![
2023                InstructionCode::ADD.into(),
2024                InstructionCode::INT_8.into(),
2025                1,
2026                InstructionCode::INT_8.into(),
2027                2,
2028            ])
2029        );
2030    }
2031
2032    #[test]
2033    fn test_remote_execution() {
2034        let script = "42 :: 43";
2035        let (res, _) =
2036            compile_script(script, CompileOptions::default()).unwrap();
2037        assert_eq!(
2038            res,
2039            vec![
2040                InstructionCode::REMOTE_EXECUTION.into(),
2041                // caller (literal value 42 for test)
2042                InstructionCode::INT_8.into(),
2043                42,
2044                // start of block
2045                InstructionCode::EXECUTION_BLOCK.into(),
2046                // block size (2 bytes)
2047                2,
2048                0,
2049                0,
2050                0,
2051                // injected slots (0)
2052                0,
2053                0,
2054                0,
2055                0,
2056                // literal value 43
2057                InstructionCode::INT_8.into(),
2058                43,
2059            ]
2060        );
2061    }
2062
2063    #[test]
2064    fn test_remote_execution_expression() {
2065        let script = "42 :: 1 + 2";
2066        let (res, _) =
2067            compile_script(script, CompileOptions::default()).unwrap();
2068        assert_eq!(
2069            res,
2070            vec![
2071                InstructionCode::REMOTE_EXECUTION.into(),
2072                // caller (literal value 42 for test)
2073                InstructionCode::INT_8.into(),
2074                42,
2075                // start of block
2076                InstructionCode::EXECUTION_BLOCK.into(),
2077                // block size (5 bytes)
2078                5,
2079                0,
2080                0,
2081                0,
2082                // injected slots (0)
2083                0,
2084                0,
2085                0,
2086                0,
2087                // expression: 1 + 2
2088                InstructionCode::ADD.into(),
2089                InstructionCode::INT_8.into(),
2090                1,
2091                InstructionCode::INT_8.into(),
2092                2,
2093            ]
2094        );
2095    }
2096
2097    #[test]
2098    fn test_remote_execution_injected_const() {
2099        init_logger_debug();
2100        let script = "const x = 42; 1 :: x";
2101        let (res, _) =
2102            compile_script(script, CompileOptions::default()).unwrap();
2103        assert_eq!(
2104            res,
2105            vec![
2106                InstructionCode::ALLOCATE_SLOT.into(),
2107                // slot index as u32
2108                0,
2109                0,
2110                0,
2111                0,
2112                InstructionCode::INT_8.into(),
2113                42,
2114                InstructionCode::SCOPE_END.into(),
2115                InstructionCode::CLOSE_AND_STORE.into(),
2116                InstructionCode::REMOTE_EXECUTION.into(),
2117                // caller (literal value 1 for test)
2118                InstructionCode::INT_8.into(),
2119                1,
2120                // start of block
2121                InstructionCode::EXECUTION_BLOCK.into(),
2122                // block size (5 bytes)
2123                5,
2124                0,
2125                0,
2126                0,
2127                // injected slots (1)
2128                1,
2129                0,
2130                0,
2131                0,
2132                // slot 0
2133                0,
2134                0,
2135                0,
2136                0,
2137                // slot 0 (mapped from slot 0)
2138                InstructionCode::GET_SLOT.into(),
2139                // slot index as u32
2140                0,
2141                0,
2142                0,
2143                0,
2144            ]
2145        );
2146    }
2147
2148    #[test]
2149    fn test_remote_execution_injected_var() {
2150        init_logger_debug();
2151        // var x only refers to a value, not a ref, but since it is transferred to a
2152        // remote context, its state is synced via a ref (VariableReference model)
2153        let script = "var x = 42; 1 :: x; x = 43;";
2154        let (res, _) =
2155            compile_script(script, CompileOptions::default()).unwrap();
2156        assert_eq!(
2157            res,
2158            vec![
2159                InstructionCode::ALLOCATE_SLOT.into(),
2160                // slot index as u32
2161                0,
2162                0,
2163                0,
2164                0,
2165                InstructionCode::INT_8.into(),
2166                42,
2167                InstructionCode::SCOPE_END.into(),
2168                InstructionCode::ALLOCATE_SLOT.into(),
2169                // slot index as u32
2170                1,
2171                0,
2172                0,
2173                0,
2174                // create ref
2175                InstructionCode::CREATE_REF.into(),
2176                // slot 0
2177                InstructionCode::GET_SLOT.into(),
2178                // slot index as u32
2179                0,
2180                0,
2181                0,
2182                0,
2183                InstructionCode::SCOPE_END.into(),
2184                InstructionCode::CLOSE_AND_STORE.into(),
2185                InstructionCode::REMOTE_EXECUTION.into(),
2186                // caller (literal value 1 for test)
2187                InstructionCode::INT_8.into(),
2188                1,
2189                // start of block
2190                InstructionCode::EXECUTION_BLOCK.into(),
2191                // block size (5 bytes)
2192                5,
2193                0,
2194                0,
2195                0,
2196                // injected slots (1)
2197                1,
2198                0,
2199                0,
2200                0,
2201                // slot 0
2202                0,
2203                0,
2204                0,
2205                0,
2206                // slot 0 (mapped from slot 0)
2207                InstructionCode::GET_SLOT.into(),
2208                // slot index as u32
2209                0,
2210                0,
2211                0,
2212                0,
2213                InstructionCode::CLOSE_AND_STORE.into(),
2214                // TODO #238: this is not the correct slot assignment for VariableReference model
2215                // set x to 43
2216                InstructionCode::SET_SLOT.into(),
2217                // slot index as u32
2218                0,
2219                0,
2220                0,
2221                0,
2222                InstructionCode::INT_8.into(),
2223                43,
2224                InstructionCode::SCOPE_END.into(),
2225                InstructionCode::CLOSE_AND_STORE.into(),
2226            ]
2227        );
2228    }
2229
2230    #[test]
2231    fn test_remote_execution_injected_consts() {
2232        let script = "const x = 42; const y = 69; 1 :: x + y";
2233        let (res, _) =
2234            compile_script(script, CompileOptions::default()).unwrap();
2235        assert_eq!(
2236            res,
2237            vec![
2238                InstructionCode::ALLOCATE_SLOT.into(),
2239                // slot index as u32
2240                0,
2241                0,
2242                0,
2243                0,
2244                InstructionCode::INT_8.into(),
2245                42,
2246                InstructionCode::SCOPE_END.into(),
2247                InstructionCode::CLOSE_AND_STORE.into(),
2248                InstructionCode::ALLOCATE_SLOT.into(),
2249                // slot index as u32
2250                1,
2251                0,
2252                0,
2253                0,
2254                InstructionCode::INT_8.into(),
2255                69,
2256                InstructionCode::SCOPE_END.into(),
2257                InstructionCode::CLOSE_AND_STORE.into(),
2258                InstructionCode::REMOTE_EXECUTION.into(),
2259                // caller (literal value 1 for test)
2260                InstructionCode::INT_8.into(),
2261                1,
2262                // start of block
2263                InstructionCode::EXECUTION_BLOCK.into(),
2264                // block size (11 bytes)
2265                11,
2266                0,
2267                0,
2268                0,
2269                // injected slots (2)
2270                2,
2271                0,
2272                0,
2273                0,
2274                // slot 0
2275                0,
2276                0,
2277                0,
2278                0,
2279                // slot 1
2280                1,
2281                0,
2282                0,
2283                0,
2284                // expression: x + y
2285                InstructionCode::ADD.into(),
2286                InstructionCode::GET_SLOT.into(),
2287                // slot index as u32
2288                0,
2289                0,
2290                0,
2291                0,
2292                InstructionCode::GET_SLOT.into(),
2293                // slot index as u32
2294                1,
2295                0,
2296                0,
2297                0,
2298            ]
2299        );
2300    }
2301
2302    #[test]
2303    fn test_remote_execution_shadow_const() {
2304        let script = "const x = 42; const y = 69; 1 :: (const x = 5; x + y)";
2305        let (res, _) =
2306            compile_script(script, CompileOptions::default()).unwrap();
2307        assert_eq!(
2308            res,
2309            vec![
2310                InstructionCode::ALLOCATE_SLOT.into(),
2311                // slot index as u32
2312                0,
2313                0,
2314                0,
2315                0,
2316                InstructionCode::INT_8.into(),
2317                42,
2318                InstructionCode::SCOPE_END.into(),
2319                InstructionCode::CLOSE_AND_STORE.into(),
2320                InstructionCode::ALLOCATE_SLOT.into(),
2321                // slot index as u32
2322                1,
2323                0,
2324                0,
2325                0,
2326                InstructionCode::INT_8.into(),
2327                69,
2328                InstructionCode::SCOPE_END.into(),
2329                InstructionCode::CLOSE_AND_STORE.into(),
2330                InstructionCode::REMOTE_EXECUTION.into(),
2331                // caller (literal value 1 for test)
2332                InstructionCode::INT_8.into(),
2333                1,
2334                // start of block
2335                InstructionCode::EXECUTION_BLOCK.into(),
2336                // block size (20 bytes)
2337                20,
2338                0,
2339                0,
2340                0,
2341                // injected slots (1)
2342                1,
2343                0,
2344                0,
2345                0,
2346                // slot 1 (y)
2347                1,
2348                0,
2349                0,
2350                0,
2351                // allocate slot for x
2352                InstructionCode::ALLOCATE_SLOT.into(),
2353                // slot index as u32
2354                1,
2355                0,
2356                0,
2357                0,
2358                InstructionCode::INT_8.into(),
2359                5,
2360                InstructionCode::SCOPE_END.into(),
2361                InstructionCode::CLOSE_AND_STORE.into(),
2362                // expression: x + y
2363                InstructionCode::ADD.into(),
2364                InstructionCode::GET_SLOT.into(),
2365                // slot index as u32
2366                1,
2367                0,
2368                0,
2369                0,
2370                InstructionCode::GET_SLOT.into(),
2371                // slot index as u32
2372                0,
2373                0,
2374                0,
2375                0,
2376            ]
2377        );
2378    }
2379
2380    #[test]
2381    fn test_remote_execution_nested() {
2382        let script = "const x = 42; (1 :: (2 :: x))";
2383        let (res, _) =
2384            compile_script(script, CompileOptions::default()).unwrap();
2385
2386        assert_eq!(
2387            res,
2388            vec![
2389                InstructionCode::ALLOCATE_SLOT.into(),
2390                // slot index as u32
2391                0,
2392                0,
2393                0,
2394                0,
2395                InstructionCode::INT_8.into(),
2396                42,
2397                InstructionCode::SCOPE_END.into(),
2398                InstructionCode::CLOSE_AND_STORE.into(),
2399                InstructionCode::REMOTE_EXECUTION.into(),
2400                // caller (literal value 1 for test)
2401                InstructionCode::INT_8.into(),
2402                1,
2403                // start of block
2404                InstructionCode::EXECUTION_BLOCK.into(),
2405                // block size (21 bytes)
2406                21,
2407                0,
2408                0,
2409                0,
2410                // injected slots (1)
2411                1,
2412                0,
2413                0,
2414                0,
2415                // slot 0
2416                0,
2417                0,
2418                0,
2419                0,
2420                // nested remote execution
2421                InstructionCode::REMOTE_EXECUTION.into(),
2422                // caller (literal value 2 for test)
2423                InstructionCode::INT_8.into(),
2424                2,
2425                // start of block
2426                InstructionCode::EXECUTION_BLOCK.into(),
2427                // block size (5 bytes)
2428                5,
2429                0,
2430                0,
2431                0,
2432                // injected slots (1)
2433                1,
2434                0,
2435                0,
2436                0,
2437                // slot 0
2438                0,
2439                0,
2440                0,
2441                0,
2442                InstructionCode::GET_SLOT.into(),
2443                // slot index as u32
2444                0,
2445                0,
2446                0,
2447                0,
2448            ]
2449        );
2450    }
2451
2452    #[test]
2453    fn test_remote_execution_nested2() {
2454        let script = "const x = 42; (1 :: (x :: x))";
2455        let (res, _) =
2456            compile_script(script, CompileOptions::default()).unwrap();
2457
2458        assert_eq!(
2459            res,
2460            vec![
2461                InstructionCode::ALLOCATE_SLOT.into(),
2462                // slot index as u32
2463                0,
2464                0,
2465                0,
2466                0,
2467                InstructionCode::INT_8.into(),
2468                42,
2469                InstructionCode::SCOPE_END.into(),
2470                InstructionCode::CLOSE_AND_STORE.into(),
2471                InstructionCode::REMOTE_EXECUTION.into(),
2472                // caller (literal value 1 for test)
2473                InstructionCode::INT_8.into(),
2474                1,
2475                // start of block
2476                InstructionCode::EXECUTION_BLOCK.into(),
2477                // block size (21 bytes)
2478                24,
2479                0,
2480                0,
2481                0,
2482                // injected slots (1)
2483                1,
2484                0,
2485                0,
2486                0,
2487                // slot 0
2488                0,
2489                0,
2490                0,
2491                0,
2492                // nested remote execution
2493                InstructionCode::REMOTE_EXECUTION.into(),
2494                // caller (literal value 2 for test)
2495                InstructionCode::GET_SLOT.into(),
2496                0,
2497                0,
2498                0,
2499                0,
2500                // start of block
2501                InstructionCode::EXECUTION_BLOCK.into(),
2502                // block size (5 bytes)
2503                5,
2504                0,
2505                0,
2506                0,
2507                // injected slots (1)
2508                1,
2509                0,
2510                0,
2511                0,
2512                // slot 0
2513                0,
2514                0,
2515                0,
2516                0,
2517                InstructionCode::GET_SLOT.into(),
2518                // slot index as u32
2519                0,
2520                0,
2521                0,
2522                0,
2523            ]
2524        );
2525    }
2526
2527    #[test]
2528    fn test_assignment_to_const() {
2529        init_logger_debug();
2530        let script = "const a = 42; a = 43";
2531        let result = compile_script(script, CompileOptions::default());
2532        assert_matches!(
2533            result,
2534            Err(CompilerError::AssignmentToConst { .. })
2535        );
2536    }
2537
2538    #[test]
2539    fn test_assignment_to_const_mut() {
2540        init_logger_debug();
2541        let script = "const mut a = 42; a = 43";
2542        let result = compile_script(script, CompileOptions::default());
2543        assert_matches!(
2544            result,
2545            Err(CompilerError::AssignmentToConst { .. })
2546        );
2547    }
2548
2549    #[test]
2550    fn test_slot_endpoint() {
2551        let script = "#endpoint";
2552        let (res, _) =
2553            compile_script(script, CompileOptions::default()).unwrap();
2554        assert_eq!(
2555            res,
2556            vec![
2557                InstructionCode::GET_SLOT.into(),
2558                // slot index as u32
2559                0, 0xff, 0xff, 0xff
2560            ]
2561        );
2562    }
2563}