Skip to main content

rib/compiler/
byte_code.rs

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