From 09330f5bb94b6be772afdc2efab073c7e4489232 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Fallas=20Avenda=C3=B1o?= Date: Tue, 24 Oct 2023 09:53:49 -0600 Subject: [PATCH] Add support for a couple of elements (#60) * Add support for a couple of elements - Adds basic support for case/of with Union types with params ``` case x of First a b c -> a Second y -> y ``` - Adds support for Maybe case/of patterns ``` case x of Just w -> w Nothing -> 19 ``` - Adds basic support for conversion of `Maybe.Just` and `Maybe.Nothing` - Adds a new Scala literal for `null` * Code review changes --- src/Morphir/Scala/AST.elm | 1 + src/Morphir/Scala/PrettyPrinter.elm | 3 + src/Morphir/Snowpark/AccessElementMapping.elm | 45 ++++-- src/Morphir/Snowpark/Backend.elm | 23 ++- src/Morphir/Snowpark/MapFunctionsMapping.elm | 32 +++- src/Morphir/Snowpark/MappingContext.elm | 48 +++++- src/Morphir/Snowpark/PatternMatchMapping.elm | 138 +++++++++++++++++- .../Snowpark/FunctionGenerationTests.elm | 3 +- 8 files changed, 249 insertions(+), 44 deletions(-) diff --git a/src/Morphir/Scala/AST.elm b/src/Morphir/Scala/AST.elm index 948a0465e..d8399f70d 100644 --- a/src/Morphir/Scala/AST.elm +++ b/src/Morphir/Scala/AST.elm @@ -286,6 +286,7 @@ type Lit | IntegerLit Int | FloatLit Float | DecimalLit Decimal + | NullLit {-| -} diff --git a/src/Morphir/Scala/PrettyPrinter.elm b/src/Morphir/Scala/PrettyPrinter.elm index b0412987b..843401a3e 100644 --- a/src/Morphir/Scala/PrettyPrinter.elm +++ b/src/Morphir/Scala/PrettyPrinter.elm @@ -654,6 +654,9 @@ mapLit lit = DecimalLit decimal -> Decimal.toString decimal + NullLit -> + "null" + statementBlock : Options -> List String -> Doc statementBlock opt statements = diff --git a/src/Morphir/Snowpark/AccessElementMapping.elm b/src/Morphir/Snowpark/AccessElementMapping.elm index ca5b7113a..c16ba6af7 100644 --- a/src/Morphir/Snowpark/AccessElementMapping.elm +++ b/src/Morphir/Snowpark/AccessElementMapping.elm @@ -15,7 +15,8 @@ import Morphir.Snowpark.MappingContext as MappingContext import Morphir.IR.FQName as FQName import Morphir.IR.Literal exposing (Literal(..)) import Morphir.Snowpark.MappingContext exposing (ValueMappingContext) -import Morphir.Snowpark.MappingContext exposing (isUnionTypeWithoutParams) +import Morphir.Snowpark.MappingContext exposing (isUnionTypeWithoutParams + , getReplacementForIdentifier) import Html.Attributes exposing (name) import Morphir.IR.FQName as FQName import Morphir.IR.Value exposing (Value(..)) @@ -23,35 +24,49 @@ import Morphir.Snowpark.ReferenceUtils exposing (isValueReferenceToSimpleTypesRe , scalaReferenceToUnionTypeCase , scalaPathToModule) import Morphir.IR.Value as Value +import Morphir.Snowpark.MappingContext exposing (isAnonymousRecordWithSimpleTypes) +import Morphir.Snowpark.Constants exposing (applySnowparkFunc) -mapFieldAccess : va -> (Value ta (IrType.Type a)) -> Name.Name -> ValueMappingContext -> Scala.Value +mapFieldAccess : va -> (Value ta (IrType.Type ())) -> Name.Name -> ValueMappingContext -> Scala.Value mapFieldAccess _ value name ctx = - let + (let simpleFieldName = name |> Name.toCamelCase valueIsFunctionParameter = case value of Value.Variable _ varName -> (varName, List.member varName ctx.parameters) _ -> (Name.fromString "a",False) - in - case (isValueReferenceToSimpleTypesRecord value ctx.typesContextInfo, valueIsFunctionParameter) of - (_, (paramName, True)) -> Scala.Ref [paramName |> Name.toCamelCase] simpleFieldName - (Just (path, refererName), (_, False)) -> Scala.Ref (path ++ [refererName |> Name.toTitleCase]) simpleFieldName - _ -> Scala.Literal (Scala.StringLit "Field access not converted") - + in + case (isValueReferenceToSimpleTypesRecord value ctx.typesContextInfo, valueIsFunctionParameter, value) of + (_, (paramName, True), _) -> + Scala.Ref [paramName |> Name.toCamelCase] simpleFieldName + (Just (path, refererName), (_, False), _) -> + Scala.Ref (path ++ [refererName |> Name.toTitleCase]) simpleFieldName + _ -> + (if isAnonymousRecordWithSimpleTypes (value |> Value.valueAttribute) ctx.typesContextInfo then + applySnowparkFunc "col" [Scala.Literal (Scala.StringLit (Name.toCamelCase name)) ] + else + Scala.Literal (Scala.StringLit "Field access to not converted"))) mapVariableAccess : (IrType.Type a) -> Name.Name -> ValueMappingContext -> Scala.Value -mapVariableAccess _ name _ = - Scala.Variable (name |> Name.toCamelCase) +mapVariableAccess _ name ctx = + case getReplacementForIdentifier name ctx of + Just replacement -> + replacement + _ -> + Scala.Variable (name |> Name.toCamelCase) mapConstructorAccess : (IrType.Type a) -> FQName.FQName -> ValueMappingContext -> Scala.Value mapConstructorAccess tpe name ctx = - case tpe of - IrType.Reference _ typeName _ -> + case (tpe, name) of + ((IrType.Reference _ _ _), ([ [ "morphir" ], [ "s", "d", "k" ] ], [ [ "maybe" ] ], [ "nothing" ])) -> + applySnowparkFunc "lit" [Scala.Literal Scala.NullLit] + (IrType.Reference _ typeName _, _) -> if isUnionTypeWithoutParams typeName ctx.typesContextInfo then scalaReferenceToUnionTypeCase typeName name else - (Scala.Literal (Scala.StringLit "Field access not converted")) - _ -> (Scala.Literal (Scala.StringLit "Field access not converted")) + Scala.Literal (Scala.StringLit "Constructor access not converted") + _ -> + Scala.Literal (Scala.StringLit "Constructor access not converted") mapReferenceAccess : (IrType.Type a) -> FQName.FQName -> Scala.Value mapReferenceAccess tpe name = diff --git a/src/Morphir/Snowpark/Backend.elm b/src/Morphir/Snowpark/Backend.elm index cd5e0a907..7121f7e24 100644 --- a/src/Morphir/Snowpark/Backend.elm +++ b/src/Morphir/Snowpark/Backend.elm @@ -15,7 +15,7 @@ import Morphir.Scala.PrettyPrinter as PrettyPrinter import Morphir.TypeSpec.Backend exposing (mapModuleDefinition) import Morphir.Scala.Common exposing (mapValueName) import Morphir.IR.Value as Value exposing (Pattern(..), Value(..)) -import Morphir.Snowpark.MappingContext as MappingContext +import Morphir.Snowpark.MappingContext as MappingContext exposing (emptyValueMappingContext) import Morphir.IR.Literal exposing (Literal(..)) import Morphir.Snowpark.Constants as Constants import Morphir.Snowpark.RecordWrapperGenerator as RecordWrapperGenerator @@ -97,7 +97,7 @@ mapModuleDefinition currentPackagePath currentModulePath accessControlledModuleD |> Dict.toList |> List.concatMap (\( valueName, accessControlledValueDef ) -> - [ mapFunctionDefinition valueName accessControlledValueDef mappingCtx + [ mapFunctionDefinition valueName accessControlledValueDef currentPackagePath mappingCtx ] ) |> List.map Scala.withoutAnnotation @@ -133,12 +133,12 @@ mapModuleDefinition currentPackagePath currentModulePath accessControlledModuleD [ moduleUnit ] -mapFunctionDefinition : Name.Name -> AccessControlled (Documented (Value.Definition () (Type ()))) -> MappingContextInfo () -> Scala.MemberDecl -mapFunctionDefinition functionName body mappingCtx = +mapFunctionDefinition : Name.Name -> AccessControlled (Documented (Value.Definition () (Type ()))) -> Path -> MappingContextInfo () -> Scala.MemberDecl +mapFunctionDefinition functionName body currentPackagePath mappingCtx = let (parameters, localVariables) = processParameters body.value.value.inputTypes mappingCtx parameterNames = body.value.value.inputTypes |> List.map (\(name, _, _) -> name) - valueMappingContext = { typesContextInfo = mappingCtx, parameters = parameterNames } + valueMappingContext = { emptyValueMappingContext | typesContextInfo = mappingCtx, parameters = parameterNames, packagePath = currentPackagePath} bodyCandidate = mapFunctionBody body.value.value valueMappingContext resultingBody = case (localVariables, bodyCandidate) of @@ -220,9 +220,16 @@ mapValue value ctx = MapFunctionsMapping.mapFunctionsMapping value mapValue ctx PatternMatch tpe expr cases -> mapPatternMatch (tpe, expr, cases) mapValue ctx + IfThenElse _ condition thenExpr elseExpr -> + mapIfThenElse condition thenExpr elseExpr ctx _ -> - Scala.Literal (Scala.StringLit "To Do") - - + Scala.Literal (Scala.StringLit ("Unsupported element")) +mapIfThenElse : Value ta (Type ()) -> Value ta (Type ()) -> Value ta (Type ()) -> ValueMappingContext -> Scala.Value +mapIfThenElse condition thenExpr elseExpr ctx = + let + whenCall = + Constants.applySnowparkFunc "when" [ mapValue condition ctx, mapValue thenExpr ctx ] + in + Scala.Apply (Scala.Select whenCall "otherwise") [Scala.ArgValue Nothing (mapValue elseExpr ctx)] diff --git a/src/Morphir/Snowpark/MapFunctionsMapping.elm b/src/Morphir/Snowpark/MapFunctionsMapping.elm index 05298b13f..067c84a22 100644 --- a/src/Morphir/Snowpark/MapFunctionsMapping.elm +++ b/src/Morphir/Snowpark/MapFunctionsMapping.elm @@ -7,11 +7,17 @@ import Morphir.IR.Type exposing (Type) import Morphir.Snowpark.MappingContext exposing (ValueMappingContext, isCandidateForDataFrame) import Morphir.IR.Value exposing (valueAttribute) import Morphir.IR.Type as TypeIR -import Morphir.Snowpark.MappingContext exposing (isAnonymousRecordWithSimpleTypes) +import Morphir.Snowpark.MappingContext exposing (isAnonymousRecordWithSimpleTypes + , isLocalFunctionName) import Morphir.IR.Name as Name import Morphir.Snowpark.Constants exposing (applySnowparkFunc) import Morphir.Snowpark.MappingContext exposing (isBasicType) import Morphir.Snowpark.Operatorsmaps exposing (mapOperator) +import Morphir.Snowpark.ReferenceUtils exposing (scalaPathToModule) +import Morphir.Visual.BoolOperatorTree exposing (functionName) +import Morphir.IR.FQName exposing (FQName) +import Morphir.IR.FQName as FQName +import Morphir.Snowpark.MappingContext exposing (isTypeRefToRecordWithSimpleTypes) type alias MapValueType ta = ValueIR.Value ta (TypeIR.Type ()) -> ValueMappingContext -> Scala.Value @@ -33,6 +39,8 @@ mapFunctionsMapping value mapValue ctx = generateForListFilterMap predicateAction sourceRelation ctx mapValue ValueIR.Apply _ (ValueIR.Reference _ ( [ [ "morphir" ], [ "s", "d", "k" ] ], [ [ "list" ] ], [ "sum" ] )) collection -> generateForListSum collection ctx mapValue + ValueIR.Apply _ (ValueIR.Constructor _ ( [ [ "morphir" ], [ "s", "d", "k" ] ], [ [ "maybe" ] ], [ "just" ] )) justValue -> + mapValue justValue ctx ValueIR.Apply (TypeIR.Reference () ([["morphir"],["s","d","k"]],[["basics"]], _) []) (ValueIR.Apply @@ -114,12 +122,23 @@ generateForListSum collection ctx mapValue = generateForListFilter : Value ta (Type ()) -> (Value ta (Type ())) -> ValueMappingContext -> MapValueType ta -> Scala.Value generateForListFilter predicate sourceRelation ctx mapValue = + let + generateFilterCall functionExpr = + Scala.Apply + (Scala.Select (mapValue sourceRelation ctx) "filter") + [Scala.ArgValue Nothing functionExpr] + in if isCandidateForDataFrame (valueAttribute sourceRelation) ctx.typesContextInfo then case predicate of ValueIR.Lambda _ _ binExpr -> - Scala.Apply (Scala.Select (mapValue sourceRelation ctx) "filter") [Scala.ArgValue Nothing <| mapValue binExpr ctx] + generateFilterCall <| mapValue binExpr ctx + ValueIR.Reference _ functionName -> + if isLocalFunctionName functionName ctx then + generateFilterCall <| Scala.Ref (scalaPathToModule functionName) (functionName |> FQName.getLocalName |> Name.toCamelCase) + else + Scala.Literal (Scala.StringLit ("Unsupported filter function scenario2" )) _ -> - Scala.Literal (Scala.StringLit "To Do") + Scala.Literal (Scala.StringLit ("Unsupported filter function scenario" )) else Scala.Literal (Scala.StringLit "Unsupported filter scenario") @@ -148,15 +167,16 @@ generateForListMap projection sourceRelation ctx mapValue = Just arguments -> Scala.Apply (Scala.Select (mapValue sourceRelation ctx) "select") arguments Nothing -> - Scala.Literal (Scala.StringLit "Unsupported map scenario") + Scala.Literal (Scala.StringLit "Unsupported map scenario 1") else - Scala.Literal (Scala.StringLit "Unsupported map scenario") + Scala.Literal (Scala.StringLit "Unsupported map scenario 2") processLambdaWithRecordBody : Value ta (Type ()) -> ValueMappingContext -> MapValueType ta -> Maybe (List Scala.ArgValue) processLambdaWithRecordBody functionExpr ctx mapValue = case functionExpr of ValueIR.Lambda (TypeIR.Function _ _ returnType) (ValueIR.AsPattern _ _ _) (ValueIR.Record _ fields) -> - if isAnonymousRecordWithSimpleTypes returnType ctx.typesContextInfo then + if isAnonymousRecordWithSimpleTypes returnType ctx.typesContextInfo + || isTypeRefToRecordWithSimpleTypes returnType ctx.typesContextInfo then Just (fields |> Dict.toList |> List.map (\(fieldName, value) -> (Name.toCamelCase fieldName, (mapValue value ctx))) diff --git a/src/Morphir/Snowpark/MappingContext.elm b/src/Morphir/Snowpark/MappingContext.elm index a51880d3a..81dd44538 100644 --- a/src/Morphir/Snowpark/MappingContext.elm +++ b/src/Morphir/Snowpark/MappingContext.elm @@ -5,6 +5,7 @@ module Morphir.Snowpark.MappingContext exposing , isTypeAlias , isUnionTypeWithoutParams , isUnionTypeWithParams + , isUnionTypeRefWithParams , isUnionTypeRefWithoutParams , isBasicType , MappingContextInfo @@ -12,7 +13,11 @@ module Morphir.Snowpark.MappingContext exposing , isCandidateForDataFrame , ValueMappingContext , emptyValueMappingContext - , isAnonymousRecordWithSimpleTypes ) + , isAnonymousRecordWithSimpleTypes + , getReplacementForIdentifier + , isDataFrameFriendlyType + , isLocalFunctionName + , isTypeRefToRecordWithSimpleTypes ) {-| This module contains functions to collect information about type definitions in a distribution. It classifies type definitions in the following kinds: @@ -24,6 +29,7 @@ It classifies type definitions in the following kinds: |-} import Dict exposing (Dict) +import Morphir.Scala.AST as Scala import Morphir.IR.Type as Type exposing (Type, Type(..), Definition(..)) import Morphir.IR.Module as Module import Morphir.IR.AccessControlled exposing (AccessControlled) @@ -32,6 +38,7 @@ import Morphir.IR.FQName as FQName import Morphir.IR.Package as Package import Morphir.IR.Module exposing (ModuleName) import Morphir.IR.Name exposing (Name) +import Morphir.IR.Path as Path type TypeDefinitionClassification a = RecordWithSimpleTypes @@ -46,22 +53,38 @@ type alias MappingContextInfo a = type alias ValueMappingContext = { parameters: List Name , typesContextInfo : MappingContextInfo () + , inlinedIds: Dict Name Scala.Value + , packagePath: Path.Path } emptyValueMappingContext : ValueMappingContext emptyValueMappingContext = { parameters = [] - , typesContextInfo = emptyContext } + , inlinedIds = Dict.empty + , typesContextInfo = emptyContext + , packagePath = Path.fromString "default" + } +getReplacementForIdentifier : Name -> ValueMappingContext -> Maybe Scala.Value +getReplacementForIdentifier name ctx = + Dict.get name ctx.inlinedIds emptyContext : MappingContextInfo a emptyContext = Dict.empty +isLocalFunctionName : FQName -> ValueMappingContext -> Bool +isLocalFunctionName name ctx = + (FQName.getPackagePath name) == ctx.packagePath + isRecordWithSimpleTypes : FQName -> MappingContextInfo a -> Bool isRecordWithSimpleTypes name ctx = case Dict.get name ctx of Just (TypeClassified RecordWithSimpleTypes) -> True _ -> False +isTypeRefToRecordWithSimpleTypes : Type a -> MappingContextInfo a -> Bool +isTypeRefToRecordWithSimpleTypes tpe ctx = + typeRefNamePredicate tpe isRecordWithSimpleTypes ctx + isRecordWithComplexTypes : FQName -> MappingContextInfo a -> Bool isRecordWithComplexTypes name ctx = case Dict.get name ctx of @@ -74,12 +97,20 @@ isUnionTypeWithoutParams name ctx = Just (TypeClassified UnionTypeWithoutParams) -> True _ -> False -isUnionTypeRefWithoutParams : Type a -> MappingContextInfo a -> Bool -isUnionTypeRefWithoutParams tpe ctx = +typeRefNamePredicate : Type a -> (FQName -> MappingContextInfo a -> Bool) -> MappingContextInfo a -> Bool +typeRefNamePredicate tpe predicateToCheckOnName ctx = case tpe of - Type.Reference _ name _ -> isUnionTypeWithoutParams name ctx + Type.Reference _ name _ -> predicateToCheckOnName name ctx _ -> False +isUnionTypeRefWithoutParams : Type a -> MappingContextInfo a -> Bool +isUnionTypeRefWithoutParams tpe ctx = + typeRefNamePredicate tpe isUnionTypeWithoutParams ctx + +isUnionTypeRefWithParams : Type a -> MappingContextInfo a -> Bool +isUnionTypeRefWithParams tpe ctx = + typeRefNamePredicate tpe isUnionTypeWithParams ctx + isUnionTypeWithParams : FQName -> MappingContextInfo a -> Bool isUnionTypeWithParams name ctx = case Dict.get name ctx of @@ -92,12 +123,17 @@ isTypeAlias name ctx = _ -> False isCandidateForDataFrame : (Type ()) -> MappingContextInfo () -> Bool -isCandidateForDataFrame typeRef ctx= +isCandidateForDataFrame typeRef ctx = case typeRef of Type.Reference _ ( [ [ "morphir" ], [ "s", "d", "k" ] ], [ [ "list" ] ], [ "list" ] ) [ Type.Reference _ itemTypeName [] ] -> isRecordWithSimpleTypes itemTypeName ctx + Type.Reference _ + ( [ [ "morphir" ], [ "s", "d", "k" ] ], [ [ "list" ] ], [ "list" ] ) + [ Type.Record _ fields ] -> + fields + |> List.all (\{tpe} -> isDataFrameFriendlyType tpe ctx ) _ -> False isAnonymousRecordWithSimpleTypes : Type.Type () -> MappingContextInfo () -> Bool diff --git a/src/Morphir/Snowpark/PatternMatchMapping.elm b/src/Morphir/Snowpark/PatternMatchMapping.elm index 59efb29c8..347f7b33f 100644 --- a/src/Morphir/Snowpark/PatternMatchMapping.elm +++ b/src/Morphir/Snowpark/PatternMatchMapping.elm @@ -4,6 +4,7 @@ module Morphir.Snowpark.PatternMatchMapping exposing (..) -} +import Dict exposing (Dict) import Morphir.IR.Type exposing (Type) import Morphir.IR.Value exposing (Value, Pattern(..)) import Morphir.Snowpark.MappingContext exposing (ValueMappingContext) @@ -13,8 +14,12 @@ import Morphir.Snowpark.Constants exposing (applySnowparkFunc) import Morphir.Snowpark.ReferenceUtils exposing (mapLiteral , scalaReferenceToUnionTypeCase) import Morphir.IR.Value as Value -import Morphir.Snowpark.MappingContext exposing (isUnionTypeRefWithoutParams) +import Morphir.Snowpark.MappingContext exposing (isUnionTypeRefWithoutParams + , isUnionTypeRefWithParams ) import Morphir.IR.Type as Type +import Morphir.IR.Name as Name +import Morphir.IR.FQName as FQName +import Morphir.Snowpark.MappingContext exposing (isUnionTypeWithParams) type alias PatternMatchValues ta = (Type (), Value ta (Type ()), List ( Pattern (Type ()), Value ta (Type ()) )) @@ -61,12 +66,58 @@ mapPatternMatch (tpe, expr, cases) mapValue ctx = restPairs = rest |> List.map (\(constr, val) -> (compareWithExpr constr, (mapValue val ctx))) in createChainOfWhenCalls (compareWithExpr firstConstr, mapValue firstExpr ctx) restPairs + UnionTypesWithParams ((firstConstr, firstBindings, firstExpr)::rest) Nothing -> + let + referenceExpr = mapValue expr ctx + compareWithExpr : Name.Name -> Scala.Value + compareWithExpr = \name -> + equalExpr (Scala.Apply referenceExpr [Scala.ArgValue Nothing (applySnowparkFunc "lit" [Scala.Literal (Scala.StringLit "__tag")])]) + (applySnowparkFunc "lit" [Scala.Literal (Scala.StringLit (Name.toTitleCase name))]) + changeCtxt = addBindingReplacementsToContext ctx + restPairs = rest |> List.map (\(constr, bindings, val) -> ((compareWithExpr constr), (mapValue val (changeCtxt bindings referenceExpr)))) + in + createChainOfWhenCalls (compareWithExpr firstConstr, mapValue firstExpr ctx) restPairs + UnionTypesWithParams ((firstConstr, firstBindings, firstExpr)::rest) (Just defaultValue) -> + let + referenceExpr = mapValue expr ctx + compareWithExpr = \name -> + equalExpr (Scala.Apply (mapValue expr ctx) [Scala.ArgValue Nothing (applySnowparkFunc "lit" [Scala.Literal (Scala.StringLit "__tag")])]) + (applySnowparkFunc "lit" [Scala.Literal (Scala.StringLit ( Name.toTitleCase name))]) + changeCtxt = addBindingReplacementsToContext ctx + restPairs = rest |> List.map (\(constr, bindings, val) -> ((compareWithExpr constr), (mapValue val (changeCtxt bindings referenceExpr)))) + in + createChainOfWhenWithOtherwise (compareWithExpr firstConstr, mapValue firstExpr (changeCtxt firstBindings referenceExpr)) restPairs (mapValue defaultValue ctx) + MaybeCase (justVariable, justExpr) nothingExpr -> + let + referenceExpr = mapValue expr ctx + generatedJustExpr = + mapValue justExpr (Maybe.withDefault ctx (Maybe.map (\varName -> { ctx | inlinedIds = Dict.insert varName referenceExpr ctx.inlinedIds }) justVariable)) + generatedNothingExpr = mapValue nothingExpr ctx + in + createChainOfWhenWithOtherwise ((Scala.Select referenceExpr "is_not_null"), generatedJustExpr) [] generatedNothingExpr _ -> Scala.Variable "NOT_CONVERTED" +generateBindingVariableExpr : Name.Name -> Scala.Value -> Scala.Value +generateBindingVariableExpr name expr = + Scala.Apply expr [Scala.ArgValue Nothing (applySnowparkFunc "lit" [Scala.Literal (Scala.StringLit (name |> Name.toCamelCase))])] + +addBindingReplacementsToContext : ValueMappingContext -> List Name.Name -> Scala.Value -> ValueMappingContext +addBindingReplacementsToContext ctxt bindingVariables referenceExpr = + let + newReplacements = + bindingVariables + |> List.map (\name -> (name, generateBindingVariableExpr name referenceExpr)) + |> Dict.fromList + in + { ctxt | inlinedIds = Dict.union ctxt.inlinedIds newReplacements } + type PatternMatchScenario ta = LiteralsWithDefault (List (Literal, Value ta (Type ()))) (Value ta (Type ())) | UnionTypesWithoutParams (List (Scala.Value, Value ta (Type ()))) (Maybe (Value ta (Type ()))) + | UnionTypesWithParams (List (Name.Name, List Name.Name, Value ta (Type ()))) (Maybe (Value ta (Type ()))) + -- Right now we just support `Just` with variables + | MaybeCase (Maybe Name.Name, Value ta (Type ())) (Value ta (Type ())) | Unsupported collectMaybeList : (a -> Maybe b) -> List a -> Maybe (List b) @@ -108,6 +159,24 @@ checkForUnionOfWithNoParams (pattern, caseValue) = _ -> Nothing +checkConstructorForUnionOfWithParams : ( Pattern (Type ()), Value ta (Type ()) ) -> Maybe (Name.Name, List Name.Name, Value ta (Type ())) +checkConstructorForUnionOfWithParams (pattern, caseValue) = + let + identifyNameOnlyPattern : Pattern (Type ()) -> Maybe Name.Name + identifyNameOnlyPattern = + \p -> + case p of + AsPattern _ (WildcardPattern _) name -> + Just name + _ -> + Nothing + in + case pattern of + (ConstructorPattern _ name patternArgs) -> + collectMaybeList identifyNameOnlyPattern patternArgs + |> Maybe.map (\names -> (name |> FQName.getLocalName, names, caseValue)) + _ -> + Nothing checkUnionWithNoParamsWithDefault : Value ta (Type ()) -> List ( Pattern (Type ()), Value ta (Type ()) ) -> ValueMappingContext -> (Maybe (PatternMatchScenario ta)) checkUnionWithNoParamsWithDefault expr cases ctx = @@ -123,19 +192,72 @@ checkUnionWithNoParamsWithDefault expr cases ctx = Nothing else Nothing -tryAlternative : Maybe a -> (() -> Maybe a) -> Maybe a -tryAlternative currentResult nextAction = + + +checkUnionWithParams : Value ta (Type ()) -> List ( Pattern (Type ()), Value ta (Type ()) ) -> ValueMappingContext -> (Maybe (PatternMatchScenario ta)) +checkUnionWithParams expr cases ctx = + if isUnionTypeRefWithParams (Value.valueAttribute expr) ctx.typesContextInfo then + case List.reverse cases of + ((WildcardPattern _, wildCardResult)::restReversed) -> + (collectMaybeList checkConstructorForUnionOfWithParams restReversed) + |> Maybe.map (\parts -> (UnionTypesWithParams parts (Just wildCardResult))) + ((ConstructorPattern _ _ [], _)::_) as constructorCases -> + (collectMaybeList checkConstructorForUnionOfWithParams constructorCases) + |> Maybe.map (\parts -> (UnionTypesWithParams parts Nothing)) + _ -> + Nothing + else + Nothing + +checkMaybePattern : Value ta (Type ()) -> List ( Pattern (Type ()), Value ta (Type ()) ) -> ValueMappingContext -> (Maybe (PatternMatchScenario ta)) +checkMaybePattern expr cases ctx = + case (Value.valueAttribute expr) of + Type.Reference _ ([["morphir"],["s","d","k"]],[["maybe"]],["maybe"]) _ -> + case cases of + [(ConstructorPattern _ _ [ AsPattern _ (WildcardPattern _) varName], justExpr), + (WildcardPattern _, wildCardResult)] -> + Just <| MaybeCase (Just varName, justExpr) wildCardResult + [(ConstructorPattern _ _ [], nothingExpr), + (WildcardPattern _, wildCardResult)] -> + Just <| MaybeCase (Nothing, wildCardResult) nothingExpr + [(ConstructorPattern _ _ [ AsPattern _ (WildcardPattern _) varName], justExpr), + (ConstructorPattern _ _ [], nothingExpr)] -> + Just <| MaybeCase (Just varName, justExpr) nothingExpr + [(ConstructorPattern _ _ [], nothingExpr), + (ConstructorPattern _ _ [ AsPattern _ (WildcardPattern _) varName], justExpr)] -> + Just <| MaybeCase (Just varName, justExpr) nothingExpr + _ -> Nothing + _ -> Nothing + + +tryAlternative : (() -> Maybe a) -> Maybe a -> Maybe a +tryAlternative nextAction currentResult = case currentResult of Nothing -> nextAction () _ -> currentResult +tryAlternatives : List (() -> Maybe a) -> Maybe a +tryAlternatives cases = + case cases of + first::rest -> + case first() of + Just _ as result -> + result + _ -> + tryAlternatives rest + [] -> + Nothing + classifyScenario : (Value ta (Type ())) -> List (Pattern (Type ()), Value ta (Type ())) -> ValueMappingContext -> PatternMatchScenario ta classifyScenario value cases ctx = Maybe.withDefault - Unsupported - (tryAlternative - (checkLiteralsWithDefault cases - |> Maybe.map (\(literalCases, defaultResult) -> LiteralsWithDefault literalCases defaultResult)) - (\_ -> checkUnionWithNoParamsWithDefault value cases ctx)) + Unsupported + (tryAlternatives + [ (\_ -> + (checkLiteralsWithDefault cases + |> Maybe.map (\(literalCases, defaultResult) -> LiteralsWithDefault literalCases defaultResult))) + , (\_ -> checkUnionWithNoParamsWithDefault value cases ctx) + , (\_ -> checkUnionWithParams value cases ctx) + , (\_ -> checkMaybePattern value cases ctx) ]) diff --git a/tests/Morphir/Snowpark/FunctionGenerationTests.elm b/tests/Morphir/Snowpark/FunctionGenerationTests.elm index 493632130..4af0c3ee4 100644 --- a/tests/Morphir/Snowpark/FunctionGenerationTests.elm +++ b/tests/Morphir/Snowpark/FunctionGenerationTests.elm @@ -18,6 +18,7 @@ import Morphir.Snowpark.CommonTestUtils exposing (stringTypeInstance , testDistributionName , testDistributionPackage) import Morphir.Snowpark.Constants exposing (typeRefForSnowparkType) +import Morphir.IR.Path as Path functionGenTests: Test functionGenTests = @@ -32,7 +33,7 @@ functionGenTests = , body = Value.Variable stringTypeInstance (Name.fromString "x") }} mappedFunctionDefinition = - mapFunctionDefinition (Name.fromString "foo") functionDefinition calculatedContext + mapFunctionDefinition (Name.fromString "foo") functionDefinition (Path.fromString "UTest") calculatedContext expectedRef = Scala.Ref ["uTest", "MyMod"] "Emp" expectedTypeRef = Scala.TypeRef ["uTest", "MyMod"] "Emp"