Skip to content

Commit

Permalink
Adds basic generation of record wrappers (finos#40)
Browse files Browse the repository at this point in the history
* Adds basic generation of record wrappers

* Fix missing `extends` generation
  • Loading branch information
sfc-gh-lfallasavendano committed Dec 13, 2023
1 parent a989535 commit e88525e
Show file tree
Hide file tree
Showing 7 changed files with 225 additions and 12 deletions.
18 changes: 13 additions & 5 deletions src/Morphir/Snowpark/Backend.elm
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import Morphir.Snowpark.MappingContext as MappingContext
import Morphir.IR.FQName as FQName
import Morphir.IR.Literal exposing (Literal(..))
import Morphir.Snowpark.Constants as Constants
import Morphir.Snowpark.RecordWrapperGenerator as RecordWrapperGenerator
import Morphir.Snowpark.MappingContext exposing (MappingContextInfo)


type alias Options =
{}
Expand All @@ -42,7 +45,7 @@ mapPackageDefinition _ packagePath packageDef =
|> Dict.toList
|> List.concatMap
(\( modulePath, moduleImpl ) ->
mapModuleDefinition packagePath modulePath moduleImpl
mapModuleDefinition packagePath modulePath moduleImpl contextInfo
)
in
generatedScala
Expand All @@ -58,8 +61,8 @@ mapPackageDefinition _ packagePath packageDef =
|> Dict.fromList


mapModuleDefinition : Package.PackageName -> Path -> AccessControlled (Module.Definition ta (Type ())) -> List Scala.CompilationUnit
mapModuleDefinition currentPackagePath currentModulePath accessControlledModuleDef =
mapModuleDefinition : Package.PackageName -> Path -> AccessControlled (Module.Definition ta (Type ())) -> MappingContextInfo a -> List Scala.CompilationUnit
mapModuleDefinition currentPackagePath currentModulePath accessControlledModuleDef mappingCtx =
let
( scalaPackagePath, moduleName ) =
case currentModulePath |> List.reverse of
Expand All @@ -73,6 +76,11 @@ mapModuleDefinition currentPackagePath currentModulePath accessControlledModuleD
in
( parts |> (List.concat >> List.map String.toLower), lastName )

classDefinitions : List (Scala.Documented (Scala.Annotated Scala.TypeDecl))
classDefinitions =
accessControlledModuleDef.value.types
|> RecordWrapperGenerator.generateRecordWrappers currentPackagePath currentModulePath mappingCtx

functionMembers : List (Scala.Annotated Scala.MemberDecl)
functionMembers =
accessControlledModuleDef.value.values
Expand All @@ -97,7 +105,7 @@ mapModuleDefinition currentPackagePath currentModulePath accessControlledModuleD
, fileName = (moduleName |> Name.toTitleCase) ++ ".scala"
, packageDecl = scalaPackagePath
, imports = []
, typeDecls = [ Scala.Documented (Just (String.join "" [ "Generated based on ", currentModulePath |> Path.toString Name.toTitleCase "." ]))
, typeDecls = (( Scala.Documented (Just (String.join "" [ "Generated based on ", currentModulePath |> Path.toString Name.toTitleCase "." ]))
(Scala.Annotated []
(Scala.Object
{ modifiers =
Expand All @@ -117,7 +125,7 @@ mapModuleDefinition currentPackagePath currentModulePath accessControlledModuleD
}
)
)
]
) :: classDefinitions)
}
in
[ moduleUnit ]
Expand Down
15 changes: 13 additions & 2 deletions src/Morphir/Snowpark/Constants.elm
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
module Morphir.Snowpark.Constants exposing (..)
import Morphir.Scala.AST as Scala

snowflakeNamespace : List String
snowflakeNamespace = ["com", "snowflake", "snowpark"]

functionsNamespace : List String
functionsNamespace = ["com", "snowpark", "functions"]
functionsNamespace = snowflakeNamespace ++ ["functions"]



applySnowparkFunc : String -> List Scala.Value -> Scala.Value
applySnowparkFunc name args =
Scala.Apply
(Scala.Ref functionsNamespace name)
(args |> List.map (\v -> Scala.ArgValue Nothing v))
(args |> List.map (\v -> Scala.ArgValue Nothing v))



typeRefForSnowparkType : String -> Scala.Type
typeRefForSnowparkType typeName =
(Scala.TypeRef snowflakeNamespace typeName)
7 changes: 3 additions & 4 deletions src/Morphir/Snowpark/MappingContext.elm
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ module Morphir.Snowpark.MappingContext exposing
, isRecordWithComplexTypes
, isTypeAlias
, isUnionTypeWithoutParams
, isUnionTypeWithParams )
, isUnionTypeWithParams
, MappingContextInfo )

