From 1b0902a5de687fc1bbb402adcfd3d5012e912bf1 Mon Sep 17 00:00:00 2001 From: Afsal Thaj Date: Tue, 8 Oct 2024 10:32:00 +1100 Subject: [PATCH] Avoid recursive algorithm, expand complex rib test, and include and/or (#989) --- golem-api-grpc/proto/golem/rib/expr.proto | 6 + golem-api-grpc/proto/golem/rib/ir.proto | 2 + golem-rib/src/compiler/byte_code.rs | 38 +- golem-rib/src/compiler/desugar.rs | 244 +-- golem-rib/src/compiler/ir.rs | 5 +- golem-rib/src/expr.rs | 88 +- golem-rib/src/function_name.rs | 8 + golem-rib/src/interpreter/literal.rs | 3 + golem-rib/src/interpreter/rib_interpreter.rs | 51 +- golem-rib/src/interpreter/tests/mod.rs | 552 ++++++- golem-rib/src/parser/binary_comparison.rs | 6 + golem-rib/src/parser/multi_line_code_block.rs | 10 +- golem-rib/src/parser/pattern_match.rs | 52 +- golem-rib/src/parser/rib_expr.rs | 2 + golem-rib/src/parser/select_field.rs | 29 +- golem-rib/src/text/mod.rs | 187 ++- golem-rib/src/text/writer.rs | 5 + golem-rib/src/type_inference/expr_visitor.rs | 29 +- golem-rib/src/type_inference/mod.rs | 105 +- .../type_inference/pattern_match_binding.rs | 71 +- golem-rib/src/type_inference/type_binding.rs | 1 + golem-rib/src/type_inference/type_pull_up.rs | 1388 +++++++++++++---- .../src/type_inference/type_unification.rs | 9 +- .../http/http_api_definition.rs | 1 - 24 files changed, 2204 insertions(+), 688 deletions(-) diff --git a/golem-api-grpc/proto/golem/rib/expr.proto b/golem-api-grpc/proto/golem/rib/expr.proto index a82108934..5b874dd4d 100644 --- a/golem-api-grpc/proto/golem/rib/expr.proto +++ b/golem-api-grpc/proto/golem/rib/expr.proto @@ -35,6 +35,7 @@ message Expr { GetTagExpr tag = 26; UnwrapExpr unwrap = 27; ThrowExpr throw = 28; + OrExpr or = 29; } } @@ -126,6 +127,11 @@ message AndExpr { Expr right = 2; } +message OrExpr { + Expr left = 1; + Expr right = 2; +} + message GreaterThanOrEqualToExpr { Expr left = 1; Expr right = 2; diff --git a/golem-api-grpc/proto/golem/rib/ir.proto b/golem-api-grpc/proto/golem/rib/ir.proto index f5f0d7f5a..228d904ae 100644 --- a/golem-api-grpc/proto/golem/rib/ir.proto +++ b/golem-api-grpc/proto/golem/rib/ir.proto @@ -42,6 +42,7 @@ message RibIR { EnumConstructionInstruction enum_construction = 30; And and = 31; CreateFunctionNameInstruction create_function_name = 32; + Or or = 33; } } @@ -114,6 +115,7 @@ message LessThanOrEqualTo {} message GetTag {} message Negate {} message And {} +message Or {} message FunctionReferenceType { oneof type { diff --git a/golem-rib/src/compiler/byte_code.rs b/golem-rib/src/compiler/byte_code.rs index 71e6ed990..b8ef1ac43 100644 --- a/golem-rib/src/compiler/byte_code.rs +++ b/golem-rib/src/compiler/byte_code.rs @@ -151,10 +151,42 @@ mod internal { instructions.push(RibIR::LessThanOrEqualTo); } Expr::And(lhs, rhs, _) => { - stack.push(ExprState::from_expr(rhs.deref())); - stack.push(ExprState::from_expr(lhs.deref())); - instructions.push(RibIR::And); + // This optimization isn't optional, it's required for the correct functioning of the interpreter + let optimised_expr = Expr::cond( + Expr::EqualTo( + lhs.clone(), + Box::new(Expr::Boolean(true, InferredType::Bool)), + InferredType::Bool, + ), + Expr::EqualTo( + rhs.clone(), + Box::new(Expr::Boolean(true, InferredType::Bool)), + InferredType::Bool, + ), + Expr::Boolean(false, InferredType::Bool), + ); + + stack.push(ExprState::from_expr(&optimised_expr)); } + + Expr::Or(lhs, rhs, _) => { + let optimised_expr = Expr::cond( + Expr::EqualTo( + lhs.clone(), + Box::new(Expr::Boolean(true, InferredType::Bool)), + InferredType::Bool, + ), + Expr::Boolean(true, InferredType::Bool), + Expr::EqualTo( + rhs.clone(), + Box::new(Expr::Boolean(true, InferredType::Bool)), + InferredType::Bool, + ), + ); + + stack.push(ExprState::from_expr(&optimised_expr)); + } + Expr::Record(fields, inferred_type) => { // Push field instructions in reverse order for (field_name, field_expr) in fields.iter().rev() { diff --git a/golem-rib/src/compiler/desugar.rs b/golem-rib/src/compiler/desugar.rs index cab54a592..1af693e66 100644 --- a/golem-rib/src/compiler/desugar.rs +++ b/golem-rib/src/compiler/desugar.rs @@ -31,6 +31,7 @@ pub fn desugar_pattern_match( } mod internal { + use crate::call_type::CallType; use crate::{ArmPattern, Expr, InferredType, MatchArm, VariableId}; pub(crate) fn build_expr_from(if_branches: Vec) -> Option { @@ -89,13 +90,9 @@ mod internal { // some(some(x)) => "hello" // } match arm_pattern { - ArmPattern::Literal(arm_pattern_expr) => handle_literal( - arm_pattern_expr, - pred_expr, - resolution, - tag, - inferred_type_of_pred, - ), + ArmPattern::Literal(arm_pattern_expr) => { + handle_literal(arm_pattern_expr, pred_expr, resolution, tag) + } ArmPattern::Constructor(constructor_name, arm_patterns) => hande_constructor( pred_expr, @@ -103,6 +100,7 @@ mod internal { arm_patterns, resolution, inferred_type_of_pred, + tag, ), ArmPattern::TupleConstructor(arm_patterns) => hande_constructor( @@ -111,6 +109,7 @@ mod internal { arm_patterns, resolution, inferred_type_of_pred, + tag, ), ArmPattern::ListConstructor(arm_patterns) => hande_constructor( @@ -119,14 +118,18 @@ mod internal { arm_patterns, resolution, inferred_type_of_pred, + tag, ), - ArmPattern::RecordConstructor(field_arm_pattern_collection) => handle_record( - pred_expr, - field_arm_pattern_collection, - resolution, - inferred_type_of_pred, - ), + ArmPattern::RecordConstructor(field_arm_pattern_collection) => { + handle_record_constructor( + pred_expr, + field_arm_pattern_collection, + resolution, + inferred_type_of_pred, + tag, + ) + } ArmPattern::As(name, inner_pattern) => handle_as_pattern( name, @@ -152,80 +155,8 @@ mod internal { pred_expr: &Expr, resolution: &Expr, tag: Option, - pred_expr_inferred_type: InferredType, ) -> Option { match arm_pattern_expr { - Expr::Option(Some(inner_pattern), _) => { - let unwrapped_inferred_type = match pred_expr_inferred_type { - InferredType::Option(inner) => *inner, - _ => InferredType::Unknown, - }; - - get_conditions( - &MatchArm::new( - ArmPattern::Literal(inner_pattern.clone()), - resolution.clone(), - ), - &pred_expr.unwrap(), - Some(Expr::equal_to( - Expr::tag(pred_expr.clone()), - Expr::literal("some"), - )), - unwrapped_inferred_type, - ) - } - - Expr::Option(None, _) => { - let branch = IfThenBranch { - condition: Expr::equal_to(Expr::tag(pred_expr.clone()), Expr::literal("none")), - body: resolution.clone(), - }; - Some(branch) - } - - Expr::Result(Ok(inner_pattern), _) => { - let unwrapped_inferred_type = match pred_expr_inferred_type { - InferredType::Result { ok, .. } => { - ok.unwrap_or(Box::new(InferredType::Unknown)) - } - _ => Box::new(InferredType::Unknown), - }; - - get_conditions( - &MatchArm::new( - ArmPattern::Literal(inner_pattern.clone()), - resolution.clone(), - ), - &pred_expr.unwrap(), - Some(Expr::equal_to( - Expr::tag(pred_expr.clone()), - Expr::literal("ok"), - )), - *unwrapped_inferred_type, - ) - } - Expr::Result(Err(inner_pattern), _) => { - let unwrapped_inferred_type = match pred_expr_inferred_type { - InferredType::Result { error, .. } => { - error.unwrap_or(Box::new(InferredType::Unknown)) - } - _ => Box::new(InferredType::Unknown), - }; - - get_conditions( - &MatchArm::new( - ArmPattern::Literal(inner_pattern.clone()), - resolution.clone(), - ), - &pred_expr.unwrap(), - Some(Expr::equal_to( - Expr::tag(pred_expr.clone()), - Expr::literal("err"), - )), - *unwrapped_inferred_type, - ) - } - Expr::Identifier(identifier, inferred_type) => { let assign_var = Expr::Let( identifier.clone(), @@ -235,6 +166,7 @@ mod internal { ); let block = Expr::multiple(vec![assign_var, resolution.clone()]); + let branch = IfThenBranch { condition: tag.unwrap_or(Expr::boolean(true)), body: block, @@ -242,22 +174,40 @@ mod internal { Some(branch) } + Expr::Call(CallType::EnumConstructor(name), _, _) => { + let cond = if let Some(t) = tag { + Expr::and( + t, + Expr::equal_to(Expr::get_tag(pred_expr.clone()), Expr::literal(name)), + ) + } else { + Expr::equal_to(Expr::get_tag(pred_expr.clone()), Expr::literal(name)) + }; + + let branch = IfThenBranch { + condition: cond, + body: resolution.clone(), + }; + Some(branch) + } + _ => { - // use tag lookup let branch = IfThenBranch { condition: Expr::equal_to(pred_expr.clone(), arm_pattern_expr.clone()), body: resolution.clone(), }; + Some(branch) } } } - fn handle_record( + fn handle_record_constructor( pred_expr: &Expr, bind_patterns: &[(String, ArmPattern)], resolution: &Expr, pred_expr_inferred_type: InferredType, + tag: Option, ) -> Option { match pred_expr_inferred_type { InferredType::Record(field_and_types) => { @@ -301,7 +251,13 @@ mod internal { let and_cond = Expr::and_combine(conditions); and_cond.map(|c| IfThenBranch { - condition: c, + condition: { + if let Some(t) = tag { + Expr::and(t, c) + } else { + c + } + }, body: Expr::multiple(resolution_body), }) } @@ -316,6 +272,7 @@ mod internal { bind_patterns: &[ArmPattern], resolution: &Expr, pred_expr_inferred_type: InferredType, + tag: Option, ) -> Option { match pred_expr_inferred_type { InferredType::Variant(variant) => { @@ -326,53 +283,96 @@ mod internal { .find(|(case_name, _)| case_name == constructor_name); let inner_variant_arg_type = inner_type.and_then(|(_, typ)| typ.clone()); + let cond = if let Some(t) = tag { + Expr::and( + t, + Expr::equal_to( + Expr::get_tag(pred_expr.clone()), + Expr::literal(constructor_name), + ), + ) + } else { + Expr::equal_to( + Expr::get_tag(pred_expr.clone()), + Expr::literal(constructor_name), + ) + }; match (arg_pattern_opt, inner_variant_arg_type) { (None, None) => None, (Some(pattern), Some(inferred_type)) => get_conditions( &MatchArm::new(pattern.clone(), resolution.clone()), &pred_expr.unwrap(), - Some(Expr::equal_to( - Expr::tag(pred_expr.clone()), - Expr::literal(constructor_name), - )), + Some(cond), inferred_type, ), - _ => None, // Probably fail here + _ => None, } } - InferredType::Option(inner) => match bind_patterns.first() { - Some(pattern) => get_conditions( - &MatchArm::new(pattern.clone(), resolution.clone()), - &pred_expr.unwrap(), - Some(Expr::equal_to( - Expr::tag(pred_expr.clone()), - Expr::literal(constructor_name), - )), - *inner, - ), - _ => Some(IfThenBranch { - condition: Expr::equal_to( - Expr::tag(pred_expr.clone()), + + InferredType::Option(inner) if constructor_name == "some" => { + let cond = if let Some(t) = tag { + Expr::and( + t, + Expr::equal_to( + Expr::get_tag(pred_expr.clone()), + Expr::literal(constructor_name), + ), + ) + } else { + Expr::equal_to( + Expr::get_tag(pred_expr.clone()), Expr::literal(constructor_name), + ) + }; + + match bind_patterns.first() { + Some(pattern) => get_conditions( + &MatchArm::new(pattern.clone(), resolution.clone()), + &pred_expr.unwrap(), + Some(cond), + *inner, ), - body: resolution.clone(), - }), - }, + _ => None, + } + } + + InferredType::Option(_) if constructor_name == "none" => Some(IfThenBranch { + condition: Expr::equal_to( + Expr::get_tag(pred_expr.clone()), + Expr::literal(constructor_name), + ), + body: resolution.clone(), + }), + InferredType::Result { ok, error } => { let inner_variant_arg_type = if constructor_name == "ok" { ok.as_deref() - } else { + } else if constructor_name == "err" { error.as_deref() + } else { + return None; + }; + + let cond = if let Some(t) = tag { + Expr::and( + t, + Expr::equal_to( + Expr::get_tag(pred_expr.clone()), + Expr::literal(constructor_name), + ), + ) + } else { + Expr::equal_to( + Expr::get_tag(pred_expr.clone()), + Expr::literal(constructor_name), + ) }; match bind_patterns.first() { Some(pattern) => get_conditions( &MatchArm::new(pattern.clone(), resolution.clone()), &pred_expr.unwrap(), - Some(Expr::equal_to( - Expr::tag(pred_expr.clone()), - Expr::literal(constructor_name), - )), + Some(cond), inner_variant_arg_type .unwrap_or(&InferredType::Unknown) .clone(), @@ -419,7 +419,13 @@ mod internal { let and_cond = Expr::and_combine(conditions); and_cond.map(|c| IfThenBranch { - condition: c, + condition: { + if let Some(t) = tag { + Expr::and(t, c) + } else { + c + } + }, body: Expr::multiple(resolution_body), }) } @@ -461,7 +467,13 @@ mod internal { let and_cond = Expr::and_combine(conditions); and_cond.map(|c| IfThenBranch { - condition: c, + condition: { + if let Some(t) = tag { + Expr::and(t, c) + } else { + c + } + }, body: Expr::multiple(resolution_body), }) } diff --git a/golem-rib/src/compiler/ir.rs b/golem-rib/src/compiler/ir.rs index 2d7722e30..25a375a48 100644 --- a/golem-rib/src/compiler/ir.rs +++ b/golem-rib/src/compiler/ir.rs @@ -17,7 +17,7 @@ use bincode::{Decode, Encode}; use golem_api_grpc::proto::golem::rib::rib_ir::Instruction; use golem_api_grpc::proto::golem::rib::{ And, CallInstruction, ConcatInstruction, CreateFunctionNameInstruction, EqualTo, GetTag, - GreaterThan, GreaterThanOrEqualTo, JumpInstruction, LessThan, LessThanOrEqualTo, Negate, + GreaterThan, GreaterThanOrEqualTo, JumpInstruction, LessThan, LessThanOrEqualTo, Negate, Or, PushListInstruction, PushNoneInstruction, PushTupleInstruction, RibIr as ProtoRibIR, }; use golem_wasm_ast::analysis::{AnalysedType, TypeStr}; @@ -44,6 +44,7 @@ pub enum RibIR { EqualTo, GreaterThan, And, + Or, LessThan, GreaterThanOrEqualTo, LessThanOrEqualTo, @@ -318,6 +319,7 @@ impl TryFrom for RibIR { Instruction::GreaterThanOrEqualTo(_) => Ok(RibIR::GreaterThanOrEqualTo), Instruction::LessThanOrEqualTo(_) => Ok(RibIR::LessThanOrEqualTo), Instruction::And(_) => Ok(RibIR::And), + Instruction::Or(_) => Ok(RibIR::Or), Instruction::JumpIfFalse(value) => Ok(RibIR::JumpIfFalse(InstructionId::from( value.instruction_id as usize, ))), @@ -424,6 +426,7 @@ impl From for ProtoRibIR { }) } RibIR::And => Instruction::And(And {}), + RibIR::Or => Instruction::Or(Or {}), RibIR::AssignVar(value) => Instruction::AssignVar(value.into()), RibIR::LoadVar(value) => Instruction::LoadVar(value.into()), RibIR::CreateAndPushRecord(value) => Instruction::CreateAndPushRecord((&value).into()), diff --git a/golem-rib/src/expr.rs b/golem-rib/src/expr.rs index 787e5e127..2981f52a1 100644 --- a/golem-rib/src/expr.rs +++ b/golem-rib/src/expr.rs @@ -50,6 +50,7 @@ pub enum Expr { Not(Box, InferredType), GreaterThan(Box, Box, InferredType), And(Box, Box, InferredType), + Or(Box, Box, InferredType), GreaterThanOrEqualTo(Box, Box, InferredType), LessThanOrEqualTo(Box, Box, InferredType), EqualTo(Box, Box, InferredType), @@ -325,6 +326,10 @@ impl Expr { ) } + pub fn or(left: Expr, right: Expr) -> Self { + Expr::Or(Box::new(left), Box::new(right), InferredType::Bool).unwrap() + } + pub fn pattern_match(expr: Expr, match_arms: Vec) -> Self { Expr::PatternMatch(Box::new(expr), match_arms, InferredType::Unknown) } @@ -358,7 +363,7 @@ impl Expr { Expr::SelectIndex(Box::new(expr), index, InferredType::Unknown) } - pub fn tag(expr: Expr) -> Self { + pub fn get_tag(expr: Expr) -> Self { Expr::GetTag(Box::new(expr), InferredType::Unknown) } @@ -412,6 +417,7 @@ impl Expr { | Expr::Throw(_, inferred_type) | Expr::GetTag(_, inferred_type) | Expr::And(_, _, inferred_type) + | Expr::Or(_, _, inferred_type) | Expr::Call(_, _, inferred_type) => inferred_type.clone(), } } @@ -420,11 +426,7 @@ impl Expr { &mut self, function_type_registry: &FunctionTypeRegistry, ) -> Result<(), Vec> { - self.bind_types(); - self.name_binding_pattern_match_variables(); - self.name_binding_local_variables(); - self.infer_variants(function_type_registry); - self.infer_enums(function_type_registry); + self.infer_types_initial_phase(function_type_registry)?; self.infer_call_arguments_type(function_type_registry) .map_err(|x| vec![x])?; type_inference::type_inference_fix_point(Self::inference_scan, self) @@ -433,29 +435,47 @@ impl Expr { Ok(()) } + pub fn infer_types_initial_phase( + &mut self, + function_type_registry: &FunctionTypeRegistry, + ) -> Result<(), Vec> { + self.bind_types(); + self.name_binding_pattern_match_variables(); + self.name_binding_local_variables(); + self.infer_variants(function_type_registry); + self.infer_enums(function_type_registry); + + Ok(()) + } + // An inference scan is a single cycle of to-and-fro scanning of Rib expression // to infer the types pub fn inference_scan(&mut self) -> Result<(), String> { self.infer_all_identifiers()?; self.push_types_down()?; self.infer_all_identifiers()?; - self.pull_types_up()?; + let expr = self.pull_types_up()?; + *self = expr; self.unify_types().unwrap_or(()); self.infer_global_inputs(); Ok(()) } + // Make sure the bindings in the arm pattern of a pattern match are given variable-ids. + // The same variable-ids will be tagged to the corresponding identifiers in the arm resolution + // to avoid conflicts. pub fn name_binding_pattern_match_variables(&mut self) { type_inference::name_binding_pattern_matches(self); } - // We make sure the let bindings name are properly - // bound to the named identifiers. + + // Make sure the variable assignment (let binding) are given variable ids, + // which will be tagged to the corresponding identifiers to avoid conflicts. + // This is done only for local variables and not global variables pub fn name_binding_local_variables(&mut self) { type_inference::name_binding_local_variables(self); } - // At this point we simply update the types to the parameter type expressions and the call expression itself. pub fn infer_call_arguments_type( &mut self, function_type_registry: &FunctionTypeRegistry, @@ -471,8 +491,8 @@ impl Expr { type_inference::infer_all_identifiers(self) } - pub fn pull_types_up(&mut self) -> Result<(), String> { - type_inference::pull_types_up(self) + pub fn pull_types_up(&self) -> Result { + type_inference::type_pull_up(self) } pub fn infer_global_inputs(&mut self) { @@ -526,6 +546,7 @@ impl Expr { | Expr::Throw(_, inferred_type) | Expr::GetTag(_, inferred_type) | Expr::And(_, _, inferred_type) + | Expr::Or(_, _, inferred_type) | Expr::Call(_, _, inferred_type) => { if new_inferred_type != InferredType::Unknown { *inferred_type = inferred_type.merge(new_inferred_type); @@ -566,6 +587,7 @@ impl Expr { | Expr::Unwrap(_, inferred_type) | Expr::Throw(_, inferred_type) | Expr::And(_, _, inferred_type) + | Expr::Or(_, _, inferred_type) | Expr::GetTag(_, inferred_type) | Expr::Call(_, _, inferred_type) => { if new_inferred_type != InferredType::Unknown { @@ -692,6 +714,14 @@ pub enum ArmPattern { } impl ArmPattern { + pub fn constructor(name: &str, patterns: Vec) -> ArmPattern { + ArmPattern::Constructor(name.to_string(), patterns) + } + + pub fn literal(expr: Expr) -> ArmPattern { + ArmPattern::Literal(Box::new(expr)) + } + pub fn get_expr_literals_mut(&mut self) -> Vec<&mut Box> { match self { ArmPattern::Literal(expr) => vec![expr], @@ -961,9 +991,15 @@ impl TryFrom for Expr { Expr::and((*left).try_into()?, (*right).try_into()?) } + golem_api_grpc::proto::golem::rib::expr::Expr::Or(expr) => { + let left = expr.left.ok_or("Missing left expr")?; + let right = expr.right.ok_or("Missing right expr")?; + Expr::or((*left).try_into()?, (*right).try_into()?) + } + golem_api_grpc::proto::golem::rib::expr::Expr::Tag(expr) => { let expr = expr.expr.ok_or("Missing expr in tag")?; - Expr::tag((*expr).try_into()?) + Expr::get_tag((*expr).try_into()?) } golem_api_grpc::proto::golem::rib::expr::Expr::Unwrap(expr) => { @@ -1273,6 +1309,13 @@ impl From for golem_api_grpc::proto::golem::rib::Expr { right: Some(Box::new((*right).into())), }), )), + + Expr::Or(left, right, _) => Some(golem_api_grpc::proto::golem::rib::expr::Expr::Or( + Box::new(golem_api_grpc::proto::golem::rib::OrExpr { + left: Some(Box::new((*left).into())), + right: Some(Box::new((*right).into())), + }), + )), }; golem_api_grpc::proto::golem::rib::Expr { expr } @@ -1546,13 +1589,14 @@ mod tests { Expr::identifier("foo"), vec![ MatchArm::new( - ArmPattern::Literal(Box::new(Expr::option(Some(Expr::identifier( - "x", - ))))), + ArmPattern::constructor( + "some", + vec![ArmPattern::Literal(Box::new(Expr::identifier("x")))], + ), Expr::identifier("x"), ), MatchArm::new( - ArmPattern::Literal(Box::new(Expr::option(None))), + ArmPattern::constructor("none", vec![]), Expr::boolean(false), ), ], @@ -1564,11 +1608,17 @@ mod tests { Expr::identifier("bar"), vec![ MatchArm::new( - ArmPattern::Literal(Box::new(Expr::ok(Expr::identifier("x")))), + ArmPattern::constructor( + "ok", + vec![ArmPattern::Literal(Box::new(Expr::identifier("x")))], + ), Expr::identifier("x"), ), MatchArm::new( - ArmPattern::Literal(Box::new(Expr::err(Expr::identifier("msg")))), + ArmPattern::constructor( + "err", + vec![ArmPattern::Literal(Box::new(Expr::identifier("msg")))], + ), Expr::boolean(false), ), ], diff --git a/golem-rib/src/function_name.rs b/golem-rib/src/function_name.rs index 2586d7a6a..db59c5d1f 100644 --- a/golem-rib/src/function_name.rs +++ b/golem-rib/src/function_name.rs @@ -896,6 +896,14 @@ pub struct ParsedFunctionName { pub function: ParsedFunctionReference, } +// DynamicParsedFunctionName is different from ParsedFunctionName. +// In `DynamicParsedFunctionName` the resource parameters are `Expr` (Rib) while they are `String` +// in `ParsedFunctionName`. +// `Expr` implies the real values are yet to be computed, while `String` +// in ParsedFunctionName is a textual representation of the "real" values. +// `Examples`: +// `DynamicParsedFunctionName` : ns:name/interface.{resource1(identifier1, { field-a: some(identifier2) }).new} +// `ParsedFunctionName` : ns:name/interface.{resource1("foo", { field-a: some("bar") }).new} #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] pub struct DynamicParsedFunctionName { pub site: ParsedFunctionSite, diff --git a/golem-rib/src/interpreter/literal.rs b/golem-rib/src/interpreter/literal.rs index 4ee09c806..5a787e9be 100644 --- a/golem-rib/src/interpreter/literal.rs +++ b/golem-rib/src/interpreter/literal.rs @@ -25,6 +25,9 @@ impl GetLiteralValue for TypeAnnotatedValue { fn get_literal(&self) -> Option { match self { TypeAnnotatedValue::Str(value) => Some(LiteralValue::String(value.clone())), + TypeAnnotatedValue::Char(code_point) => char::from_u32(*code_point as u32) + .map(|c| c.to_string()) + .map(LiteralValue::String), TypeAnnotatedValue::Bool(value) => Some(LiteralValue::Bool(*value)), TypeAnnotatedValue::Enum(value) => { // An enum can be turned into a simple literal and can be part of string concatenations diff --git a/golem-rib/src/interpreter/rib_interpreter.rs b/golem-rib/src/interpreter/rib_interpreter.rs index 087984dcc..01e784fc5 100644 --- a/golem-rib/src/interpreter/rib_interpreter.rs +++ b/golem-rib/src/interpreter/rib_interpreter.rs @@ -202,6 +202,10 @@ impl Interpreter { RibIR::And => { internal::run_and_instruction(&mut self.stack)?; } + + RibIR::Or => { + internal::run_or_instruction(&mut self.stack)?; + } } } @@ -365,7 +369,6 @@ mod internal { .pop_n(list_size) .ok_or(format!("Expected {} value on the stack", list_size))?; - dbg!(last_list.clone()); let type_annotated_values = last_list .iter() .map(|interpreter_result| { @@ -403,12 +406,12 @@ mod internal { pub(crate) fn run_and_instruction( interpreter_stack: &mut InterpreterStack, ) -> Result<(), String> { - let left = interpreter_stack.pop().ok_or( - "Empty stack and failed to get a value to do the comparison operation".to_string(), - )?; - let right = interpreter_stack.pop().ok_or( - "Failed to get a value from the stack to do the comparison operation".to_string(), - )?; + let left = interpreter_stack + .pop() + .ok_or("Internal Error: Failed to get LHS &&".to_string())?; + let right = interpreter_stack + .pop() + .ok_or("Internal Error: Failed to get RHS of &&".to_string())?; let result = left.compare(&right, |a, b| match (a.get_bool(), b.get_bool()) { (Some(a), Some(b)) => a && b, @@ -420,6 +423,26 @@ mod internal { Ok(()) } + pub(crate) fn run_or_instruction( + interpreter_stack: &mut InterpreterStack, + ) -> Result<(), String> { + let left = interpreter_stack + .pop() + .ok_or("Internal Error: Failed to get LHS &&".to_string())?; + let right = interpreter_stack + .pop() + .ok_or("Internal Error: Failed to get RHS of &&".to_string())?; + + let result = left.compare(&right, |a, b| match (a.get_bool(), b.get_bool()) { + (Some(a), Some(b)) => a || b, + _ => false, + })?; + + interpreter_stack.push(result); + + Ok(()) + } + pub(crate) fn run_compare_instruction( interpreter_stack: &mut InterpreterStack, compare_fn: fn(LiteralValue, LiteralValue) -> bool, @@ -837,8 +860,9 @@ mod internal { .ok_or("Failed to get a value from the stack to unwrap".to_string())?; let unwrapped_value = value + .clone() .unwrap() - .ok_or("Failed to unwrap the value".to_string())?; + .ok_or(format!("Failed to unwrap the value {:?}", value))?; interpreter_stack.push_val(unwrapped_value); Ok(()) @@ -864,6 +888,7 @@ mod internal { }, None => "err".to_string(), }, + TypeAnnotatedValue::Enum(enum_) => enum_.value, _ => "untagged".to_string(), }; @@ -1282,12 +1307,11 @@ mod interpreter_tests { let mut interpreter = Interpreter::default(); let expr = r#" - let x = some(some(1u64)); + let x: option> = none; match x { - some(x) => match x { - some(x) => x - } + some(some(x)) => x, + none => 0u64 } "#; @@ -1296,7 +1320,7 @@ mod interpreter_tests { let compiled = compiler::compile(&expr, &vec![]).unwrap(); let result = interpreter.run(compiled.byte_code).await.unwrap(); - assert_eq!(result.get_val().unwrap(), TypeAnnotatedValue::U64(1)); + assert_eq!(result.get_val().unwrap(), TypeAnnotatedValue::U64(0)); } #[tokio::test] @@ -1515,7 +1539,6 @@ mod interpreter_tests { "#; let expr = Expr::from_text(expr).unwrap(); - dbg!(expr.clone()); let compiled = compiler::compile(&expr, &analysed_exports).unwrap(); let result = interpreter.run(compiled.byte_code).await.unwrap(); diff --git a/golem-rib/src/interpreter/tests/mod.rs b/golem-rib/src/interpreter/tests/mod.rs index 16374df42..bad6bbb28 100644 --- a/golem-rib/src/interpreter/tests/mod.rs +++ b/golem-rib/src/interpreter/tests/mod.rs @@ -1,7 +1,10 @@ #[cfg(test)] mod comprehensive_test { use crate::{compiler, Expr}; - use golem_wasm_ast::analysis::{AnalysedType, NameTypePair, TypeRecord, TypeStr, TypeU64}; + use golem_wasm_ast::analysis::{ + AnalysedType, NameTypePair, TypeBool, TypeF32, TypeF64, TypeRecord, TypeS16, TypeS32, + TypeStr, TypeU64, TypeU8, + }; use golem_wasm_rpc::protobuf::type_annotated_value::TypeAnnotatedValue; #[tokio::test] @@ -22,21 +25,37 @@ mod comprehensive_test { let number_response = function-number-response(str1); - let option_str_response = function-option-str-response(str2); + let some_str_response = function-some-str-response(str2); - let option_number_response = function-option-number-response(str1); + let none_str_response = function-none-str-response(str2); - let option_option_response = function-option-option-response(str1); + let some_number_response = function-some-number-response(str1); - let option_variant_response = function-option-variant-response(str1); + let none_number_response = function-none-number-response(str1); - let option_enum_response = function-option-enum-response(str1); + let some_option_response = function-some-option-response(str1); - let option_tuple_response = function-option-tuple-response(str1); + let none_option_response = function-none-option-response(str1); - let option_record_response = function-option-record-response(str1); + let some_variant_response = function-some-variant-response(str1); - let option_list_response = function-option-list-response(str1); + let none_variant_response = function-none-variant-response(str1); + + let some_enum_response = function-some-enum-response(str1); + + let none_enum_response = function-none-enum-response(str1); + + let some_tuple_response = function-some-tuple-response(str1); + + let none_tuple_response = function-none-tuple-response(str1); + + let some_record_response = function-some-record-response(str1); + + let none_record_response = function-none-record-response(str1); + + let some_list_response = function-some-list-response(str1); + + let none_list_response = function-none-list-response(str1); let list_number_response = function-list-number-response(str1); @@ -82,44 +101,87 @@ mod comprehensive_test { let number_response_processed = if number_response == 42u64 then "foo" else "bar"; - let option_str_response_processed = match option_str_response { + let some_str_response_processed = match some_str_response { + some(text) => text, + none => "not found" + }; + + let none_str_response_processed = match none_str_response { some(text) => text, none => "not found" }; - let option_number_response_processed = match option_number_response { + + let some_number_response_processed = match some_number_response { some(number) => number, none => 0 }; - let option_option_response_processed = match option_option_response { + let none_number_response_processed = match none_number_response { + some(number) => number, + none => 0 + }; + + let some_option_response_processed = match some_option_response { + some(some(x)) => x, + none => "not found" + }; + + let none_option_response_processed = match none_option_response { some(some(x)) => x, none => "not found" }; - let option_variant_response_processed = match option_variant_response { + let some_variant_response_processed = match some_variant_response { + some(case-str(_)) => "found", + _ => "not found" + }; + + let none_variant_response_processed = match none_variant_response { some(case-str(_)) => "found", _ => "not found" }; - let option_enum_response_processed = match option_enum_response { + let some_enum_response_processed = match some_enum_response { some(enum-a) => "a", some(enum-b) => "b", _ => "not found" }; - let option_tuple_response_processed = match option_tuple_response { + let none_enum_response_processed = match none_enum_response { + some(enum-a) => "a", + some(enum-b) => "b", + _ => "not found" + }; + + let some_tuple_response_processed = match some_tuple_response { + some((text, _, _, _, _, _, _, _, _, _, _, _)) => text, + _ => "not found" + }; + + let none_tuple_response_processed = match none_tuple_response { some((text, _, _, _, _, _, _, _, _, _, _, _)) => text, _ => "not found" }; - let option_record_response_processed = match option_record_response { - some({data-body: {list-of-str : _}}) => "found list", + + let some_record_response_processed = match some_record_response { + some({data-body: {list-of-str : mylist}}) => mylist[0], + _ => "not found" + }; + + let none_record_response_processed = match none_record_response { + some({data-body: {list-of-str : mylist}}) => mylist[0], _ => "not found" }; - let option_list_response_processed = match option_list_response { - some([_]) => "found list", + let some_list_response_processed = match some_list_response { + some([foo]) => foo, + _ => "not found" + }; + + let none_list_response_processed = match none_list_response { + some([foo]) => foo, _ => "not found" }; @@ -157,11 +219,68 @@ mod comprehensive_test { _ => "not found" }; - let list_tuple_response_processed = match list_tuple_response { + let list_tuple_response_processed1 = match list_tuple_response { [(text, _, _, _, _, _, _, _, _, _, _, _)] => text, _ => "not found" }; + + let list_tuple_response_processed2 = match list_tuple_response { + [(_, number, _, _, _, _, _, _, _, _, _, _)] => number, + _ => 0 + }; + + let list_tuple_response_processed3 = match list_tuple_response { + [(_, _, number, _, _, _, _, _, _, _, _, _)] => number, + _ => 0 + }; + + let list_tuple_response_processed4 = match list_tuple_response { + [(_, _, _, number, _, _, _, _, _, _, _, _)] => number, + _ => 0 + }; + + let list_tuple_response_processed5 = match list_tuple_response { + [(_, _, _, _, number, _, _, _, _, _, _, _)] => number, + _ => 0 + }; + + let list_tuple_response_processed6 = match list_tuple_response { + [(_, _, _, _, _, boolean, _, _, _, _, _, _)] => boolean, + _ => false + }; + + let list_tuple_response_processed7 = match list_tuple_response { + [(_, _, _, _, _, _, char, _, _, _, _, _)] => "${char}", + _ => "not found" + }; + + let list_tuple_response_processed8 = match list_tuple_response { + [(_, _, _, _, _, _, _, some(number), _, _, _, _)] => number, + _ => 0 + }; + + let list_tuple_response_processed9 = match list_tuple_response { + [(_, _, _, _, _, _, _, _, ok(number), _, _, _)] => number, + _ => 0 + }; + + let list_tuple_response_processed10 = match list_tuple_response { + [(_, _, _, _, _, _, _, _, _, [boolean], _, _)] => boolean, + _ => false + }; + + let list_tuple_response_processed11 = match list_tuple_response { + [(_, _, _, _, _, _, _, _, _, _, case-hello(number), _)] => number, + _ => 0 + }; + + let list_tuple_response_processed12 = match list_tuple_response { + [(_, _, _, _, _, _, _, _, _, _, _, {field-one: boolean, field-two: text})] => "${boolean}-${text}", + _ => "not found" + }; + + let list_record_response_processed = match list_record_response { [{data-body: {list-of-str : [text]}}] => text, _ => "not found" @@ -169,17 +288,17 @@ mod comprehensive_test { let result_of_str_response_processed = match result_of_str_response { ok(text) => text, - err(msg) => "not found" + err(msg) => msg }; let result_of_number_response_processed = match result_of_number_response { ok(number) => number, - err(msg) => 0 + err(number) => number }; let result_of_variant_response_processed = match result_of_variant_response { - ok(case-str(_)) => "found", - err(msg) => "not found" + ok(case-str(a)) => a, + err(case-str(b)) => b }; let result_of_enum_response_processed = match result_of_enum_response { @@ -203,7 +322,7 @@ mod comprehensive_test { }; let result_of_record_response_processed = match result_of_record_response { - ok({data-body: {list-of-str : _}}) => "found list", + ok({data-body: {list-of-str : mylist}}) => mylist[0], err(msg) => "not found" }; @@ -224,21 +343,39 @@ mod comprehensive_test { }; { - a : option_str_response_processed, - b: option_number_response_processed, - c: option_option_response_processed, - d: option_variant_response_processed, - e: option_enum_response_processed, - f: option_tuple_response_processed, - g: option_record_response_processed, - h: option_list_response_processed, + a : some_str_response_processed, + aa: list_tuple_response_processed2, + ab: list_tuple_response_processed3, + ac: list_tuple_response_processed4, + ad: list_tuple_response_processed5, + ae: list_tuple_response_processed6, + af: list_tuple_response_processed7, + ag: list_tuple_response_processed8, + ah: list_tuple_response_processed9, + ai: list_tuple_response_processed10, + aj: list_tuple_response_processed11, + ak: list_tuple_response_processed12, + b: some_number_response_processed, + bb: none_number_response_processed, + c: some_option_response_processed, + cc: none_option_response_processed, + d: some_variant_response_processed, + dd: none_variant_response_processed, + e: some_enum_response_processed, + ee: none_enum_response_processed, + f: some_tuple_response_processed, + ff: none_tuple_response_processed, + g: some_record_response_processed, + gg: none_record_response_processed, + h: some_list_response_processed, + hh: none_list_response_processed, i: list_number_response_processed, j: list_str_response_processed, k: list_option_response_processed, l: list_list_response_processed, m: list_variant_response_processed, n: list_enum_response_processed, - o: list_tuple_response_processed, + o: list_tuple_response_processed1, p: list_record_response_processed, q: result_of_str_response_processed, r: result_of_number_response_processed, @@ -266,7 +403,54 @@ mod comprehensive_test { } fn expected_type_annotated_value() -> TypeAnnotatedValue { - let wasm_wave_str = "{a: \"foo\", b: 42, c: \"foo\", d: \"found\", e: \"a\", f: \"foo\", g: \"found list\", h: \"found list\", i: \"greater\", j: \"foo\", k: \"foo\", l: \"foo\", m: \"foo\", n: \"a\", o: \"foo\", p: \"foo\", q: \"foo\", r: 42, s: \"found\", t: \"a\", u: \"foo\", v: \"found x\", w: \"found list\", x: \"42\", y: \"a\", z: \"foo\"}"; + let wasm_wave_str = r#" + { + a: "foo", + b: 42, + bb: 0, + c: "foo", + cc: "not found", + d: "found", + dd: "not found", + e: "a", + ee: "not found", + f: "foo", + ff: "not found", + g: "foo", + gg: "not found", + h: "foo", + hh: "not found", + i: "greater", + j: "foo", + k: "foo", + l: "foo", + m: "foo", + n: "a", + o: "foo", + p: "foo", + q: "foo", + r: 42, + s: "foo", + t: "a", + u: "foo", + v: "found x", + w: "foo", + x: "42", + y: "a", + z: "foo", + aa: 42, + ab: 42, + ac: 42, + ad: 42, + ae: true, + af: "a", + ag: 42, + ah: 42, + ai: true, + aj: 42, + ak: "true-foo" + } + "#; test_utils::get_type_annotated_value(&expected_analysed_type(), wasm_wave_str) } @@ -278,34 +462,106 @@ mod comprehensive_test { name: "a".to_string(), typ: AnalysedType::Str(TypeStr), }, + NameTypePair { + name: "aa".to_string(), + typ: AnalysedType::U64(TypeU64), + }, + NameTypePair { + name: "ab".to_string(), + typ: AnalysedType::S32(TypeS32), + }, + NameTypePair { + name: "ac".to_string(), + typ: AnalysedType::F32(TypeF32), + }, + NameTypePair { + name: "ad".to_string(), + typ: AnalysedType::F64(TypeF64), + }, + NameTypePair { + name: "ae".to_string(), + typ: AnalysedType::Bool(TypeBool), + }, + NameTypePair { + name: "af".to_string(), + typ: AnalysedType::Str(TypeStr), + }, + NameTypePair { + name: "ag".to_string(), + typ: AnalysedType::S16(TypeS16), + }, + NameTypePair { + name: "ah".to_string(), + typ: AnalysedType::U8(TypeU8), + }, + NameTypePair { + name: "ai".to_string(), + typ: AnalysedType::Bool(TypeBool), + }, + NameTypePair { + name: "aj".to_string(), + typ: AnalysedType::F64(TypeF64), + }, + NameTypePair { + name: "ak".to_string(), + typ: AnalysedType::Str(TypeStr), + }, NameTypePair { name: "b".to_string(), typ: AnalysedType::U64(TypeU64), }, + NameTypePair { + name: "bb".to_string(), + typ: AnalysedType::U64(TypeU64), + }, NameTypePair { name: "c".to_string(), typ: AnalysedType::Str(TypeStr), }, + NameTypePair { + name: "cc".to_string(), + typ: AnalysedType::Str(TypeStr), + }, NameTypePair { name: "d".to_string(), typ: AnalysedType::Str(TypeStr), }, + NameTypePair { + name: "dd".to_string(), + typ: AnalysedType::Str(TypeStr), + }, NameTypePair { name: "e".to_string(), typ: AnalysedType::Str(TypeStr), }, + NameTypePair { + name: "ee".to_string(), + typ: AnalysedType::Str(TypeStr), + }, NameTypePair { name: "f".to_string(), typ: AnalysedType::Str(TypeStr), }, + NameTypePair { + name: "ff".to_string(), + typ: AnalysedType::Str(TypeStr), + }, NameTypePair { name: "g".to_string(), typ: AnalysedType::Str(TypeStr), }, + NameTypePair { + name: "gg".to_string(), + typ: AnalysedType::Str(TypeStr), + }, NameTypePair { name: "h".to_string(), typ: AnalysedType::Str(TypeStr), }, + NameTypePair { + name: "hh".to_string(), + typ: AnalysedType::Str(TypeStr), + }, NameTypePair { name: "i".to_string(), typ: AnalysedType::Str(TypeStr), @@ -393,14 +649,22 @@ mod comprehensive_test { exports.extend(function_metadata::function_no_arg_unit()); exports.extend(function_metadata::function_str_response()); exports.extend(function_metadata::function_number_response()); - exports.extend(function_metadata::function_option_of_str_response()); - exports.extend(function_metadata::function_option_of_number_response()); - exports.extend(function_metadata::function_option_of_option_response()); - exports.extend(function_metadata::function_option_of_variant_response()); - exports.extend(function_metadata::function_option_of_enum_response()); - exports.extend(function_metadata::function_option_of_tuple_response()); - exports.extend(function_metadata::function_option_of_record_response()); - exports.extend(function_metadata::function_option_of_list_response()); + exports.extend(function_metadata::function_some_of_str_response()); + exports.extend(function_metadata::function_none_of_str_response()); + exports.extend(function_metadata::function_some_of_number_response()); + exports.extend(function_metadata::function_none_of_number_response()); + exports.extend(function_metadata::function_some_of_option_response()); + exports.extend(function_metadata::function_none_of_option_response()); + exports.extend(function_metadata::function_some_of_variant_response()); + exports.extend(function_metadata::function_none_of_variant_response()); + exports.extend(function_metadata::function_some_of_enum_response()); + exports.extend(function_metadata::function_none_of_enum_response()); + exports.extend(function_metadata::function_some_of_tuple_response()); + exports.extend(function_metadata::function_none_of_tuple_response()); + exports.extend(function_metadata::function_some_of_record_response()); + exports.extend(function_metadata::function_none_of_record_response()); + exports.extend(function_metadata::function_some_of_list_response()); + exports.extend(function_metadata::function_none_of_list_response()); exports.extend(function_metadata::function_list_of_number_response()); exports.extend(function_metadata::function_list_of_str_response()); exports.extend(function_metadata::function_list_of_option_response()); @@ -469,65 +733,129 @@ mod comprehensive_test { ) } - pub(crate) fn function_option_of_str_response() -> Vec { + pub(crate) fn function_some_of_str_response() -> Vec { + test_utils::get_function_component_metadata( + "function-some-str-response", + vec![data_types::str_type()], + Some(data_types::option_of_str_type()), + ) + } + + pub(crate) fn function_none_of_str_response() -> Vec { test_utils::get_function_component_metadata( - "function-option-str-response", + "function-none-str-response", vec![data_types::str_type()], Some(data_types::option_of_str_type()), ) } - pub(crate) fn function_option_of_number_response() -> Vec { + pub(crate) fn function_some_of_number_response() -> Vec { + test_utils::get_function_component_metadata( + "function-some-number-response", + vec![data_types::str_type()], + Some(data_types::option_of_number_type()), + ) + } + + pub(crate) fn function_none_of_number_response() -> Vec { test_utils::get_function_component_metadata( - "function-option-number-response", + "function-none-number-response", vec![data_types::str_type()], Some(data_types::option_of_number_type()), ) } - pub(crate) fn function_option_of_option_response() -> Vec { + pub(crate) fn function_some_of_option_response() -> Vec { + test_utils::get_function_component_metadata( + "function-some-option-response", + vec![data_types::str_type()], + Some(data_types::option_of_option_type()), + ) + } + + pub(crate) fn function_none_of_option_response() -> Vec { test_utils::get_function_component_metadata( - "function-option-option-response", + "function-none-option-response", vec![data_types::str_type()], Some(data_types::option_of_option_type()), ) } - pub(crate) fn function_option_of_variant_response() -> Vec { + pub(crate) fn function_some_of_variant_response() -> Vec { + test_utils::get_function_component_metadata( + "function-some-variant-response", + vec![data_types::str_type()], + Some(data_types::option_of_variant_type()), + ) + } + + pub(crate) fn function_none_of_variant_response() -> Vec { test_utils::get_function_component_metadata( - "function-option-variant-response", + "function-none-variant-response", vec![data_types::str_type()], Some(data_types::option_of_variant_type()), ) } - pub(crate) fn function_option_of_enum_response() -> Vec { + pub(crate) fn function_some_of_enum_response() -> Vec { + test_utils::get_function_component_metadata( + "function-some-enum-response", + vec![data_types::str_type()], + Some(data_types::option_of_enum_type()), + ) + } + + pub(crate) fn function_none_of_enum_response() -> Vec { test_utils::get_function_component_metadata( - "function-option-enum-response", + "function-none-enum-response", vec![data_types::str_type()], Some(data_types::option_of_enum_type()), ) } - pub(crate) fn function_option_of_tuple_response() -> Vec { + pub(crate) fn function_some_of_tuple_response() -> Vec { + test_utils::get_function_component_metadata( + "function-some-tuple-response", + vec![data_types::str_type()], + Some(data_types::option_of_tuple()), + ) + } + + pub(crate) fn function_none_of_tuple_response() -> Vec { test_utils::get_function_component_metadata( - "function-option-tuple-response", + "function-none-tuple-response", vec![data_types::str_type()], Some(data_types::option_of_tuple()), ) } - pub(crate) fn function_option_of_record_response() -> Vec { + pub(crate) fn function_some_of_record_response() -> Vec { + test_utils::get_function_component_metadata( + "function-some-record-response", + vec![data_types::str_type()], + Some(data_types::option_of_record_type()), + ) + } + + pub(crate) fn function_none_of_record_response() -> Vec { test_utils::get_function_component_metadata( - "function-option-record-response", + "function-none-record-response", vec![data_types::str_type()], Some(data_types::option_of_record_type()), ) } - pub(crate) fn function_option_of_list_response() -> Vec { + pub(crate) fn function_some_of_list_response() -> Vec { + test_utils::get_function_component_metadata( + "function-some-list-response", + vec![data_types::str_type()], + Some(data_types::option_of_list()), + ) + } + + pub(crate) fn function_none_of_list_response() -> Vec { test_utils::get_function_component_metadata( - "function-option-list-response", + "function-none-list-response", vec![data_types::str_type()], Some(data_types::option_of_list()), ) @@ -1295,39 +1623,63 @@ mod comprehensive_test { ) } - pub(crate) fn option_of_number() -> TypeAnnotatedValue { + pub(crate) fn some_of_number() -> TypeAnnotatedValue { test_utils::get_type_annotated_value(&data_types::option_of_number_type(), "some(42)") } - pub(crate) fn option_of_str() -> TypeAnnotatedValue { + pub(crate) fn none_of_number() -> TypeAnnotatedValue { + test_utils::get_type_annotated_value(&data_types::option_of_number_type(), "none") + } + + pub(crate) fn some_of_str() -> TypeAnnotatedValue { test_utils::get_type_annotated_value(&data_types::option_of_str_type(), "some(\"foo\")") } - pub(crate) fn option_of_option() -> TypeAnnotatedValue { + pub(crate) fn none_of_str() -> TypeAnnotatedValue { + test_utils::get_type_annotated_value(&data_types::option_of_str_type(), "none") + } + + pub(crate) fn some_of_some() -> TypeAnnotatedValue { test_utils::get_type_annotated_value( &data_types::option_of_option_type(), "some(some(\"foo\"))", ) } - pub(crate) fn option_of_variant() -> TypeAnnotatedValue { + pub(crate) fn none_of_some() -> TypeAnnotatedValue { + test_utils::get_type_annotated_value(&data_types::option_of_option_type(), "none") + } + + pub(crate) fn some_of_variant() -> TypeAnnotatedValue { test_utils::get_type_annotated_value( &data_types::option_of_variant_type(), "some(case-str(\"foo\"))", ) } - pub(crate) fn option_of_enum() -> TypeAnnotatedValue { + pub(crate) fn none_of_variant() -> TypeAnnotatedValue { + test_utils::get_type_annotated_value(&data_types::option_of_variant_type(), "none") + } + + pub(crate) fn some_of_enum() -> TypeAnnotatedValue { test_utils::get_type_annotated_value(&data_types::option_of_enum_type(), "some(enum-a)") } - pub(crate) fn option_of_tuple() -> TypeAnnotatedValue { + pub(crate) fn none_of_enum() -> TypeAnnotatedValue { + test_utils::get_type_annotated_value(&data_types::option_of_enum_type(), "none") + } + + pub(crate) fn some_of_tuple() -> TypeAnnotatedValue { let tuple_str = test_utils::convert_type_annotated_value_to_str(&tuple()); let wave_str = format!("some({})", tuple_str); test_utils::get_type_annotated_value(&data_types::option_of_tuple(), wave_str.as_str()) } - pub(crate) fn option_of_record() -> TypeAnnotatedValue { + pub(crate) fn none_of_tuple() -> TypeAnnotatedValue { + test_utils::get_type_annotated_value(&data_types::option_of_tuple(), "none") + } + + pub(crate) fn some_of_record() -> TypeAnnotatedValue { let record_str = test_utils::convert_type_annotated_value_to_str(&record()); let wave_str = format!("some({})", &record_str); test_utils::get_type_annotated_value( @@ -1336,10 +1688,18 @@ mod comprehensive_test { ) } - pub(crate) fn option_of_list() -> TypeAnnotatedValue { + pub(crate) fn none_of_record() -> TypeAnnotatedValue { + test_utils::get_type_annotated_value(&data_types::option_of_record_type(), "none") + } + + pub(crate) fn some_of_list() -> TypeAnnotatedValue { test_utils::get_type_annotated_value(&data_types::option_of_list(), "some([\"foo\"])") } + pub(crate) fn none_of_list() -> TypeAnnotatedValue { + test_utils::get_type_annotated_value(&data_types::option_of_list(), "none") + } + pub(crate) fn tuple() -> TypeAnnotatedValue { test_utils::get_type_annotated_value( &data_types::tuple_type(), @@ -1456,37 +1816,63 @@ mod comprehensive_test { ("function-no-arg-unit", None), ("function-str-response", Some(mock_data::str_data())), ("function-number-response", Some(mock_data::number_data())), + ("function-some-str-response", Some(mock_data::some_of_str())), + ("function-none-str-response", Some(mock_data::none_of_str())), + ( + "function-some-number-response", + Some(mock_data::some_of_number()), + ), + ( + "function-none-number-response", + Some(mock_data::none_of_number()), + ), + ( + "function-some-option-response", + Some(mock_data::some_of_some()), + ), + ( + "function-none-option-response", + Some(mock_data::none_of_some()), + ), + ( + "function-some-variant-response", + Some(mock_data::some_of_variant()), + ), + ( + "function-none-variant-response", + Some(mock_data::none_of_variant()), + ), ( - "function-option-str-response", - Some(mock_data::option_of_str()), + "function-some-enum-response", + Some(mock_data::some_of_enum()), ), ( - "function-option-number-response", - Some(mock_data::option_of_number()), + "function-none-enum-response", + Some(mock_data::none_of_enum()), ), ( - "function-option-option-response", - Some(mock_data::option_of_option()), + "function-some-tuple-response", + Some(mock_data::some_of_tuple()), ), ( - "function-option-variant-response", - Some(mock_data::option_of_variant()), + "function-none-tuple-response", + Some(mock_data::none_of_tuple()), ), ( - "function-option-enum-response", - Some(mock_data::option_of_enum()), + "function-some-record-response", + Some(mock_data::some_of_record()), ), ( - "function-option-tuple-response", - Some(mock_data::option_of_tuple()), + "function-none-record-response", + Some(mock_data::none_of_record()), ), ( - "function-option-record-response", - Some(mock_data::option_of_record()), + "function-some-list-response", + Some(mock_data::some_of_list()), ), ( - "function-option-list-response", - Some(mock_data::option_of_list()), + "function-none-list-response", + Some(mock_data::none_of_list()), ), ( "function-list-number-response", diff --git a/golem-rib/src/parser/binary_comparison.rs b/golem-rib/src/parser/binary_comparison.rs index aa51c1173..1908ebc38 100644 --- a/golem-rib/src/parser/binary_comparison.rs +++ b/golem-rib/src/parser/binary_comparison.rs @@ -29,6 +29,8 @@ where attempt(string("==")), string("<"), string(">"), + string("&&"), + string("||"), )) .and_then(|str| match str { ">" => Ok(BinaryOp::GreaterThan), @@ -36,6 +38,8 @@ where "==" => Ok(BinaryOp::EqualTo), ">=" => Ok(BinaryOp::GreaterThanOrEqualTo), "<=" => Ok(BinaryOp::LessThanOrEqualTo), + "&&" => Ok(BinaryOp::And), + "||" => Ok(BinaryOp::Or), _ => Err(RibParseError::Message( "Invalid binary operator".to_string(), )), @@ -48,6 +52,8 @@ pub enum BinaryOp { LessThanOrEqualTo, GreaterThanOrEqualTo, EqualTo, + And, + Or, } #[cfg(test)] diff --git a/golem-rib/src/parser/multi_line_code_block.rs b/golem-rib/src/parser/multi_line_code_block.rs index 11cf77527..4fd4652dd 100644 --- a/golem-rib/src/parser/multi_line_code_block.rs +++ b/golem-rib/src/parser/multi_line_code_block.rs @@ -150,7 +150,10 @@ mod tests { let expected = Expr::pattern_match( Expr::identifier("foo"), vec![MatchArm::new( - ArmPattern::Literal(Box::new(Expr::option(Some(Expr::identifier("x"))))), + ArmPattern::Constructor( + "some".to_string(), + vec![ArmPattern::Literal(Box::new(Expr::identifier("x")))], + ), Expr::multiple(vec![ Expr::let_binding("x", Expr::number(1f64)), Expr::let_binding("y", Expr::number(2f64)), @@ -190,7 +193,10 @@ mod tests { Expr::pattern_match( Expr::identifier("foo"), vec![MatchArm::new( - ArmPattern::Literal(Box::new(Expr::option(Some(Expr::identifier("x"))))), + ArmPattern::Constructor( + "some".to_string(), + vec![ArmPattern::Literal(Box::new(Expr::identifier("x")))], + ), Expr::multiple(vec![ Expr::let_binding("x", Expr::number(1f64)), Expr::let_binding("y", Expr::number(2f64)), diff --git a/golem-rib/src/parser/pattern_match.rs b/golem-rib/src/parser/pattern_match.rs index 7225d3685..2ca00dcac 100644 --- a/golem-rib/src/parser/pattern_match.rs +++ b/golem-rib/src/parser/pattern_match.rs @@ -128,9 +128,8 @@ mod internal { use crate::expr::ArmPattern; use crate::parser::errors::RibParseError; - use crate::parser::optional::option; use crate::parser::pattern_match::arm_pattern::*; - use crate::parser::result::result; + use crate::parser::rib_expr::rib_expr; pub(crate) fn arm_pattern_constructor() -> impl Parser @@ -141,9 +140,7 @@ mod internal { >, { choice(( - attempt(option().map(|expr| ArmPattern::Literal(Box::new(expr)))), - attempt(result().map(|expr| ArmPattern::Literal(Box::new(expr)))), - attempt(custom_arm_pattern_constructor()), + attempt(arm_pattern_constructor_with_name()), attempt(tuple_arm_pattern_constructor()), attempt(list_arm_pattern_constructor()), attempt(record_arm_pattern_constructor()), @@ -172,20 +169,32 @@ mod internal { .message("Unable to parse alias name") } - fn custom_arm_pattern_constructor() -> impl Parser + fn arm_pattern_constructor_with_name() -> impl Parser where Input: combine::Stream, RibParseError: Into< >::StreamError, >, { - ( + let custom = ( constructor_type_name().skip(spaces()), string("(").skip(spaces()), sep_by(arm_pattern().skip(spaces()), char_(',').skip(spaces())), string(")").skip(spaces()), ) - .map(|(name, _, patterns, _)| ArmPattern::Constructor(name, patterns)) + .map(|(name, _, patterns, _)| ArmPattern::Constructor(name, patterns)); + + attempt(none_constructor()).or(custom) + } + + fn none_constructor() -> impl Parser + where + Input: combine::Stream, + RibParseError: Into< + >::StreamError, + >, + { + string("none").map(|_| ArmPattern::constructor("none", vec![])) } fn tuple_arm_pattern_constructor() -> impl Parser @@ -410,22 +419,29 @@ mod tests { vec![ MatchArm::new(ArmPattern::WildCard, Expr::identifier("bar")), MatchArm::new( - ArmPattern::Literal(Box::new(Expr::ok(Expr::identifier("x")))), - Expr::identifier("x") + ArmPattern::constructor( + "ok", + vec![ArmPattern::Literal(Box::new(Expr::identifier("x")))], + ), + Expr::identifier("x"), ), MatchArm::new( - ArmPattern::Literal(Box::new(Expr::err(Expr::identifier("x")))), - Expr::identifier("x") + ArmPattern::constructor( + "err", + vec![ArmPattern::Literal(Box::new(Expr::identifier("x")))], + ), + Expr::identifier("x"), ), MatchArm::new( - ArmPattern::Literal(Box::new(Expr::option(None))), - Expr::identifier("foo") + ArmPattern::constructor("none", vec![]), + Expr::identifier("foo"), ), MatchArm::new( - ArmPattern::Literal(Box::new(Expr::option(Some(Expr::identifier( - "x" - ))))), - Expr::identifier("x") + ArmPattern::constructor( + "some", + vec![ArmPattern::Literal(Box::new(Expr::identifier("x")))], + ), + Expr::identifier("x"), ), ] ), diff --git a/golem-rib/src/parser/rib_expr.rs b/golem-rib/src/parser/rib_expr.rs index d627ed687..d1d0f9275 100644 --- a/golem-rib/src/parser/rib_expr.rs +++ b/golem-rib/src/parser/rib_expr.rs @@ -70,6 +70,8 @@ where BinaryOp::LessThanOrEqualTo => Expr::less_than_or_equal_to(acc, next), BinaryOp::GreaterThanOrEqualTo => Expr::greater_than_or_equal_to(acc, next), BinaryOp::EqualTo => Expr::equal_to(acc, next), + BinaryOp::And => Expr::and(acc, next), + BinaryOp::Or => Expr::or(acc, next), }) }), ) diff --git a/golem-rib/src/parser/select_field.rs b/golem-rib/src/parser/select_field.rs index b62674568..37452184f 100644 --- a/golem-rib/src/parser/select_field.rs +++ b/golem-rib/src/parser/select_field.rs @@ -261,26 +261,33 @@ mod tests { vec![ MatchArm::new(ArmPattern::WildCard, Expr::identifier("bar")), MatchArm::new( - ArmPattern::Literal(Box::new(Expr::ok(Expr::identifier("x")))), - Expr::identifier("x") + ArmPattern::constructor( + "ok", + vec![ArmPattern::Literal(Box::new(Expr::identifier("x")))] + ), + Expr::identifier("x"), ), MatchArm::new( - ArmPattern::Literal(Box::new(Expr::err(Expr::identifier("x")))), - Expr::identifier("x") + ArmPattern::constructor( + "err", + vec![ArmPattern::Literal(Box::new(Expr::identifier("x")))] + ), + Expr::identifier("x"), ), MatchArm::new( - ArmPattern::Literal(Box::new(Expr::option(None))), - Expr::identifier("foo") + ArmPattern::constructor("none", vec![]), + Expr::identifier("foo"), ), MatchArm::new( - ArmPattern::Literal(Box::new(Expr::option(Some(Expr::identifier( - "x" - ))))), - Expr::identifier("x") + ArmPattern::constructor( + "some", + vec![ArmPattern::Literal(Box::new(Expr::identifier("x")))] + ), + Expr::identifier("x"), ), MatchArm::new( ArmPattern::Literal(Box::new(Expr::identifier("foo"))), - Expr::select_field(Expr::identifier("foo"), "bar") + Expr::select_field(Expr::identifier("foo"), "bar"), ), ] ), diff --git a/golem-rib/src/text/mod.rs b/golem-rib/src/text/mod.rs index 62ff7320f..97aa262c4 100644 --- a/golem-rib/src/text/mod.rs +++ b/golem-rib/src/text/mod.rs @@ -337,8 +337,20 @@ mod record_tests { Expr::pattern_match( Expr::identifier("request"), vec![ - MatchArm::new(ArmPattern::ok("foo"), Expr::literal("success")), - MatchArm::new(ArmPattern::err("msg"), Expr::literal("failure")), + MatchArm::new( + ArmPattern::constructor( + "ok", + vec![ArmPattern::literal(Expr::identifier("foo"))], + ), + Expr::literal("success"), + ), + MatchArm::new( + ArmPattern::constructor( + "err", + vec![ArmPattern::literal(Expr::identifier("msg"))], + ), + Expr::literal("failure"), + ), ], ), ), @@ -347,14 +359,35 @@ mod record_tests { Expr::pattern_match( Expr::identifier("request"), vec![ - MatchArm::new(ArmPattern::ok("foo"), Expr::literal("success")), MatchArm::new( - ArmPattern::err("msg"), + ArmPattern::constructor( + "ok", + vec![ArmPattern::literal(Expr::identifier("foo"))], + ), // Use Constructor for ok + Expr::literal("success"), + ), + MatchArm::new( + ArmPattern::constructor( + "err", + vec![ArmPattern::literal(Expr::identifier("msg"))], + ), Expr::pattern_match( Expr::identifier("request"), vec![ - MatchArm::new(ArmPattern::ok("foo"), Expr::literal("success")), - MatchArm::new(ArmPattern::err("msg"), Expr::literal("failure")), + MatchArm::new( + ArmPattern::constructor( + "ok", + vec![ArmPattern::literal(Expr::identifier("foo"))], + ), + Expr::literal("success"), + ), + MatchArm::new( + ArmPattern::constructor( + "err", + vec![ArmPattern::literal(Expr::identifier("msg"))], + ), + Expr::literal("failure"), + ), ], ), ), @@ -362,6 +395,7 @@ mod record_tests { ), ), ]); + let expr_str = to_string(&input_expr).unwrap(); let expected_record_str = r#"{a: match request { ok(foo) => "success", err(msg) => "failure" } , b: match request { ok(foo) => "success", err(msg) => match request { ok(foo) => "success", err(msg) => "failure" } } }"#.to_string(); let output_expr = from_string(expr_str.as_str()).unwrap(); @@ -595,27 +629,61 @@ mod sequence_tests { Expr::pattern_match( Expr::identifier("request"), vec![ - MatchArm::new(ArmPattern::ok("foo"), Expr::literal("success")), - MatchArm::new(ArmPattern::err("msg"), Expr::literal("failure")), + MatchArm::new( + ArmPattern::Constructor( + "ok".to_string(), + vec![ArmPattern::literal(Expr::identifier("foo"))], + ), + Expr::literal("success"), + ), + MatchArm::new( + ArmPattern::Constructor( + "err".to_string(), + vec![ArmPattern::literal(Expr::identifier("msg"))], + ), + Expr::literal("failure"), + ), ], ), Expr::pattern_match( Expr::identifier("request"), vec![ - MatchArm::new(ArmPattern::ok("foo"), Expr::literal("success")), MatchArm::new( - ArmPattern::err("msg"), + ArmPattern::Constructor( + "ok".to_string(), + vec![ArmPattern::literal(Expr::identifier("foo"))], + ), + Expr::literal("success"), + ), + MatchArm::new( + ArmPattern::Constructor( + "err".to_string(), + vec![ArmPattern::literal(Expr::identifier("msg"))], + ), Expr::pattern_match( Expr::identifier("request"), vec![ - MatchArm::new(ArmPattern::ok("foo"), Expr::literal("success")), - MatchArm::new(ArmPattern::err("msg"), Expr::literal("failure")), + MatchArm::new( + ArmPattern::Constructor( + "ok".to_string(), + vec![ArmPattern::literal(Expr::identifier("foo"))], + ), // Use Constructor for ok + Expr::literal("success"), + ), + MatchArm::new( + ArmPattern::Constructor( + "err".to_string(), + vec![ArmPattern::literal(Expr::identifier("msg"))], + ), + Expr::literal("failure"), + ), ], ), ), ], ), ]); + let expr_str = to_string(&input_expr).unwrap(); let expected_str = r#"[match request { ok(foo) => "success", err(msg) => "failure" } , match request { ok(foo) => "success", err(msg) => match request { ok(foo) => "success", err(msg) => "failure" } } ]"#.to_string(); let output_expr = from_string(expr_str.as_str()).unwrap(); @@ -1159,8 +1227,20 @@ mod match_tests { let mut input_expr = Expr::pattern_match( Expr::identifier("request"), vec![ - MatchArm::new(ArmPattern::ok("foo"), Expr::literal("success")), - MatchArm::new(ArmPattern::err("msg"), Expr::literal("failure")), + MatchArm::new( + ArmPattern::constructor( + "ok", + vec![ArmPattern::literal(Expr::identifier("foo"))], + ), + Expr::literal("success"), + ), + MatchArm::new( + ArmPattern::constructor( + "err", + vec![ArmPattern::literal(Expr::identifier("msg"))], + ), + Expr::literal("failure"), + ), ], ); @@ -1180,10 +1260,19 @@ mod match_tests { Expr::identifier("request"), vec![ MatchArm::new( - ArmPattern::ok("foo"), + ArmPattern::constructor( + "ok", + vec![ArmPattern::literal(Expr::identifier("foo"))], + ), Expr::flags(vec!["flag1".to_string(), "flag2".to_string()]), ), - MatchArm::new(ArmPattern::err("msg"), Expr::literal("failure")), + MatchArm::new( + ArmPattern::constructor( + "err", + vec![ArmPattern::literal(Expr::identifier("msg"))], + ), + Expr::literal("failure"), + ), ], ); @@ -1200,13 +1289,22 @@ mod match_tests { Expr::identifier("request"), vec![ MatchArm::new( - ArmPattern::ok("foo"), + ArmPattern::constructor( + "ok", + vec![ArmPattern::literal(Expr::identifier("foo"))], + ), Expr::tuple(vec![ Expr::identifier("request"), Expr::identifier("request"), ]), ), - MatchArm::new(ArmPattern::err("msg"), Expr::literal("failure")), + MatchArm::new( + ArmPattern::constructor( + "err", + vec![ArmPattern::literal(Expr::identifier("msg"))], + ), + Expr::literal("failure"), + ), ], ); @@ -1224,13 +1322,22 @@ mod match_tests { Expr::identifier("request"), vec![ MatchArm::new( - ArmPattern::ok("foo"), + ArmPattern::constructor( + "ok", + vec![ArmPattern::literal(Expr::identifier("foo"))], + ), Expr::sequence(vec![ Expr::identifier("request"), Expr::identifier("request"), ]), ), - MatchArm::new(ArmPattern::err("msg"), Expr::literal("failure")), + MatchArm::new( + ArmPattern::constructor( + "err", + vec![ArmPattern::literal(Expr::identifier("msg"))], + ), + Expr::literal("failure"), + ), ], ); @@ -1248,10 +1355,19 @@ mod match_tests { Expr::identifier("request"), vec![ MatchArm::new( - ArmPattern::ok("foo"), + ArmPattern::constructor( + "ok", + vec![ArmPattern::literal(Expr::identifier("foo"))], + ), Expr::record(vec![("field".to_string(), Expr::identifier("request"))]), ), - MatchArm::new(ArmPattern::err("msg"), Expr::literal("failure")), + MatchArm::new( + ArmPattern::constructor( + "err", + vec![ArmPattern::literal(Expr::identifier("msg"))], + ), + Expr::literal("failure"), + ), ], ); @@ -1268,12 +1384,18 @@ mod match_tests { Expr::identifier("request"), vec![ MatchArm::new( - ArmPattern::ok("foo"), - Expr::greater_than(Expr::number(1f64), Expr::number(2f64)), + ArmPattern::constructor( + "ok", + vec![ArmPattern::literal(Expr::identifier("foo"))], + ), + Expr::greater_than(Expr::number(1.0), Expr::number(2.0)), ), MatchArm::new( - ArmPattern::err("msg"), - Expr::less_than(Expr::number(1f64), Expr::number(2f64)), + ArmPattern::constructor( + "err", + vec![ArmPattern::literal(Expr::identifier("msg"))], + ), + Expr::less_than(Expr::number(1.0), Expr::number(2.0)), ), ], ); @@ -1290,7 +1412,10 @@ mod match_tests { Expr::identifier("request"), vec![ MatchArm::new( - ArmPattern::ok("foo"), + ArmPattern::constructor( + "ok", + vec![ArmPattern::literal(Expr::identifier("foo"))], + ), Expr::cond( Expr::equal_to( Expr::select_field(Expr::identifier("request"), "foo"), @@ -1300,7 +1425,13 @@ mod match_tests { Expr::literal("failed"), ), ), - MatchArm::new(ArmPattern::err("msg"), Expr::literal("failure")), + MatchArm::new( + ArmPattern::constructor( + "err", + vec![ArmPattern::literal(Expr::identifier("msg"))], + ), + Expr::literal("failure"), + ), ], ); diff --git a/golem-rib/src/text/writer.rs b/golem-rib/src/text/writer.rs index ebe701585..7013eaddc 100644 --- a/golem-rib/src/text/writer.rs +++ b/golem-rib/src/text/writer.rs @@ -276,6 +276,11 @@ impl Writer { self.write_str(" && ")?; self.write_expr(right) } + Expr::Or(left, right, _) => { + self.write_expr(left)?; + self.write_str(" || ")?; + self.write_expr(right) + } } } diff --git a/golem-rib/src/type_inference/expr_visitor.rs b/golem-rib/src/type_inference/expr_visitor.rs index 0f7ea5d14..722cdb042 100644 --- a/golem-rib/src/type_inference/expr_visitor.rs +++ b/golem-rib/src/type_inference/expr_visitor.rs @@ -63,6 +63,16 @@ pub fn visit_children_bottom_up_mut<'a>(expr: &'a mut Expr, queue: &mut VecDeque queue.push_back(&mut *expr1); queue.push_back(&mut *expr2) } + + Expr::Or(expr1, expr2, _) => { + queue.push_back(&mut *expr1); + queue.push_back(&mut *expr2) + } + + Expr::GetTag(exr, _) => { + queue.push_back(&mut *exr); + } + Expr::Literal(_, _) => {} Expr::Number(_, _, _) => {} Expr::Flags(_, _) => {} @@ -70,7 +80,6 @@ pub fn visit_children_bottom_up_mut<'a>(expr: &'a mut Expr, queue: &mut VecDeque Expr::Boolean(_, _) => {} Expr::Option(None, _) => {} Expr::Throw(_, _) => {} - Expr::GetTag(_, _) => {} } } @@ -134,6 +143,14 @@ pub fn visit_children_bottom_up<'a>(expr: &'a Expr, queue: &mut VecDeque<&'a Exp queue.push_back(expr1); queue.push_back(expr2); } + Expr::Or(expr1, expr2, _) => { + queue.push_back(expr1); + queue.push_back(expr2); + } + Expr::GetTag(expr, _) => { + queue.push_back(expr); + } + Expr::Literal(_, _) => {} Expr::Number(_, _, _) => {} Expr::Flags(_, _) => {} @@ -141,7 +158,6 @@ pub fn visit_children_bottom_up<'a>(expr: &'a Expr, queue: &mut VecDeque<&'a Exp Expr::Boolean(_, _) => {} Expr::Option(None, _) => {} Expr::Throw(_, _) => {} - Expr::GetTag(_, _) => {} } } @@ -206,6 +222,10 @@ pub fn visit_children_mut_top_down<'a>(expr: &'a mut Expr, queue: &mut VecDeque< queue.push_front(&mut *expr1); queue.push_front(&mut *expr2) } + Expr::Or(expr1, expr2, _) => { + queue.push_front(&mut *expr1); + queue.push_front(&mut *expr2) + } Expr::PatternMatch(expr, arms, _) => { queue.push_front(&mut *expr); for arm in arms { @@ -228,6 +248,10 @@ pub fn visit_children_mut_top_down<'a>(expr: &'a mut Expr, queue: &mut VecDeque< queue.push_front(expr); } } + Expr::GetTag(expr, _) => { + queue.push_front(&mut *expr); + } + Expr::Unwrap(expr, _) => queue.push_front(&mut *expr), Expr::Literal(_, _) => {} Expr::Number(_, _, _) => {} @@ -236,7 +260,6 @@ pub fn visit_children_mut_top_down<'a>(expr: &'a mut Expr, queue: &mut VecDeque< Expr::Boolean(_, _) => {} Expr::Option(None, _) => {} Expr::Throw(_, _) => {} - Expr::GetTag(_, _) => {} } } diff --git a/golem-rib/src/type_inference/mod.rs b/golem-rib/src/type_inference/mod.rs index 36ad43937..c7449a8bd 100644 --- a/golem-rib/src/type_inference/mod.rs +++ b/golem-rib/src/type_inference/mod.rs @@ -984,23 +984,23 @@ mod type_inference_tests { )), vec![ MatchArm::new( - ArmPattern::Literal(Box::new(Expr::Option( - Some(Box::new(Expr::Identifier( + ArmPattern::constructor( + "some", + vec![ArmPattern::literal(Expr::Identifier( VariableId::match_identifier("z".to_string(), 1), InferredType::U64, - ))), - InferredType::Option(Box::new(InferredType::U64)), - ))), + ))], + ), Expr::Identifier(VariableId::local("y", 0), InferredType::U64), ), MatchArm::new( - ArmPattern::Literal(Box::new(Expr::Option( - Some(Box::new(Expr::Identifier( + ArmPattern::constructor( + "some", + vec![ArmPattern::literal(Expr::Identifier( VariableId::match_identifier("z".to_string(), 2), InferredType::U64, - ))), - InferredType::Option(Box::new(InferredType::U64)), - ))), + ))], + ), Expr::Identifier( VariableId::match_identifier("z".to_string(), 2), InferredType::U64, @@ -1187,13 +1187,13 @@ mod type_inference_tests { )), vec![ MatchArm::new( - ArmPattern::Literal(Box::new(Expr::Option( - Some(Box::new(Expr::Identifier( + ArmPattern::Constructor( + "some".to_string(), + vec![ArmPattern::Literal(Box::new(Expr::Identifier( VariableId::match_identifier("x".to_string(), 1), InferredType::U64, - ))), - InferredType::Option(Box::new(InferredType::U64)), - ))), + )))], + ), Expr::Option( Some(Box::new(Expr::Option( Some(Box::new(Expr::Identifier( @@ -1208,10 +1208,7 @@ mod type_inference_tests { ), ), MatchArm::new( - ArmPattern::Literal(Box::new(Expr::Option( - None, - InferredType::Option(Box::new(InferredType::U64)), - ))), + ArmPattern::constructor("none", vec![]), Expr::Option( Some(Box::new(Expr::Option( Some(Box::new(Expr::Identifier( @@ -1273,19 +1270,16 @@ mod type_inference_tests { )]))), )), vec![MatchArm::new( - ArmPattern::Literal(Box::new(Expr::Option( - Some(Box::new(Expr::Identifier( + ArmPattern::Constructor( + "some".to_string(), + vec![ArmPattern::Literal(Box::new(Expr::Identifier( VariableId::match_identifier("x".to_string(), 1), InferredType::Record(vec![( "foo".to_string(), InferredType::Str, )]), - ))), - InferredType::Option(Box::new(InferredType::Record(vec![( - "foo".to_string(), - InferredType::Str, - )]))), - ))), + )))], + ), Expr::Identifier( VariableId::match_identifier("x".to_string(), 1), InferredType::Record(vec![("foo".to_string(), InferredType::Str)]), @@ -1336,19 +1330,16 @@ mod type_inference_tests { )]))), )), vec![MatchArm::new( - ArmPattern::Literal(Box::new(Expr::Option( - Some(Box::new(Expr::Identifier( + ArmPattern::constructor( + "some", + vec![ArmPattern::literal(Expr::Identifier( VariableId::match_identifier("x".to_string(), 1), InferredType::Record(vec![( "foo".to_string(), InferredType::Str, )]), - ))), - InferredType::Option(Box::new(InferredType::Record(vec![( - "foo".to_string(), - InferredType::Str, - )]))), - ))), + ))], + ), Expr::SelectField( Box::new(Expr::Identifier( VariableId::match_identifier("x".to_string(), 1), @@ -1425,19 +1416,16 @@ mod type_inference_tests { )]))), )), vec![MatchArm::new( - ArmPattern::Literal(Box::new(Expr::Option( - Some(Box::new(Expr::Identifier( + ArmPattern::Constructor( + "some".to_string(), + vec![ArmPattern::Literal(Box::new(Expr::Identifier( VariableId::match_identifier("x".to_string(), 1), InferredType::Record(vec![( "foo".to_string(), InferredType::Str, )]), - ))), - InferredType::Option(Box::new(InferredType::Record(vec![( - "foo".to_string(), - InferredType::Str, - )]))), - ))), + )))], + ), Expr::SelectField( Box::new(Expr::Identifier( VariableId::match_identifier("x".to_string(), 1), @@ -1463,15 +1451,13 @@ mod type_inference_tests { )))), )), vec![MatchArm::new( - ArmPattern::Literal(Box::new(Expr::Option( - Some(Box::new(Expr::Identifier( + ArmPattern::Constructor( + "some".to_string(), + vec![ArmPattern::Literal(Box::new(Expr::Identifier( VariableId::match_identifier("y".to_string(), 2), InferredType::List(Box::new(InferredType::U64)), - ))), - InferredType::Option(Box::new(InferredType::List(Box::new( - InferredType::U64, - )))), - ))), + )))], + ), Expr::SelectIndex( Box::new(Expr::Identifier( VariableId::match_identifier("y".to_string(), 2), @@ -2589,27 +2575,20 @@ mod type_inference_tests { )), vec![ MatchArm { - arm_pattern: ArmPattern::Literal(Box::new(Expr::Option( - Some(Box::new(Expr::Identifier( + arm_pattern: ArmPattern::constructor( + "some", + vec![ArmPattern::literal(Expr::Identifier( VariableId::MatchIdentifier(MatchIdentifier::new( "value".to_string(), 1, )), InferredType::Option(Box::new(InferredType::Str)), - ))), - InferredType::Option(Box::new(InferredType::Option(Box::new( - InferredType::Str, - )))), - ))), + ))], + ), arm_resolution_expr: Box::new(Expr::literal("personal-id")), }, MatchArm { - arm_pattern: ArmPattern::Literal(Box::new(Expr::Option( - None, - InferredType::Option(Box::new(InferredType::Option(Box::new( - InferredType::Str, - )))), - ))), + arm_pattern: ArmPattern::constructor("none", vec![]), arm_resolution_expr: Box::new(Expr::SelectIndex( Box::new(Expr::SelectField( Box::new(Expr::SelectField( diff --git a/golem-rib/src/type_inference/pattern_match_binding.rs b/golem-rib/src/type_inference/pattern_match_binding.rs index e4eea777f..ef249ddde 100644 --- a/golem-rib/src/type_inference/pattern_match_binding.rs +++ b/golem-rib/src/type_inference/pattern_match_binding.rs @@ -260,19 +260,25 @@ mod pattern_match_bindings { pub(crate) fn expected_match(index: usize) -> Expr { Expr::PatternMatch( - Box::new(Expr::option(Some(Expr::identifier("x")))), // x is still global + Box::new(Expr::Option( + Some(Box::new(Expr::Identifier( + VariableId::Global("x".to_string()), + InferredType::Unknown, + ))), + InferredType::Option(Box::new(InferredType::Unknown)), + )), vec![ MatchArm { - arm_pattern: ArmPattern::Literal(Box::new(Expr::Option( - Some(Box::new(Expr::Identifier( + arm_pattern: ArmPattern::constructor( + "some", + vec![ArmPattern::literal(Expr::Identifier( VariableId::MatchIdentifier(MatchIdentifier::new( "x".to_string(), index, )), InferredType::Unknown, - ))), - InferredType::Option(Box::new(InferredType::Unknown)), - ))), + ))], + ), arm_resolution_expr: Box::new(Expr::Identifier( VariableId::MatchIdentifier(MatchIdentifier::new( "x".to_string(), @@ -282,7 +288,7 @@ mod pattern_match_bindings { )), }, MatchArm { - arm_pattern: ArmPattern::Literal(Box::new(Expr::option(None))), + arm_pattern: ArmPattern::constructor("none", vec![]), arm_resolution_expr: Box::new(Expr::number(0f64)), }, ], @@ -300,20 +306,20 @@ mod pattern_match_bindings { Box::new(Expr::option(Some(Expr::identifier("x")))), // x is still global vec![ MatchArm { - arm_pattern: ArmPattern::Literal(Box::new(Expr::Option( - Some(Box::new(Expr::Identifier( + arm_pattern: ArmPattern::constructor( + "some", + vec![ArmPattern::literal(Expr::Identifier( VariableId::MatchIdentifier(MatchIdentifier::new( "x".to_string(), index, )), InferredType::Unknown, - ))), - InferredType::Option(Box::new(InferredType::Unknown)), - ))), + ))], + ), arm_resolution_expr: Box::new(block), }, MatchArm { - arm_pattern: ArmPattern::Literal(Box::new(Expr::option(None))), + arm_pattern: ArmPattern::constructor("none", vec![]), arm_resolution_expr: Box::new(Expr::number(0f64)), }, ], @@ -340,10 +346,16 @@ mod pattern_match_bindings { )), vec![ MatchArm { - arm_pattern: ArmPattern::Literal(Box::new(Expr::ok(Expr::Identifier( - VariableId::MatchIdentifier(MatchIdentifier::new("x".to_string(), 1)), - InferredType::Unknown, - )))), + arm_pattern: ArmPattern::constructor( + "ok", + vec![ArmPattern::literal(Expr::Identifier( + VariableId::MatchIdentifier(MatchIdentifier::new( + "x".to_string(), + 1, + )), + InferredType::Unknown, + ))], + ), arm_resolution_expr: Box::new(Expr::PatternMatch( Box::new(Expr::Identifier( VariableId::MatchIdentifier(MatchIdentifier::new( @@ -354,15 +366,16 @@ mod pattern_match_bindings { )), vec![ MatchArm { - arm_pattern: ArmPattern::Literal(Box::new(Expr::option(Some( - Expr::Identifier( + arm_pattern: ArmPattern::constructor( + "some", + vec![ArmPattern::literal(Expr::Identifier( VariableId::MatchIdentifier(MatchIdentifier::new( "x".to_string(), 2, )), InferredType::Unknown, - ), - )))), + ))], + ), arm_resolution_expr: Box::new(Expr::Identifier( VariableId::MatchIdentifier(MatchIdentifier::new( "x".to_string(), @@ -372,7 +385,7 @@ mod pattern_match_bindings { )), }, MatchArm { - arm_pattern: ArmPattern::Literal(Box::new(Expr::option(None))), + arm_pattern: ArmPattern::constructor("none", vec![]), arm_resolution_expr: Box::new(Expr::number(0f64)), }, ], @@ -380,10 +393,16 @@ mod pattern_match_bindings { )), }, MatchArm { - arm_pattern: ArmPattern::Literal(Box::new(Expr::err(Expr::Identifier( - VariableId::MatchIdentifier(MatchIdentifier::new("x".to_string(), 4)), // 4 because none is an arm where we allotted the arm index to be 3 - InferredType::Unknown, - )))), + arm_pattern: ArmPattern::constructor( + "err", + vec![ArmPattern::literal(Expr::Identifier( + VariableId::MatchIdentifier(MatchIdentifier::new( + "x".to_string(), + 4, + )), + InferredType::Unknown, + ))], + ), arm_resolution_expr: Box::new(Expr::number(0f64)), }, ], diff --git a/golem-rib/src/type_inference/type_binding.rs b/golem-rib/src/type_inference/type_binding.rs index 45aa84818..bbaf35fc4 100644 --- a/golem-rib/src/type_inference/type_binding.rs +++ b/golem-rib/src/type_inference/type_binding.rs @@ -71,6 +71,7 @@ mod internal { | Expr::Throw(_, inferred_type) | Expr::GetTag(_, inferred_type) | Expr::And(_, _, inferred_type) + | Expr::Or(_, _, inferred_type) | Expr::Call(_, _, inferred_type) => { *inferred_type = new_type; } diff --git a/golem-rib/src/type_inference/type_pull_up.rs b/golem-rib/src/type_inference/type_pull_up.rs index 0f5663fba..ad1f8b0c4 100644 --- a/golem-rib/src/type_inference/type_pull_up.rs +++ b/golem-rib/src/type_inference/type_pull_up.rs @@ -13,187 +13,708 @@ // limitations under the License. use crate::{Expr, InferredType}; +use std::collections::VecDeque; -// TODO; This is recursion because we bumped into Rust borrowing issues with the following logic, -// which may require changing Expr data structure with RefCells. -// Logic that we need: -// * Fill up a queue with the root node being first -// [select_field(select_field(a, b), c), select_field(a, b), identifier(a)] -// Pop from back and push to the front of a stack of the current expression's inferred type, and keep assigning in between -// Example: -// * Pop back to get identifier(a) -// * Try to pop_front inferred_type_stack, and its None. Push front the identifier(a)'s inferred_type: Record(b -> Record(c -> u64)) -// * Pop back from stack to get select_field(a, b) -// * Try to pop_front inferred_type_stack, and its Record(b -> Record(c -> u64)). Get the type of b and assign itself and push_front to stack. -// * Pop back from stack to get select_field(select_field(a, b), c) -// * Try to pop_front inferred_type_stack, and its Record(c -> u64). Get the type of c and assign itself and push to stack. -pub fn pull_types_up(expr: &mut Expr) -> Result<(), String> { - match expr { - Expr::Tuple(exprs, inferred_type) => { - let mut types = vec![]; - for expr in exprs { - expr.pull_types_up()?; - types.push(expr.inferred_type()); - } - let tuple_type = InferredType::Tuple(types); - *inferred_type = inferred_type.merge(tuple_type) - } - Expr::Sequence(exprs, inferred_type) => { - let mut types = vec![]; - for expr in exprs { - expr.pull_types_up()?; - types.push(expr.inferred_type()); +pub fn type_pull_up(expr: &Expr) -> Result { + let mut expr_queue = VecDeque::new(); + internal::make_expr_nodes_queue(expr, &mut expr_queue); + + let mut inferred_type_stack = VecDeque::new(); + + while let Some(expr) = expr_queue.pop_back() { + match expr { + Expr::Tuple(tuple_elems, current_inferred_type) => { + internal::handle_tuple( + tuple_elems, + current_inferred_type, + &mut inferred_type_stack, + ); } - if let Some(new_inferred_type) = types.first() { - let sequence_type = InferredType::List(Box::new(new_inferred_type.clone())); - *inferred_type = inferred_type.merge(sequence_type) + + expr @ Expr::Identifier(_, _) => { + inferred_type_stack.push_front(expr.clone()); } - } - Expr::Record(exprs, inferred_type) => { - let mut types = vec![]; - for (field_name, expr) in exprs { - expr.pull_types_up()?; - types.push((field_name.clone(), expr.inferred_type())); - } - let record_type = InferredType::Record(types); - *inferred_type = inferred_type.merge(record_type); - } - Expr::Option(Some(expr), inferred_type) => { - expr.pull_types_up()?; - let option_type = InferredType::Option(Box::new(expr.inferred_type())); - *inferred_type = inferred_type.merge(option_type) - } - Expr::Result(Ok(expr), inferred_type) => { - expr.pull_types_up()?; - let result_type = InferredType::Result { - ok: Some(Box::new(expr.inferred_type())), - error: None, - }; - *inferred_type = inferred_type.merge(result_type) - } - Expr::Result(Err(expr), inferred_type) => { - expr.pull_types_up()?; - let result_type = InferredType::Result { - ok: None, - error: Some(Box::new(expr.inferred_type())), - }; - *inferred_type = inferred_type.merge(result_type) - } - Expr::Cond(_, then_, else_, inferred_type) => { - then_.pull_types_up()?; - else_.pull_types_up()?; - let then_type = then_.inferred_type(); - let else_type = else_.inferred_type(); + expr @ Expr::Flags(_, _) => { + inferred_type_stack.push_front(expr.clone()); + } - if then_type == else_type { - *inferred_type = inferred_type.merge(then_type); - } else if let Some(cond_then_else_type) = - InferredType::all_of(vec![then_type, else_type]) - { - *inferred_type = inferred_type.merge(cond_then_else_type); + Expr::SelectField(expr, field, current_inferred_type) => { + internal::handle_select_field( + expr, + field, + current_inferred_type, + &mut inferred_type_stack, + )?; } - } - // When it comes to pattern match, the only way to resolve the type of the pattern match - // from children (pulling types up) is from the match_arms - Expr::PatternMatch(predicate, match_arms, inferred_type) => { - predicate.pull_types_up()?; - let mut possible_inference_types = vec![]; + Expr::SelectIndex(expr, index, current_inferred_type) => { + internal::handle_select_index( + expr, + index, + current_inferred_type, + &mut inferred_type_stack, + )?; + } - for match_arm in match_arms { - internal::pull_up_types_of_arm_pattern(&mut match_arm.arm_pattern)?; + Expr::Result(Ok(_), current_inferred_type) => { + internal::handle_result_ok(expr, current_inferred_type, &mut inferred_type_stack); + } - match_arm.arm_resolution_expr.pull_types_up()?; - possible_inference_types.push(match_arm.arm_resolution_expr.inferred_type()) + Expr::Result(Err(_), current_inferred_type) => { + internal::handle_result_error( + expr, + current_inferred_type, + &mut inferred_type_stack, + ); } - if !possible_inference_types.is_empty() { - let first_type = possible_inference_types[0].clone(); - if possible_inference_types.iter().all(|t| t == &first_type) { - *inferred_type = inferred_type.merge(first_type); - } else if let Some(all_of) = InferredType::all_of(possible_inference_types) { - *inferred_type = inferred_type.merge(all_of); - } + Expr::Option(Some(expr), current_inferred_type) => { + internal::handle_option_some(expr, current_inferred_type, &mut inferred_type_stack); } - } - Expr::Let(_, _, expr, _) => expr.pull_types_up()?, - Expr::SelectField(expr, field, inferred_type) => { - expr.pull_types_up()?; - let expr_type = expr.inferred_type(); - let field_type = internal::get_inferred_type_of_selected_field(field, &expr_type)?; - *inferred_type = inferred_type.merge(field_type); - } - Expr::SelectIndex(expr, index, inferred_type) => { - expr.pull_types_up()?; - let expr_type = expr.inferred_type(); - let list_type = internal::get_inferred_type_of_selected_index(*index, &expr_type)?; - *inferred_type = inferred_type.merge(list_type); - } - Expr::Literal(_, _) => {} - Expr::Number(_, _, _) => {} - Expr::Flags(_, _) => {} - Expr::Identifier(_, _) => {} - Expr::Boolean(_, _) => {} - Expr::Concat(exprs, _) => { - for expr in exprs { - expr.pull_types_up()? + Expr::Option(None, current_inferred_type) => { + inferred_type_stack.push_front(Expr::Option(None, current_inferred_type.clone())); } - } - Expr::Multiple(exprs, inferred_type) => { - let length = &exprs.len(); - for (index, expr) in exprs.iter_mut().enumerate() { - expr.pull_types_up()?; - if index == length - 1 { - *inferred_type = inferred_type.merge(expr.inferred_type()); - } + Expr::Cond(pred, then, else_, current_inferred_type) => { + internal::handle_if_else( + pred, + then, + else_, + current_inferred_type, + &mut inferred_type_stack, + ); + } + + Expr::PatternMatch(predicate, match_arms, current_inferred_type) => { + internal::handle_pattern_match( + predicate, + match_arms, + current_inferred_type, + &mut inferred_type_stack, + ); + } + + Expr::Concat(exprs, _) => { + internal::handle_concat(exprs, &mut inferred_type_stack); + } + + Expr::Multiple(exprs, current_inferred_type) => { + internal::handle_multiple(exprs, current_inferred_type, &mut inferred_type_stack); + } + + Expr::Not(_, current_inferred_type) => { + internal::handle_not(expr, current_inferred_type, &mut inferred_type_stack); + } + + Expr::GreaterThan(left, right, current_inferred_type) => { + internal::handle_binary( + left, + right, + current_inferred_type, + &mut inferred_type_stack, + Expr::GreaterThan, + ); + } + + Expr::GreaterThanOrEqualTo(left, right, current_inferred_type) => { + internal::handle_binary( + left, + right, + current_inferred_type, + &mut inferred_type_stack, + Expr::GreaterThanOrEqualTo, + ); + } + + Expr::LessThanOrEqualTo(left, right, current_inferred_type) => { + internal::handle_binary( + left, + right, + current_inferred_type, + &mut inferred_type_stack, + Expr::LessThanOrEqualTo, + ); + } + + Expr::EqualTo(left, right, current_inferred_type) => { + internal::handle_binary( + left, + right, + current_inferred_type, + &mut inferred_type_stack, + Expr::EqualTo, + ); + } + + Expr::LessThan(left, right, current_inferred_type) => { + internal::handle_binary( + left, + right, + current_inferred_type, + &mut inferred_type_stack, + Expr::LessThan, + ); + } + + Expr::Let(variable_id, typ, expr, inferred_type) => { + internal::handle_let( + variable_id, + expr, + typ, + inferred_type, + &mut inferred_type_stack, + ); + } + Expr::Sequence(exprs, current_inferred_type) => { + internal::handle_sequence(exprs, current_inferred_type, &mut inferred_type_stack); + } + Expr::Record(expr, inferred_type) => { + internal::handle_record(expr, inferred_type, &mut inferred_type_stack); + } + Expr::Literal(_, _) => { + inferred_type_stack.push_front(expr.clone()); + } + Expr::Number(_, _, _) => { + inferred_type_stack.push_front(expr.clone()); + } + Expr::Boolean(_, _) => { + inferred_type_stack.push_front(expr.clone()); + } + Expr::And(left, right, _) => { + internal::handle_binary( + left, + right, + &InferredType::Bool, + &mut inferred_type_stack, + Expr::And, + ); + } + + Expr::Or(left, right, _) => { + internal::handle_binary( + left, + right, + &InferredType::Bool, + &mut inferred_type_stack, + Expr::Or, + ); + } + + Expr::Call(call_type, exprs, inferred_type) => { + internal::handle_call(call_type, exprs, inferred_type, &mut inferred_type_stack); + } + + Expr::Unwrap(expr, inferred_type) => { + internal::handle_unwrap(expr, inferred_type, &mut inferred_type_stack); + } + + Expr::Throw(_, _) => { + inferred_type_stack.push_front(expr.clone()); + } + + Expr::GetTag(_, inferred_type) => { + internal::handle_get_tag(expr, inferred_type, &mut inferred_type_stack); } } - Expr::Not(expr, _) => expr.pull_types_up()?, - Expr::GreaterThan(left, right, _) => { - left.pull_types_up()?; - right.pull_types_up()?; + } + + inferred_type_stack + .pop_front() + .ok_or("Failed type inference during pull up".to_string()) +} + +mod internal { + use crate::call_type::CallType; + + use crate::type_refinement::precise_types::{ListType, RecordType}; + use crate::type_refinement::TypeRefinement; + use crate::{Expr, InferredType, MatchArm, VariableId}; + use std::collections::VecDeque; + use std::ops::Deref; + + pub(crate) fn make_expr_nodes_queue<'a>(expr: &'a Expr, expr_queue: &mut VecDeque<&'a Expr>) { + let mut stack = VecDeque::new(); + + stack.push_back(expr); + + while let Some(current_expr) = stack.pop_back() { + expr_queue.push_back(current_expr); + + current_expr.visit_children_bottom_up(&mut stack) + } + } + + pub(crate) fn handle_tuple( + tuple_elems: &[Expr], + current_tuple_type: &InferredType, + inferred_type_stack: &mut VecDeque, + ) { + let mut new_tuple_elems = vec![]; + + for current_tuple_elem in tuple_elems.iter().rev() { + let pulled_up_type = inferred_type_stack.pop_front(); + let new_tuple_elem = pulled_up_type.unwrap_or(current_tuple_elem.clone()); + new_tuple_elems.push(new_tuple_elem); } - Expr::GreaterThanOrEqualTo(left, right, _) => { - left.pull_types_up()?; - right.pull_types_up()?; + + new_tuple_elems.reverse(); + + let new_tuple_type = + InferredType::Tuple(new_tuple_elems.iter().map(|x| x.inferred_type()).collect()); + + let merged_tuple_type = current_tuple_type.merge(new_tuple_type); + let new_tuple = Expr::Tuple(new_tuple_elems, merged_tuple_type); + inferred_type_stack.push_front(new_tuple); + } + + pub(crate) fn handle_select_field( + original_selection_expr: &Expr, + field: &str, + current_field_type: &InferredType, + inferred_type_stack: &mut VecDeque, + ) -> Result<(), String> { + let expr = inferred_type_stack + .pop_front() + .unwrap_or(original_selection_expr.clone()); + let select_from_expr_type = expr.inferred_type(); + let selection_field_type = + get_inferred_type_of_selected_field(field, &select_from_expr_type)?; + + let new_select_field = Expr::SelectField( + Box::new(expr.clone()), + field.to_string(), + current_field_type.merge(selection_field_type), + ); + + inferred_type_stack.push_front(new_select_field); + + Ok(()) + } + + pub fn handle_select_index( + original_selection_expr: &Expr, + index: &usize, + current_index_type: &InferredType, + inferred_type_stack: &mut VecDeque, + ) -> Result<(), String> { + let expr = inferred_type_stack + .pop_front() + .unwrap_or(original_selection_expr.clone()); + let inferred_type_of_selection_expr = expr.inferred_type(); + let list_type = + get_inferred_type_of_selection_index(*index, &inferred_type_of_selection_expr)?; + let new_select_index = Expr::SelectIndex( + Box::new(expr.clone()), + *index, + current_index_type.merge(list_type), + ); + inferred_type_stack.push_front(new_select_index); + + Ok(()) + } + + pub(crate) fn handle_result_ok( + original_ok_expr: &Expr, + current_ok_type: &InferredType, + inferred_type_stack: &mut VecDeque, + ) { + let ok_expr = inferred_type_stack + .pop_front() + .unwrap_or(original_ok_expr.clone()); + let inferred_type_of_ok_expr = ok_expr.inferred_type(); + let result_type = InferredType::Result { + ok: Some(Box::new(inferred_type_of_ok_expr)), + error: None, + }; + let new_result = Expr::Result( + Ok(Box::new(ok_expr.clone())), + current_ok_type.merge(result_type), + ); + inferred_type_stack.push_front(new_result); + } + + pub(crate) fn handle_result_error( + original_error_expr: &Expr, + current_error_type: &InferredType, + inferred_type_stack: &mut VecDeque, + ) { + let expr = inferred_type_stack + .pop_front() + .unwrap_or(original_error_expr.clone()); + let inferred_type_of_error_expr = expr.inferred_type(); + let result_type = InferredType::Result { + ok: None, + error: Some(Box::new(inferred_type_of_error_expr)), + }; + let new_result = Expr::Result( + Err(Box::new(expr.clone())), + current_error_type.merge(result_type), + ); + inferred_type_stack.push_front(new_result); + } + + pub(crate) fn handle_option_some( + original_some_expr: &Expr, + current_some_type: &InferredType, + inferred_type_stack: &mut VecDeque, + ) { + let expr = inferred_type_stack + .pop_front() + .unwrap_or(original_some_expr.clone()); + let inferred_type_of_some_expr = expr.inferred_type(); + let option_type = InferredType::Option(Box::new(inferred_type_of_some_expr)); + let new_option = Expr::Option( + Some(Box::new(expr.clone())), + current_some_type.merge(option_type), + ); + inferred_type_stack.push_front(new_option); + } + + pub(crate) fn handle_if_else( + original_predicate: &Expr, + original_then_expr: &Expr, + original_else_expr: &Expr, + current_inferred_type: &InferredType, + inferred_type_stack: &mut VecDeque, + ) { + let else_expr = inferred_type_stack + .pop_front() + .unwrap_or(original_else_expr.clone()); + let then_expr = inferred_type_stack + .pop_front() + .unwrap_or(original_then_expr.clone()); + let cond_expr = inferred_type_stack + .pop_front() + .unwrap_or(original_predicate.clone()); + let inferred_type_of_then_expr = then_expr.inferred_type(); + let inferred_type_of_else_expr = else_expr.inferred_type(); + + let new_type = current_inferred_type + .merge(inferred_type_of_then_expr.merge(inferred_type_of_else_expr)); + + let new_expr = Expr::Cond( + Box::new(cond_expr), + Box::new(then_expr.clone()), + Box::new(else_expr.clone()), + new_type, + ); + + inferred_type_stack.push_front(new_expr); + } + + pub fn handle_pattern_match( + predicate: &Expr, + current_match_arms: &[MatchArm], + current_inferred_type: &InferredType, + inferred_type_stack: &mut VecDeque, + ) { + let mut new_resolutions = vec![]; + let mut new_arm_patterns = vec![]; + for un_inferred_match_arm in current_match_arms.iter().rev() { + let arm_resolution = inferred_type_stack + .pop_front() + .unwrap_or(un_inferred_match_arm.arm_resolution_expr.deref().clone()); + + let mut arm_pattern = un_inferred_match_arm.arm_pattern.clone(); + let mut current_arm_pattern_exprs = arm_pattern.get_expr_literals_mut(); + + let mut new_arm_pattern_exprs = vec![]; + + for _ in ¤t_arm_pattern_exprs { + let arm_expr = inferred_type_stack.pop_front(); + new_arm_pattern_exprs.push(arm_expr) + } + new_arm_pattern_exprs.reverse(); + + current_arm_pattern_exprs + .iter_mut() + .zip(new_arm_pattern_exprs.iter()) + .for_each(|(arm_expr, new_expr_opt)| { + if let Some(new_expr) = new_expr_opt { + **arm_expr = Box::new(new_expr.clone()); + } + }); + + new_resolutions.push(arm_resolution); + new_arm_patterns.push(arm_pattern); } - Expr::LessThanOrEqualTo(left, right, _) => { - left.pull_types_up()?; - right.pull_types_up()?; + + let inferred_types = new_resolutions + .iter() + .map(|expr| expr.inferred_type()) + .collect::>(); + + let new_inferred_type = InferredType::all_of(inferred_types); + + let mut new_match_arms = new_arm_patterns + .iter() + .zip(new_resolutions.iter()) + .map(|(arm_pattern, arm_resolution)| crate::MatchArm { + arm_pattern: arm_pattern.clone(), + arm_resolution_expr: Box::new(arm_resolution.clone()), + }) + .collect::>(); + + new_match_arms.reverse(); + + let new_type = if let Some(new_inferred_type) = new_inferred_type { + current_inferred_type.merge(new_inferred_type) + } else { + current_inferred_type.clone() + }; + + let pred = inferred_type_stack.pop_front().unwrap_or(predicate.clone()); + + let new_expr = Expr::PatternMatch(Box::new(pred.clone()), new_match_arms, new_type); + + inferred_type_stack.push_front(new_expr); + } + + pub(crate) fn handle_concat(exprs: &Vec, inferred_type_stack: &mut VecDeque) { + let mut new_exprs = vec![]; + for expr in exprs { + let expr = inferred_type_stack.pop_front().unwrap_or(expr.clone()); + new_exprs.push(expr); } - Expr::EqualTo(left, right, _) => { - left.pull_types_up()?; - right.pull_types_up()?; + + new_exprs.reverse(); + + let new_concat = Expr::Concat(new_exprs, InferredType::Str); + inferred_type_stack.push_front(new_concat); + } + + pub(crate) fn handle_multiple( + current_expr_list: &Vec, + current_inferred_type: &InferredType, + inferred_type_stack: &mut VecDeque, + ) { + let mut new_exprs = vec![]; + for _ in current_expr_list { + let expr = inferred_type_stack.pop_front(); + if let Some(expr) = expr { + new_exprs.push(expr); + } else { + break; + } } - Expr::LessThan(left, right, _) => { - left.pull_types_up()?; - right.pull_types_up()?; + + new_exprs.reverse(); + + let new_inferred_type = if let Some(last_expr) = new_exprs.last() { + last_expr.inferred_type() + } else { + InferredType::Unknown + }; + + let new_multiple = + Expr::Multiple(new_exprs, current_inferred_type.merge(new_inferred_type)); + inferred_type_stack.push_front(new_multiple); + } + + pub(crate) fn handle_not( + original_not_expr: &Expr, + current_not_type: &InferredType, + inferred_type_stack: &mut VecDeque, + ) { + let expr = inferred_type_stack + .pop_front() + .unwrap_or(original_not_expr.clone()); + let new_not = Expr::Not(Box::new(expr), current_not_type.clone()); + inferred_type_stack.push_front(new_not); + } + + pub(crate) fn handle_binary( + original_left_expr: &Expr, + original_right_expr: &Expr, + current_inferred_type: &InferredType, + inferred_type_stack: &mut VecDeque, + f: F, + ) where + F: Fn(Box, Box, InferredType) -> Expr, + { + let right_expr = inferred_type_stack + .pop_front() + .unwrap_or(original_right_expr.clone()); + let left_expr = inferred_type_stack + .pop_front() + .unwrap_or(original_left_expr.clone()); + let new_binary = f( + Box::new(left_expr), + Box::new(right_expr), + current_inferred_type.clone(), + ); + inferred_type_stack.push_front(new_binary); + } + + pub(crate) fn handle_call( + call_type: &CallType, + arguments: &[Expr], + inferred_type: &InferredType, + inferred_type_stack: &mut VecDeque, + ) { + let mut new_arg_exprs = vec![]; + + for expr in arguments.iter().rev() { + let expr = inferred_type_stack.pop_front().unwrap_or(expr.clone()); + new_arg_exprs.push(expr); } - Expr::Call(_, exprs, _) => { - for expr in exprs { - expr.pull_types_up()? + + new_arg_exprs.reverse(); + + match call_type { + CallType::Function(fun_name) => { + let mut function_name = fun_name.clone(); + + let resource_params = function_name.function.raw_resource_params_mut(); + + if let Some(resource_params) = resource_params { + let mut new_resource_params = vec![]; + for expr in resource_params.iter().rev() { + let expr = inferred_type_stack.pop_front().unwrap_or(expr.clone()); + new_resource_params.push(expr); + } + + new_resource_params.reverse(); + + resource_params + .iter_mut() + .zip(new_resource_params.iter()) + .for_each(|(param, new_expr)| { + *param = new_expr.clone(); + }); + } + + let new_call = Expr::Call( + CallType::Function(function_name), + new_arg_exprs, + inferred_type.clone(), + ); + inferred_type_stack.push_front(new_call); + } + + CallType::VariantConstructor(str) => { + let new_call = Expr::Call( + CallType::VariantConstructor(str.clone()), + new_arg_exprs, + inferred_type.clone(), + ); + inferred_type_stack.push_front(new_call); + } + + CallType::EnumConstructor(str) => { + let new_call = Expr::Call( + CallType::EnumConstructor(str.clone()), + new_arg_exprs, + inferred_type.clone(), + ); + inferred_type_stack.push_front(new_call); } } - Expr::Unwrap(expr, _) => expr.pull_types_up()?, - Expr::And(left, right, _) => { - left.pull_types_up()?; - right.pull_types_up()?; + } + + pub(crate) fn handle_unwrap( + expr: &Expr, + current_inferred_type: &InferredType, + inferred_type_stack: &mut VecDeque, + ) { + let expr = inferred_type_stack.pop_front().unwrap_or(expr.clone()); + let new_unwrap = Expr::Unwrap( + Box::new(expr.clone()), + current_inferred_type.merge(expr.inferred_type()), + ); + inferred_type_stack.push_front(new_unwrap); + } + + pub(crate) fn handle_get_tag( + expr: &Expr, + current_inferred_type: &InferredType, + inferred_type_stack: &mut VecDeque, + ) { + let expr = inferred_type_stack.pop_front().unwrap_or(expr.clone()); + let new_get_tag = Expr::GetTag( + Box::new(expr.clone()), + current_inferred_type.merge(expr.inferred_type()), + ); + inferred_type_stack.push_front(new_get_tag); + } + + pub(crate) fn handle_let( + original_variable_id: &VariableId, + original_expr: &Expr, + optional_type: &Option, + current_inferred_type: &InferredType, + inferred_type_stack: &mut VecDeque, + ) { + let expr = inferred_type_stack + .pop_front() + .unwrap_or(original_expr.clone()); + let new_let = Expr::Let( + original_variable_id.clone(), + optional_type.clone(), + Box::new(expr), + current_inferred_type.clone(), + ); + inferred_type_stack.push_front(new_let); + } + + pub(crate) fn handle_sequence( + current_expr_list: &[Expr], + current_inferred_type: &InferredType, + inferred_type_stack: &mut VecDeque, + ) { + let mut new_exprs = vec![]; + + for expr in current_expr_list.iter().rev() { + let expr = inferred_type_stack.pop_front().unwrap_or(expr.clone()); + new_exprs.push(expr); } - Expr::Throw(_, _) => {} - Expr::GetTag(expr, _) => expr.pull_types_up()?, - Expr::Option(None, _) => {} + + new_exprs.reverse(); + + let new_sequence = { + if let Some(first_expr) = new_exprs.clone().first() { + Expr::Sequence( + new_exprs, + current_inferred_type + .clone() + .merge(InferredType::List(Box::new(first_expr.inferred_type()))), + ) + } else { + Expr::Sequence(new_exprs, current_inferred_type.clone()) + } + }; + + inferred_type_stack.push_front(new_sequence); } - Ok(()) -} + pub(crate) fn handle_record( + current_expr_list: &[(String, Box)], + current_inferred_type: &InferredType, + inferred_type_stack: &mut VecDeque, + ) { + let mut ordered_types = vec![]; + let mut new_exprs = vec![]; -mod internal { - use crate::type_refinement::precise_types::{ListType, RecordType}; - use crate::type_refinement::TypeRefinement; - use crate::{ArmPattern, InferredType}; + for (field, expr) in current_expr_list.iter().rev() { + let expr: Expr = inferred_type_stack + .pop_front() + .unwrap_or(expr.deref().clone()); + ordered_types.push((field.clone(), expr.inferred_type())); + new_exprs.push((field.clone(), Box::new(expr.clone()))); + } + + new_exprs.reverse(); + ordered_types.reverse(); + + let new_record_type = InferredType::Record(ordered_types); + + let merged_record_type = current_inferred_type.merge(new_record_type); + + let new_record = Expr::Record(new_exprs.to_vec(), merged_record_type); + inferred_type_stack.push_front(new_record); + } pub(crate) fn get_inferred_type_of_selected_field( select_field: &str, @@ -207,7 +728,7 @@ mod internal { Ok(refined_record.inner_type_by_field(select_field)) } - pub(crate) fn get_inferred_type_of_selected_index( + pub(crate) fn get_inferred_type_of_selection_index( selected_index: usize, select_from_type: &InferredType, ) -> Result { @@ -218,57 +739,25 @@ mod internal { Ok(refined_list.inner_type()) } - - pub(crate) fn pull_up_types_of_arm_pattern(arm_pattern: &mut ArmPattern) -> Result<(), String> { - match arm_pattern { - ArmPattern::WildCard => {} - ArmPattern::As(_, arms_patterns) => { - pull_up_types_of_arm_pattern(arms_patterns)?; - } - ArmPattern::Constructor(_, arm_patterns) => { - for arm_pattern in arm_patterns { - pull_up_types_of_arm_pattern(arm_pattern)?; - } - } - ArmPattern::TupleConstructor(arm_patterns) => { - for arm_pattern in arm_patterns { - pull_up_types_of_arm_pattern(arm_pattern)?; - } - } - - ArmPattern::ListConstructor(arm_patterns) => { - for arm_pattern in arm_patterns { - pull_up_types_of_arm_pattern(arm_pattern)?; - } - } - - ArmPattern::RecordConstructor(fields) => { - for (_, arm_pattern) in fields { - pull_up_types_of_arm_pattern(arm_pattern)?; - } - } - - ArmPattern::Literal(expr) => { - expr.pull_types_up()?; - } - } - - Ok(()) - } } #[cfg(test)] mod type_pull_up_tests { + use crate::call_type::CallType; use crate::function_name::DynamicParsedFunctionName; - use crate::{ArmPattern, Expr, InferredType, Number}; + use crate::DynamicParsedFunctionReference::IndexedResourceMethod; + use crate::ParsedFunctionSite::PackagedInterface; + use crate::{ + ArmPattern, Expr, FunctionTypeRegistry, InferredType, MatchArm, Number, VariableId, + }; #[test] pub fn test_pull_up_identifier() { let expr = "foo"; let mut expr = Expr::from_text(expr).unwrap(); expr.add_infer_type_mut(InferredType::Str); - expr.pull_types_up().unwrap(); - assert_eq!(expr.inferred_type(), InferredType::Str); + let new_expr = expr.pull_types_up().unwrap(); + assert_eq!(new_expr.inferred_type(), InferredType::Str); } #[test] @@ -279,144 +768,274 @@ mod type_pull_up_tests { InferredType::Record(vec![("bar".to_string(), InferredType::U64)]), )])); let select_expr = Expr::select_field(record_identifier, "foo"); - let mut expr = Expr::select_field(select_expr, "bar"); - expr.pull_types_up().unwrap(); - assert_eq!(expr.inferred_type(), InferredType::U64); + let expr = Expr::select_field(select_expr, "bar"); + let new_expr = expr.pull_types_up().unwrap(); + assert_eq!(new_expr.inferred_type(), InferredType::U64); } #[test] pub fn test_pull_up_for_select_index() { - let expr = + let identifier = Expr::identifier("foo").add_infer_type(InferredType::List(Box::new(InferredType::U64))); - let mut expr = Expr::select_index(expr, 0); - expr.pull_types_up().unwrap(); - assert_eq!(expr.inferred_type(), InferredType::U64); + let expr = Expr::select_index(identifier.clone(), 0); + let new_expr = expr.pull_types_up().unwrap(); + let expected = Expr::select_index(identifier, 0).add_infer_type(InferredType::U64); + assert_eq!(new_expr, expected); } #[test] pub fn test_pull_up_for_sequence() { - let mut expr = Expr::Sequence( - vec![ - Expr::Number(Number { value: 1f64 }, None, InferredType::U64), - Expr::Number(Number { value: 1f64 }, None, InferredType::U32), - ], - InferredType::Unknown, - ); - expr.pull_types_up().unwrap(); + let elems = vec![ + Expr::Number(Number { value: 1f64 }, None, InferredType::U64), + Expr::Number(Number { value: 2f64 }, None, InferredType::U64), + ]; + + let expr = Expr::Sequence(elems.clone(), InferredType::Unknown); + let new_expr = expr.pull_types_up().unwrap(); + assert_eq!( - expr.inferred_type(), - InferredType::List(Box::new(InferredType::U64)) + new_expr, + Expr::Sequence(elems, InferredType::List(Box::new(InferredType::U64))) ); } #[test] pub fn test_pull_up_for_tuple() { - let mut expr = Expr::tuple(vec![ + let expr = Expr::tuple(vec![ Expr::literal("foo"), Expr::Number(Number { value: 1f64 }, None, InferredType::U64), ]); - expr.pull_types_up().unwrap(); + let new_expr = expr.pull_types_up().unwrap(); assert_eq!( - expr.inferred_type(), + new_expr.inferred_type(), InferredType::Tuple(vec![InferredType::Str, InferredType::U64]) ); } #[test] pub fn test_pull_up_for_record() { - let mut expr = Expr::Record( - vec![ - ( - "foo".to_string(), - Box::new(Expr::Number( - Number { value: 1f64 }, - None, - InferredType::U64, - )), - ), - ( - "bar".to_string(), - Box::new(Expr::Number( - Number { value: 1f64 }, - None, - InferredType::U64, - )), - ), - ], + let elems = vec![ + ( + "foo".to_string(), + Box::new(Expr::Number( + Number { value: 1f64 }, + None, + InferredType::U64, + )), + ), + ( + "bar".to_string(), + Box::new(Expr::Number( + Number { value: 2f64 }, + None, + InferredType::U32, + )), + ), + ]; + let expr = Expr::Record( + elems.clone(), InferredType::Record(vec![ ("foo".to_string(), InferredType::Unknown), ("bar".to_string(), InferredType::Unknown), ]), ); - expr.pull_types_up().unwrap(); + let new_expr = expr.pull_types_up().unwrap(); assert_eq!( - expr.inferred_type(), - InferredType::AllOf(vec![ - InferredType::Record(vec![ - ("foo".to_string(), InferredType::U64), - ("bar".to_string(), InferredType::U64) - ]), - InferredType::Record(vec![ - ("foo".to_string(), InferredType::Unknown), - ("bar".to_string(), InferredType::Unknown) + new_expr, + Expr::Record( + elems, + InferredType::AllOf(vec![ + InferredType::Record(vec![ + ("foo".to_string(), InferredType::U64), + ("bar".to_string(), InferredType::U32) + ]), + InferredType::Record(vec![ + ("foo".to_string(), InferredType::Unknown), + ("bar".to_string(), InferredType::Unknown) + ]) ]) - ]) + ) ); } #[test] pub fn test_pull_up_for_concat() { - let mut expr = Expr::concat(vec![Expr::number(1f64), Expr::number(2f64)]); - expr.pull_types_up().unwrap(); - assert_eq!(expr.inferred_type(), InferredType::Str); + let expr = Expr::concat(vec![Expr::literal("foo"), Expr::literal("bar")]); + let new_expr = expr.pull_types_up().unwrap(); + let expected = Expr::Concat( + vec![Expr::literal("foo"), Expr::literal("bar")], + InferredType::Str, + ); + assert_eq!(new_expr, expected); } #[test] pub fn test_pull_up_for_not() { - let mut expr = Expr::not(Expr::boolean(true)); - expr.pull_types_up().unwrap(); - assert_eq!(expr.inferred_type(), InferredType::Bool); + let expr = Expr::not(Expr::boolean(true)); + let new_expr = expr.pull_types_up().unwrap(); + assert_eq!(new_expr.inferred_type(), InferredType::Bool); + } + + #[test] + pub fn test_pull_up_if_else() { + let inner1 = + Expr::identifier("foo").add_infer_type(InferredType::List(Box::new(InferredType::U64))); + + let select_index1 = Expr::select_index(inner1.clone(), 0); + let select_index2 = Expr::select_index(inner1, 1); + + let inner2 = + Expr::identifier("bar").add_infer_type(InferredType::List(Box::new(InferredType::U64))); + + let select_index3 = Expr::select_index(inner2.clone(), 0); + let select_index4 = Expr::select_index(inner2, 1); + + let expr = Expr::cond( + Expr::greater_than(select_index1.clone(), select_index2.clone()), + select_index3.clone(), + select_index4.clone(), + ); + + let new_expr = expr.pull_types_up().unwrap(); + let expected = Expr::Cond( + Box::new(Expr::GreaterThan( + Box::new(Expr::SelectIndex( + Box::new(Expr::Identifier( + VariableId::global("foo".to_string()), + InferredType::List(Box::new(InferredType::U64)), + )), + 0, + InferredType::U64, + )), + Box::new(Expr::SelectIndex( + Box::new(Expr::Identifier( + VariableId::global("foo".to_string()), + InferredType::List(Box::new(InferredType::U64)), + )), + 1, + InferredType::U64, + )), + InferredType::Bool, + )), + Box::new(Expr::SelectIndex( + Box::new(Expr::Identifier( + VariableId::global("bar".to_string()), + InferredType::List(Box::new(InferredType::U64)), + )), + 0, + InferredType::U64, + )), + Box::new(Expr::SelectIndex( + Box::new(Expr::Identifier( + VariableId::global("bar".to_string()), + InferredType::List(Box::new(InferredType::U64)), + )), + 1, + InferredType::U64, + )), + InferredType::U64, + ); + assert_eq!(new_expr, expected); } #[test] pub fn test_pull_up_for_greater_than() { - let mut expr = Expr::greater_than(Expr::number(1f64), Expr::number(2f64)); - expr.pull_types_up().unwrap(); - assert_eq!(expr.inferred_type(), InferredType::Bool); + let inner = Expr::identifier("foo").add_infer_type(InferredType::Record(vec![ + ("bar".to_string(), InferredType::Str), + ("baz".to_string(), InferredType::U64), + ])); + + let select_field1 = Expr::select_field(inner.clone(), "bar"); + let select_field2 = Expr::select_field(inner, "baz"); + let expr = Expr::greater_than(select_field1.clone(), select_field2.clone()); + + let new_expr = expr.pull_types_up().unwrap(); + + let expected = Expr::greater_than( + select_field1.add_infer_type(InferredType::Str), + select_field2.add_infer_type(InferredType::U64), + ) + .add_infer_type(InferredType::Bool); + assert_eq!(new_expr, expected); } #[test] pub fn test_pull_up_for_greater_than_or_equal_to() { - let mut expr = Expr::greater_than_or_equal_to(Expr::number(1f64), Expr::number(2f64)); - expr.pull_types_up().unwrap(); - assert_eq!(expr.inferred_type(), InferredType::Bool); + let inner = + Expr::identifier("foo").add_infer_type(InferredType::List(Box::new(InferredType::U64))); + + let select_index1 = Expr::select_index(inner.clone(), 0); + let select_index2 = Expr::select_index(inner, 1); + let expr = Expr::greater_than_or_equal_to(select_index1.clone(), select_index2.clone()); + + let new_expr = expr.pull_types_up().unwrap(); + + let expected = Expr::greater_than_or_equal_to( + select_index1.add_infer_type(InferredType::U64), + select_index2.add_infer_type(InferredType::U64), + ) + .add_infer_type(InferredType::Bool); + assert_eq!(new_expr, expected); } #[test] pub fn test_pull_up_for_less_than_or_equal_to() { - let mut expr = Expr::less_than_or_equal_to(Expr::number(1f64), Expr::number(2f64)); - expr.pull_types_up().unwrap(); - assert_eq!(expr.inferred_type(), InferredType::Bool); + let record_type = InferredType::Record(vec![ + ("bar".to_string(), InferredType::Str), + ("baz".to_string(), InferredType::U64), + ]); + + let inner = Expr::identifier("foo") + .add_infer_type(InferredType::List(Box::new(record_type.clone()))); + + let select_field_from_first = + Expr::select_field(Expr::select_index(inner.clone(), 0), "bar"); + let select_field_from_second = + Expr::select_field(Expr::select_index(inner.clone(), 1), "baz"); + let expr = Expr::less_than_or_equal_to( + select_field_from_first.clone(), + select_field_from_second.clone(), + ); + + let new_expr = expr.pull_types_up().unwrap(); + + let new_select_field_from_first = Expr::select_field( + Expr::select_index(inner.clone(), 0).add_infer_type(record_type.clone()), + "bar", + ) + .add_infer_type(InferredType::Str); + + let new_select_field_from_second = Expr::select_field( + Expr::select_index(inner.clone(), 1).add_infer_type(record_type), + "baz", + ) + .add_infer_type(InferredType::U64); + + let expected = + Expr::less_than_or_equal_to(new_select_field_from_first, new_select_field_from_second) + .add_infer_type(InferredType::Bool); + + assert_eq!(new_expr, expected); } #[test] pub fn test_pull_up_for_equal_to() { - let mut expr = Expr::equal_to(Expr::number(1f64), Expr::number(2f64)); - expr.pull_types_up().unwrap(); - assert_eq!(expr.inferred_type(), InferredType::Bool); + let expr = Expr::equal_to(Expr::number(1f64), Expr::number(2f64)); + let new_expr = expr.pull_types_up().unwrap(); + assert_eq!(new_expr.inferred_type(), InferredType::Bool); } #[test] pub fn test_pull_up_for_less_than() { - let mut expr = Expr::less_than(Expr::number(1f64), Expr::number(2f64)); - expr.pull_types_up().unwrap(); - assert_eq!(expr.inferred_type(), InferredType::Bool); + let expr = Expr::less_than(Expr::number(1f64), Expr::number(2f64)); + let new_expr = expr.pull_types_up().unwrap(); + assert_eq!(new_expr.inferred_type(), InferredType::Bool); } #[test] pub fn test_pull_up_for_call() { - let mut expr = Expr::call( + let expr = Expr::call( DynamicParsedFunctionName::parse("global_fn").unwrap(), vec![Expr::number(1f64)], ); @@ -424,52 +1043,229 @@ mod type_pull_up_tests { assert_eq!(expr.inferred_type(), InferredType::Unknown); } + #[test] + pub fn test_pull_up_for_dynamic_call() { + let rib = r#" + let input = { foo: "afs", bar: "al" }; + golem:it/api.{cart(input.foo).checkout}() + "#; + + let mut expr = Expr::from_text(rib).unwrap(); + let function_registry = FunctionTypeRegistry::empty(); + expr.infer_types_initial_phase(&function_registry).unwrap(); + expr.infer_all_identifiers().unwrap(); + let new_expr = expr.pull_types_up().unwrap(); + + let expected = Expr::Multiple( + vec![ + Expr::Let( + VariableId::local("input", 0), + None, + Box::new(Expr::Record( + vec![ + ( + "foo".to_string(), + Box::new(Expr::Literal("afs".to_string(), InferredType::Str)), + ), + ( + "bar".to_string(), + Box::new(Expr::Literal("al".to_string(), InferredType::Str)), + ), + ], + InferredType::Record(vec![ + ("foo".to_string(), InferredType::Str), + ("bar".to_string(), InferredType::Str), + ]), + )), + InferredType::Unknown, + ), + Expr::Call( + CallType::Function(DynamicParsedFunctionName { + site: PackagedInterface { + namespace: "golem".to_string(), + package: "it".to_string(), + interface: "api".to_string(), + version: None, + }, + function: IndexedResourceMethod { + resource: "cart".to_string(), + resource_params: vec![Expr::SelectField( + Box::new(Expr::Identifier( + VariableId::local("input", 0), + InferredType::Record(vec![ + ("foo".to_string(), InferredType::Str), + ("bar".to_string(), InferredType::Str), + ]), + )), + "foo".to_string(), + InferredType::Str, + )], + method: "checkout".to_string(), + }, + }), + vec![], + InferredType::Unknown, + ), + ], + InferredType::Unknown, + ); + + assert_eq!(new_expr, expected); + } + #[test] pub fn test_pull_up_for_unwrap() { - let mut expr = Expr::option(Some(Expr::number(1f64))).unwrap(); - expr.pull_types_up().unwrap(); - assert_eq!(expr.inferred_type(), InferredType::Unknown); + let mut number = Expr::number(1f64); + number.override_type_type_mut(InferredType::F64); + let expr = Expr::option(Some(number)).unwrap(); + let expr = expr.pull_types_up().unwrap(); + assert_eq!( + expr.inferred_type(), + InferredType::Option(Box::new(InferredType::F64)) + ); } #[test] pub fn test_pull_up_for_tag() { - let mut expr = Expr::tag(Expr::number(1f64)); - expr.pull_types_up().unwrap(); - assert_eq!(expr.inferred_type(), InferredType::Unknown); + let mut number = Expr::number(1f64); + number.override_type_type_mut(InferredType::F64); + let expr = Expr::get_tag(Expr::option(Some(number))); + let expr = expr.pull_types_up().unwrap(); + assert_eq!( + expr.inferred_type(), + InferredType::Option(Box::new(InferredType::F64)) + ); } #[test] pub fn test_pull_up_for_pattern_match() { - let mut expr = Expr::pattern_match( - Expr::number(1f64), + let expr = Expr::pattern_match( + Expr::select_field( + Expr::identifier("foo").add_infer_type(InferredType::Record(vec![( + "bar".to_string(), + InferredType::Str, + )])), + "bar", + ), vec![ - crate::MatchArm { - arm_pattern: ArmPattern::Literal(Box::new(Expr::Number( - Number { value: 1f64 }, - None, - InferredType::U64, - ))), - arm_resolution_expr: Box::new(Expr::Number( - Number { value: 1f64 }, - None, - InferredType::U64, + MatchArm { + arm_pattern: ArmPattern::Constructor( + "cons1".to_string(), + vec![ArmPattern::Literal(Box::new(Expr::SelectField( + Box::new(Expr::identifier("foo").add_infer_type(InferredType::Record( + vec![("bar".to_string(), InferredType::Str)], + ))), + "bar".to_string(), + InferredType::Unknown, + )))], + ), + arm_resolution_expr: Box::new(Expr::SelectField( + Box::new(Expr::identifier("baz").add_infer_type(InferredType::Record( + vec![("qux".to_string(), InferredType::Str)], + ))), + "qux".to_string(), + InferredType::Unknown, )), }, - crate::MatchArm { - arm_pattern: ArmPattern::Literal(Box::new(Expr::Number( - Number { value: 2f64 }, - None, - InferredType::U64, - ))), - arm_resolution_expr: Box::new(Expr::Number( - Number { value: 2f64 }, - None, - InferredType::U64, + MatchArm { + arm_pattern: ArmPattern::Constructor( + "cons2".to_string(), + vec![ArmPattern::Literal(Box::new(Expr::SelectField( + Box::new(Expr::identifier("quux").add_infer_type( + InferredType::Record(vec![( + "corge".to_string(), + InferredType::Str, + )]), + )), + "corge".to_string(), + InferredType::Unknown, + )))], + ), + arm_resolution_expr: Box::new(Expr::SelectField( + Box::new( + Expr::identifier("grault").add_infer_type(InferredType::Record(vec![ + ("garply".to_string(), InferredType::Str), + ])), + ), + "garply".to_string(), + InferredType::Unknown, )), }, ], ); - expr.pull_types_up().unwrap(); - assert_eq!(expr.inferred_type(), InferredType::U64); + let new_expr = expr.pull_types_up().unwrap(); + let expected = internal::expected_pattern_match(); + assert_eq!(new_expr, expected); + } + + mod internal { + use crate::{ArmPattern, Expr, InferredType, MatchArm, VariableId}; + + pub(crate) fn expected_pattern_match() -> Expr { + Expr::PatternMatch( + Box::new(Expr::SelectField( + Box::new(Expr::Identifier( + VariableId::global("foo".to_string()), + InferredType::Record(vec![("bar".to_string(), InferredType::Str)]), + )), + "bar".to_string(), + InferredType::Str, + )), + vec![ + MatchArm { + arm_pattern: ArmPattern::Constructor( + "cons1".to_string(), + vec![ArmPattern::Literal(Box::new(Expr::SelectField( + Box::new(Expr::Identifier( + VariableId::global("foo".to_string()), + InferredType::Record(vec![( + "bar".to_string(), + InferredType::Str, + )]), + )), + "bar".to_string(), + InferredType::Str, + )))], + ), + arm_resolution_expr: Box::new(Expr::SelectField( + Box::new(Expr::Identifier( + VariableId::global("baz".to_string()), + InferredType::Record(vec![("qux".to_string(), InferredType::Str)]), + )), + "qux".to_string(), + InferredType::Str, + )), + }, + MatchArm { + arm_pattern: ArmPattern::Constructor( + "cons2".to_string(), + vec![ArmPattern::Literal(Box::new(Expr::SelectField( + Box::new(Expr::Identifier( + VariableId::global("quux".to_string()), + InferredType::Record(vec![( + "corge".to_string(), + InferredType::Str, + )]), + )), + "corge".to_string(), + InferredType::Str, + )))], + ), + arm_resolution_expr: Box::new(Expr::SelectField( + Box::new(Expr::Identifier( + VariableId::global("grault".to_string()), + InferredType::Record(vec![( + "garply".to_string(), + InferredType::Str, + )]), + )), + "garply".to_string(), + InferredType::Str, + )), + }, + ], + InferredType::Str, + ) + } } } diff --git a/golem-rib/src/type_inference/type_unification.rs b/golem-rib/src/type_inference/type_unification.rs index b0912564c..76d30bc64 100644 --- a/golem-rib/src/type_inference/type_unification.rs +++ b/golem-rib/src/type_inference/type_unification.rs @@ -268,10 +268,6 @@ pub fn unify_types(expr: &mut Expr) -> Result<(), Vec> { match unified_inferred_type { Ok(unified_type) => *inferred_type = unified_type, Err(e) => { - errors.push(format!( - "Unable to resolve the type of code block {}", - expr_str - )); errors.extend(e); } } @@ -334,6 +330,11 @@ pub fn unify_types(expr: &mut Expr) -> Result<(), Vec> { queue.push(left); queue.push(right); } + Expr::Or(left, right, _) => { + queue.push(left); + queue.push(right); + } + Expr::GreaterThanOrEqualTo(left, right, _) => { queue.push(left); queue.push(right); diff --git a/golem-worker-service-base/src/api_definition/http/http_api_definition.rs b/golem-worker-service-base/src/api_definition/http/http_api_definition.rs index b374f005c..f163b087b 100644 --- a/golem-worker-service-base/src/api_definition/http/http_api_definition.rs +++ b/golem-worker-service-base/src/api_definition/http/http_api_definition.rs @@ -682,7 +682,6 @@ mod tests { let yaml = get_api_spec(path_pattern, worker_id, response_mapping); let original: HttpApiDefinition = serde_yaml::from_value(yaml.clone()).unwrap(); - dbg!(original.clone()); let proto: grpc_apidefinition::ApiDefinition = original.clone().try_into().unwrap(); let decoded: HttpApiDefinition = proto.try_into().unwrap(); assert_eq!(original, decoded);