Skip to main content

rib/compiler/
byte_code.rs

1use crate::compiler::byte_code::internal::ExprState;
2use crate::compiler::ir::RibIR;
3use crate::type_inference::TypeHint;
4use crate::{Expr, InferredExpr, InstructionId};
5use std::fmt::{Display, Formatter};
6
7#[derive(Debug, Clone, Default, PartialEq)]
8pub struct RibByteCode {
9    pub instructions: Vec<RibIR>,
10}
11
12#[derive(Debug, Clone, PartialEq)]
13pub enum RibByteCodeGenerationError {
14    CastError(String),
15    AnalysedTypeConversionError(String),
16    PatternMatchDesugarError,
17    RangeSelectionDesugarError(String),
18    UnexpectedTypeError {
19        expected: TypeHint,
20        actual: TypeHint,
21    },
22    UnresolvedWasmComponent {
23        function: String,
24    },
25    UnresolvedWorkerName,
26    UnresolvedResourceVariable,
27}
28
29impl std::error::Error for RibByteCodeGenerationError {}
30
31impl Display for RibByteCodeGenerationError {
32    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
33        match self {
34            RibByteCodeGenerationError::CastError(msg) => write!(f, "cast error: {msg}"),
35            RibByteCodeGenerationError::AnalysedTypeConversionError(msg) => {
36                write!(f, "{msg}")
37            }
38            RibByteCodeGenerationError::PatternMatchDesugarError => {
39                write!(f, "Pattern match desugar error")
40            }
41            RibByteCodeGenerationError::RangeSelectionDesugarError(msg) => {
42                write!(f, "Range selection desugar error: {msg}")
43            }
44            RibByteCodeGenerationError::UnexpectedTypeError { expected, actual } => {
45                write!(
46                    f,
47                    "Expected type: {}, but got: {}",
48                    expected.get_type_kind(),
49                    actual.get_type_kind()
50                )
51            }
52            RibByteCodeGenerationError::UnresolvedWasmComponent { function } => {
53                write!(f, "Unresolved wasm component for function: {function}")
54            }
55            RibByteCodeGenerationError::UnresolvedWorkerName => {
56                write!(f, "inline invocation of functions on an instance expression is currently not supported")
57            }
58            _ => {
59                write!(f, "inline invocation of methods on resource constructor instance is currently not supported")
60            }
61        }
62    }
63}
64
65impl RibByteCode {
66    pub fn len(&self) -> usize {
67        self.instructions.len()
68    }
69
70    pub fn is_empty(&self) -> bool {
71        self.instructions.is_empty()
72    }
73
74    // Convert expression to bytecode instructions
75    pub fn from_expr(
76        inferred_expr: &InferredExpr,
77    ) -> Result<RibByteCode, RibByteCodeGenerationError> {
78        let expr: &Expr = inferred_expr.get_expr();
79        let mut instructions = Vec::new();
80        let mut stack: Vec<ExprState> = Vec::new();
81        let mut instruction_id = InstructionId::init();
82        stack.push(ExprState::from_expr(expr));
83
84        while let Some(remaining) = stack.pop() {
85            match remaining {
86                ExprState::Expr(expr) => {
87                    internal::process_expr(
88                        &expr,
89                        &mut stack,
90                        &mut instructions,
91                        &mut instruction_id,
92                    )?;
93                }
94
95                ExprState::Instruction(instruction) => {
96                    instructions.push(instruction);
97                }
98            }
99        }
100
101        // Use VecDeque to avoid reversal, but ok as well since this is compilation
102        Ok(RibByteCode {
103            instructions: instructions.into_iter().rev().collect(),
104        })
105    }
106}
107mod internal {
108    use crate::compiler::desugar::{desugar_pattern_match, desugar_range_selection};
109    use crate::wit_type::{TypeFlags, WitType};
110    use crate::{
111        DynamicParsedFunctionReference, Expr, FunctionReferenceType, InferredType,
112        InstanceIdentifier, InstanceVariable, InstructionId, Range, RibByteCodeGenerationError,
113        RibIR, TypeInternal, VariableId, WitTypeWithUnit,
114    };
115    use std::collections::HashSet;
116
117    use crate::call_type::{CallType, InstanceCreationType};
118    use crate::type_inference::{GetTypeHint, TypeHint};
119    use crate::wit_type::bool;
120    use crate::{IntoValueAndType, Value, ValueAndType};
121    use std::ops::Deref;
122
123    pub(crate) fn process_expr(
124        expr: &Expr,
125        stack: &mut Vec<ExprState>,
126        instructions: &mut Vec<RibIR>,
127        instruction_id: &mut InstructionId,
128    ) -> Result<(), RibByteCodeGenerationError> {
129        match expr {
130            Expr::Unwrap { expr, .. } => {
131                stack.push(ExprState::from_expr(expr.deref()));
132                instructions.push(RibIR::Deconstruct);
133            }
134
135            Expr::GenerateWorkerName { variable_id, .. } => {
136                instructions.push(RibIR::GenerateWorkerName(variable_id.clone()));
137            }
138
139            Expr::Length { expr, .. } => {
140                stack.push(ExprState::from_expr(expr.deref()));
141                instructions.push(RibIR::Length);
142            }
143
144            Expr::Throw { message, .. } => {
145                instructions.push(RibIR::Throw(message.to_string()));
146            }
147            Expr::Identifier { variable_id, .. } => {
148                instructions.push(RibIR::LoadVar(variable_id.clone()));
149            }
150            Expr::Literal { value, .. } => {
151                let value_and_type = value.clone().into_value_and_type();
152                instructions.push(RibIR::PushLit(value_and_type));
153            }
154            Expr::Number {
155                number,
156                inferred_type,
157                ..
158            } => {
159                let analysed_type = convert_to_analysed_type(expr, inferred_type)?;
160
161                let value_and_type =
162                    number
163                        .to_val(&analysed_type)
164                        .ok_or(RibByteCodeGenerationError::CastError(format!(
165                            "internal error: cannot convert {} to a value of type {}",
166                            number.value,
167                            analysed_type.get_type_hint()
168                        )))?;
169
170                instructions.push(RibIR::PushLit(value_and_type));
171            }
172            Expr::EqualTo { lhs, rhs, .. } => {
173                stack.push(ExprState::from_expr(rhs.deref()));
174                stack.push(ExprState::from_expr(lhs.deref()));
175                instructions.push(RibIR::EqualTo);
176            }
177            Expr::GreaterThan { lhs, rhs, .. } => {
178                stack.push(ExprState::from_expr(rhs.deref()));
179                stack.push(ExprState::from_expr(lhs.deref()));
180                instructions.push(RibIR::GreaterThan);
181            }
182            Expr::LessThan { lhs, rhs, .. } => {
183                stack.push(ExprState::from_expr(rhs.deref()));
184                stack.push(ExprState::from_expr(lhs.deref()));
185                instructions.push(RibIR::LessThan);
186            }
187            Expr::GreaterThanOrEqualTo { lhs, rhs, .. } => {
188                stack.push(ExprState::from_expr(rhs.deref()));
189                stack.push(ExprState::from_expr(lhs.deref()));
190                instructions.push(RibIR::GreaterThanOrEqualTo);
191            }
192            Expr::LessThanOrEqualTo { lhs, rhs, .. } => {
193                stack.push(ExprState::from_expr(rhs.deref()));
194                stack.push(ExprState::from_expr(lhs.deref()));
195                instructions.push(RibIR::LessThanOrEqualTo);
196            }
197            Expr::Plus {
198                lhs,
199                rhs,
200                inferred_type,
201                ..
202            } => {
203                let analysed_type = convert_to_analysed_type(expr, inferred_type)?;
204                stack.push(ExprState::from_expr(rhs.deref()));
205                stack.push(ExprState::from_expr(lhs.deref()));
206                instructions.push(RibIR::Plus(analysed_type));
207            }
208            Expr::Minus {
209                lhs,
210                rhs,
211                inferred_type,
212                ..
213            } => {
214                let analysed_type = convert_to_analysed_type(expr, inferred_type)?;
215
216                stack.push(ExprState::from_expr(rhs.deref()));
217                stack.push(ExprState::from_expr(lhs.deref()));
218                instructions.push(RibIR::Minus(analysed_type));
219            }
220            Expr::Divide {
221                lhs,
222                rhs,
223                inferred_type,
224                ..
225            } => {
226                let analysed_type = convert_to_analysed_type(expr, inferred_type)?;
227
228                stack.push(ExprState::from_expr(rhs.deref()));
229                stack.push(ExprState::from_expr(lhs.deref()));
230                instructions.push(RibIR::Divide(analysed_type));
231            }
232            Expr::Multiply {
233                lhs,
234                rhs,
235                inferred_type,
236                ..
237            } => {
238                let analysed_type = convert_to_analysed_type(expr, inferred_type)?;
239
240                stack.push(ExprState::from_expr(rhs.deref()));
241                stack.push(ExprState::from_expr(lhs.deref()));
242                instructions.push(RibIR::Multiply(analysed_type));
243            }
244            Expr::And { lhs, rhs, .. } => {
245                // This optimization isn't optional, it's required for the correct functioning of the interpreter
246                let optimised_expr = Expr::cond(
247                    Expr::equal_to(lhs.deref().clone(), Expr::boolean(true)),
248                    Expr::equal_to(rhs.deref().clone(), Expr::boolean(true)),
249                    Expr::boolean(false),
250                );
251
252                stack.push(ExprState::from_expr(&optimised_expr));
253            }
254
255            Expr::Or { lhs, rhs, .. } => {
256                let optimised_expr = Expr::cond(
257                    Expr::equal_to(lhs.deref().clone(), Expr::boolean(true)),
258                    Expr::boolean(true),
259                    Expr::equal_to(rhs.deref().clone(), Expr::boolean(true)),
260                );
261
262                stack.push(ExprState::from_expr(&optimised_expr));
263            }
264
265            Expr::Record {
266                exprs,
267                inferred_type,
268                ..
269            } => {
270                // Push field instructions in reverse order
271                for (field_name, field_expr) in exprs.iter().rev() {
272                    stack.push(ExprState::from_expr(field_expr.as_ref()));
273                    instructions.push(RibIR::UpdateRecord(field_name.clone()));
274                }
275                // Push record creation instruction
276                let analysed_type = convert_to_analysed_type(expr, inferred_type);
277                instructions.push(RibIR::CreateAndPushRecord(analysed_type?));
278            }
279            Expr::Sequence {
280                exprs,
281                inferred_type,
282                ..
283            } => {
284                // Push all expressions in reverse order
285                for expr in exprs.iter().rev() {
286                    stack.push(ExprState::from_expr(expr));
287                }
288
289                let analysed_type = convert_to_analysed_type(expr, inferred_type)?;
290                instructions.push(RibIR::PushList(analysed_type, exprs.len()));
291            }
292            Expr::ExprBlock { exprs, .. } => {
293                // Push all expressions in reverse order
294                for expr in exprs.iter() {
295                    stack.push(ExprState::from_expr(expr));
296                }
297            }
298            Expr::Let {
299                variable_id, expr, ..
300            } => {
301                stack.push(ExprState::from_expr(expr.deref()));
302                instructions.push(RibIR::AssignVar(variable_id.clone()));
303            }
304            Expr::PatternMatch {
305                predicate,
306                match_arms,
307                inferred_type,
308                ..
309            } => {
310                let desugared_pattern_match =
311                    desugar_pattern_match(predicate.deref(), match_arms, inferred_type.clone())
312                        .ok_or(RibByteCodeGenerationError::PatternMatchDesugarError)?;
313
314                stack.push(ExprState::from_expr(&desugared_pattern_match));
315            }
316            Expr::Cond { cond, lhs, rhs, .. } => {
317                handle_if_condition(
318                    instruction_id,
319                    cond.deref(),
320                    lhs.deref(),
321                    rhs.deref(),
322                    stack,
323                );
324            }
325
326            Expr::SelectField { expr, field, .. } => {
327                stack.push(ExprState::from_expr(expr.deref()));
328                instructions.push(RibIR::SelectField(field.clone()));
329            }
330
331            Expr::SelectIndex { expr, index, .. } => match index.inferred_type().internal_type() {
332                TypeInternal::Range { .. } => {
333                    let list_comprehension =
334                        desugar_range_selection(expr, index).map_err(|err| {
335                            RibByteCodeGenerationError::RangeSelectionDesugarError(format!(
336                                "Failed to desugar range selection: {err}"
337                            ))
338                        })?;
339                    stack.push(ExprState::from_expr(&list_comprehension));
340                }
341                _ => {
342                    stack.push(ExprState::from_expr(index.deref()));
343                    stack.push(ExprState::from_expr(expr.deref()));
344                    instructions.push(RibIR::SelectIndexV1);
345                }
346            },
347
348            Expr::Option {
349                expr: Some(inner_expr),
350                inferred_type,
351                ..
352            } => {
353                stack.push(ExprState::from_expr(inner_expr.deref()));
354                instructions.push(RibIR::PushSome(convert_to_analysed_type(
355                    expr,
356                    inferred_type,
357                )?));
358            }
359
360            Expr::Option { inferred_type, .. } => {
361                let optional = convert_to_analysed_type(expr, inferred_type);
362                instructions.push(RibIR::PushNone(optional.ok()));
363            }
364
365            Expr::Result {
366                expr: Ok(inner_expr),
367                inferred_type,
368                ..
369            } => {
370                stack.push(ExprState::from_expr(inner_expr.deref()));
371                instructions.push(RibIR::PushOkResult(convert_to_analysed_type(
372                    expr,
373                    inferred_type,
374                )?));
375            }
376
377            Expr::Result {
378                expr: Err(inner_expr),
379                inferred_type,
380                ..
381            } => {
382                stack.push(ExprState::from_expr(inner_expr.deref()));
383                instructions.push(RibIR::PushErrResult(convert_to_analysed_type(
384                    expr,
385                    inferred_type,
386                )?));
387            }
388
389            Expr::Call {
390                call_type,
391                args,
392                inferred_type,
393                ..
394            } => {
395                // If the call type is an instance creation (worker creation),
396                // this will push worker name expression.
397                match call_type {
398                    CallType::Function {
399                        function_name,
400                        instance_identifier: module,
401                        component_info,
402                    } => {
403                        for expr in args.iter().rev() {
404                            stack.push(ExprState::from_expr(expr));
405                        }
406
407                        let function_result_type = if inferred_type.is_unit() {
408                            WitTypeWithUnit::Unit
409                        } else {
410                            WitTypeWithUnit::Type(convert_to_analysed_type(expr, inferred_type)?)
411                        };
412
413                        let module = module
414                            .as_ref()
415                            .expect("Module should be present for function calls");
416
417                        let instance_variable = match module.as_ref() {
418                            InstanceIdentifier::WitResource { variable_id, .. } => {
419                                let variable_id = variable_id.clone().unwrap_or_else(|| {
420                                    VariableId::global("___STATIC_WIT_RESOURCE".to_string())
421                                });
422                                InstanceVariable::WitResource(variable_id)
423                            }
424                            InstanceIdentifier::WitWorker { variable_id, .. } => {
425                                let variable_id = variable_id
426                                    .clone()
427                                    .ok_or(RibByteCodeGenerationError::UnresolvedWorkerName)?;
428
429                                InstanceVariable::WitWorker(variable_id)
430                            }
431                        };
432
433                        let component_info = component_info.as_ref().ok_or(
434                            RibByteCodeGenerationError::UnresolvedWasmComponent {
435                                function: function_name.to_string(),
436                            },
437                        )?;
438
439                        instructions.push(RibIR::InvokeFunction(
440                            component_info.clone(),
441                            instance_variable,
442                            args.len(),
443                            function_result_type,
444                        ));
445
446                        let site = function_name.site.clone();
447
448                        // Resolve the function name and update stack
449                        match &function_name.function {
450                            DynamicParsedFunctionReference::Function { function } => instructions
451                                .push(RibIR::CreateFunctionName(
452                                    site,
453                                    FunctionReferenceType::Function {
454                                        function: function.clone(),
455                                    },
456                                )),
457
458                            DynamicParsedFunctionReference::RawResourceConstructor { resource } => {
459                                instructions.push(RibIR::CreateFunctionName(
460                                    site,
461                                    FunctionReferenceType::RawResourceConstructor {
462                                        resource: resource.clone(),
463                                    },
464                                ))
465                            }
466                            DynamicParsedFunctionReference::RawResourceDrop { resource } => {
467                                instructions.push(RibIR::CreateFunctionName(
468                                    site,
469                                    FunctionReferenceType::RawResourceDrop {
470                                        resource: resource.clone(),
471                                    },
472                                ))
473                            }
474                            DynamicParsedFunctionReference::RawResourceMethod {
475                                resource,
476                                method,
477                            } => instructions.push(RibIR::CreateFunctionName(
478                                site,
479                                FunctionReferenceType::RawResourceMethod {
480                                    resource: resource.clone(),
481                                    method: method.clone(),
482                                },
483                            )),
484                            DynamicParsedFunctionReference::RawResourceStaticMethod {
485                                resource,
486                                method,
487                            } => instructions.push(RibIR::CreateFunctionName(
488                                site,
489                                FunctionReferenceType::RawResourceStaticMethod {
490                                    resource: resource.clone(),
491                                    method: method.clone(),
492                                },
493                            )),
494                        }
495                    }
496
497                    // if there are no arguments to instance that would typically mean
498                    // it's an ephemeral worker or a resource with no arguments
499                    // This would imply there is nothing in the stack of instructions related
500                    // to these cases. So to make sure expressions such as the following work,
501                    // we need to push a place holder in the stack that does nothing
502                    CallType::InstanceCreation(instance_creation_type) => {
503                        match instance_creation_type {
504                            InstanceCreationType::WitWorker { worker_name, .. } => {
505                                if let Some(worker_name) = worker_name {
506                                    stack.push(ExprState::from_expr(worker_name));
507                                } else {
508                                    for expr in args.iter().rev() {
509                                        stack.push(ExprState::from_expr(expr));
510                                    }
511                                }
512                            }
513
514                            InstanceCreationType::WitResource {
515                                module,
516                                resource_name,
517                                component_info,
518                            } => {
519                                for expr in args.iter().rev() {
520                                    stack.push(ExprState::from_expr(expr));
521                                }
522
523                                let module = module
524                                    .as_ref()
525                                    .expect("Module should be present for resource calls");
526
527                                let instance_variable = match module {
528                                    InstanceIdentifier::WitResource { variable_id, .. } => {
529                                        let variable_id = variable_id.as_ref().ok_or({
530                                            RibByteCodeGenerationError::UnresolvedResourceVariable
531                                        })?;
532
533                                        InstanceVariable::WitResource(variable_id.clone())
534                                    }
535                                    InstanceIdentifier::WitWorker { variable_id, .. } => {
536                                        let variable_id = variable_id.as_ref().ok_or({
537                                            RibByteCodeGenerationError::UnresolvedWorkerName
538                                        })?;
539
540                                        InstanceVariable::WitWorker(variable_id.clone())
541                                    }
542                                };
543
544                                let site = resource_name.parsed_function_site();
545
546                                let component_info = component_info.as_ref().ok_or(
547                                    RibByteCodeGenerationError::UnresolvedWasmComponent {
548                                        function: resource_name.resource_name.clone(),
549                                    },
550                                )?;
551
552                                let function_result_type = if inferred_type.is_unit() {
553                                    WitTypeWithUnit::Unit
554                                } else {
555                                    WitTypeWithUnit::Type(convert_to_analysed_type(
556                                        expr,
557                                        inferred_type,
558                                    )?)
559                                };
560
561                                instructions.push(RibIR::InvokeFunction(
562                                    component_info.clone(),
563                                    instance_variable,
564                                    args.len(),
565                                    function_result_type,
566                                ));
567
568                                instructions.push(RibIR::CreateFunctionName(
569                                    site,
570                                    FunctionReferenceType::RawResourceConstructor {
571                                        resource: resource_name.resource_name.clone(),
572                                    },
573                                ));
574                            }
575                        }
576                    }
577
578                    CallType::VariantConstructor(variant_name) => {
579                        for expr in args.iter().rev() {
580                            stack.push(ExprState::from_expr(expr));
581                        }
582
583                        instructions.push(RibIR::PushVariant(
584                            variant_name.clone(),
585                            convert_to_analysed_type(expr, inferred_type)?,
586                        ));
587                    }
588                    CallType::EnumConstructor(enum_name) => {
589                        for expr in args.iter().rev() {
590                            stack.push(ExprState::from_expr(expr));
591                        }
592
593                        instructions.push(RibIR::PushEnum(
594                            enum_name.clone(),
595                            convert_to_analysed_type(expr, inferred_type)?,
596                        ));
597                    }
598                }
599            }
600
601            Expr::Flags {
602                flags,
603                inferred_type,
604                ..
605            } => match inferred_type.internal_type() {
606                TypeInternal::Flags(all_flags) => {
607                    let mut bitmap = Vec::new();
608                    let flag_values_set: HashSet<&String> = HashSet::from_iter(flags.iter());
609                    for flag in all_flags.iter() {
610                        bitmap.push(flag_values_set.contains(flag));
611                    }
612                    instructions.push(RibIR::PushFlag(ValueAndType {
613                        value: Value::Flags(bitmap),
614                        typ: WitType::Flags(TypeFlags {
615                            names: all_flags.iter().map(|n| n.to_string()).collect(),
616                            owner: None,
617                            name: None,
618                        }),
619                    }));
620                }
621                _ => {
622                    return Err(RibByteCodeGenerationError::UnexpectedTypeError {
623                        expected: TypeHint::Flag(Some(flags.clone())),
624                        actual: inferred_type.get_type_hint(),
625                    });
626                }
627            },
628            Expr::Boolean { value, .. } => {
629                instructions.push(RibIR::PushLit(value.into_value_and_type()));
630            }
631            Expr::GetTag { expr, .. } => {
632                stack.push(ExprState::from_expr(expr.deref()));
633                stack.push(ExprState::from_ir(RibIR::GetTag));
634            }
635
636            Expr::Concat { exprs, .. } => {
637                for expr in exprs.iter().rev() {
638                    stack.push(ExprState::from_expr(expr));
639                }
640
641                instructions.push(RibIR::Concat(exprs.len()));
642            }
643
644            Expr::Not { expr, .. } => {
645                stack.push(ExprState::from_expr(expr.deref()));
646                instructions.push(RibIR::Negate);
647            }
648
649            Expr::Tuple {
650                exprs,
651                inferred_type,
652                ..
653            } => {
654                for expr in exprs.iter().rev() {
655                    stack.push(ExprState::from_expr(expr));
656                }
657                let analysed_type = convert_to_analysed_type(expr, inferred_type)?;
658                instructions.push(RibIR::PushTuple(analysed_type, exprs.len()));
659            }
660
661            Expr::ListComprehension {
662                iterated_variable,
663                iterable_expr,
664                yield_expr,
665                inferred_type,
666                ..
667            } => {
668                let analysed_type = convert_to_analysed_type(expr, inferred_type)?;
669                handle_list_comprehension(
670                    instruction_id,
671                    stack,
672                    iterable_expr,
673                    yield_expr,
674                    iterated_variable,
675                    &analysed_type,
676                )
677            }
678
679            range_expr @ Expr::Range {
680                range,
681                inferred_type,
682                ..
683            } => match inferred_type.internal_type() {
684                TypeInternal::Range { .. } => {
685                    let analysed_type = convert_to_analysed_type(range_expr, inferred_type)?;
686
687                    handle_range(range, stack, analysed_type, instructions);
688                }
689
690                _ => {
691                    return Err(RibByteCodeGenerationError::UnexpectedTypeError {
692                        expected: TypeHint::Range,
693                        actual: inferred_type.get_type_hint(),
694                    });
695                }
696            },
697
698            // Invoke is always handled by the CallType::Function branch
699            Expr::InvokeMethodLazy { .. } => {}
700
701            Expr::ListReduce {
702                reduce_variable,
703                iterated_variable,
704                iterable_expr,
705                init_value_expr,
706                yield_expr,
707                ..
708            } => handle_list_reduce(
709                instruction_id,
710                stack,
711                reduce_variable,
712                iterated_variable,
713                iterable_expr,
714                init_value_expr,
715                yield_expr,
716            ),
717        }
718
719        Ok(())
720    }
721
722    pub(crate) fn convert_to_analysed_type(
723        expr: &Expr,
724        inferred_type: &InferredType,
725    ) -> Result<WitType, RibByteCodeGenerationError> {
726        WitType::try_from(inferred_type).map_err(|error| {
727            RibByteCodeGenerationError::AnalysedTypeConversionError(format!(
728                "Invalid Rib {}. Error converting {} to WitType: {}",
729                expr,
730                inferred_type.get_type_hint(),
731                error
732            ))
733        })
734    }
735
736    // We create a temporary stack of expressions that we pop one by one,
737    // while injecting some pre-defined IRs such as Jump in certain cases
738    // A stack is required on one side to maintain the order of expressions
739    // As soon a `Expr` becomes `Instruction` the instruction stack will be in order.
740    pub(crate) enum ExprState {
741        Expr(Expr),
742        Instruction(RibIR),
743    }
744
745    impl ExprState {
746        pub(crate) fn from_expr(expr: &Expr) -> Self {
747            ExprState::Expr(expr.clone())
748        }
749
750        pub(crate) fn from_ir(ir: RibIR) -> Self {
751            ExprState::Instruction(ir)
752        }
753    }
754
755    fn handle_range(
756        range: &Range,
757        stack: &mut Vec<ExprState>,
758        analysed_type: WitType,
759        instructions: &mut Vec<RibIR>,
760    ) {
761        let from = range.from();
762        let to = range.to();
763        let inclusive = range.inclusive();
764
765        if let Some(from) = from {
766            stack.push(ExprState::from_expr(from));
767            instructions.push(RibIR::UpdateRecord("from".to_string()));
768        }
769
770        if let Some(to) = to {
771            stack.push(ExprState::from_expr(to));
772            instructions.push(RibIR::UpdateRecord("to".to_string()));
773        }
774
775        stack.push(ExprState::from_ir(RibIR::PushLit(ValueAndType::new(
776            Value::Bool(inclusive),
777            bool(),
778        ))));
779
780        instructions.push(RibIR::UpdateRecord("inclusive".to_string()));
781
782        instructions.push(RibIR::CreateAndPushRecord(analysed_type));
783    }
784
785    fn handle_list_comprehension(
786        instruction_id: &mut InstructionId,
787        stack: &mut Vec<ExprState>,
788        iterable_expr: &Expr,
789        yield_expr: &Expr,
790        variable_id: &VariableId,
791        sink_type: &WitType,
792    ) {
793        stack.push(ExprState::from_expr(iterable_expr));
794
795        stack.push(ExprState::from_ir(RibIR::ToIterator));
796
797        stack.push(ExprState::from_ir(RibIR::CreateSink(sink_type.clone())));
798
799        let loop_start_label = instruction_id.increment_mut();
800
801        stack.push(ExprState::from_ir(RibIR::Label(loop_start_label.clone())));
802
803        let exit_label = instruction_id.increment_mut();
804
805        stack.push(ExprState::from_ir(RibIR::IsEmpty));
806
807        stack.push(ExprState::from_ir(RibIR::JumpIfFalse(exit_label.clone())));
808
809        stack.push(ExprState::from_ir(RibIR::AdvanceIterator));
810
811        stack.push(ExprState::from_ir(RibIR::AssignVar(variable_id.clone())));
812
813        stack.push(ExprState::from_expr(yield_expr));
814
815        stack.push(ExprState::from_ir(RibIR::PushToSink));
816
817        stack.push(ExprState::from_ir(RibIR::Jump(loop_start_label)));
818
819        stack.push(ExprState::from_ir(RibIR::Label(exit_label)));
820
821        stack.push(ExprState::from_ir(RibIR::SinkToList))
822    }
823
824    fn handle_list_reduce(
825        instruction_id: &mut InstructionId,
826        stack: &mut Vec<ExprState>,
827        reduce_variable: &VariableId,
828        iterated_variable: &VariableId,
829        iterable_expr: &Expr,
830        initial_value_expr: &Expr,
831        yield_expr: &Expr,
832    ) {
833        stack.push(ExprState::from_expr(iterable_expr));
834
835        stack.push(ExprState::from_expr(initial_value_expr));
836
837        stack.push(ExprState::from_ir(RibIR::AssignVar(
838            reduce_variable.clone(),
839        )));
840
841        stack.push(ExprState::from_ir(RibIR::ToIterator));
842
843        let loop_start_label = instruction_id.increment_mut();
844
845        stack.push(ExprState::from_ir(RibIR::Label(loop_start_label.clone())));
846
847        let exit_label = instruction_id.increment_mut();
848
849        stack.push(ExprState::from_ir(RibIR::IsEmpty));
850
851        stack.push(ExprState::from_ir(RibIR::JumpIfFalse(exit_label.clone())));
852
853        stack.push(ExprState::from_ir(RibIR::AdvanceIterator));
854
855        stack.push(ExprState::from_ir(RibIR::AssignVar(
856            iterated_variable.clone(),
857        )));
858
859        stack.push(ExprState::from_expr(yield_expr));
860
861        stack.push(ExprState::from_ir(RibIR::AssignVar(
862            reduce_variable.clone(),
863        )));
864
865        stack.push(ExprState::from_ir(RibIR::Jump(loop_start_label)));
866
867        stack.push(ExprState::from_ir(RibIR::Label(exit_label)));
868
869        stack.push(ExprState::from_ir(RibIR::LoadVar(reduce_variable.clone())))
870    }
871
872    fn handle_if_condition(
873        instruction_id: &mut InstructionId,
874        if_expr: &Expr,
875        then_expr: &Expr,
876        else_expr: &Expr,
877        stack: &mut Vec<ExprState>,
878    ) {
879        instruction_id.increment_mut();
880        let else_beginning_id = instruction_id.clone();
881        instruction_id.increment_mut();
882        let else_ending_id = instruction_id.clone();
883
884        stack.push(ExprState::from_expr(if_expr));
885
886        stack.push(ExprState::from_ir(RibIR::JumpIfFalse(
887            else_beginning_id.clone(),
888        )));
889
890        stack.push(ExprState::from_expr(then_expr));
891
892        stack.push(ExprState::from_ir(RibIR::Jump(else_ending_id.clone())));
893
894        stack.push(ExprState::from_ir(RibIR::Label(else_beginning_id.clone())));
895
896        stack.push(ExprState::from_expr(else_expr));
897
898        stack.push(ExprState::from_ir(RibIR::Label(else_ending_id.clone())));
899    }
900}
901
902#[cfg(test)]
903mod compiler_tests {
904    use bigdecimal::BigDecimal;
905    use test_r::test;
906
907    use super::*;
908    use crate::wit_type::builders as wit_type;
909    use crate::wit_type::{field, list, record, s32, str};
910    use crate::{ArmPattern, InferredType, MatchArm, RibCompiler, VariableId};
911    use crate::{IntoValueAndType, Value, ValueAndType};
912
913    #[test]
914    fn test_instructions_for_literal() {
915        let literal = Expr::literal("hello");
916
917        let compiler = RibCompiler::default();
918
919        let compiler_output = compiler.compile(literal).unwrap();
920
921        let instruction_set = vec![RibIR::PushLit("hello".into_value_and_type())];
922
923        let expected_instructions = RibByteCode {
924            instructions: instruction_set,
925        };
926
927        assert_eq!(compiler_output.byte_code, expected_instructions);
928    }
929
930    #[test]
931    fn test_instructions_for_identifier() {
932        let inferred_input_type = InferredType::string();
933
934        let variable_id = VariableId::local("request", 0);
935
936        let expr = Expr::identifier_with_variable_id(variable_id.clone(), None)
937            .with_inferred_type(inferred_input_type);
938
939        let compiler = RibCompiler::default();
940
941        let inferred_expr = compiler.infer_types(expr).unwrap();
942
943        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
944
945        let instruction_set = vec![RibIR::LoadVar(variable_id)];
946
947        let expected_instructions = RibByteCode {
948            instructions: instruction_set,
949        };
950
951        assert_eq!(instructions, expected_instructions);
952    }
953
954    #[test]
955    fn test_instructions_assign_variable() {
956        let literal = Expr::literal("hello");
957
958        let variable_id = VariableId::local("request", 0);
959
960        let expr = Expr::let_binding_with_variable_id(variable_id.clone(), literal, None);
961
962        let compiler = RibCompiler::default();
963
964        let inferred_expr = compiler.infer_types(expr).unwrap();
965
966        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
967
968        let instruction_set = vec![
969            RibIR::PushLit("hello".into_value_and_type()),
970            RibIR::AssignVar(variable_id),
971        ];
972
973        let expected_instructions = RibByteCode {
974            instructions: instruction_set,
975        };
976
977        assert_eq!(instructions, expected_instructions);
978    }
979
980    #[test]
981    fn test_instructions_equal_to() {
982        let number_f32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::f32());
983
984        let number_u32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::u32());
985
986        let expr = Expr::equal_to(number_f32, number_u32);
987
988        let compiler = RibCompiler::default();
989
990        let inferred_expr = compiler.infer_types(expr).unwrap();
991
992        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
993
994        let value_and_type1 = 1.0f32.into_value_and_type();
995
996        let value_and_type2 = 1u32.into_value_and_type();
997
998        let instruction_set = vec![
999            RibIR::PushLit(value_and_type2),
1000            RibIR::PushLit(value_and_type1),
1001            RibIR::EqualTo,
1002        ];
1003
1004        let expected_instructions = RibByteCode {
1005            instructions: instruction_set,
1006        };
1007
1008        assert_eq!(instructions, expected_instructions);
1009    }
1010
1011    #[test]
1012    fn test_instructions_greater_than() {
1013        let number_f32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::f32());
1014
1015        let number_u32 = Expr::number_inferred(BigDecimal::from(2), None, InferredType::u32());
1016
1017        let expr = Expr::greater_than(number_f32, number_u32);
1018
1019        let compiler = RibCompiler::default();
1020
1021        let inferred_expr = compiler.infer_types(expr).unwrap();
1022
1023        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1024
1025        let value_and_type1 = 1.0f32.into_value_and_type();
1026
1027        let value_and_type2 = 2u32.into_value_and_type();
1028
1029        let instruction_set = vec![
1030            RibIR::PushLit(value_and_type2),
1031            RibIR::PushLit(value_and_type1),
1032            RibIR::GreaterThan,
1033        ];
1034
1035        let expected_instructions = RibByteCode {
1036            instructions: instruction_set,
1037        };
1038
1039        assert_eq!(instructions, expected_instructions);
1040    }
1041
1042    #[test]
1043    fn test_instructions_less_than() {
1044        let number_f32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::f32());
1045
1046        let number_u32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::u32());
1047
1048        let expr = Expr::less_than(number_f32, number_u32);
1049
1050        let compiler = RibCompiler::default();
1051
1052        let inferred_expr = compiler.infer_types(expr).unwrap();
1053
1054        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1055
1056        let value_and_type1 = 1.0f32.into_value_and_type();
1057
1058        let value_and_type2 = 1u32.into_value_and_type();
1059
1060        let instruction_set = vec![
1061            RibIR::PushLit(value_and_type2),
1062            RibIR::PushLit(value_and_type1),
1063            RibIR::LessThan,
1064        ];
1065
1066        let expected_instructions = RibByteCode {
1067            instructions: instruction_set,
1068        };
1069
1070        assert_eq!(instructions, expected_instructions);
1071    }
1072
1073    #[test]
1074    fn test_instructions_greater_than_or_equal_to() {
1075        let number_f32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::f32());
1076
1077        let number_u32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::u32());
1078
1079        let expr = Expr::greater_than_or_equal_to(number_f32, number_u32);
1080
1081        let compiler = RibCompiler::default();
1082
1083        let inferred_expr = compiler.infer_types(expr).unwrap();
1084
1085        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1086
1087        let value_and_type1 = 1.0f32.into_value_and_type();
1088
1089        let value_and_type2 = 1u32.into_value_and_type();
1090
1091        let instruction_set = vec![
1092            RibIR::PushLit(value_and_type2),
1093            RibIR::PushLit(value_and_type1),
1094            RibIR::GreaterThanOrEqualTo,
1095        ];
1096
1097        let expected_instructions = RibByteCode {
1098            instructions: instruction_set,
1099        };
1100
1101        assert_eq!(instructions, expected_instructions);
1102    }
1103
1104    #[test]
1105    fn test_instructions_less_than_or_equal_to() {
1106        let number_f32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::f32());
1107
1108        let number_u32 = Expr::number_inferred(BigDecimal::from(1), None, InferredType::u32());
1109
1110        let expr = Expr::less_than_or_equal_to(number_f32, number_u32);
1111
1112        let compiler = RibCompiler::default();
1113
1114        let inferred_expr = compiler.infer_types(expr).unwrap();
1115
1116        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1117
1118        let value_and_type1 = 1.0f32.into_value_and_type();
1119
1120        let value_and_type2 = 1u32.into_value_and_type();
1121
1122        let instruction_set = vec![
1123            RibIR::PushLit(value_and_type2),
1124            RibIR::PushLit(value_and_type1),
1125            RibIR::LessThanOrEqualTo,
1126        ];
1127
1128        let expected_instructions = RibByteCode {
1129            instructions: instruction_set,
1130        };
1131
1132        assert_eq!(instructions, expected_instructions);
1133    }
1134
1135    #[test]
1136    fn test_instructions_for_record() {
1137        let expr = Expr::record(vec![
1138            ("foo_key".to_string(), Expr::literal("foo_value")),
1139            ("bar_key".to_string(), Expr::literal("bar_value")),
1140        ])
1141        .with_inferred_type(InferredType::record(vec![
1142            (String::from("foo_key"), InferredType::string()),
1143            (String::from("bar_key"), InferredType::string()),
1144        ]));
1145
1146        let compiler = RibCompiler::default();
1147
1148        let inferred_expr = compiler.infer_types(expr).unwrap();
1149
1150        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1151
1152        let bar_value = "bar_value".into_value_and_type();
1153
1154        let foo_value = "foo_value".into_value_and_type();
1155
1156        let instruction_set = vec![
1157            RibIR::PushLit(bar_value),
1158            RibIR::PushLit(foo_value),
1159            RibIR::CreateAndPushRecord(record(vec![
1160                field("foo_key", str()),
1161                field("bar_key", str()),
1162            ])),
1163            RibIR::UpdateRecord("foo_key".to_string()),
1164            RibIR::UpdateRecord("bar_key".to_string()),
1165        ];
1166
1167        let expected_instructions = RibByteCode {
1168            instructions: instruction_set,
1169        };
1170
1171        assert_eq!(instructions, expected_instructions);
1172    }
1173
1174    #[test]
1175    fn test_instructions_for_multiple() {
1176        let expr = Expr::expr_block(vec![Expr::literal("foo"), Expr::literal("bar")]);
1177
1178        let compiler = RibCompiler::default();
1179
1180        let inferred_expr = compiler.infer_types(expr).unwrap();
1181
1182        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1183
1184        let instruction_set = vec![
1185            RibIR::PushLit("foo".into_value_and_type()),
1186            RibIR::PushLit("bar".into_value_and_type()),
1187        ];
1188
1189        let expected_instructions = RibByteCode {
1190            instructions: instruction_set,
1191        };
1192
1193        assert_eq!(instructions, expected_instructions);
1194    }
1195
1196    #[test]
1197    fn test_instructions_if_conditional() {
1198        let if_expr = Expr::literal("pred").with_inferred_type(InferredType::bool());
1199
1200        let then_expr = Expr::literal("then");
1201
1202        let else_expr = Expr::literal("else");
1203
1204        let expr =
1205            Expr::cond(if_expr, then_expr, else_expr).with_inferred_type(InferredType::string());
1206
1207        let compiler = RibCompiler::default();
1208
1209        let inferred_expr = compiler.infer_types(expr).unwrap();
1210
1211        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1212
1213        let instruction_set = vec![
1214            RibIR::PushLit("pred".into_value_and_type()),
1215            RibIR::JumpIfFalse(InstructionId { index: 1 }), // jumps to the next label having Id 1 (which is else block)
1216            RibIR::PushLit("then".into_value_and_type()),
1217            RibIR::Jump(InstructionId { index: 2 }), // Once if is executed then jump to the end of the else block with id 2
1218            RibIR::Label(InstructionId { index: 1 }),
1219            RibIR::PushLit("else".into_value_and_type()),
1220            RibIR::Label(InstructionId { index: 2 }),
1221        ];
1222
1223        let expected_instructions = RibByteCode {
1224            instructions: instruction_set,
1225        };
1226
1227        assert_eq!(instructions, expected_instructions);
1228    }
1229
1230    #[test]
1231    fn test_instructions_for_nested_if_else() {
1232        let if_expr = Expr::literal("if-pred1").with_inferred_type(InferredType::bool());
1233
1234        let then_expr = Expr::literal("then1").with_inferred_type(InferredType::string());
1235
1236        let else_expr = Expr::cond(
1237            Expr::literal("else-pred2").with_inferred_type(InferredType::bool()),
1238            Expr::literal("else-then2"),
1239            Expr::literal("else-else2"),
1240        )
1241        .with_inferred_type(InferredType::string());
1242
1243        let expr =
1244            Expr::cond(if_expr, then_expr, else_expr).with_inferred_type(InferredType::string());
1245
1246        let compiler = RibCompiler::default();
1247
1248        let inferred_expr = compiler.infer_types(expr).unwrap();
1249
1250        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1251
1252        let instruction_set = vec![
1253            // if case
1254            RibIR::PushLit("if-pred1".into_value_and_type()),
1255            RibIR::JumpIfFalse(InstructionId { index: 1 }), // jumps to the next label having Id 1 (which is else block)
1256            RibIR::PushLit("then1".into_value_and_type()),
1257            RibIR::Jump(InstructionId { index: 2 }), // Once if is executed then jump to the end of the else block with id 3
1258            RibIR::Label(InstructionId { index: 1 }),
1259            RibIR::PushLit("else-pred2".into_value_and_type()),
1260            RibIR::JumpIfFalse(InstructionId { index: 3 }), // jumps to the next label having Id 2 (which is else block)
1261            RibIR::PushLit("else-then2".into_value_and_type()),
1262            RibIR::Jump(InstructionId { index: 4 }), // Once if is executed then jump to the end of the else block with id 3
1263            RibIR::Label(InstructionId { index: 3 }),
1264            RibIR::PushLit("else-else2".into_value_and_type()),
1265            RibIR::Label(InstructionId { index: 4 }),
1266            RibIR::Label(InstructionId { index: 2 }),
1267        ];
1268
1269        let expected_instructions = RibByteCode {
1270            instructions: instruction_set,
1271        };
1272
1273        assert_eq!(instructions, expected_instructions);
1274    }
1275
1276    #[test]
1277    fn test_instructions_for_select_field() {
1278        let record = Expr::record(vec![
1279            ("foo_key".to_string(), Expr::literal("foo_value")),
1280            ("bar_key".to_string(), Expr::literal("bar_value")),
1281        ])
1282        .with_inferred_type(InferredType::record(vec![
1283            (String::from("foo_key"), InferredType::string()),
1284            (String::from("bar_key"), InferredType::string()),
1285        ]));
1286
1287        let expr =
1288            Expr::select_field(record, "bar_key", None).with_inferred_type(InferredType::string());
1289
1290        let compiler = RibCompiler::default();
1291
1292        let inferred_expr = compiler.infer_types(expr).unwrap();
1293
1294        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1295
1296        let bar_value = "bar_value".into_value_and_type();
1297
1298        let foo_value = "foo_value".into_value_and_type();
1299
1300        let instruction_set = vec![
1301            RibIR::PushLit(bar_value),
1302            RibIR::PushLit(foo_value),
1303            RibIR::CreateAndPushRecord(wit_type::record(vec![
1304                field("bar_key", str()),
1305                field("foo_key", str()),
1306            ])),
1307            RibIR::UpdateRecord("foo_key".to_string()), // next pop is foo_value
1308            RibIR::UpdateRecord("bar_key".to_string()), // last pop is bar_value
1309            RibIR::SelectField("bar_key".to_string()),
1310        ];
1311
1312        let expected_instructions = RibByteCode {
1313            instructions: instruction_set,
1314        };
1315
1316        assert_eq!(instructions, expected_instructions);
1317    }
1318
1319    #[test]
1320    fn test_instructions_for_select_index() {
1321        let sequence = Expr::sequence(vec![Expr::literal("foo"), Expr::literal("bar")], None)
1322            .with_inferred_type(InferredType::list(InferredType::string()));
1323
1324        let expr = Expr::select_index(sequence, Expr::number(BigDecimal::from(1)))
1325            .with_inferred_type(InferredType::string());
1326
1327        let compiler = RibCompiler::default();
1328
1329        let inferred_expr = compiler.infer_types(expr).unwrap();
1330
1331        let instructions = RibByteCode::from_expr(&inferred_expr).unwrap();
1332
1333        let instruction_set = vec![
1334            RibIR::PushLit(ValueAndType::new(Value::S32(1), s32())),
1335            RibIR::PushLit("bar".into_value_and_type()),
1336            RibIR::PushLit("foo".into_value_and_type()),
1337            RibIR::PushList(list(str()), 2),
1338            RibIR::SelectIndexV1,
1339        ];
1340
1341        let expected_instructions = RibByteCode {
1342            instructions: instruction_set,
1343        };
1344
1345        assert_eq!(instructions, expected_instructions);
1346    }
1347
1348    #[test]
1349    fn test_instructions_for_expr_arm_pattern_match() {
1350        let expr = Expr::pattern_match(
1351            Expr::literal("pred"),
1352            vec![
1353                MatchArm::new(
1354                    ArmPattern::Literal(Box::new(Expr::literal("arm1_pattern_expr"))),
1355                    Expr::literal("arm1_resolution_expr"),
1356                ),
1357                MatchArm::new(
1358                    ArmPattern::Literal(Box::new(Expr::literal("arm2_pattern_expr"))),
1359                    Expr::literal("arm2_resolution_expr"),
1360                ),
1361                MatchArm::new(
1362                    ArmPattern::Literal(Box::new(Expr::literal("arm3_pattern_expr"))),
1363                    Expr::literal("arm3_resolution_expr"),
1364                ),
1365            ],
1366        )
1367        .with_inferred_type(InferredType::string());
1368
1369        let rib_compiler = RibCompiler::default();
1370
1371        let instructions = rib_compiler.compile(expr).unwrap().byte_code;
1372
1373        // instructions will correspond to an if-else statement
1374        let instruction_set = vec![
1375            RibIR::PushLit("arm1_pattern_expr".into_value_and_type()),
1376            RibIR::PushLit("pred".into_value_and_type()),
1377            RibIR::EqualTo,
1378            RibIR::JumpIfFalse(InstructionId { index: 1 }),
1379            RibIR::PushLit("arm1_resolution_expr".into_value_and_type()),
1380            RibIR::Jump(InstructionId { index: 2 }),
1381            RibIR::Label(InstructionId { index: 1 }),
1382            RibIR::PushLit("arm2_pattern_expr".into_value_and_type()),
1383            RibIR::PushLit("pred".into_value_and_type()),
1384            RibIR::EqualTo,
1385            RibIR::JumpIfFalse(InstructionId { index: 3 }),
1386            RibIR::PushLit("arm2_resolution_expr".into_value_and_type()),
1387            RibIR::Jump(InstructionId { index: 4 }),
1388            RibIR::Label(InstructionId { index: 3 }),
1389            RibIR::PushLit("arm3_pattern_expr".into_value_and_type()),
1390            RibIR::PushLit("pred".into_value_and_type()),
1391            RibIR::EqualTo,
1392            RibIR::JumpIfFalse(InstructionId { index: 5 }),
1393            RibIR::PushLit("arm3_resolution_expr".into_value_and_type()),
1394            RibIR::Jump(InstructionId { index: 6 }),
1395            RibIR::Label(InstructionId { index: 5 }),
1396            RibIR::Throw("No match found".to_string()),
1397            RibIR::Label(InstructionId { index: 6 }),
1398            RibIR::Label(InstructionId { index: 4 }),
1399            RibIR::Label(InstructionId { index: 2 }),
1400        ];
1401
1402        let expected_instructions = RibByteCode {
1403            instructions: instruction_set,
1404        };
1405
1406        assert_eq!(instructions, expected_instructions);
1407    }
1408
1409    #[cfg(test)]
1410    mod invalid_function_invoke_tests {
1411        use test_r::test;
1412
1413        use crate::compiler::byte_code::compiler_tests::internal;
1414        use crate::wit_type::str;
1415        use crate::{Expr, RibCompiler, RibCompilerConfig};
1416
1417        #[test]
1418        fn test_unknown_function() {
1419            let expr = r#"
1420               foo(request);
1421               "success"
1422            "#;
1423
1424            let expr = Expr::from_text(expr).unwrap();
1425            let compiler = RibCompiler::default();
1426
1427            let compiler_error = compiler.compile(expr).unwrap_err().to_string();
1428
1429            assert_eq!(compiler_error, "error in the following rib found at line 2, column 16\n`foo(request)`\ncause: invalid function call `foo`\nunknown function\n");
1430        }
1431
1432        #[test]
1433        fn test_invalid_arg_size_function() {
1434            let metadata = internal::get_component_metadata("foo", vec![str()], str());
1435
1436            let expr = r#"
1437               let user_id = "user";
1438               let result = foo(user_id, user_id);
1439               result
1440            "#;
1441
1442            let expr = Expr::from_text(expr).unwrap();
1443
1444            let compiler_config = RibCompilerConfig::new(metadata, vec![], vec![]);
1445
1446            let compiler = RibCompiler::new(compiler_config);
1447
1448            let compiler_error = compiler.compile(expr).unwrap_err().to_string();
1449            assert_eq!(
1450                compiler_error,
1451                "error in the following rib found at line 3, column 29\n`foo(user_id, user_id)`\ncause: invalid argument size for function `foo`. expected 1 arguments, found 2\n"
1452            );
1453        }
1454
1455        #[test]
1456        fn test_invalid_arg_types_function() {
1457            let metadata = internal::get_component_metadata("foo", vec![str()], str());
1458
1459            let expr = r#"
1460               let result = foo(1u64);
1461               result
1462            "#;
1463
1464            let expr = Expr::from_text(expr).unwrap();
1465
1466            let compiler_config = RibCompilerConfig::new(metadata, vec![], vec![]);
1467
1468            let compiler = RibCompiler::new(compiler_config);
1469
1470            let compiler_error = compiler.compile(expr).unwrap_err().to_string();
1471            assert_eq!(
1472                compiler_error,
1473                "error in the following rib found at line 2, column 33\n`1: u64`\ncause: type mismatch. expected string, found u64\ninvalid argument to the function `foo`\n"
1474            );
1475        }
1476
1477        #[test]
1478        fn test_invalid_arg_types_variants() {
1479            let metadata = internal::metadata_with_variants();
1480
1481            let expr = r#"
1482               let regiser_user_action = register-user("foo");
1483               let result = golem:it/api.{foo}(regiser_user_action);
1484               result
1485            "#;
1486
1487            let expr = Expr::from_text(expr).unwrap();
1488
1489            let compiler_config = RibCompilerConfig::new(metadata, vec![], vec![]);
1490
1491            let compiler = RibCompiler::new(compiler_config);
1492
1493            let compiler_error = compiler.compile(expr).unwrap_err().to_string();
1494            assert_eq!(
1495                compiler_error,
1496                "error in the following rib found at line 2, column 56\n`\"foo\"`\ncause: type mismatch. expected u64, found string\ninvalid argument to the function `register-user`\n"
1497            );
1498        }
1499    }
1500
1501    #[cfg(test)]
1502    mod global_input_tests {
1503        use test_r::test;
1504
1505        use crate::compiler::byte_code::compiler_tests::internal;
1506        use crate::wit_type::{
1507            case, field, list, option, r#enum, record, result, str, tuple, u32, u64, unit_case,
1508            variant,
1509        };
1510        use crate::{Expr, RibCompiler, RibCompilerConfig};
1511
1512        #[test]
1513        async fn test_str_global_input() {
1514            let request_value_type = str();
1515
1516            let output_analysed_type = str();
1517
1518            let analysed_exports = internal::get_component_metadata(
1519                "my-worker-function",
1520                vec![request_value_type.clone()],
1521                output_analysed_type,
1522            );
1523
1524            let expr = r#"
1525               let x = request;
1526               let worker = instance();
1527               worker.my-worker-function(x);
1528               match x {
1529                "foo"  => "success",
1530                 _ => "fallback"
1531               }
1532            "#;
1533
1534            let expr = Expr::from_text(expr).unwrap();
1535            let compiler =
1536                RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![], vec![]));
1537            let compiled = compiler.compile(expr).unwrap();
1538            let expected_type_info =
1539                internal::rib_input_type_info(vec![("request", request_value_type)]);
1540
1541            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1542        }
1543
1544        #[test]
1545        async fn test_number_global_input() {
1546            let request_value_type = u32();
1547
1548            let output_analysed_type = str();
1549
1550            let analysed_exports = internal::get_component_metadata(
1551                "my-worker-function",
1552                vec![request_value_type.clone()],
1553                output_analysed_type,
1554            );
1555
1556            let expr = r#"
1557               let x = request;
1558               let worker = instance();
1559               worker.my-worker-function(x);
1560               match x {
1561                1  => "success",
1562                0 => "failure"
1563               }
1564            "#;
1565
1566            let expr = Expr::from_text(expr).unwrap();
1567            let compiler =
1568                RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![], vec![]));
1569            let compiled = compiler.compile(expr).unwrap();
1570            let expected_type_info =
1571                internal::rib_input_type_info(vec![("request", request_value_type)]);
1572
1573            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1574        }
1575
1576        #[test]
1577        async fn test_variant_type_info() {
1578            let request_value_type = variant(vec![
1579                case("register-user", u64()),
1580                case("process-user", str()),
1581                unit_case("validate"),
1582            ]);
1583
1584            let output_analysed_type = str();
1585
1586            let analysed_exports = internal::get_component_metadata(
1587                "my-worker-function",
1588                vec![request_value_type.clone()],
1589                output_analysed_type,
1590            );
1591
1592            // x = request, implies we are expecting a global variable
1593            // called request as the  input to Rib.
1594            // my-worker-function is a function that takes a Variant as input,
1595            // implies the type of request is a Variant.
1596            // This means the rib interpreter env has to have a request variable in it,
1597            // with a value that should be of the type Variant
1598            let expr = r#"
1599               let worker = instance();
1600               worker.my-worker-function(request);
1601               match request {
1602                 process-user(user) => user,
1603                 _ => "default"
1604               }
1605            "#;
1606
1607            let expr = Expr::from_text(expr).unwrap();
1608            let compiler =
1609                RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![], vec![]));
1610            let compiled = compiler.compile(expr).unwrap();
1611            let expected_type_info =
1612                internal::rib_input_type_info(vec![("request", request_value_type)]);
1613
1614            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1615        }
1616
1617        #[test]
1618        async fn test_result_type_info() {
1619            let request_value_type = result(u64(), str());
1620
1621            let output_analysed_type = str();
1622
1623            let analysed_exports = internal::get_component_metadata(
1624                "my-worker-function",
1625                vec![request_value_type.clone()],
1626                output_analysed_type,
1627            );
1628
1629            // x = request, implies we are expecting a global variable
1630            // called request as the  input to Rib.
1631            // my-worker-function is a function that takes a Result as input,
1632            // implies the type of request is a Result.
1633            // This means the rib interpreter env has to have a request variable in it,
1634            // with a value that should be of the type Result
1635            let expr = r#"
1636               let worker = instance();
1637               worker.my-worker-function(request);
1638               match request {
1639                 ok(x) => "${x}",
1640                 err(msg) => msg
1641               }
1642            "#;
1643
1644            let expr = Expr::from_text(expr).unwrap();
1645            let compiler =
1646                RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![], vec![]));
1647            let compiled = compiler.compile(expr).unwrap();
1648            let expected_type_info =
1649                internal::rib_input_type_info(vec![("request", request_value_type)]);
1650
1651            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1652        }
1653
1654        #[test]
1655        async fn test_option_type_info() {
1656            let request_value_type = option(str());
1657
1658            let output_analysed_type = str();
1659
1660            let analysed_exports = internal::get_component_metadata(
1661                "my-worker-function",
1662                vec![request_value_type.clone()],
1663                output_analysed_type,
1664            );
1665
1666            // x = request, implies we are expecting a global variable
1667            // called request as the input to Rib.
1668            // my-worker-function is a function that takes a Option as input,
1669            // implies the type of request is a Result.
1670            // This means the rib interpreter env has to have a request variable in it,
1671            // with a value that should be of the type Option
1672            let expr = r#"
1673               let worker = instance();
1674               worker.my-worker-function(request);
1675               match request {
1676                 some(x) => x,
1677                 none => "error"
1678               }
1679            "#;
1680
1681            let expr = Expr::from_text(expr).unwrap();
1682            let compiler =
1683                RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![], vec![]));
1684            let compiled = compiler.compile(expr).unwrap();
1685            let expected_type_info =
1686                internal::rib_input_type_info(vec![("request", request_value_type)]);
1687
1688            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1689        }
1690
1691        #[test]
1692        async fn test_enum_type_info() {
1693            let request_value_type = r#enum(&["prod", "dev", "test"]);
1694            let output_analysed_type = str();
1695
1696            let analysed_exports = internal::get_component_metadata(
1697                "my-worker-function",
1698                vec![request_value_type.clone()],
1699                output_analysed_type,
1700            );
1701
1702            // x = request, implies we are expecting a global variable
1703            // called request as the input to Rib.
1704            // my-worker-function is a function that takes a Option as input,
1705            // implies the type of request is a Result.
1706            // This means the rib interpreter env has to have a request variable in it,
1707            // with a value that should be of the type Option
1708            let expr = r#"
1709               let worker = instance();
1710               worker.my-worker-function(request);
1711               match request {
1712                 prod  => "p",
1713                 dev => "d",
1714                 test => "t"
1715               }
1716            "#;
1717
1718            let expr = Expr::from_text(expr).unwrap();
1719            let compiler =
1720                RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![], vec![]));
1721            let compiled = compiler.compile(expr).unwrap();
1722            let expected_type_info =
1723                internal::rib_input_type_info(vec![("request", request_value_type)]);
1724
1725            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1726        }
1727
1728        #[test]
1729        async fn test_record_global_input() {
1730            let request_value_type =
1731                record(vec![field("path", record(vec![field("user", str())]))]);
1732
1733            let output_analysed_type = str();
1734
1735            let analysed_exports = internal::get_component_metadata(
1736                "my-worker-function",
1737                vec![request_value_type.clone()],
1738                output_analysed_type,
1739            );
1740
1741            // x = request, implies we are expecting a global variable
1742            // called request as the  input to Rib.
1743            // my-worker-function is a function that takes a Record of path -> user -> str as input
1744            // implies the type of request is a Record.
1745            // This means the rib interpreter env has to have a request variable in it,
1746            // with a value that should be of the type Record
1747            let expr = r#"
1748               let x = request;
1749               let worker = instance();
1750               worker.my-worker-function(x);
1751
1752               let name = x.path.user;
1753
1754               match x {
1755                 { path : { user : some_name } } => some_name,
1756                 _ => name
1757               }
1758            "#;
1759
1760            let expr = Expr::from_text(expr).unwrap();
1761            let compiler =
1762                RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![], vec![]));
1763            let compiled = compiler.compile(expr).unwrap();
1764            let expected_type_info =
1765                internal::rib_input_type_info(vec![("request", request_value_type)]);
1766
1767            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1768        }
1769
1770        #[test]
1771        async fn test_tuple_global_input() {
1772            let request_value_type = tuple(vec![str(), u32(), record(vec![field("user", str())])]);
1773
1774            let output_analysed_type = str();
1775
1776            let analysed_exports = internal::get_component_metadata(
1777                "my-worker-function",
1778                vec![request_value_type.clone()],
1779                output_analysed_type,
1780            );
1781
1782            // x = request, implies we are expecting a global variable
1783            // called request as the  input to Rib.
1784            // my-worker-function is a function that takes a Tuple,
1785            // implies the type of request is a Tuple.
1786            let expr = r#"
1787               let x = request;
1788               let worker = instance();
1789               worker.my-worker-function(x);
1790               match x {
1791                (_, _, record) =>  record.user,
1792                 _ => "fallback"
1793               }
1794            "#;
1795
1796            let expr = Expr::from_text(expr).unwrap();
1797            let compiler =
1798                RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![], vec![]));
1799            let compiled = compiler.compile(expr).unwrap();
1800            let expected_type_info =
1801                internal::rib_input_type_info(vec![("request", request_value_type)]);
1802
1803            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1804        }
1805
1806        #[test]
1807        async fn test_list_global_input() {
1808            let request_value_type = list(str());
1809
1810            let output_analysed_type = str();
1811
1812            let analysed_exports = internal::get_component_metadata(
1813                "my-worker-function",
1814                vec![request_value_type.clone()],
1815                output_analysed_type,
1816            );
1817
1818            // x = request, implies we are expecting a global variable
1819            // called request as the  input to Rib.
1820            // my-worker-function is a function that takes a List,
1821            // implies the type of request should be a List
1822            let expr = r#"
1823               let x = request;
1824               let worker = instance();
1825               worker.my-worker-function(x);
1826               match x {
1827               [a, b, c]  => a,
1828                 _ => "fallback"
1829               }
1830            "#;
1831
1832            let expr = Expr::from_text(expr).unwrap();
1833            let compiler =
1834                RibCompiler::new(RibCompilerConfig::new(analysed_exports, vec![], vec![]));
1835            let compiled = compiler.compile(expr).unwrap();
1836            let expected_type_info =
1837                internal::rib_input_type_info(vec![("request", request_value_type)]);
1838
1839            assert_eq!(compiled.rib_input_type_info, expected_type_info);
1840        }
1841    }
1842
1843    mod internal {
1844        use crate::wit_type::*;
1845        use crate::wit_type::{case, str, u64, unit_case, variant};
1846        use crate::{ComponentDependency, ComponentDependencyKey, RibInputTypeInfo};
1847        use std::collections::HashMap;
1848        use uuid::Uuid;
1849
1850        pub(crate) fn metadata_with_variants() -> ComponentDependency {
1851            let instance = WitExport::Interface(WitInterface {
1852                name: "golem:it/api".to_string(),
1853                functions: vec![WitFunction {
1854                    name: "foo".to_string(),
1855                    parameters: vec![WitFunctionParameter {
1856                        name: "param1".to_string(),
1857                        typ: variant(vec![
1858                            case("register-user", u64()),
1859                            case("process-user", str()),
1860                            unit_case("validate"),
1861                        ]),
1862                    }],
1863                    result: Some(WitFunctionResult {
1864                        typ: WitType::Handle(TypeHandle {
1865                            resource_id: AnalysedResourceId(0),
1866                            mode: AnalysedResourceMode::Owned,
1867                            name: None,
1868                            owner: None,
1869                        }),
1870                    }),
1871                }],
1872            });
1873
1874            let component_info = ComponentDependencyKey {
1875                component_name: "foo".to_string(),
1876                component_id: Uuid::new_v4(),
1877                component_revision: 0,
1878                root_package_name: None,
1879                root_package_version: None,
1880            };
1881
1882            ComponentDependency::from_wit_metadata(component_info, &[instance]).unwrap()
1883        }
1884
1885        pub(crate) fn get_component_metadata(
1886            function_name: &str,
1887            input_types: Vec<WitType>,
1888            output: WitType,
1889        ) -> ComponentDependency {
1890            let analysed_function_parameters = input_types
1891                .into_iter()
1892                .enumerate()
1893                .map(|(index, typ)| WitFunctionParameter {
1894                    name: format!("param{index}"),
1895                    typ,
1896                })
1897                .collect();
1898
1899            let component_info = ComponentDependencyKey {
1900                component_name: "foo".to_string(),
1901                component_id: Uuid::new_v4(),
1902                component_revision: 0,
1903                root_package_name: None,
1904                root_package_version: None,
1905            };
1906
1907            let exports = vec![WitExport::Function(WitFunction {
1908                name: function_name.to_string(),
1909                parameters: analysed_function_parameters,
1910                result: Some(WitFunctionResult { typ: output }),
1911            })];
1912            ComponentDependency::from_wit_metadata(component_info, &exports).unwrap()
1913        }
1914
1915        pub(crate) fn rib_input_type_info(types: Vec<(&str, WitType)>) -> RibInputTypeInfo {
1916            let mut type_info = HashMap::new();
1917            for (name, typ) in types {
1918                type_info.insert(name.to_string(), typ);
1919            }
1920            RibInputTypeInfo { types: type_info }
1921        }
1922    }
1923}