{-| This module contains functions to collect information about type definitions in a distribution.
It classifies type definitions in the following kinds:
Expand Down Expand Up @@ -74,8 +75,7 @@ classifyType : Type.Definition a -> MappingContextInfo a -> (TypeClassificationS
classifyType typeDefinition ctx =
case typeDefinition of
Type.TypeAliasDefinition _ t ->
--Debug.log ("=>>>" ++ (Debug.toString t))
(classifyActualType t ctx)
classifyActualType t ctx
Type.CustomTypeDefinition _ {value} ->
let
zeroArgConstructors =
Expand Down Expand Up @@ -145,7 +145,6 @@ classifyActualType tpe ctx =
_ -> TypeNotClassified

simpleName packagePath modName name =
--Name.toCamelCase name
FQName.fQName packagePath modName name

processModuleDefinition : Package.PackageName -> ModuleName -> MappingContextInfo ta -> AccessControlled (Module.Definition ta (Type ())) -> MappingContextInfo ta
Expand Down
125 changes: 125 additions & 0 deletions src/Morphir/Snowpark/RecordWrapperGenerator.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
module Morphir.Snowpark.RecordWrapperGenerator exposing (generateRecordWrappers)

import Dict exposing (Dict)
import Morphir.Scala.AST as Scala
import Morphir.IR.Documented exposing (Documented)
import Morphir.IR.AccessControlled exposing (Access(..), AccessControlled)
import Morphir.IR.Name exposing (Name, toTitleCase)
import Morphir.IR.Type as Type
import Morphir.IR.Package as Package
import Morphir.IR.Module exposing (ModuleName)
import Morphir.IR.FQName as FQName
import Morphir.Snowpark.MappingContext exposing (MappingContextInfo, isRecordWithSimpleTypes)
import Morphir.IR.Type exposing (Field)
import Morphir.IR.Name as Name
import Morphir.Snowpark.Constants exposing (applySnowparkFunc)
import Morphir.Snowpark.Constants exposing (typeRefForSnowparkType)


{-| This module contains to create wrappers for record declarations that represent tables.
For each record a Trait and an Object is generated with `Column` fields for each member.
|-}

generateRecordWrappers : Package.PackageName -> ModuleName -> MappingContextInfo a -> Dict Name (AccessControlled (Documented (Type.Definition ta))) -> List (Scala.Documented (Scala.Annotated Scala.TypeDecl))
generateRecordWrappers packageName moduleName ctx typesInModule =
typesInModule
|> Dict.toList
|> List.concatMap (processTypeDeclaration packageName moduleName ctx)


processTypeDeclaration : Package.PackageName -> ModuleName -> MappingContextInfo a -> (Name, (AccessControlled (Documented (Type.Definition ta)))) -> List (Scala.Documented (Scala.Annotated Scala.TypeDecl))
processTypeDeclaration packageName moduleName ctx (name, typeDeclAc) =
-- For the moment we are going generating wrappers for record types
case typeDeclAc.value.value of
Type.TypeAliasDefinition _ (Type.Record _ members) ->
processRecordDeclaration
name
typeDeclAc.value.doc
members
(isRecordWithSimpleTypes (FQName.fQName packageName moduleName name) ctx)
_ -> []

processRecordDeclaration : Name -> String -> (List (Field a)) -> Bool -> List (Scala.Documented (Scala.Annotated Scala.TypeDecl))
processRecordDeclaration name doc fields recordWithSimpleTypes =
if recordWithSimpleTypes then
[
traitForRecordWrapper name doc fields,
objectForRecordWrapper name fields
]
else
[]

traitForRecordWrapper : Name -> String -> (List (Field a)) -> (Scala.Documented (Scala.Annotated Scala.TypeDecl))
traitForRecordWrapper name doc fields =
let
members = fields |> List.map generateTraitMember
in
( Scala.Documented (Just doc)
(Scala.Annotated []
(Scala.Trait
{ modifiers =
[]
, name =
name |> toTitleCase
, typeArgs =
[]
, members =
members
, extends =
[]
}
)
))

generateTraitMember : (Field a) -> (Scala.Annotated Scala.MemberDecl)
generateTraitMember field =
(Scala.Annotated
[]
(Scala.FunctionDecl
{
modifiers = []
, name = (field.name |> Name.toCamelCase)
, typeArgs = []
, args = []
, returnType = Just (typeRefForSnowparkType "Column")
, body = Nothing
}))


objectForRecordWrapper : Name -> (List (Field a)) -> (Scala.Documented (Scala.Annotated Scala.TypeDecl))
objectForRecordWrapper name fields =
let
nameToUse = name |> toTitleCase
members = fields |> List.map generateObjectMember
in
( Scala.Documented Nothing
(Scala.Annotated []
(Scala.Object
{ modifiers =
[]
, name =
nameToUse
, members =
members
, extends =
[ Scala.TypeRef [] nameToUse ]
, body =
Nothing
}
)
))

generateObjectMember : (Field a) -> (Scala.Annotated Scala.MemberDecl)
generateObjectMember field =
(Scala.Annotated
[]
(Scala.FunctionDecl
{
modifiers = []
, name = (field.name |> Name.toCamelCase)
, typeArgs = []
, args = []
, returnType = Just (typeRefForSnowparkType "Column")
, body = Just (applySnowparkFunc "col" [(Scala.Literal (Scala.StringLit (field.name |> Name.toCamelCase)))])
}))
2 changes: 1 addition & 1 deletion tests/Morphir/Snowpark/MapValueLiteralTests.elm
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Morphir.IR.Value as Value
import Morphir.IR.Type as Type

functionNamespace : List String
functionNamespace = ["com", "snowpark", "functions"]
functionNamespace = ["com", "snowflake", "snowpark", "functions"]

booleanReference : Type.Type ()
booleanReference = Type.Reference () ( [ [ "morphir" ], [ "s", "d", "k" ] ], [ [ "basics" ] ], [ "boolean" ] ) []
Expand Down
1 change: 1 addition & 0 deletions tests/Morphir/Snowpark/MappingContextTests.elm
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Morphir.Snowpark.MappingContext as MappingContext
import Morphir.IR.FQName exposing (FQName)
import Morphir.IR.FQName as FQName


floatTypeInstance : Type ()
floatTypeInstance = Reference () ( [ [ "morphir" ], [ "s", "d", "k" ] ], [ [ "basics" ] ], [ "float" ] ) []

Expand Down
69 changes: 69 additions & 0 deletions tests/Morphir/Snowpark/RecordWrapperGenerationTests.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
module Morphir.Snowpark.RecordWrapperGenerationTests exposing (..)


import Dict
import Test exposing (Test, describe, test)
import Expect
import Morphir.IR.Path as Path
import Morphir.IR.Module exposing (emptyDefinition)
import Morphir.IR.AccessControlled exposing (public)
import Morphir.IR.Name as Name
import Morphir.IR.Type as Type
import Morphir.IR.Type exposing (Type(..))
import Morphir.Snowpark.MappingContext as MappingContext
import Morphir.IR.Type exposing (Type(..))
import Morphir.Snowpark.RecordWrapperGenerator as RecordWrapperGenerator
import Morphir.IR.Path as Path
import Morphir.Scala.AST as Scala

stringTypeInstance : Type ()
stringTypeInstance = Reference () ( [ [ "morphir" ], [ "s", "d", "k" ] ], [ [ "string" ] ], [ "string" ] ) []

testDistributionName = (Path.fromString "UTest")

typesDict =
Dict.fromList [
-- A record with simple types
(Name.fromString "Emp1",
public { doc = "", value = Type.TypeAliasDefinition [] (Type.Record () [
{ name = Name.fromString "firstname", tpe = stringTypeInstance },
{ name = Name.fromString "lastname", tpe = stringTypeInstance }
]) })
]

testDistributionPackage =
({ modules = Dict.fromList [
( Path.fromString "MyMod",
public { emptyDefinition | types = typesDict } )
]})

typeClassificationTests : Test
typeClassificationTests =
let
calculatedContext = MappingContext.processDistributionModules testDistributionName testDistributionPackage
firstModule = Dict.get [(Name.fromString "MyMod")] testDistributionPackage.modules |> Maybe.map (\access -> access.value)
assertItCreatedWrapper =
test ("Wrapper creation") <|
\_ ->
let
generationResult =
firstModule
|> Maybe.map (\mod -> RecordWrapperGenerator.generateRecordWrappers testDistributionName (Path.fromString "MyMod") calculatedContext mod.types)
|> Maybe.map (\scalaElementList -> scalaElementList |> List.map stringFromScalaTypeDefinition)
|> Maybe.withDefault []

in
Expect.equal ["Trait:Emp1:2", "Object:Emp1:2"] generationResult

in
describe "resolveTNam"
[ assertItCreatedWrapper
]


stringFromScalaTypeDefinition : (Scala.Documented (Scala.Annotated Scala.TypeDecl)) -> String
stringFromScalaTypeDefinition scalaElement =
case scalaElement.value.value of
Scala.Object { name, members } -> "Object:" ++ name ++ ":" ++ (members |> List.length |> String.fromInt)
Scala.Trait { name, members } -> "Trait:" ++ name ++ ":" ++ (members |> List.length |> String.fromInt)
_ -> ""

0 comments on commit e88525e

Please sign in to comment.