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 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 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 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 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 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 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 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 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 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 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 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 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 }), RibIR::PushLit("then".into_value_and_type()),
1217 RibIR::Jump(InstructionId { index: 2 }), 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 RibIR::PushLit("if-pred1".into_value_and_type()),
1255 RibIR::JumpIfFalse(InstructionId { index: 1 }), RibIR::PushLit("then1".into_value_and_type()),
1257 RibIR::Jump(InstructionId { index: 2 }), RibIR::Label(InstructionId { index: 1 }),
1259 RibIR::PushLit("else-pred2".into_value_and_type()),
1260 RibIR::JumpIfFalse(InstructionId { index: 3 }), RibIR::PushLit("else-then2".into_value_and_type()),
1262 RibIR::Jump(InstructionId { index: 4 }), 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()), RibIR::UpdateRecord("bar_key".to_string()), 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 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 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 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 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 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 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 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 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}