hydra-0.1.0: src/main/haskell/Hydra/Ext/Java/Coder.hs
module Hydra.Ext.Java.Coder (printModule) where
import Hydra.All
import Hydra.CoreDecoding
import Hydra.Reduction
import Hydra.Ext.Java.Utils
import Hydra.Ext.Java.Language
import qualified Hydra.Impl.Haskell.Dsl.Terms as Terms
import qualified Hydra.Impl.Haskell.Dsl.Types as Types
import qualified Hydra.Ext.Java.Syntax as Java
import Hydra.Adapters.Coders
import Hydra.Util.Codetree.Script
import Hydra.Ext.Java.Serde
import Hydra.Ext.Java.Settings
import Hydra.Adapters.UtilsEtc
import Hydra.Types.Inference
import Hydra.Util.Context
import qualified Control.Monad as CM
import qualified Data.List as L
import qualified Data.List.Split as LS
import qualified Data.Map as M
import qualified Data.Set as S
import qualified Data.Maybe as Y
type Aliases = M.Map Namespace Java.PackageName
printModule :: (Ord m, Read m, Show m) => Module m -> GraphFlow m (M.Map FilePath String)
printModule mod = do
withTrace "encode in Java" $ do
units <- moduleToJavaCompilationUnit mod
return $ M.fromList $ forPair <$> M.toList units
where
forPair (name, unit) = (
elementNameToFilePath name,
printExpr $ parenthesize $ writeCompilationUnit unit)
boundTypeVariables :: Type m -> [VariableType]
boundTypeVariables typ = case typ of
TypeAnnotated (Annotated typ1 _) -> boundTypeVariables typ1
TypeLambda (LambdaType v body) -> v:(boundTypeVariables body)
_ -> []
commentsFromElement :: Element m -> GraphFlow m (Maybe String)
commentsFromElement el = do
cx <- getState
annotationClassTermDescription (contextAnnotations cx) (elementData el)
commentsFromFieldType :: FieldType m -> GraphFlow m (Maybe String)
commentsFromFieldType (FieldType _ t) = do
cx <- getState
annotationClassTypeDescription (contextAnnotations cx) t
addComment :: Java.ClassBodyDeclaration -> FieldType m -> GraphFlow m Java.ClassBodyDeclarationWithComments
addComment decl field = Java.ClassBodyDeclarationWithComments decl <$> commentsFromFieldType field
noComment :: Java.ClassBodyDeclaration -> Java.ClassBodyDeclarationWithComments
noComment decl = Java.ClassBodyDeclarationWithComments decl Nothing
elementNameToFilePath :: Name -> FilePath
elementNameToFilePath name = nameToFilePath False (FileExtension "java") $ fromQname ns (sanitizeJavaName local)
where
(ns, local) = toQnameEager name
moduleToJavaCompilationUnit :: (Ord m, Read m, Show m) => Module m -> GraphFlow m (M.Map Name Java.CompilationUnit)
moduleToJavaCompilationUnit mod = transformModule javaLanguage encode constructModule mod
where
aliases = importAliasesForModule mod
encode = encodeTerm aliases Nothing . contractTerm
classModsPublic :: [Java.ClassModifier]
classModsPublic = [Java.ClassModifierPublic]
constructModule :: (Ord m, Read m, Show m)
=> Module m -> M.Map (Type m) (Coder (Context m) (Context m) (Term m) Java.Expression) -> [(Element m, TypedTerm m)]
-> GraphFlow m (M.Map Name Java.CompilationUnit)
constructModule mod coders pairs = do
cx <- getState
let isTypePair = isType cx . typedTermType . snd
let typePairs = L.filter isTypePair pairs
let dataPairs = L.filter (not . isTypePair) pairs
typeUnits <- CM.mapM typeToClass typePairs
dataMembers <- CM.mapM (termToInterfaceMember coders) dataPairs
return $ M.fromList $ typeUnits ++ ([constructElementsInterface mod dataMembers | not (L.null dataMembers)])
where
pkg = javaPackageDeclaration $ moduleNamespace mod
aliases = importAliasesForModule mod
typeToClass pair@(el, _) = do
let imports = []
decl <- declarationForType aliases pair
return (elementName el,
Java.CompilationUnitOrdinary $ Java.OrdinaryCompilationUnit (Just pkg) imports [decl])
termToInterfaceMember coders pair = do
withTrace ("element " ++ unName (elementName el)) $ do
expanded <- contractTerm <$> (expandLambdas $ typedTermTerm $ snd pair) >>= annotateTermWithTypes
if isLambda expanded
then termToMethod coders el (typedTermType $ snd pair) expanded
else termToConstant coders el (typedTermType $ snd pair) expanded
where
el = fst pair
isLambda t = case stripTerm t of
TermFunction (FunctionLambda _) -> True
_ -> False
termToConstant coders el typ term = do
jtype <- Java.UnannType <$> encodeType aliases typ
jterm <- coderEncode (Y.fromJust $ M.lookup typ coders) term
let mods = []
let var = javaVariableDeclarator (javaVariableName $ elementName el) $ Just $ Java.VariableInitializerExpression jterm
return $ Java.InterfaceMemberDeclarationConstant $ Java.ConstantDeclaration mods jtype [var]
-- Lambdas cannot (in general) be turned into top-level constants, as there is no way of declaring type parameters for constants
termToMethod coders el typ term = case stripType typ of
TypeFunction (FunctionType dom cod) -> case stripTerm term of
TermFunction (FunctionLambda (Lambda v body)) -> do
jdom <- encodeType aliases dom
jcod <- encodeType aliases cod
let mods = [Java.InterfaceMethodModifierStatic]
let anns = []
let mname = sanitizeJavaName $ decapitalize $ localNameOfEager $ elementName el
let param = javaTypeToJavaFormalParameter jdom (FieldName $ unVariable v)
let result = javaTypeToJavaResult jcod
jbody <- encodeTerm aliases (Just cod) body
let returnSt = Java.BlockStatementStatement $ javaReturnStatement $ Just jbody
let tparams = javaTypeParametersForType typ
return $ interfaceMethodDeclaration mods tparams mname [param] result (Just [returnSt])
_ -> unexpected "function term" term
_ -> unexpected "function type" typ
constructElementsInterface :: Module m -> [Java.InterfaceMemberDeclaration] -> (Name, Java.CompilationUnit)
constructElementsInterface mod members = (elName, cu)
where
cu = Java.CompilationUnitOrdinary $ Java.OrdinaryCompilationUnit (Just pkg) [] [decl]
pkg = javaPackageDeclaration $ moduleNamespace mod
mods = [Java.InterfaceModifierPublic]
className = elementsClassName $ moduleNamespace mod
elName = fromQname (moduleNamespace mod) className
body = Java.InterfaceBody members
itf = Java.TypeDeclarationInterface $ Java.InterfaceDeclarationNormalInterface $
Java.NormalInterfaceDeclaration mods (javaTypeIdentifier className) [] [] body
decl = Java.TypeDeclarationWithComments itf $ moduleDescription mod
declarationForLambdaType :: (Eq m, Ord m, Read m, Show m) => Aliases
-> [Java.TypeParameter] -> Name -> LambdaType m -> GraphFlow m Java.ClassDeclaration
declarationForLambdaType aliases tparams elName (LambdaType (VariableType v) body) =
toClassDecl False aliases (tparams ++ [param]) elName body
where
param = javaTypeParameter $ capitalize v
declarationForRecordType :: (Ord m, Read m, Show m) => Bool -> Aliases -> [Java.TypeParameter] -> Name
-> [FieldType m] -> GraphFlow m Java.ClassDeclaration
declarationForRecordType isInner aliases tparams elName fields = do
memberVars <- CM.mapM toMemberVar fields
cx <- getState
memberVars' <- CM.zipWithM addComment memberVars fields
withMethods <- if L.length fields > 1
then CM.mapM toWithMethod fields
else pure []
cons <- constructor
tn <- if isInner then pure [] else do
d <- typeNameDecl aliases elName
return [d]
let bodyDecls = tn ++ memberVars' ++ (noComment <$> [cons, equalsMethod, hashCodeMethod] ++ withMethods)
return $ javaClassDeclaration aliases tparams elName classModsPublic Nothing bodyDecls
where
constructor = do
params <- CM.mapM (fieldTypeToFormalParam aliases) fields
let stmts = Java.BlockStatementStatement . toAssignStmt . fieldTypeName <$> fields
return $ makeConstructor aliases elName False params stmts
fieldArgs = fieldNameToJavaExpression . fieldTypeName <$> fields
toMemberVar (FieldType fname ft) = do
let mods = [Java.FieldModifierPublic, Java.FieldModifierFinal]
jt <- encodeType aliases ft
let var = fieldNameToJavaVariableDeclarator fname
return $ javaMemberField mods jt var
toWithMethod field = do
let mods = [Java.MethodModifierPublic]
let methodName = "with" ++ capitalize (unFieldName $ fieldTypeName field)
param <- fieldTypeToFormalParam aliases field
let anns = [] -- TODO
let result = referenceTypeToResult $ nameToJavaReferenceType aliases False elName Nothing
let consId = Java.Identifier $ sanitizeJavaName $ localNameOfEager elName
let returnStmt = Java.BlockStatementStatement $ javaReturnStatement $ Just $
javaConstructorCall (javaConstructorName consId Nothing) fieldArgs Nothing
return $ methodDeclaration mods [] anns methodName [param] result (Just [returnStmt])
equalsMethod = methodDeclaration mods [] anns "equals" [param] result $
Just [instanceOfStmt,
castStmt,
returnAllFieldsEqual]
where
anns = [overrideAnnotation]
mods = [Java.MethodModifierPublic]
param = javaTypeToJavaFormalParameter (javaRefType [] Nothing "Object") (FieldName otherName)
result = javaTypeToJavaResult javaBooleanType
otherName = "other"
tmpName = "o"
instanceOfStmt = Java.BlockStatementStatement $ Java.StatementIfThen $
Java.IfThenStatement cond returnFalse
where
cond = javaUnaryExpressionToJavaExpression $
Java.UnaryExpressionOther $
Java.UnaryExpressionNotPlusMinusNot $
javaRelationalExpressionToJavaUnaryExpression $
javaInstanceOf other parent
where
other = javaIdentifierToJavaRelationalExpression $ javaIdentifier otherName
parent = nameToJavaReferenceType aliases False elName Nothing
returnFalse = javaReturnStatement $ Just $ javaBooleanExpression False
castStmt = variableDeclarationStatement aliases elName id rhs
where
id = javaIdentifier tmpName
rhs = javaCastExpressionToJavaExpression $ javaCastExpression aliases rt var
var = javaIdentifierToJavaUnaryExpression $ Java.Identifier $ sanitizeJavaName otherName
rt = nameToJavaReferenceType aliases False elName Nothing
returnAllFieldsEqual = Java.BlockStatementStatement $ javaReturnStatement $ Just $ if L.null fields
then javaBooleanExpression True
else javaConditionalAndExpressionToJavaExpression $
Java.ConditionalAndExpression (eqClause . fieldTypeName <$> fields)
where
eqClause (FieldName fname) = javaPostfixExpressionToJavaInclusiveOrExpression $
javaMethodInvocationToJavaPostfixExpression $ Java.MethodInvocation header [arg]
where
arg = javaExpressionNameToJavaExpression $
fieldExpression (javaIdentifier tmpName) (javaIdentifier fname)
header = Java.MethodInvocation_HeaderComplex $ Java.MethodInvocation_Complex var [] (Java.Identifier "equals")
var = Java.MethodInvocation_VariantExpression $ Java.ExpressionName Nothing $ Java.Identifier $
sanitizeJavaName fname
hashCodeMethod = methodDeclaration mods [] anns "hashCode" [] result $ Just [returnSum]
where
anns = [overrideAnnotation]
mods = [Java.MethodModifierPublic]
result = javaTypeToJavaResult javaIntType
returnSum = Java.BlockStatementStatement $ if L.null fields
then returnZero
else javaReturnStatement $ Just $
javaAdditiveExpressionToJavaExpression $ addExpressions $
L.zipWith multPair multipliers (fieldTypeName <$> fields)
where
returnZero = javaReturnStatement $ Just $ javaIntExpression 0
multPair :: Int -> FieldName -> Java.MultiplicativeExpression
multPair i (FieldName fname) = Java.MultiplicativeExpressionTimes $
Java.MultiplicativeExpression_Binary lhs rhs
where
lhs = Java.MultiplicativeExpressionUnary $ javaPrimaryToJavaUnaryExpression $
javaLiteralToPrimary $ javaInt i
rhs = javaPostfixExpressionToJavaUnaryExpression $
javaMethodInvocationToJavaPostfixExpression $
methodInvocationStatic (javaIdentifier fname) (Java.Identifier "hashCode") []
multipliers = L.cycle first20Primes
where
first20Primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71]
declarationForType :: (Ord m, Read m, Show m)
=> Aliases -> (Element m, TypedTerm m) -> GraphFlow m Java.TypeDeclarationWithComments
declarationForType aliases (el, TypedTerm _ term) = do
t <- decodeType term >>= adaptType javaLanguage
cd <- toClassDecl False aliases [] (elementName el) t
cx <- getState
comments <- commentsFromElement el
return $ Java.TypeDeclarationWithComments (Java.TypeDeclarationClass cd) comments
declarationForUnionType :: (Eq m, Ord m, Read m, Show m)
=> Aliases
-> [Java.TypeParameter] -> Name -> [FieldType m] -> GraphFlow m Java.ClassDeclaration
declarationForUnionType aliases tparams elName fields = do
variantClasses <- CM.mapM (fmap augmentVariantClass . unionFieldClass) fields
let variantDecls = Java.ClassBodyDeclarationClassMember . Java.ClassMemberDeclarationClass <$> variantClasses
cx <- getState
variantDecls' <- CM.zipWithM addComment variantDecls fields
let otherDecls = noComment <$> [privateConstructor, toAcceptMethod True, visitor, partialVisitor]
tn <- typeNameDecl aliases elName
let bodyDecls = [tn] ++ otherDecls ++ variantDecls'
let mods = classModsPublic ++ [Java.ClassModifierAbstract]
return $ javaClassDeclaration aliases tparams elName mods Nothing bodyDecls
where
privateConstructor = makeConstructor aliases elName True [] []
unionFieldClass (FieldType fname ftype) = do
let rtype = Types.record $ if Types.isUnit ftype then [] else [FieldType (FieldName valueFieldName) ftype]
toClassDecl True aliases [] (variantClassName False elName fname) rtype
augmentVariantClass (Java.ClassDeclarationNormal cd) = Java.ClassDeclarationNormal $ cd {
Java.normalClassDeclarationModifiers = [Java.ClassModifierPublic, Java.ClassModifierStatic, Java.ClassModifierFinal],
Java.normalClassDeclarationExtends = Just $ nameToJavaClassType aliases True args elName Nothing,
Java.normalClassDeclarationParameters = tparams,
Java.normalClassDeclarationBody = newBody (Java.normalClassDeclarationBody cd)}
where
newBody (Java.ClassBody decls) = Java.ClassBody $ decls ++ [noComment $ toAcceptMethod False]
args = typeParameterToTypeArgument <$> tparams
visitor = javaInterfaceDeclarationToJavaClassBodyDeclaration $
Java.NormalInterfaceDeclaration mods ti tparams extends body
where
mods = [Java.InterfaceModifierPublic]
ti = Java.TypeIdentifier $ Java.Identifier visitorName
tparams = [javaTypeParameter "R"]
extends = []
body = Java.InterfaceBody (toVisitMethod . fieldTypeName <$> fields)
where
toVisitMethod fname = interfaceMethodDeclaration [] [] visitMethodName [variantInstanceParam fname] resultR Nothing
partialVisitor = javaInterfaceDeclarationToJavaClassBodyDeclaration $
Java.NormalInterfaceDeclaration {
Java.normalInterfaceDeclarationModifiers = [Java.InterfaceModifierPublic],
Java.normalInterfaceDeclarationIdentifier = Java.TypeIdentifier $ Java.Identifier partialVisitorName,
Java.normalInterfaceDeclarationParameters = [javaTypeParameter "R"],
Java.normalInterfaceDeclarationExtends =
[Java.InterfaceType $ javaClassType [visitorTypeVariable] Nothing visitorName],
Java.normalInterfaceDeclarationBody = Java.InterfaceBody $ otherwise:(toVisitMethod . fieldTypeName <$> fields)}
where
otherwise = interfaceMethodDeclaration defaultMod [] "otherwise" [mainInstanceParam] resultR $ Just [throw]
where
throw = Java.BlockStatementStatement $ Java.StatementWithoutTrailing $
Java.StatementWithoutTrailingSubstatementThrow $ Java.ThrowStatement $
javaConstructorCall (javaConstructorName (Java.Identifier "IllegalStateException") Nothing) args Nothing
where
args = [javaAdditiveExpressionToJavaExpression $ addExpressions [
javaStringMultiplicativeExpression "Non-exhaustive patterns when matching: ",
Java.MultiplicativeExpressionUnary $ javaIdentifierToJavaUnaryExpression $ Java.Identifier "instance"]]
toVisitMethod fname = interfaceMethodDeclaration defaultMod [] visitMethodName [variantInstanceParam fname] resultR $
Just [returnOtherwise]
where
returnOtherwise = Java.BlockStatementStatement $ javaReturnStatement $ Just $
javaPrimaryToJavaExpression $ Java.PrimaryNoNewArray $ Java.PrimaryNoNewArrayMethodInvocation $
methodInvocation Nothing (Java.Identifier "otherwise") [javaIdentifierToJavaExpression $ Java.Identifier "instance"]
defaultMod = [Java.InterfaceMethodModifierDefault]
resultR = javaTypeToJavaResult $ Java.TypeReference visitorTypeVariable
mainInstanceParam = javaTypeToJavaFormalParameter classRef $ FieldName instanceName
where
classRef = javaClassTypeToJavaType $
nameToJavaClassType aliases False [] elName Nothing
variantInstanceParam fname = javaTypeToJavaFormalParameter classRef $ FieldName instanceName
where
classRef = javaClassTypeToJavaType $
nameToJavaClassType aliases False [] (variantClassName False elName fname) Nothing
elementJavaIdentifier :: Aliases -> Name -> Java.Identifier
elementJavaIdentifier aliases name = Java.Identifier $ jname ++ "." ++ local
where
(gname, local) = toQnameEager name
Java.Identifier jname = nameToJavaName aliases $ fromQname gname $ elementsClassName gname
elementsClassName :: Namespace -> String
elementsClassName (Namespace ns) = capitalize $ L.last $ LS.splitOn "/" ns
encodeElimination :: (Eq m, Ord m, Read m, Show m)
=> Aliases -> Maybe Java.Expression -> Type m -> Type m -> Elimination m -> GraphFlow m Java.Expression
encodeElimination aliases marg dom cod elm = case elm of
EliminationElement -> case marg of
Nothing -> encodeFunction aliases dom cod $ FunctionLambda $ Lambda var $ TermVariable var
where
var = Variable "v"
Just jarg -> pure jarg
EliminationNominal name -> case marg of
Nothing -> pure $ javaLambda var jbody
where
var = Variable "v"
arg = javaIdentifierToJavaExpression $ variableToJavaIdentifier var
jbody = javaConstructorCall (javaConstructorName (nameToJavaName aliases name) Nothing) [arg] Nothing
Just jarg -> pure $ javaFieldAccessToJavaExpression $ Java.FieldAccess qual (javaIdentifier valueFieldName)
where
qual = Java.FieldAccess_QualifierPrimary $ javaExpressionToJavaPrimary jarg
-- EliminationOptional (OptionalCases nothing just) ->
EliminationRecord (Projection _ fname) -> do
jdomr <- encodeType aliases dom >>= javaTypeToJavaReferenceType
jexp <- case marg of
Nothing -> pure $ javaLambda var jbody
where
var = Variable "v"
jbody = javaExpressionNameToJavaExpression $
fieldExpression (variableToJavaIdentifier var) (javaIdentifier $ unFieldName fname)
Just jarg -> pure $ javaFieldAccessToJavaExpression $ Java.FieldAccess qual (javaIdentifier $ unFieldName fname)
where
qual = Java.FieldAccess_QualifierPrimary $ javaExpressionToJavaPrimary jarg
return $ javaCastExpressionToJavaExpression $ javaCastExpression aliases jdomr $ javaExpressionToJavaUnaryExpression jexp
EliminationUnion (CaseStatement tname fields) -> case marg of
Nothing -> do
cx <- getState
let anns = contextAnnotations cx
let lhs = annotationClassSetTermType anns cx (Just $ Types.function (Types.nominal tname) cod) $ Terms.elimination elm
encodeTerm aliases Nothing $ Terms.lambda "v" $ Terms.apply lhs (Terms.variable "v")
Just jarg -> applyElimination jarg
where
applyElimination jarg = do
let prim = javaExpressionToJavaPrimary jarg
let consId = innerClassRef aliases tname visitorName
jcod <- encodeType aliases cod
let targs = Java.TypeArgumentsOrDiamondArguments [javaTypeToJavaTypeArgument jcod]
body <- Java.ClassBody <$> CM.mapM (bodyDecl jcod) fields
let visitor = javaConstructorCall (javaConstructorName consId $ Just targs) [] (Just body)
return $ javaMethodInvocationToJavaExpression $
methodInvocation (Just $ Right prim) (Java.Identifier "accept") [visitor]
where
bodyDecl jcod field = do
let jdom = Java.TypeReference $ nameToJavaReferenceType aliases True tname (Just $ capitalize $ unFieldName $ fieldName field)
let mods = [Java.MethodModifierPublic]
let anns = [overrideAnnotation]
let param = javaTypeToJavaFormalParameter jdom $ FieldName instanceName
let result = Java.ResultType $ Java.UnannType jcod
-- Note: the escaping is necessary because the instance.value field reference does not correspond to an actual Hydra projection term
let value = Terms.variable ("$" ++ instanceName ++ "." ++ valueFieldName)
jret <- encodeTerm aliases (Just cod) $ contractTerm $ Terms.apply (fieldTerm field) value
let returnStmt = Java.BlockStatementStatement $ javaReturnStatement $ Just jret
return $ noComment $ methodDeclaration mods [] anns visitMethodName [param] result (Just [returnStmt])
_ -> pure $ encodeLiteral $ LiteralString $
"Unimplemented elimination variant: " ++ show (eliminationVariant elm) -- TODO: temporary
encodeFunction :: (Eq m, Ord m, Read m, Show m)
=> Aliases -> Type m -> Type m -> Function m -> GraphFlow m Java.Expression
encodeFunction aliases dom cod fun = case fun of
-- FunctionCompareTo other ->
FunctionElimination elm -> encodeElimination aliases Nothing dom cod elm
FunctionLambda (Lambda var body) -> do
jbody <- encodeTerm aliases Nothing body
return $ javaLambda var jbody
-- FunctionPrimitive name ->
_ -> pure $ encodeLiteral $ LiteralString $
"Unimplemented function variant: " ++ show (functionVariant fun) -- TODO: temporary
encodeLiteral :: Literal -> Java.Expression
encodeLiteral lit = javaLiteralToJavaExpression $ case lit of
LiteralBoolean b -> javaBoolean b
LiteralFloat f -> Java.LiteralFloatingPoint $ Java.FloatingPointLiteral $ case f of
FloatValueFloat32 v -> realToFrac v
FloatValueFloat64 v -> v
LiteralInteger i -> case i of
IntegerValueBigint v -> integer v -- BigInteger
IntegerValueInt16 v -> int v -- short
IntegerValueInt32 v -> int v -- int
IntegerValueInt64 v -> integer v -- long
IntegerValueUint8 v -> int v -- byte
IntegerValueUint16 v -> Java.LiteralCharacter $ fromIntegral v -- char
where
integer = Java.LiteralInteger . Java.IntegerLiteral
int = integer . fromIntegral
LiteralString s -> javaString s
-- Note: we use Java object types everywhere, rather than primitive types, as the latter cannot be used
-- to build function types, parameterized types, etc.
encodeLiteralType :: LiteralType -> GraphFlow m Java.Type
encodeLiteralType lt = case lt of
LiteralTypeBoolean -> simple "Boolean"
LiteralTypeFloat ft -> case ft of
FloatTypeFloat32 -> simple "Float"
FloatTypeFloat64 -> simple "Double"
_ -> fail $ "unexpected float type: " ++ show ft
LiteralTypeInteger it -> case it of
IntegerTypeBigint -> pure $ javaRefType [] (Just $ javaPackageName ["java", "math"]) "BigInteger"
IntegerTypeInt16 -> simple "Short"
IntegerTypeInt32 -> simple "Integer"
IntegerTypeInt64 -> simple "Long"
IntegerTypeUint8 -> simple "Byte"
IntegerTypeUint16 -> simple "Character"
_ -> fail $ "unexpected integer type: " ++ show it
LiteralTypeString -> simple "String"
_ -> fail $ "unexpected literal type: " ++ show lt
where
simple n = pure $ javaRefType [] Nothing n
encodeTerm :: (Eq m, Ord m, Read m, Show m)
=> Aliases -> Maybe (Type m) -> Term m -> GraphFlow m Java.Expression
encodeTerm aliases mtype term = case term of
-- Note: we are currently only reading the type from the annotation, leaving any documentation etc. behind
TermAnnotated (Annotated term' ann) -> case mtype of
Just t -> encodeTerm aliases mtype term'
Nothing -> do
cx <- getState
mt <- annotationClassTypeOf (contextAnnotations cx) ann
encodeTerm aliases mt term'
TermApplication a -> case stripTerm fun of
TermFunction f -> case f of
FunctionPrimitive name -> forNamedFunction name args
FunctionElimination EliminationElement -> if L.length args > 0
then case stripTerm (L.head args) of
TermElement name -> do
forNamedFunction name (L.tail args)
_ -> fallback
else fallback
_ -> fallback
_ -> fallback
where
forNamedFunction name args = do
jargs <- CM.mapM encode args
let header = Java.MethodInvocation_HeaderSimple $ Java.MethodName $ elementJavaIdentifier aliases name
return $ javaMethodInvocationToJavaExpression $ Java.MethodInvocation header jargs
(fun, args) = uncurry [] term
where
uncurry args term = case term of
TermAnnotated (Annotated body _) -> uncurry args body
TermApplication (Application lhs rhs) -> uncurry (rhs:args) lhs
_ -> (term, args)
fallback = forApplication a
forApplication (Application lhs rhs) = do
cx <- getState
mt <- annotationClassTermType (contextAnnotations cx) lhs
t <- case mt of
Just t' -> pure t'
Nothing -> fail $ "expected a type annotation on function " ++ show lhs
(dom, cod) <- case stripType t of
TypeFunction (FunctionType dom cod) -> pure (dom, cod)
_ -> fail $ "expected a function type on function " ++ show lhs
case stripTerm lhs of
TermFunction f -> case f of
FunctionElimination e -> case e of
EliminationElement -> encodeTerm aliases Nothing rhs
_ -> do
jarg <- encode rhs
encodeElimination aliases (Just jarg) dom cod e
_ -> defaultExpression dom cod
_ -> defaultExpression dom cod
where
defaultExpression dom cod = do
-- Note: the domain type will not be used, so we just substitute the unit type
jfun <- encodeTerm aliases (Just $ Types.function dom cod) lhs
jarg <- encodeTerm aliases (Just dom) rhs
let prim = javaExpressionToJavaPrimary jfun
return $ javaMethodInvocationToJavaExpression $ methodInvocation (Just $ Right prim) (Java.Identifier "apply") [jarg]
TermElement name -> pure $ javaIdentifierToJavaExpression $ elementJavaIdentifier aliases name
TermFunction f -> case mtype of
Just t -> case stripType t of
TypeFunction (FunctionType dom cod) -> encodeFunction aliases dom cod f
_ -> unexpected "function type" $ t
Nothing -> failAsLiteral $ "unannotated function: " ++ show f
TermList els -> do
jels <- CM.mapM encode els
return $ javaMethodInvocationToJavaExpression $
methodInvocationStatic (Java.Identifier "java.util.Arrays") (Java.Identifier "asList") jels
TermLiteral l -> pure $ encodeLiteral l
-- TermMap (Map (Term m) (Term m))
TermNominal (Named name arg) -> do
jarg <- encode arg
return $ javaConstructorCall (javaConstructorName (nameToJavaName aliases name) Nothing) [jarg] Nothing
TermOptional mt -> case mt of
Nothing -> pure $ javaMethodInvocationToJavaExpression $
methodInvocationStatic (javaIdentifier "java.util.Optional") (Java.Identifier "empty") []
Just term1 -> do
expr <- encode term1
return $ javaMethodInvocationToJavaExpression $
methodInvocationStatic (javaIdentifier "java.util.Optional") (Java.Identifier "of") [expr]
TermRecord (Record name fields) -> do
fieldExprs <- CM.mapM encode (fieldTerm <$> fields)
let consId = nameToJavaName aliases name
return $ javaConstructorCall (javaConstructorName consId Nothing) fieldExprs Nothing
TermSet s -> do
jels <- CM.mapM encode $ S.toList s
let prim = javaMethodInvocationToJavaPrimary $
methodInvocationStatic (Java.Identifier "java.util.Stream") (Java.Identifier "of") jels
let coll = javaMethodInvocationToJavaExpression $
methodInvocationStatic (javaIdentifier "java.util.stream.Collectors") (Java.Identifier "toSet") []
return $ javaMethodInvocationToJavaExpression $
methodInvocation (Just $ Right prim) (Java.Identifier "collect") [coll]
TermUnion (Union name (Field (FieldName fname) v)) -> do
let (Java.Identifier typeId) = nameToJavaName aliases name
let consId = Java.Identifier $ typeId ++ "." ++ sanitizeJavaName (capitalize fname)
args <- if Terms.isUnit v
then return []
else do
ex <- encode v
return [ex]
return $ javaConstructorCall (javaConstructorName consId Nothing) args Nothing
TermVariable (Variable v) -> pure $ javaIdentifierToJavaExpression $ javaIdentifier v
_ -> failAsLiteral $ "Unimplemented term variant: " ++ show (termVariant term)
where
encode = encodeTerm aliases Nothing
failAsLiteral msg = pure $ encodeLiteral $ LiteralString msg
encodeType :: Show m => Aliases -> Type m -> GraphFlow m Java.Type
encodeType aliases t = case stripType t of
TypeApplication (ApplicationType lhs rhs) -> do
jlhs <- encode lhs
jrhs <- encode rhs >>= javaTypeToJavaReferenceType
addJavaTypeParameter jrhs jlhs
TypeElement et -> encode et -- Elements are simply unboxed
TypeFunction (FunctionType dom cod) -> do
jdom <- encode dom >>= javaTypeToJavaReferenceType
jcod <- encode cod >>= javaTypeToJavaReferenceType
return $ javaRefType [jdom, jcod] javaUtilFunctionPackageName "Function"
TypeLambda (LambdaType (VariableType v) body) -> do
jbody <- encode body
addJavaTypeParameter (javaTypeVariable v) jbody
TypeList et -> do
jet <- encode et
if listsAsArrays
then toJavaArrayType jet
else do
rt <- javaTypeToJavaReferenceType jet
return $ javaRefType [rt] javaUtilPackageName "List"
TypeLiteral lt -> encodeLiteralType lt
TypeMap (MapType kt vt) -> do
jkt <- encode kt >>= javaTypeToJavaReferenceType
jvt <- encode vt >>= javaTypeToJavaReferenceType
return $ javaRefType [jkt, jvt] javaUtilPackageName "Map"
TypeNominal name -> pure $ Java.TypeReference $ nameToJavaReferenceType aliases True name Nothing
TypeRecord (RowType _UnitType _ []) -> return $ javaRefType [] javaLangPackageName "Void"
TypeRecord (RowType name _ _) -> pure $ Java.TypeReference $ nameToJavaReferenceType aliases True name Nothing
TypeOptional ot -> do
jot <- encode ot >>= javaTypeToJavaReferenceType
return $ javaRefType [jot] javaUtilPackageName "Optional"
TypeSet st -> do
jst <- encode st >>= javaTypeToJavaReferenceType
return $ javaRefType [jst] javaUtilPackageName "Set"
TypeUnion (RowType name _ _) -> pure $ Java.TypeReference $ nameToJavaReferenceType aliases True name Nothing
TypeVariable (VariableType v) -> pure $ Java.TypeReference $ javaTypeVariable v
_ -> fail $ "can't encode unsupported type in Java: " ++ show t
where
encode = encodeType aliases
fieldTypeToFormalParam aliases (FieldType fname ft) = do
jt <- encodeType aliases ft
return $ javaTypeToJavaFormalParameter jt fname
getCodomain :: Show m => m -> GraphFlow m (Type m)
getCodomain ann = functionTypeCodomain <$> getFunctionType ann
getFunctionType :: Show m => m -> GraphFlow m (FunctionType m)
getFunctionType ann = do
cx <- getState
mt <- annotationClassTypeOf (contextAnnotations cx) ann
case mt of
Nothing -> fail "type annotation is required for function and elimination terms in Java"
Just t -> case t of
TypeFunction ft -> return ft
_ -> unexpected "function type" t
innerClassRef :: Aliases -> Name -> String -> Java.Identifier
innerClassRef aliases name local = Java.Identifier $ id ++ "." ++ local
where
Java.Identifier id = nameToJavaName aliases name
instanceName = "instance"
javaTypeParametersForType :: Type m -> [Java.TypeParameter]
javaTypeParametersForType typ = toParam <$> vars
where
toParam (VariableType v) = Java.TypeParameter [] (javaTypeIdentifier $ capitalize v) Nothing
-- vars = boundTypeVariables typ
vars = S.toList $ freeVariablesInType typ -- TODO: the fact that the variables are free is a bug, not a feature
partialVisitorName :: String
partialVisitorName = "PartialVisitor"
toClassDecl :: (Eq m, Ord m, Read m, Show m) => Bool -> Aliases -> [Java.TypeParameter]
-> Name -> Type m -> GraphFlow m Java.ClassDeclaration
toClassDecl isInner aliases tparams elName t = case stripType t of
TypeRecord rt -> declarationForRecordType isInner aliases tparams elName $ rowTypeFields rt
TypeUnion rt -> declarationForUnionType aliases tparams elName $ rowTypeFields rt
TypeLambda ut -> declarationForLambdaType aliases tparams elName ut
-- Other types are not supported as class declarations, so we wrap them as record types.
_ -> wrap t -- TODO: wrap and unwrap the corresponding terms as record terms.
where
wrap t' = declarationForRecordType isInner aliases tparams elName [Types.field valueFieldName t']
toDataDeclaration :: Aliases -> (a, TypedTerm m) -> GraphFlow m a
toDataDeclaration aliases (el, TypedTerm typ term) = do
fail "not implemented" -- TODO
typeNameDecl :: (Ord m, Read m, Show m) => Aliases -> Name -> GraphFlow m Java.ClassBodyDeclarationWithComments
typeNameDecl aliases name = do
jt <- encodeType aliases $ Types.nominal _Name
arg <- encodeTerm aliases Nothing $ Terms.string $ unName name
let init = Java.VariableInitializerExpression $ javaConstructorCall (javaConstructorName nameName Nothing) [arg] Nothing
let var = javaVariableDeclarator (Java.Identifier "NAME") (Just init)
return $ noComment $ javaMemberField mods jt var
where
mods = [Java.FieldModifierPublic, Java.FieldModifierStatic, Java.FieldModifierFinal]
nameName = nameToJavaName aliases _Name
valueFieldName :: String
valueFieldName = "value"
visitMethodName :: String
visitMethodName = "visit"
visitorName :: String
visitorName = "Visitor"