Skip to content

Commit

Permalink
Add conversion for identifier expressions (finos#41)
Browse files Browse the repository at this point in the history
Considers expressions like:

a.b
a
AConstructor
Close finos#23 and close finos#24
  • Loading branch information
sfc-gh-lfallasavendano committed Jan 8, 2024
1 parent 58ec90e commit 7881a59
Show file tree
Hide file tree
Showing 7 changed files with 288 additions and 31 deletions.
82 changes: 82 additions & 0 deletions src/Morphir/Snowpark/AccessElementMapping.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
module Morphir.Snowpark.AccessElementMapping exposing (
mapConstructorAccess
, mapFieldAccess
, mapReferenceAccess
, mapVariableAccess )

{-| This module contains functions to generate code like `a.b` or `a`.
|-}

import List
import Morphir.IR.Name as Name
import Morphir.IR.Type as IrType
import Morphir.Scala.AST as Scala
import Morphir.Snowpark.MappingContext as MappingContext
import Morphir.IR.FQName as FQName
import Morphir.IR.Literal exposing (Literal(..))
import Morphir.Snowpark.MappingContext exposing (MappingContextInfo, isRecordWithSimpleTypes)
import Morphir.Snowpark.MappingContext exposing (isUnionTypeWithoutParams)
import Html.Attributes exposing (name)
import Morphir.IR.FQName as FQName
import Morphir.IR.Value exposing (Value(..))

isReferenceToSimpleTypesRecord : (Value ta (IrType.Type a)) -> MappingContextInfo a -> Maybe (Scala.Path, Name.Name)
isReferenceToSimpleTypesRecord expression ctx =
case expression of
Variable (IrType.Reference _ typeName _) _ ->
if isRecordWithSimpleTypes typeName ctx then
Just (scalaPathToModule typeName, (FQName.getLocalName typeName))
else
Nothing
_ ->
Nothing

mapFieldAccess : va -> (Value ta (IrType.Type a)) -> Name.Name -> MappingContextInfo a -> Scala.Value
mapFieldAccess _ value name ctx =
let
simpleFieldName = name |> Name.toCamelCase
in
case isReferenceToSimpleTypesRecord value ctx of
Just (path, refererName) -> Scala.Ref (path ++ [refererName |> Name.toTitleCase]) simpleFieldName
_ -> Scala.Literal (Scala.StringLit "Field access not converted")


mapVariableAccess : (IrType.Type a) -> Name.Name -> MappingContextInfo a -> Scala.Value
mapVariableAccess _ name _ =
Scala.Variable (name |> Name.toCamelCase)

mapConstructorAccess : (IrType.Type a) -> FQName.FQName -> MappingContextInfo a -> Scala.Value
mapConstructorAccess tpe name ctx =
case tpe of
IrType.Reference _ typeName _ ->
if isUnionTypeWithoutParams typeName ctx then
let
nsName = scalaPathToModule name
containerObjectName = FQName.getLocalName typeName |> Name.toTitleCase
containerObjectFieldName = FQName.getLocalName name |> Name.toTitleCase
in
Scala.Ref (nsName ++ [containerObjectName]) containerObjectFieldName
else
(Scala.Literal (Scala.StringLit "Field access not converted"))
_ -> (Scala.Literal (Scala.StringLit "Field access not converted"))

scalaPathToModule : FQName.FQName -> Scala.Path
scalaPathToModule name =
let
packagePath = FQName.getPackagePath name |> List.map Name.toCamelCase
modulePath = case FQName.getModulePath name |> List.reverse of
(last::restInverted) -> ((Name.toTitleCase last) :: (List.map Name.toCamelCase restInverted)) |> List.reverse
_ -> []
in
packagePath ++ modulePath

mapReferenceAccess : (IrType.Type a) -> FQName.FQName -> Scala.Value
mapReferenceAccess tpe name =
if MappingContext.isBasicType tpe then
let
nsName = scalaPathToModule name
containerObjectFieldName = FQName.getLocalName name |> Name.toCamelCase
in
Scala.Ref (nsName ) containerObjectFieldName
else
(Scala.Literal (Scala.StringLit "Reference access not converted"))
48 changes: 31 additions & 17 deletions src/Morphir/Snowpark/Backend.elm
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import Morphir.IR.Type exposing (Type)
import Morphir.Scala.AST as Scala
import Morphir.Scala.PrettyPrinter as PrettyPrinter
import Morphir.TypeSpec.Backend exposing (mapModuleDefinition)
import Morphir.IR.Type exposing (Type)
import Morphir.Scala.Common exposing (mapValueName)
import Morphir.IR.Value as Value exposing (Pattern(..), Value(..))
import Morphir.Snowpark.MappingContext as MappingContext
Expand All @@ -22,7 +21,12 @@ import Morphir.IR.Literal exposing (Literal(..))
import Morphir.Snowpark.Constants as Constants
import Morphir.Snowpark.RecordWrapperGenerator as RecordWrapperGenerator
import Morphir.Snowpark.MappingContext exposing (MappingContextInfo)

import Morphir.IR.FQName as FQName
import Morphir.Snowpark.AccessElementMapping exposing (
mapFieldAccess
, mapReferenceAccess
, mapVariableAccess
, mapConstructorAccess)

type alias Options =
{}
Expand All @@ -34,7 +38,7 @@ mapDistribution _ distro =
mapPackageDefinition distro packageName packageDef


mapPackageDefinition : Distribution -> Package.PackageName -> Package.Definition ta (Type ()) -> FileMap
mapPackageDefinition : Distribution -> Package.PackageName -> Package.Definition () (Type ()) -> FileMap
mapPackageDefinition _ packagePath packageDef =
let
contextInfo = MappingContext.processDistributionModules packagePath packageDef
Expand All @@ -61,7 +65,7 @@ mapPackageDefinition _ packagePath packageDef =
|> Dict.fromList


mapModuleDefinition : Package.PackageName -> Path -> AccessControlled (Module.Definition ta (Type ())) -> MappingContextInfo a -> List Scala.CompilationUnit
mapModuleDefinition : Package.PackageName -> Path -> AccessControlled (Module.Definition ta (Type ())) -> MappingContextInfo () -> List Scala.CompilationUnit
mapModuleDefinition currentPackagePath currentModulePath accessControlledModuleDef mappingCtx =
let
( scalaPackagePath, moduleName ) =
Expand All @@ -74,12 +78,13 @@ mapModuleDefinition currentPackagePath currentModulePath accessControlledModuleD
parts =
List.append currentPackagePath (List.reverse reverseModulePath)
in
( parts |> (List.concat >> List.map String.toLower), lastName )
( parts |> (List.map Name.toCamelCase), lastName )

classDefinitions : List (Scala.Documented (Scala.Annotated Scala.TypeDecl))
classDefinitions =
moduleTypeDefinitions : List (Scala.Annotated Scala.MemberDecl)
moduleTypeDefinitions =
accessControlledModuleDef.value.types
|> RecordWrapperGenerator.generateRecordWrappers currentPackagePath currentModulePath mappingCtx
|> List.map (\doc -> { annotations = doc.value.annotations, value = Scala.MemberTypeDecl (doc.value.value) } )

functionMembers : List (Scala.Annotated Scala.MemberDecl)
functionMembers =
Expand All @@ -94,7 +99,7 @@ mapModuleDefinition currentPackagePath currentModulePath accessControlledModuleD
, args = []
, returnType = Nothing
, body =
mapFunctionBody accessControlledValueDef.value.value
mapFunctionBody accessControlledValueDef.value.value mappingCtx
}
]
)
Expand All @@ -105,7 +110,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 @@ -118,28 +123,36 @@ mapModuleDefinition currentPackagePath currentModulePath accessControlledModuleD
, name =
moduleName |> Name.toTitleCase
, members =
List.append [] functionMembers
(moduleTypeDefinitions ++ functionMembers)
, extends =
[]
, body = Nothing
}
)
)
) :: classDefinitions)
)]
}
in
[ moduleUnit ]


mapFunctionBody : Value.Definition ta (Type ()) -> Maybe Scala.Value
mapFunctionBody value =
Maybe.Just (mapValue value.body)
mapFunctionBody : Value.Definition ta (Type ()) -> MappingContextInfo () -> Maybe Scala.Value
mapFunctionBody value ctx =
Maybe.Just (mapValue value.body ctx)

mapValue : Value ta (Type ()) -> Scala.Value
mapValue value =
mapValue : Value ta (Type ()) -> MappingContextInfo () -> Scala.Value
mapValue value ctx =
case value of
Literal tpe literal ->
mapLiteral tpe literal
Field tpe val name ->
mapFieldAccess tpe val name ctx
Variable tpe name ->
mapVariableAccess tpe name ctx
Constructor tpe name ->
mapConstructorAccess tpe name ctx
Reference tpe name ->
mapReferenceAccess tpe name
_ ->
Scala.Literal (Scala.StringLit "To Do")

Expand All @@ -157,4 +170,5 @@ mapLiteral tpe literal =
FloatLiteral val ->
Constants.applySnowparkFunc "lit" [(Scala.Literal (Scala.FloatLit val))]
_ ->
Debug.todo "The type '_' is not implemented"
Debug.todo "The type '_' is not implemented"

16 changes: 11 additions & 5 deletions src/Morphir/Snowpark/MappingContext.elm
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ module Morphir.Snowpark.MappingContext exposing
, isTypeAlias
, isUnionTypeWithoutParams
, isUnionTypeWithParams
, MappingContextInfo )
, isBasicType
, MappingContextInfo
, emptyContext )

{-| 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 @@ -35,6 +37,10 @@ type TypeDefinitionClassification a =
type alias MappingContextInfo a =
Dict FQName (TypeClassificationState a)


emptyContext : MappingContextInfo a
emptyContext = Dict.empty

isRecordWithSimpleTypes : FQName -> MappingContextInfo a -> Bool
isRecordWithSimpleTypes name ctx =
case Dict.get name ctx of
Expand Down Expand Up @@ -71,7 +77,7 @@ type TypeClassificationState a =
| TypeWithPendingClassification (Maybe (Type a))
| TypeNotClassified

classifyType : Type.Definition a -> MappingContextInfo a -> (TypeClassificationState a)
classifyType : Type.Definition () -> MappingContextInfo () -> (TypeClassificationState ())
classifyType typeDefinition ctx =
case typeDefinition of
Type.TypeAliasDefinition _ t ->
Expand Down Expand Up @@ -147,7 +153,7 @@ classifyActualType tpe ctx =
simpleName packagePath modName name =
FQName.fQName packagePath modName name

processModuleDefinition : Package.PackageName -> ModuleName -> MappingContextInfo ta -> AccessControlled (Module.Definition ta (Type ())) -> MappingContextInfo ta
processModuleDefinition : Package.PackageName -> ModuleName -> MappingContextInfo () -> AccessControlled (Module.Definition () (Type ())) -> MappingContextInfo ()
processModuleDefinition packagePath modulePath currentResult moduleDefinition =
moduleDefinition.value.types
|> Dict.toList
Expand All @@ -157,7 +163,7 @@ processModuleDefinition packagePath modulePath currentResult moduleDefinition =
|> Dict.union currentResult


processSecondPassOnType : FQName -> TypeClassificationState a -> MappingContextInfo a -> MappingContextInfo a
processSecondPassOnType : FQName -> TypeClassificationState () -> MappingContextInfo () -> MappingContextInfo ()
processSecondPassOnType name typeClassification ctx =
case typeClassification of
TypeClassified _ -> ctx
Expand All @@ -171,7 +177,7 @@ processSecondPassOnType name typeClassification ctx =
_ -> ctx
_ -> ctx

processDistributionModules : Package.PackageName -> Package.Definition ta (Type ()) -> MappingContextInfo ta
processDistributionModules : Package.PackageName -> Package.Definition () (Type ()) -> MappingContextInfo ()
processDistributionModules packagePath package =
let
firstPass = package.modules
Expand Down
70 changes: 67 additions & 3 deletions src/Morphir/Snowpark/RecordWrapperGenerator.elm
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,24 @@ import Morphir.IR.Type exposing (Field)
import Morphir.IR.Name as Name
import Morphir.Snowpark.Constants exposing (applySnowparkFunc)
import Morphir.Snowpark.Constants exposing (typeRefForSnowparkType)
import Morphir.Snowpark.MappingContext exposing (isUnionTypeWithoutParams)


{-| 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.
For union types without parameters we are going to generate an object definition with accessors for each option.
|-}

generateRecordWrappers : Package.PackageName -> ModuleName -> MappingContextInfo a -> Dict Name (AccessControlled (Documented (Type.Definition ta))) -> List (Scala.Documented (Scala.Annotated Scala.TypeDecl))
generateRecordWrappers : Package.PackageName -> ModuleName -> MappingContextInfo () -> 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 : Package.PackageName -> ModuleName -> MappingContextInfo () -> (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
Expand All @@ -38,8 +41,24 @@ processTypeDeclaration packageName moduleName ctx (name, typeDeclAc) =
typeDeclAc.value.doc
members
(isRecordWithSimpleTypes (FQName.fQName packageName moduleName name) ctx)
Type.CustomTypeDefinition _ constructorsAccess ->
processUnionTypeDeclaration
name
constructorsAccess.value
(isUnionTypeWithoutParams (FQName.fQName packageName moduleName name) ctx)
_ -> []


processUnionTypeDeclaration : Name -> Morphir.IR.Type.Constructors ta -> Bool -> List (Scala.Documented (Scala.Annotated Scala.TypeDecl))
processUnionTypeDeclaration name constructors noParams =
if noParams then
[
objectForUnionWithNoParamsValues name constructors
]
else
[]


processRecordDeclaration : Name -> String -> (List (Field a)) -> Bool -> List (Scala.Documented (Scala.Annotated Scala.TypeDecl))
processRecordDeclaration name doc fields recordWithSimpleTypes =
if recordWithSimpleTypes then
Expand Down Expand Up @@ -122,4 +141,49 @@ generateObjectMember field =
, args = []
, returnType = Just (typeRefForSnowparkType "Column")
, body = Just (applySnowparkFunc "col" [(Scala.Literal (Scala.StringLit (field.name |> Name.toCamelCase)))])
}))
}))



generateUnionTypeNameMember : String -> (Scala.Annotated Scala.MemberDecl)
generateUnionTypeNameMember optionName =
(Scala.Annotated
[]
(Scala.FunctionDecl
{
modifiers = []
, name = optionName
, typeArgs = []
, args = []
, returnType = Just (typeRefForSnowparkType "Column")
, body = Just (applySnowparkFunc "lit" [(Scala.Literal (Scala.StringLit optionName))])
}))


objectForUnionWithNoParamsValues : Name -> Morphir.IR.Type.Constructors ta -> (Scala.Documented (Scala.Annotated Scala.TypeDecl))
objectForUnionWithNoParamsValues name constructors =
let
nameToUse = name |> toTitleCase
members = constructors
|> Dict.toList
|> List.map (\(constructorName, _) -> generateUnionTypeNameMember (Name.toTitleCase constructorName))
in
( Scala.Documented
Nothing
(Scala.Annotated []
(Scala.Object
{ modifiers =
[]
, name =
nameToUse
, members =
members
, extends =
[]
, body =
Nothing
}
)
))


Loading

0 comments on commit 7881a59

Please sign in to comment.