Created a 'ToTemplate' module to avoid putting too much stuff into Concretize.

This commit is contained in:
Erik Svedäng 2018-08-30 13:07:14 +02:00
parent b0998a6e12
commit 14f03252a8
7 changed files with 611 additions and 621 deletions

View File

@ -24,6 +24,7 @@ library
Deftype,
Commands,
Template,
ToTemplate,
Types,
Util,
Eval,

View File

@ -9,6 +9,7 @@ import Types
import Obj
import Parsing
import Template
import ToTemplate
import Polymorphism
import Concretize
import Lookup

View File

@ -1,5 +1,3 @@
{-# LANGUAGE MultiWayIf #-}
module Concretize where
import Control.Monad.State
@ -9,8 +7,6 @@ import qualified Data.Set as Set
import Data.Set ((\\))
import Data.List (foldl')
import Debug.Trace
import qualified Text.Parsec as Parsec
import Text.Parsec ((<|>))
import Obj
import Constraints
@ -21,7 +17,7 @@ import AssignTypes
import Polymorphism
import InitialTypes
import Lookup
import Parsing
import ToTemplate
--import Template
--import ArrayTemplates
@ -486,6 +482,7 @@ data FunctionFinderResult = FunctionFound String
| FunctionIgnored
deriving (Show)
-- | TODO: COMMENT THIS
getConcretizedPath :: XObj -> Ty -> SymPath
getConcretizedPath single functionType =
let Just t' = ty single
@ -936,375 +933,10 @@ isGlobalFunc xobj =
-- | The following functions will generate deleters and copy:ing methods for structs, they are shared with the Deftype module
data AllocationMode = StackAlloc | HeapAlloc
{-# ANN module "HLint: ignore Reduce duplication" #-}
-- | This function creates a "Type Module" with the same name as the type being defined.
-- A type module provides a namespace for all the functions that area automatically
-- generated by a deftype.
moduleForDeftype :: TypeEnv -> Env -> [String] -> String -> [Ty] -> [XObj] -> Maybe Info -> Maybe Env -> Either String (String, XObj, [XObj])
moduleForDeftype typeEnv env pathStrings typeName typeVariables rest i existingEnv =
let typeModuleName = typeName
typeModuleEnv = case existingEnv of
Just env -> env
Nothing -> Env (Map.fromList []) (Just env) (Just typeModuleName) [] ExternalEnv 0
-- The variable 'insidePath' is the path used for all member functions inside the 'typeModule'.
-- For example (module Vec2 [x Float]) creates bindings like Vec2.create, Vec2.x, etc.
insidePath = pathStrings ++ [typeModuleName]
in do validateMemberCases typeEnv typeVariables rest
let structTy = StructTy typeName typeVariables
(okMembers, membersDeps) <- templatesForMembers typeEnv env insidePath structTy rest
okInit <- binderForInit insidePath structTy rest
--okNew <- templateForNew insidePath structTy rest
(okStr, strDeps) <- binderForStrOrPrn typeEnv env insidePath structTy rest "str"
(okPrn, _) <- binderForStrOrPrn typeEnv env insidePath structTy rest "prn"
(okDelete, deleteDeps) <- binderForDelete typeEnv env insidePath structTy rest
(okCopy, copyDeps) <- binderForCopy typeEnv env insidePath structTy rest
let funcs = okInit : okStr : okPrn : okDelete : okCopy : okMembers
moduleEnvWithBindings = addListOfBindings typeModuleEnv funcs
typeModuleXObj = XObj (Mod moduleEnvWithBindings) i (Just ModuleTy)
deps = deleteDeps ++ membersDeps ++ copyDeps ++ strDeps
return (typeModuleName, typeModuleXObj, deps)
-- | Will generate getters/setters/updaters when registering EXTERNAL types.
-- | i.e. (register-type VRUnicornData [hp Int, magic Float])
-- | TODO: Remove duplication shared by moduleForDeftype-function.
bindingsForRegisteredType :: TypeEnv -> Env -> [String] -> String -> [XObj] -> Maybe Info -> Maybe Env -> Either String (String, XObj, [XObj])
bindingsForRegisteredType typeEnv env pathStrings typeName rest i existingEnv =
let typeModuleName = typeName
typeModuleEnv = case existingEnv of
Just env -> env
Nothing -> Env (Map.fromList []) (Just env) (Just typeModuleName) [] ExternalEnv 0
insidePath = pathStrings ++ [typeModuleName]
in do validateMemberCases typeEnv [] rest
let structTy = StructTy typeName []
(binders, deps) <- templatesForMembers typeEnv env insidePath structTy rest
okInit <- binderForInit insidePath structTy rest
--okNew <- templateForNew insidePath structTy rest
(okStr, strDeps) <- binderForStrOrPrn typeEnv env insidePath structTy rest "str"
(okPrn, _) <- binderForStrOrPrn typeEnv env insidePath structTy rest "prn"
let moduleEnvWithBindings = addListOfBindings typeModuleEnv (okInit : okStr : okPrn : binders)
typeModuleXObj = XObj (Mod moduleEnvWithBindings) i (Just ModuleTy)
return (typeModuleName, typeModuleXObj, deps ++ strDeps)
-- | Generate all the templates for ALL the member variables in a deftype declaration.
templatesForMembers :: TypeEnv -> Env -> [String] -> Ty -> [XObj] -> Either String ([(String, Binder)], [XObj])
templatesForMembers typeEnv env insidePath structTy [XObj (Arr membersXobjs) _ _] =
let bindersAndDeps = concatMap (templatesForSingleMember typeEnv env insidePath structTy) (pairwise membersXobjs)
in Right (map fst bindersAndDeps, concatMap snd bindersAndDeps)
templatesForMembers _ _ _ _ _ = Left "Can't create member functions for type with more than one case (yet)."
-- | Generate the templates for a single member in a deftype declaration.
templatesForSingleMember :: TypeEnv -> Env -> [String] -> Ty -> (XObj, XObj) -> [((String, Binder), [XObj])]
templatesForSingleMember typeEnv env insidePath p@(StructTy typeName _) (nameXObj, typeXObj) =
let Just t = xobjToTy typeXObj
memberName = getName nameXObj
in [instanceBinderWithDeps (SymPath insidePath memberName) (FuncTy [RefTy p] (RefTy t)) (templateGetter (mangle memberName) t)
, if isTypeGeneric t
then (templateGenericSetter insidePath p t memberName, [])
else instanceBinderWithDeps (SymPath insidePath ("set-" ++ memberName)) (FuncTy [p, t] p) (templateSetter typeEnv env (mangle memberName) t)
,instanceBinderWithDeps (SymPath insidePath ("set-" ++ memberName ++ "!")) (FuncTy [RefTy (p), t] UnitTy) (templateMutatingSetter typeEnv env (mangle memberName) t)
,instanceBinderWithDeps (SymPath insidePath ("update-" ++ memberName))
(FuncTy [p, FuncTy [t] t] p)
(templateUpdater (mangle memberName))]
-- | The template for getters of a deftype.
templateGetter :: String -> Ty -> Template
templateGetter member memberTy =
Template
(FuncTy [RefTy (VarTy "p")] (VarTy "t"))
(const (toTemplate "$t $NAME($(Ref p) p)"))
(const $
let fixForVoidStarMembers =
if isFunctionType memberTy && (not (isTypeGeneric memberTy))
then "(" ++ tyToCLambdaFix (RefTy memberTy) ++ ")"
else ""
in (toTemplate ("$DECL { return " ++ fixForVoidStarMembers ++ "(&(p->" ++ member ++ ")); }\n")))
(const [])
-- | The template for setters of a concrete deftype.
templateSetter :: TypeEnv -> Env -> String -> Ty -> Template
templateSetter typeEnv env memberName memberTy =
let callToDelete = memberDeletion typeEnv env (memberName, memberTy)
in
Template
(FuncTy [VarTy "p", VarTy "t"] (VarTy "p"))
(const (toTemplate "$p $NAME($p p, $t newValue)"))
(const (toTemplate (unlines ["$DECL {"
,callToDelete
," p." ++ memberName ++ " = newValue;"
," return p;"
,"}\n"])))
(\_ -> if | isManaged typeEnv memberTy -> depsOfPolymorphicFunction typeEnv env [] "delete" (typesDeleterFunctionType memberTy)
| isFunctionType memberTy -> [defineFunctionTypeAlias memberTy]
| otherwise -> [])
-- | The template for setters of a generic deftype.
templateGenericSetter :: [String] -> Ty -> Ty -> String -> (String, Binder)
templateGenericSetter pathStrings originalStructTy memberTy memberName =
defineTypeParameterizedTemplate templateCreator path (FuncTy [originalStructTy, memberTy] originalStructTy)
where path = SymPath pathStrings ("set-" ++ memberName)
t = (FuncTy [VarTy "p", VarTy "t"] (VarTy "p"))
templateCreator = TemplateCreator $
\typeEnv env ->
Template
t
(const (toTemplate "$p $NAME($p p, $t newValue)"))
(\(FuncTy [_, memberTy] _) ->
(let callToDelete = memberDeletion typeEnv env (memberName, memberTy)
in (toTemplate (unlines ["$DECL {"
,callToDelete
," p." ++ memberName ++ " = newValue;"
," return p;"
,"}\n"]))))
(\(FuncTy [_, memberTy] _) ->
if isManaged typeEnv memberTy
then depsOfPolymorphicFunction typeEnv env [] "delete" (typesDeleterFunctionType memberTy)
else [])
-- | The template for mutating setters of a deftype.
templateMutatingSetter :: TypeEnv -> Env -> String -> Ty -> Template
templateMutatingSetter typeEnv env memberName memberTy =
Template
(FuncTy [RefTy (VarTy "p"), VarTy "t"] UnitTy)
(const (toTemplate "void $NAME($p* pRef, $t newValue)"))
(const (toTemplate (unlines ["$DECL {"
," pRef->" ++ memberName ++ " = newValue;"
,"}\n"])))
(const [])
-- | The template for updater functions of a deftype.
-- | (allows changing a variable by passing an transformation function).
templateUpdater :: String -> Template
templateUpdater member =
Template
(FuncTy [VarTy "p", FuncTy [VarTy "t"] (VarTy "t")] (VarTy "p"))
(const (toTemplate "$p $NAME($p p, Lambda updater)")) -- "Lambda" used to be: $(Fn [t] t)
(const (toTemplate (unlines ["$DECL {"
," p." ++ member ++ " = " ++ (templateCodeForCallingLambda "updater" (FuncTy [VarTy "t"] (VarTy "t")) ["p." ++ member]) ++ ";"
," return p;"
,"}\n"])))
(\(FuncTy [_, t@(FuncTy fArgTys fRetTy)] _) ->
if isTypeGeneric fRetTy
then []
else [defineFunctionTypeAlias t, defineFunctionTypeAlias (FuncTy (lambdaEnvTy : fArgTys) fRetTy)])
-- | Helper function to create the binder for the 'init' template.
binderForInit :: [String] -> Ty -> [XObj] -> Either String (String, Binder)
binderForInit insidePath structTy@(StructTy typeName _) [XObj (Arr membersXObjs) _ _] =
if isTypeGeneric structTy
then Right (genericInit StackAlloc insidePath structTy membersXObjs)
else Right $ instanceBinder (SymPath insidePath "init")
(FuncTy (initArgListTypes membersXObjs) structTy)
(concreteInit StackAlloc structTy membersXObjs)
-- | Generate a list of types from a deftype declaration.
initArgListTypes :: [XObj] -> [Ty]
initArgListTypes xobjs = map (\(_, x) -> fromJust (xobjToTy x)) (pairwise xobjs)
-- | The template for the 'init' and 'new' functions for a concrete deftype.
concreteInit :: AllocationMode -> Ty -> [XObj] -> Template
concreteInit allocationMode originalStructTy@(StructTy typeName typeVariables) membersXObjs =
Template
(FuncTy (map snd (memberXObjsToPairs membersXObjs)) (VarTy "p"))
(\(FuncTy _ concreteStructTy) ->
let mappings = unifySignatures originalStructTy concreteStructTy
correctedMembers = replaceGenericTypeSymbolsOnMembers mappings membersXObjs
memberPairs = memberXObjsToPairs correctedMembers
in (toTemplate $ "$p $NAME(" ++ joinWithComma (map memberArg memberPairs) ++ ")"))
(const (tokensForInit allocationMode typeName membersXObjs))
(\(FuncTy _ _) -> [])
-- | The template for the 'init' and 'new' functions for a generic deftype.
genericInit :: AllocationMode -> [String] -> Ty -> [XObj] -> (String, Binder)
genericInit allocationMode pathStrings originalStructTy@(StructTy typeName _) membersXObjs =
defineTypeParameterizedTemplate templateCreator path t
where path = SymPath pathStrings "init"
t = (FuncTy (map snd (memberXObjsToPairs membersXObjs)) originalStructTy)
templateCreator = TemplateCreator $
\typeEnv env ->
Template
(FuncTy (map snd (memberXObjsToPairs membersXObjs)) (VarTy "p"))
(\(FuncTy _ concreteStructTy) ->
let mappings = unifySignatures originalStructTy concreteStructTy
correctedMembers = replaceGenericTypeSymbolsOnMembers mappings membersXObjs
memberPairs = memberXObjsToPairs correctedMembers
in (toTemplate $ "$p $NAME(" ++ joinWithComma (map memberArg memberPairs) ++ ")"))
(const (tokensForInit allocationMode typeName membersXObjs))
(\(FuncTy _ concreteStructTy) ->
case concretizeType typeEnv concreteStructTy of
Left err -> error (err ++ ". This error should not crash the compiler - change return type to Either here.")
Right ok -> ok
)
tokensForInit :: AllocationMode -> String -> [XObj] -> [Token]
tokensForInit allocationMode typeName membersXObjs =
toTemplate $ unlines [ "$DECL {"
, case allocationMode of
StackAlloc -> " $p instance;"
HeapAlloc -> " $p instance = CARP_MALLOC(sizeof(" ++ typeName ++ "));"
, joinWith "\n" (map (memberAssignment allocationMode) (memberXObjsToPairs membersXObjs))
, " return instance;"
, "}"]
-- | Creates the C code for an arg to the init function.
-- | i.e. "(deftype A [x Int])" will generate "int x" which
-- | will be used in the init function like this: "A_init(int x)"
memberArg :: (String, Ty) -> String
memberArg (memberName, memberTy) =
tyToCLambdaFix (templitizeTy memberTy) ++ " " ++ memberName
-- | If the type is just a type variable; create a template type variable by appending $ in front of it's name
templitizeTy :: Ty -> Ty
templitizeTy (VarTy vt) = VarTy ("$" ++ vt)
templitizeTy (FuncTy argTys retTy) = FuncTy (map templitizeTy argTys) (templitizeTy retTy)
templitizeTy (StructTy name tys) = StructTy name (map templitizeTy tys)
templitizeTy (RefTy t) = RefTy (templitizeTy t)
templitizeTy (PointerTy t) = PointerTy (templitizeTy t)
templitizeTy t = t
-- | Helper function to create the binder for the 'str' template.
binderForStrOrPrn :: TypeEnv -> Env -> [String] -> Ty -> [XObj] -> String -> Either String ((String, Binder), [XObj])
binderForStrOrPrn typeEnv env insidePath structTy@(StructTy typeName _) [XObj (Arr membersXObjs) _ _] strOrPrn =
if isTypeGeneric structTy
then Right (genericStr insidePath structTy membersXObjs strOrPrn, [])
else Right (instanceBinderWithDeps (SymPath insidePath strOrPrn)
(FuncTy [RefTy structTy] StringTy)
(concreteStr typeEnv env structTy (memberXObjsToPairs membersXObjs) strOrPrn))
-- | The template for the 'str' function for a concrete deftype.
concreteStr :: TypeEnv -> Env -> Ty -> [(String, Ty)] -> String -> Template
concreteStr typeEnv env concreteStructTy@(StructTy typeName _) memberPairs strOrPrn =
Template
(FuncTy [RefTy concreteStructTy] StringTy)
(\(FuncTy [RefTy structTy] StringTy) -> (toTemplate $ "String $NAME(" ++ tyToCLambdaFix structTy ++ " *p)"))
(\(FuncTy [RefTy structTy@(StructTy _ concreteMemberTys)] StringTy) ->
(tokensForStr typeEnv env typeName memberPairs concreteStructTy))
(\(ft@(FuncTy [RefTy structTy@(StructTy _ concreteMemberTys)] StringTy)) ->
concatMap (depsOfPolymorphicFunction typeEnv env [] "prn" . typesStrFunctionType typeEnv)
(filter (\t -> (not . isExternalType typeEnv) t && (not . isFullyGenericType) t)
(map snd memberPairs)))
-- | The template for the 'str' function for a generic deftype.
genericStr :: [String] -> Ty -> [XObj] -> String -> (String, Binder)
genericStr pathStrings originalStructTy@(StructTy typeName varTys) membersXObjs strOrPrn =
defineTypeParameterizedTemplate templateCreator path t
where path = SymPath pathStrings strOrPrn
t = FuncTy [(RefTy originalStructTy)] StringTy
members = memberXObjsToPairs membersXObjs
templateCreator = TemplateCreator $
\typeEnv env ->
Template
t
(\(FuncTy [RefTy concreteStructTy] StringTy) ->
(toTemplate $ "String $NAME(" ++ tyToCLambdaFix concreteStructTy ++ " *p)"))
(\(FuncTy [RefTy concreteStructTy@(StructTy _ concreteMemberTys)] StringTy) ->
let mappings = unifySignatures originalStructTy concreteStructTy
correctedMembers = replaceGenericTypeSymbolsOnMembers mappings membersXObjs
memberPairs = memberXObjsToPairs correctedMembers
in (tokensForStr typeEnv env typeName memberPairs concreteStructTy))
(\(ft@(FuncTy [RefTy concreteStructTy@(StructTy _ concreteMemberTys)] StringTy)) ->
let mappings = unifySignatures originalStructTy concreteStructTy
correctedMembers = replaceGenericTypeSymbolsOnMembers mappings membersXObjs
memberPairs = memberXObjsToPairs correctedMembers
in concatMap (depsOfPolymorphicFunction typeEnv env [] "prn" . typesStrFunctionType typeEnv)
(filter (\t -> (not . isExternalType typeEnv) t && (not . isFullyGenericType) t)
(map snd memberPairs))
++
(if isTypeGeneric concreteStructTy then [] else [defineFunctionTypeAlias ft]))
tokensForStr :: TypeEnv -> Env -> String -> [(String, Ty)] -> Ty -> [Token]
tokensForStr typeEnv env typeName memberPairs concreteStructTy =
(toTemplate $ unlines [ "$DECL {"
, " // convert members to String here:"
, " String temp = NULL;"
, " int tempsize = 0;"
, " (void)tempsize; // that way we remove the occasional unused warning "
, calculateStructStrSize typeEnv env memberPairs concreteStructTy
, " String buffer = CARP_MALLOC(size);"
, " String bufferPtr = buffer;"
, ""
, " snprintf(bufferPtr, size, \"(%s \", \"" ++ typeName ++ "\");"
, " bufferPtr += strlen(\"" ++ typeName ++ "\") + 2;\n"
, joinWith "\n" (map (memberPrn typeEnv env) memberPairs)
, " bufferPtr--;"
, " snprintf(bufferPtr, size, \")\");"
, " return buffer;"
, "}"])
-- | Figure out how big the string needed for the string representation of the struct has to be.
calculateStructStrSize :: TypeEnv -> Env -> [(String, Ty)] -> Ty -> String
calculateStructStrSize typeEnv env members structTy@(StructTy name _) =
" int size = snprintf(NULL, 0, \"(%s )\", \"" ++ name ++ "\");\n" ++
unlines (map memberPrnSize members)
where memberPrnSize (memberName, memberTy) =
let refOrNotRefType = if isManaged typeEnv memberTy then RefTy memberTy else memberTy
maybeTakeAddress = if isManaged typeEnv memberTy then "&" else ""
strFuncType = FuncTy [refOrNotRefType] StringTy
in case nameOfPolymorphicFunction typeEnv env strFuncType "prn" of
Just strFunctionPath ->
unlines [" temp = " ++ pathToC strFunctionPath ++ "(" ++ maybeTakeAddress ++ "p->" ++ memberName ++ "); "
, " size += snprintf(NULL, 0, \"%s \", temp);"
, " if(temp) { CARP_FREE(temp); temp = NULL; }"
]
Nothing ->
if isExternalType typeEnv memberTy
then unlines [ " size += snprintf(NULL, 0, \"%p \", p->" ++ memberName ++ ");"
, " if(temp) { CARP_FREE(temp); temp = NULL; }"
]
else " // Failed to find str function for " ++ memberName ++ " : " ++ show memberTy ++ "\n"
-- | Generate C code for converting a member variable to a string and appending it to a buffer.
memberPrn :: TypeEnv -> Env -> (String, Ty) -> String
memberPrn typeEnv env (memberName, memberTy) =
let refOrNotRefType = if isManaged typeEnv memberTy then RefTy memberTy else memberTy
maybeTakeAddress = if isManaged typeEnv memberTy then "&" else ""
strFuncType = FuncTy [refOrNotRefType] StringTy
in case nameOfPolymorphicFunction typeEnv env strFuncType "prn" of
Just strFunctionPath ->
unlines [" temp = " ++ pathToC strFunctionPath ++ "(" ++ maybeTakeAddress ++ "p->" ++ memberName ++ ");"
, " snprintf(bufferPtr, size, \"%s \", temp);"
, " bufferPtr += strlen(temp) + 1;"
, " if(temp) { CARP_FREE(temp); temp = NULL; }"
]
Nothing ->
if isExternalType typeEnv memberTy
then unlines [ " tempsize = snprintf(NULL, 0, \"%p\", p->" ++ memberName ++ ");"
, " temp = malloc(tempsize);"
, " snprintf(temp, tempsize, \"%p\", p->" ++ memberName ++ ");"
, " snprintf(bufferPtr, size, \"%s \", temp);"
, " bufferPtr += strlen(temp) + 1;"
, " if(temp) { CARP_FREE(temp); temp = NULL; }"
]
else " // Failed to find str function for " ++ memberName ++ " : " ++ show memberTy ++ "\n"
-- | Generate C code for assigning to a member variable.
-- | Needs to know if the instance is a pointer or stack variable.
memberAssignment :: AllocationMode -> (String, Ty) -> String
memberAssignment allocationMode (memberName, _) = " instance" ++ sep ++ memberName ++ " = " ++ memberName ++ ";"
where sep = case allocationMode of
StackAlloc -> "."
HeapAlloc -> "->"
-- | Helper function to create the binder for the 'delete' template.
binderForDelete :: TypeEnv -> Env -> [String] -> Ty -> [XObj] -> Either String ((String, Binder), [XObj])
binderForDelete typeEnv env insidePath structTy@(StructTy typeName _) [XObj (Arr membersXObjs) _ _] =
if isTypeGeneric structTy
then Right (genericDelete insidePath structTy membersXObjs, [])
else Right (instanceBinderWithDeps (SymPath insidePath "delete")
(FuncTy [structTy] UnitTy)
(concreteDelete typeEnv env (memberXObjsToPairs membersXObjs)))
-- | The template for the 'delete' function of a concrete deftype.
concreteDelete :: TypeEnv -> Env -> [(String, Ty)] -> Template
concreteDelete typeEnv env members =
@ -1317,33 +949,6 @@ concreteDelete typeEnv env members =
(\_ -> concatMap (depsOfPolymorphicFunction typeEnv env [] "delete" . typesDeleterFunctionType)
(filter (isManaged typeEnv) (map snd members)))
-- | The template for the 'delete' function of a generic deftype.
genericDelete :: [String] -> Ty -> [XObj] -> (String, Binder)
genericDelete pathStrings originalStructTy membersXObjs =
defineTypeParameterizedTemplate templateCreator path (FuncTy [originalStructTy] UnitTy)
where path = SymPath pathStrings "delete"
t = (FuncTy [VarTy "p"] UnitTy)
templateCreator = TemplateCreator $
\typeEnv env ->
Template
t
(const (toTemplate "void $NAME($p p)"))
(\(FuncTy [concreteStructTy] UnitTy) ->
let mappings = unifySignatures originalStructTy concreteStructTy
correctedMembers = replaceGenericTypeSymbolsOnMembers mappings membersXObjs
memberPairs = memberXObjsToPairs correctedMembers
in (toTemplate $ unlines [ "$DECL {"
, joinWith "\n" (map (memberDeletion typeEnv env) memberPairs)
, "}"]))
(\(FuncTy [concreteStructTy] UnitTy) ->
let mappings = unifySignatures originalStructTy concreteStructTy
correctedMembers = replaceGenericTypeSymbolsOnMembers mappings membersXObjs
memberPairs = memberXObjsToPairs correctedMembers
in if isTypeGeneric concreteStructTy
then []
else concatMap (depsOfPolymorphicFunction typeEnv env [] "delete" . typesDeleterFunctionType)
(filter (isManaged typeEnv) (map snd memberPairs)))
-- | Generate the C code for deleting a single member of the deftype.
-- | TODO: Should return an Either since this can fail!
memberDeletion :: TypeEnv -> Env -> (String, Ty) -> String
@ -1353,17 +958,6 @@ memberDeletion typeEnv env (memberName, memberType) =
FunctionNotFound msg -> error msg
FunctionIgnored -> " /* Ignore non-managed member '" ++ memberName ++ "' */"
-- | Helper function to create the binder for the 'copy' template.
binderForCopy :: TypeEnv -> Env -> [String] -> Ty -> [XObj] -> Either String ((String, Binder), [XObj])
binderForCopy typeEnv env insidePath structTy@(StructTy typeName _) [XObj (Arr membersXObjs) _ _] =
if isTypeGeneric structTy
then Right (genericCopy insidePath structTy membersXObjs, [])
else Right (instanceBinderWithDeps (SymPath insidePath "copy")
(FuncTy [RefTy structTy] structTy)
(concreteCopy typeEnv env (memberXObjsToPairs membersXObjs)))
-- | The template for the 'copy' function of a concrete deftype.
concreteCopy :: TypeEnv -> Env -> [(String, Ty)] -> Template
concreteCopy typeEnv env memberPairs =
@ -1374,31 +968,6 @@ concreteCopy typeEnv env memberPairs =
(\_ -> concatMap (depsOfPolymorphicFunction typeEnv env [] "copy" . typesCopyFunctionType)
(filter (isManaged typeEnv) (map snd memberPairs)))
-- | The template for the 'copy' function of a generic deftype.
genericCopy :: [String] -> Ty -> [XObj] -> (String, Binder)
genericCopy pathStrings originalStructTy membersXObjs =
defineTypeParameterizedTemplate templateCreator path (FuncTy [RefTy originalStructTy] originalStructTy)
where path = SymPath pathStrings "copy"
t = (FuncTy [RefTy (VarTy "p")] (VarTy "p"))
templateCreator = TemplateCreator $
\typeEnv env ->
Template
t
(const (toTemplate "$p $NAME($p* pRef)"))
(\(FuncTy [RefTy concreteStructTy] _) ->
let mappings = unifySignatures originalStructTy concreteStructTy
correctedMembers = replaceGenericTypeSymbolsOnMembers mappings membersXObjs
memberPairs = memberXObjsToPairs correctedMembers
in (tokensForCopy typeEnv env memberPairs))
(\(FuncTy [RefTy concreteStructTy] _) ->
let mappings = unifySignatures originalStructTy concreteStructTy
correctedMembers = replaceGenericTypeSymbolsOnMembers mappings membersXObjs
memberPairs = memberXObjsToPairs correctedMembers
in if isTypeGeneric concreteStructTy
then []
else concatMap (depsOfPolymorphicFunction typeEnv env [] "copy" . typesCopyFunctionType)
(filter (isManaged typeEnv) (map snd memberPairs)))
tokensForCopy :: TypeEnv -> Env -> [(String, Ty)] -> [Token]
tokensForCopy typeEnv env memberPairs=
(toTemplate $ unlines [ "$DECL {"
@ -1416,173 +985,3 @@ memberCopy typeEnv env (memberName, memberType) =
" copy." ++ memberName ++ " = " ++ functionFullName ++ "(&(pRef->" ++ memberName ++ "));"
FunctionNotFound msg -> error msg
FunctionIgnored -> " /* Ignore non-managed member '" ++ memberName ++ "' */"
-- | Create a binding pair used for adding a template instantiation to an environment.
instanceBinder :: SymPath -> Ty -> Template -> (String, Binder)
instanceBinder path@(SymPath _ name) actualType template =
let (x, _) = instantiateTemplate path actualType template
in (name, Binder emptyMeta x)
-- | Create a binding pair and don't discard the dependencies
instanceBinderWithDeps :: SymPath -> Ty -> Template -> ((String, Binder), [XObj])
instanceBinderWithDeps path@(SymPath _ name) actualType template =
let (x, deps) = instantiateTemplate path actualType template
in ((name, Binder emptyMeta x), deps)
-- | Templates are instructions for the compiler to generate some C-code
-- | based on some template and the names and types to fill into the template.
-- | Templates are generic and need to be given an explicit type to generate the
-- | correct code.
-- | Example:
-- | template1 : ((Array T) -> Int) = "int length__T(<T> xs) { return xs->len; }"
-- | Given the type ((Array Float) -> Int) the following code is produced:
-- | "int length__Float(Array__Float xs) { return xs->len; }"
-- | Create a binding pair used for adding a template definition to an environment.
defineTemplate :: SymPath -> Ty -> [Token] -> [Token] -> (Ty -> [XObj]) -> (String, Binder)
defineTemplate path t declaration definition depsFunc =
let (SymPath _ name) = path
template = Template t (const declaration) (const definition) depsFunc
i = Info 0 0 (show path ++ ".template") Set.empty 0
defLst = [XObj (Deftemplate (TemplateCreator (\_ _ -> template))) Nothing Nothing, XObj (Sym path Symbol) Nothing Nothing]
in (name, Binder emptyMeta (XObj (Lst defLst) (Just i) (Just t)))
-- | The more advanced version of a template, where the code can vary depending on the type.
defineTypeParameterizedTemplate :: TemplateCreator -> SymPath -> Ty -> (String, Binder)
defineTypeParameterizedTemplate templateCreator path t =
let (SymPath _ name) = path
i = Info 0 0 (show path ++ ".parameterizedTemplate") Set.empty 0
defLst = [XObj (Deftemplate templateCreator) Nothing Nothing, XObj (Sym path Symbol) Nothing Nothing]
in (name, Binder emptyMeta (XObj (Lst defLst) (Just i) (Just t)))
-- | Concretizes the types used in @token
-- @cName is the name of the definition, i.e. the "foo" in "void foo() { ... }"
concretizeTypesInToken :: TypeMappings -> String -> [Token] -> Token -> [Token]
concretizeTypesInToken mappings cName decl token =
case token of
TokDecl -> concatMap (concretizeTypesInToken mappings cName (error "Nope.")) decl
TokName -> [TokC cName]
TokTy t mode -> [TokTy (replaceTyVars mappings t) mode]
_ -> [token]
-- | High-level helper function for creating templates from strings of C code.
toTemplate :: String -> [Token]
toTemplate text = case Parsec.runParser templateSyntax 0 "(template)" text of
Right ok -> ok
Left err -> error (show err)
where
templateSyntax :: Parsec.Parsec String Int [Token]
templateSyntax = Parsec.many parseTok
parseTok = Parsec.try parseTokDecl <|> --- $DECL
Parsec.try parseTokName <|> --- $NAME
Parsec.try parseTokTyGrouped <|> --- i.e. $(Fn [Int] t)
Parsec.try parseTokTyRawGrouped <|>
Parsec.try parseTokTy <|> --- i.e. $t
parseTokC --- Anything else...
parseTokDecl :: Parsec.Parsec String Int Token
parseTokDecl = do _ <- Parsec.string "$DECL"
return TokDecl
parseTokName :: Parsec.Parsec String Int Token
parseTokName = do _ <- Parsec.string "$NAME"
return TokName
parseTokC :: Parsec.Parsec String Int Token
parseTokC = do s <- Parsec.many1 validInSymbol
return (TokC s)
where validInSymbol = Parsec.choice [Parsec.letter, Parsec.digit, Parsec.oneOf validCharactersInTemplate]
validCharactersInTemplate = " ><{}()[]|;:.,_-+*#/'^!?€%&=@\"\n\t"
parseTokTy :: Parsec.Parsec String Int Token
parseTokTy = do _ <- Parsec.char '$'
s <- Parsec.many1 Parsec.letter
return (toTokTy Normal s)
parseTokTyGrouped :: Parsec.Parsec String Int Token
parseTokTyGrouped = do _ <- Parsec.char '$'
_ <- Parsec.char '('
Parsec.putState 1 -- One paren to close.
s <- fmap ('(' :) (Parsec.many parseCharBalanced)
-- Note: The closing paren is read by parseCharBalanced.
return (toTokTy Normal s)
parseTokTyRawGrouped :: Parsec.Parsec String Int Token
parseTokTyRawGrouped = do _ <- Parsec.char '§'
_ <- Parsec.char '('
Parsec.putState 1 -- One paren to close.
s <- fmap ('(' :) (Parsec.many parseCharBalanced)
-- Note: The closing paren is read by parseCharBalanced.
return (toTokTy Raw s)
parseCharBalanced :: Parsec.Parsec String Int Char
parseCharBalanced = do balanceState <- Parsec.getState
if balanceState > 0
then Parsec.try openParen <|>
Parsec.try closeParen <|>
Parsec.anyChar
else Parsec.char '\0' -- Should always fail which will end the string.
openParen :: Parsec.Parsec String Int Char
openParen = do _ <- Parsec.char '('
Parsec.modifyState (+1)
return '('
closeParen :: Parsec.Parsec String Int Char
closeParen = do _ <- Parsec.char ')'
Parsec.modifyState (\x -> x - 1)
return ')'
-- | Converts a string containing a type to a template token ('TokTy').
-- | i.e. the string "(Array Int)" becomes (TokTy (StructTy "Array" IntTy)).
toTokTy :: TokTyMode -> String -> Token
toTokTy mode s =
case parse s "" of
Left err -> error (show err)
Right [] -> error ("toTokTy got [] when parsing: '" ++ s ++ "'")
Right [xobj] -> case xobjToTy xobj of
Just ok -> TokTy ok mode
Nothing -> error ("toTokTy failed to convert this s-expression to a type: " ++ pretty xobj)
Right xobjs -> error ("toTokTy parsed too many s-expressions: " ++ joinWithSpace (map pretty xobjs))
-- | The code needed to correctly call a lambda from C.
templateCodeForCallingLambda :: String -> Ty -> [String] -> String
templateCodeForCallingLambda functionName t args =
let FuncTy argTys retTy = t
castToFnWithEnv = tyToCast (FuncTy (lambdaEnvTy : argTys) retTy)
castToFn = tyToCast t
in
functionName ++ ".env ? " ++
"((" ++ castToFnWithEnv ++ ")" ++ functionName ++ ".callback)(" ++ functionName ++ ".env" ++ (if null args then "" else ", ") ++ joinWithComma args ++ ")" ++
" : " ++
"((" ++ castToFn ++ ")" ++ functionName ++ ".callback)(" ++ joinWithComma args ++ ")"
-- | Must cast a lambda:s .callback member to the correct type to be able to call it.
tyToCast :: Ty -> String
tyToCast t =
let FuncTy argTys retTy = t
in "§(Fn [" ++ joinWithSpace (map show argTys) ++ "] " ++ show retTy ++ ")" -- Note! The '§' means that the emitted type will be "raw" and not converted to 'Lambda'.
----------------------------------------------------------------------------------------------------------
-- ACTUAL TEMPLATES
-- | This function accepts a pointer and will do nothing with it.
templateNoop :: (String, Binder)
templateNoop = defineTemplate
(SymPath [] "noop")
(FuncTy [PointerTy (VarTy "a")] UnitTy)
(toTemplate "void $NAME ($a* a)")
(toTemplate "$DECL { }")
(const [])

View File

@ -1,18 +1,438 @@
-- {-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE MultiWayIf #-}
module Deftype where
-- module Deftype (moduleForDeftype, bindingsForRegisteredType) where
module Deftype (moduleForDeftype, bindingsForRegisteredType) where
-- import qualified Data.Map as Map
-- import Data.Maybe
-- import Debug.Trace
import qualified Data.Map as Map
import Data.Maybe
import Debug.Trace
-- import Obj
-- import Types
-- import Util
-- import Template
-- import Infer
-- import Concretize
-- import Polymorphism
-- import ArrayTemplates
-- import Lookup
import Obj
import Types
import Util
import Template
import ToTemplate
import Infer
import Concretize
import Polymorphism
import ArrayTemplates
import Lookup
{-# ANN module "HLint: ignore Reduce duplication" #-}
-- | This function creates a "Type Module" with the same name as the type being defined.
-- A type module provides a namespace for all the functions that area automatically
-- generated by a deftype.
moduleForDeftype :: TypeEnv -> Env -> [String] -> String -> [Ty] -> [XObj] -> Maybe Info -> Maybe Env -> Either String (String, XObj, [XObj])
moduleForDeftype typeEnv env pathStrings typeName typeVariables rest i existingEnv =
let typeModuleName = typeName
typeModuleEnv = case existingEnv of
Just env -> env
Nothing -> Env (Map.fromList []) (Just env) (Just typeModuleName) [] ExternalEnv 0
-- The variable 'insidePath' is the path used for all member functions inside the 'typeModule'.
-- For example (module Vec2 [x Float]) creates bindings like Vec2.create, Vec2.x, etc.
insidePath = pathStrings ++ [typeModuleName]
in do validateMemberCases typeEnv typeVariables rest
let structTy = StructTy typeName typeVariables
(okMembers, membersDeps) <- templatesForMembers typeEnv env insidePath structTy rest
okInit <- binderForInit insidePath structTy rest
--okNew <- templateForNew insidePath structTy rest
(okStr, strDeps) <- binderForStrOrPrn typeEnv env insidePath structTy rest "str"
(okPrn, _) <- binderForStrOrPrn typeEnv env insidePath structTy rest "prn"
(okDelete, deleteDeps) <- binderForDelete typeEnv env insidePath structTy rest
(okCopy, copyDeps) <- binderForCopy typeEnv env insidePath structTy rest
let funcs = okInit : okStr : okPrn : okDelete : okCopy : okMembers
moduleEnvWithBindings = addListOfBindings typeModuleEnv funcs
typeModuleXObj = XObj (Mod moduleEnvWithBindings) i (Just ModuleTy)
deps = deleteDeps ++ membersDeps ++ copyDeps ++ strDeps
return (typeModuleName, typeModuleXObj, deps)
-- | Will generate getters/setters/updaters when registering EXTERNAL types.
-- | i.e. (register-type VRUnicornData [hp Int, magic Float])
-- | TODO: Remove duplication shared by moduleForDeftype-function.
bindingsForRegisteredType :: TypeEnv -> Env -> [String] -> String -> [XObj] -> Maybe Info -> Maybe Env -> Either String (String, XObj, [XObj])
bindingsForRegisteredType typeEnv env pathStrings typeName rest i existingEnv =
let typeModuleName = typeName
typeModuleEnv = case existingEnv of
Just env -> env
Nothing -> Env (Map.fromList []) (Just env) (Just typeModuleName) [] ExternalEnv 0
insidePath = pathStrings ++ [typeModuleName]
in do validateMemberCases typeEnv [] rest
let structTy = StructTy typeName []
(binders, deps) <- templatesForMembers typeEnv env insidePath structTy rest
okInit <- binderForInit insidePath structTy rest
--okNew <- templateForNew insidePath structTy rest
(okStr, strDeps) <- binderForStrOrPrn typeEnv env insidePath structTy rest "str"
(okPrn, _) <- binderForStrOrPrn typeEnv env insidePath structTy rest "prn"
let moduleEnvWithBindings = addListOfBindings typeModuleEnv (okInit : okStr : okPrn : binders)
typeModuleXObj = XObj (Mod moduleEnvWithBindings) i (Just ModuleTy)
return (typeModuleName, typeModuleXObj, deps ++ strDeps)
-- | Generate all the templates for ALL the member variables in a deftype declaration.
templatesForMembers :: TypeEnv -> Env -> [String] -> Ty -> [XObj] -> Either String ([(String, Binder)], [XObj])
templatesForMembers typeEnv env insidePath structTy [XObj (Arr membersXobjs) _ _] =
let bindersAndDeps = concatMap (templatesForSingleMember typeEnv env insidePath structTy) (pairwise membersXobjs)
in Right (map fst bindersAndDeps, concatMap snd bindersAndDeps)
templatesForMembers _ _ _ _ _ = Left "Can't create member functions for type with more than one case (yet)."
-- | Generate the templates for a single member in a deftype declaration.
templatesForSingleMember :: TypeEnv -> Env -> [String] -> Ty -> (XObj, XObj) -> [((String, Binder), [XObj])]
templatesForSingleMember typeEnv env insidePath p@(StructTy typeName _) (nameXObj, typeXObj) =
let Just t = xobjToTy typeXObj
memberName = getName nameXObj
in [instanceBinderWithDeps (SymPath insidePath memberName) (FuncTy [RefTy p] (RefTy t)) (templateGetter (mangle memberName) t)
, if isTypeGeneric t
then (templateGenericSetter insidePath p t memberName, [])
else instanceBinderWithDeps (SymPath insidePath ("set-" ++ memberName)) (FuncTy [p, t] p) (templateSetter typeEnv env (mangle memberName) t)
,instanceBinderWithDeps (SymPath insidePath ("set-" ++ memberName ++ "!")) (FuncTy [RefTy (p), t] UnitTy) (templateMutatingSetter typeEnv env (mangle memberName) t)
,instanceBinderWithDeps (SymPath insidePath ("update-" ++ memberName))
(FuncTy [p, FuncTy [t] t] p)
(templateUpdater (mangle memberName))]
-- | The template for getters of a deftype.
templateGetter :: String -> Ty -> Template
templateGetter member memberTy =
Template
(FuncTy [RefTy (VarTy "p")] (VarTy "t"))
(const (toTemplate "$t $NAME($(Ref p) p)"))
(const $
let fixForVoidStarMembers =
if isFunctionType memberTy && (not (isTypeGeneric memberTy))
then "(" ++ tyToCLambdaFix (RefTy memberTy) ++ ")"
else ""
in (toTemplate ("$DECL { return " ++ fixForVoidStarMembers ++ "(&(p->" ++ member ++ ")); }\n")))
(const [])
-- | The template for setters of a concrete deftype.
templateSetter :: TypeEnv -> Env -> String -> Ty -> Template
templateSetter typeEnv env memberName memberTy =
let callToDelete = memberDeletion typeEnv env (memberName, memberTy)
in
Template
(FuncTy [VarTy "p", VarTy "t"] (VarTy "p"))
(const (toTemplate "$p $NAME($p p, $t newValue)"))
(const (toTemplate (unlines ["$DECL {"
,callToDelete
," p." ++ memberName ++ " = newValue;"
," return p;"
,"}\n"])))
(\_ -> if | isManaged typeEnv memberTy -> depsOfPolymorphicFunction typeEnv env [] "delete" (typesDeleterFunctionType memberTy)
| isFunctionType memberTy -> [defineFunctionTypeAlias memberTy]
| otherwise -> [])
-- | The template for setters of a generic deftype.
templateGenericSetter :: [String] -> Ty -> Ty -> String -> (String, Binder)
templateGenericSetter pathStrings originalStructTy memberTy memberName =
defineTypeParameterizedTemplate templateCreator path (FuncTy [originalStructTy, memberTy] originalStructTy)
where path = SymPath pathStrings ("set-" ++ memberName)
t = (FuncTy [VarTy "p", VarTy "t"] (VarTy "p"))
templateCreator = TemplateCreator $
\typeEnv env ->
Template
t
(const (toTemplate "$p $NAME($p p, $t newValue)"))
(\(FuncTy [_, memberTy] _) ->
(let callToDelete = memberDeletion typeEnv env (memberName, memberTy)
in (toTemplate (unlines ["$DECL {"
,callToDelete
," p." ++ memberName ++ " = newValue;"
," return p;"
,"}\n"]))))
(\(FuncTy [_, memberTy] _) ->
if isManaged typeEnv memberTy
then depsOfPolymorphicFunction typeEnv env [] "delete" (typesDeleterFunctionType memberTy)
else [])
-- | The template for mutating setters of a deftype.
templateMutatingSetter :: TypeEnv -> Env -> String -> Ty -> Template
templateMutatingSetter typeEnv env memberName memberTy =
Template
(FuncTy [RefTy (VarTy "p"), VarTy "t"] UnitTy)
(const (toTemplate "void $NAME($p* pRef, $t newValue)"))
(const (toTemplate (unlines ["$DECL {"
," pRef->" ++ memberName ++ " = newValue;"
,"}\n"])))
(const [])
-- | The template for updater functions of a deftype.
-- | (allows changing a variable by passing an transformation function).
templateUpdater :: String -> Template
templateUpdater member =
Template
(FuncTy [VarTy "p", FuncTy [VarTy "t"] (VarTy "t")] (VarTy "p"))
(const (toTemplate "$p $NAME($p p, Lambda updater)")) -- "Lambda" used to be: $(Fn [t] t)
(const (toTemplate (unlines ["$DECL {"
," p." ++ member ++ " = " ++ (templateCodeForCallingLambda "updater" (FuncTy [VarTy "t"] (VarTy "t")) ["p." ++ member]) ++ ";"
," return p;"
,"}\n"])))
(\(FuncTy [_, t@(FuncTy fArgTys fRetTy)] _) ->
if isTypeGeneric fRetTy
then []
else [defineFunctionTypeAlias t, defineFunctionTypeAlias (FuncTy (lambdaEnvTy : fArgTys) fRetTy)])
-- | Helper function to create the binder for the 'init' template.
binderForInit :: [String] -> Ty -> [XObj] -> Either String (String, Binder)
binderForInit insidePath structTy@(StructTy typeName _) [XObj (Arr membersXObjs) _ _] =
if isTypeGeneric structTy
then Right (genericInit StackAlloc insidePath structTy membersXObjs)
else Right $ instanceBinder (SymPath insidePath "init")
(FuncTy (initArgListTypes membersXObjs) structTy)
(concreteInit StackAlloc structTy membersXObjs)
-- | Generate a list of types from a deftype declaration.
initArgListTypes :: [XObj] -> [Ty]
initArgListTypes xobjs = map (\(_, x) -> fromJust (xobjToTy x)) (pairwise xobjs)
-- | The template for the 'init' and 'new' functions for a concrete deftype.
concreteInit :: AllocationMode -> Ty -> [XObj] -> Template
concreteInit allocationMode originalStructTy@(StructTy typeName typeVariables) membersXObjs =
Template
(FuncTy (map snd (memberXObjsToPairs membersXObjs)) (VarTy "p"))
(\(FuncTy _ concreteStructTy) ->
let mappings = unifySignatures originalStructTy concreteStructTy
correctedMembers = replaceGenericTypeSymbolsOnMembers mappings membersXObjs
memberPairs = memberXObjsToPairs correctedMembers
in (toTemplate $ "$p $NAME(" ++ joinWithComma (map memberArg memberPairs) ++ ")"))
(const (tokensForInit allocationMode typeName membersXObjs))
(\(FuncTy _ _) -> [])
-- | The template for the 'init' and 'new' functions for a generic deftype.
genericInit :: AllocationMode -> [String] -> Ty -> [XObj] -> (String, Binder)
genericInit allocationMode pathStrings originalStructTy@(StructTy typeName _) membersXObjs =
defineTypeParameterizedTemplate templateCreator path t
where path = SymPath pathStrings "init"
t = (FuncTy (map snd (memberXObjsToPairs membersXObjs)) originalStructTy)
templateCreator = TemplateCreator $
\typeEnv env ->
Template
(FuncTy (map snd (memberXObjsToPairs membersXObjs)) (VarTy "p"))
(\(FuncTy _ concreteStructTy) ->
let mappings = unifySignatures originalStructTy concreteStructTy
correctedMembers = replaceGenericTypeSymbolsOnMembers mappings membersXObjs
memberPairs = memberXObjsToPairs correctedMembers
in (toTemplate $ "$p $NAME(" ++ joinWithComma (map memberArg memberPairs) ++ ")"))
(const (tokensForInit allocationMode typeName membersXObjs))
(\(FuncTy _ concreteStructTy) ->
case concretizeType typeEnv concreteStructTy of
Left err -> error (err ++ ". This error should not crash the compiler - change return type to Either here.")
Right ok -> ok
)
tokensForInit :: AllocationMode -> String -> [XObj] -> [Token]
tokensForInit allocationMode typeName membersXObjs =
toTemplate $ unlines [ "$DECL {"
, case allocationMode of
StackAlloc -> " $p instance;"
HeapAlloc -> " $p instance = CARP_MALLOC(sizeof(" ++ typeName ++ "));"
, joinWith "\n" (map (memberAssignment allocationMode) (memberXObjsToPairs membersXObjs))
, " return instance;"
, "}"]
-- | Creates the C code for an arg to the init function.
-- | i.e. "(deftype A [x Int])" will generate "int x" which
-- | will be used in the init function like this: "A_init(int x)"
memberArg :: (String, Ty) -> String
memberArg (memberName, memberTy) =
tyToCLambdaFix (templitizeTy memberTy) ++ " " ++ memberName
-- | If the type is just a type variable; create a template type variable by appending $ in front of it's name
templitizeTy :: Ty -> Ty
templitizeTy (VarTy vt) = VarTy ("$" ++ vt)
templitizeTy (FuncTy argTys retTy) = FuncTy (map templitizeTy argTys) (templitizeTy retTy)
templitizeTy (StructTy name tys) = StructTy name (map templitizeTy tys)
templitizeTy (RefTy t) = RefTy (templitizeTy t)
templitizeTy (PointerTy t) = PointerTy (templitizeTy t)
templitizeTy t = t
-- | Helper function to create the binder for the 'str' template.
binderForStrOrPrn :: TypeEnv -> Env -> [String] -> Ty -> [XObj] -> String -> Either String ((String, Binder), [XObj])
binderForStrOrPrn typeEnv env insidePath structTy@(StructTy typeName _) [XObj (Arr membersXObjs) _ _] strOrPrn =
if isTypeGeneric structTy
then Right (genericStr insidePath structTy membersXObjs strOrPrn, [])
else Right (instanceBinderWithDeps (SymPath insidePath strOrPrn)
(FuncTy [RefTy structTy] StringTy)
(concreteStr typeEnv env structTy (memberXObjsToPairs membersXObjs) strOrPrn))
-- | The template for the 'str' function for a concrete deftype.
concreteStr :: TypeEnv -> Env -> Ty -> [(String, Ty)] -> String -> Template
concreteStr typeEnv env concreteStructTy@(StructTy typeName _) memberPairs strOrPrn =
Template
(FuncTy [RefTy concreteStructTy] StringTy)
(\(FuncTy [RefTy structTy] StringTy) -> (toTemplate $ "String $NAME(" ++ tyToCLambdaFix structTy ++ " *p)"))
(\(FuncTy [RefTy structTy@(StructTy _ concreteMemberTys)] StringTy) ->
(tokensForStr typeEnv env typeName memberPairs concreteStructTy))
(\(ft@(FuncTy [RefTy structTy@(StructTy _ concreteMemberTys)] StringTy)) ->
concatMap (depsOfPolymorphicFunction typeEnv env [] "prn" . typesStrFunctionType typeEnv)
(filter (\t -> (not . isExternalType typeEnv) t && (not . isFullyGenericType) t)
(map snd memberPairs)))
-- | The template for the 'str' function for a generic deftype.
genericStr :: [String] -> Ty -> [XObj] -> String -> (String, Binder)
genericStr pathStrings originalStructTy@(StructTy typeName varTys) membersXObjs strOrPrn =
defineTypeParameterizedTemplate templateCreator path t
where path = SymPath pathStrings strOrPrn
t = FuncTy [(RefTy originalStructTy)] StringTy
members = memberXObjsToPairs membersXObjs
templateCreator = TemplateCreator $
\typeEnv env ->
Template
t
(\(FuncTy [RefTy concreteStructTy] StringTy) ->
(toTemplate $ "String $NAME(" ++ tyToCLambdaFix concreteStructTy ++ " *p)"))
(\(FuncTy [RefTy concreteStructTy@(StructTy _ concreteMemberTys)] StringTy) ->
let mappings = unifySignatures originalStructTy concreteStructTy
correctedMembers = replaceGenericTypeSymbolsOnMembers mappings membersXObjs
memberPairs = memberXObjsToPairs correctedMembers
in (tokensForStr typeEnv env typeName memberPairs concreteStructTy))
(\(ft@(FuncTy [RefTy concreteStructTy@(StructTy _ concreteMemberTys)] StringTy)) ->
let mappings = unifySignatures originalStructTy concreteStructTy
correctedMembers = replaceGenericTypeSymbolsOnMembers mappings membersXObjs
memberPairs = memberXObjsToPairs correctedMembers
in concatMap (depsOfPolymorphicFunction typeEnv env [] "prn" . typesStrFunctionType typeEnv)
(filter (\t -> (not . isExternalType typeEnv) t && (not . isFullyGenericType) t)
(map snd memberPairs))
++
(if isTypeGeneric concreteStructTy then [] else [defineFunctionTypeAlias ft]))
tokensForStr :: TypeEnv -> Env -> String -> [(String, Ty)] -> Ty -> [Token]
tokensForStr typeEnv env typeName memberPairs concreteStructTy =
(toTemplate $ unlines [ "$DECL {"
, " // convert members to String here:"
, " String temp = NULL;"
, " int tempsize = 0;"
, " (void)tempsize; // that way we remove the occasional unused warning "
, calculateStructStrSize typeEnv env memberPairs concreteStructTy
, " String buffer = CARP_MALLOC(size);"
, " String bufferPtr = buffer;"
, ""
, " snprintf(bufferPtr, size, \"(%s \", \"" ++ typeName ++ "\");"
, " bufferPtr += strlen(\"" ++ typeName ++ "\") + 2;\n"
, joinWith "\n" (map (memberPrn typeEnv env) memberPairs)
, " bufferPtr--;"
, " snprintf(bufferPtr, size, \")\");"
, " return buffer;"
, "}"])
-- | Figure out how big the string needed for the string representation of the struct has to be.
calculateStructStrSize :: TypeEnv -> Env -> [(String, Ty)] -> Ty -> String
calculateStructStrSize typeEnv env members structTy@(StructTy name _) =
" int size = snprintf(NULL, 0, \"(%s )\", \"" ++ name ++ "\");\n" ++
unlines (map memberPrnSize members)
where memberPrnSize (memberName, memberTy) =
let refOrNotRefType = if isManaged typeEnv memberTy then RefTy memberTy else memberTy
maybeTakeAddress = if isManaged typeEnv memberTy then "&" else ""
strFuncType = FuncTy [refOrNotRefType] StringTy
in case nameOfPolymorphicFunction typeEnv env strFuncType "prn" of
Just strFunctionPath ->
unlines [" temp = " ++ pathToC strFunctionPath ++ "(" ++ maybeTakeAddress ++ "p->" ++ memberName ++ "); "
, " size += snprintf(NULL, 0, \"%s \", temp);"
, " if(temp) { CARP_FREE(temp); temp = NULL; }"
]
Nothing ->
if isExternalType typeEnv memberTy
then unlines [ " size += snprintf(NULL, 0, \"%p \", p->" ++ memberName ++ ");"
, " if(temp) { CARP_FREE(temp); temp = NULL; }"
]
else " // Failed to find str function for " ++ memberName ++ " : " ++ show memberTy ++ "\n"
-- | Generate C code for converting a member variable to a string and appending it to a buffer.
memberPrn :: TypeEnv -> Env -> (String, Ty) -> String
memberPrn typeEnv env (memberName, memberTy) =
let refOrNotRefType = if isManaged typeEnv memberTy then RefTy memberTy else memberTy
maybeTakeAddress = if isManaged typeEnv memberTy then "&" else ""
strFuncType = FuncTy [refOrNotRefType] StringTy
in case nameOfPolymorphicFunction typeEnv env strFuncType "prn" of
Just strFunctionPath ->
unlines [" temp = " ++ pathToC strFunctionPath ++ "(" ++ maybeTakeAddress ++ "p->" ++ memberName ++ ");"
, " snprintf(bufferPtr, size, \"%s \", temp);"
, " bufferPtr += strlen(temp) + 1;"
, " if(temp) { CARP_FREE(temp); temp = NULL; }"
]
Nothing ->
if isExternalType typeEnv memberTy
then unlines [ " tempsize = snprintf(NULL, 0, \"%p\", p->" ++ memberName ++ ");"
, " temp = malloc(tempsize);"
, " snprintf(temp, tempsize, \"%p\", p->" ++ memberName ++ ");"
, " snprintf(bufferPtr, size, \"%s \", temp);"
, " bufferPtr += strlen(temp) + 1;"
, " if(temp) { CARP_FREE(temp); temp = NULL; }"
]
else " // Failed to find str function for " ++ memberName ++ " : " ++ show memberTy ++ "\n"
-- | Generate C code for assigning to a member variable.
-- | Needs to know if the instance is a pointer or stack variable.
memberAssignment :: AllocationMode -> (String, Ty) -> String
memberAssignment allocationMode (memberName, _) = " instance" ++ sep ++ memberName ++ " = " ++ memberName ++ ";"
where sep = case allocationMode of
StackAlloc -> "."
HeapAlloc -> "->"
-- | Helper function to create the binder for the 'delete' template.
binderForDelete :: TypeEnv -> Env -> [String] -> Ty -> [XObj] -> Either String ((String, Binder), [XObj])
binderForDelete typeEnv env insidePath structTy@(StructTy typeName _) [XObj (Arr membersXObjs) _ _] =
if isTypeGeneric structTy
then Right (genericDelete insidePath structTy membersXObjs, [])
else Right (instanceBinderWithDeps (SymPath insidePath "delete")
(FuncTy [structTy] UnitTy)
(concreteDelete typeEnv env (memberXObjsToPairs membersXObjs)))
-- | The template for the 'delete' function of a generic deftype.
genericDelete :: [String] -> Ty -> [XObj] -> (String, Binder)
genericDelete pathStrings originalStructTy membersXObjs =
defineTypeParameterizedTemplate templateCreator path (FuncTy [originalStructTy] UnitTy)
where path = SymPath pathStrings "delete"
t = (FuncTy [VarTy "p"] UnitTy)
templateCreator = TemplateCreator $
\typeEnv env ->
Template
t
(const (toTemplate "void $NAME($p p)"))
(\(FuncTy [concreteStructTy] UnitTy) ->
let mappings = unifySignatures originalStructTy concreteStructTy
correctedMembers = replaceGenericTypeSymbolsOnMembers mappings membersXObjs
memberPairs = memberXObjsToPairs correctedMembers
in (toTemplate $ unlines [ "$DECL {"
, joinWith "\n" (map (memberDeletion typeEnv env) memberPairs)
, "}"]))
(\(FuncTy [concreteStructTy] UnitTy) ->
let mappings = unifySignatures originalStructTy concreteStructTy
correctedMembers = replaceGenericTypeSymbolsOnMembers mappings membersXObjs
memberPairs = memberXObjsToPairs correctedMembers
in if isTypeGeneric concreteStructTy
then []
else concatMap (depsOfPolymorphicFunction typeEnv env [] "delete" . typesDeleterFunctionType)
(filter (isManaged typeEnv) (map snd memberPairs)))
-- | Helper function to create the binder for the 'copy' template.
binderForCopy :: TypeEnv -> Env -> [String] -> Ty -> [XObj] -> Either String ((String, Binder), [XObj])
binderForCopy typeEnv env insidePath structTy@(StructTy typeName _) [XObj (Arr membersXObjs) _ _] =
if isTypeGeneric structTy
then Right (genericCopy insidePath structTy membersXObjs, [])
else Right (instanceBinderWithDeps (SymPath insidePath "copy")
(FuncTy [RefTy structTy] structTy)
(concreteCopy typeEnv env (memberXObjsToPairs membersXObjs)))
-- | The template for the 'copy' function of a generic deftype.
genericCopy :: [String] -> Ty -> [XObj] -> (String, Binder)
genericCopy pathStrings originalStructTy membersXObjs =
defineTypeParameterizedTemplate templateCreator path (FuncTy [RefTy originalStructTy] originalStructTy)
where path = SymPath pathStrings "copy"
t = (FuncTy [RefTy (VarTy "p")] (VarTy "p"))
templateCreator = TemplateCreator $
\typeEnv env ->
Template
t
(const (toTemplate "$p $NAME($p* pRef)"))
(\(FuncTy [RefTy concreteStructTy] _) ->
let mappings = unifySignatures originalStructTy concreteStructTy
correctedMembers = replaceGenericTypeSymbolsOnMembers mappings membersXObjs
memberPairs = memberXObjsToPairs correctedMembers
in (tokensForCopy typeEnv env memberPairs))
(\(FuncTy [RefTy concreteStructTy] _) ->
let mappings = unifySignatures originalStructTy concreteStructTy
correctedMembers = replaceGenericTypeSymbolsOnMembers mappings membersXObjs
memberPairs = memberXObjsToPairs correctedMembers
in if isTypeGeneric concreteStructTy
then []
else concatMap (depsOfPolymorphicFunction typeEnv env [] "copy" . typesCopyFunctionType)
(filter (isManaged typeEnv) (map snd memberPairs)))

View File

@ -7,6 +7,7 @@ import ColorText
import Obj
import Types
import Template
import ToTemplate
import ArrayTemplates
import Commands
import Parsing

View File

@ -1,7 +1,5 @@
module Template where
import qualified Text.Parsec as Parsec
import Text.Parsec ((<|>))
import qualified Data.Set as Set
import Debug.Trace
@ -11,3 +9,83 @@ import Obj
import Parsing
import Infer
import Concretize
import ToTemplate
-- | Create a binding pair used for adding a template instantiation to an environment.
instanceBinder :: SymPath -> Ty -> Template -> (String, Binder)
instanceBinder path@(SymPath _ name) actualType template =
let (x, _) = instantiateTemplate path actualType template
in (name, Binder emptyMeta x)
-- | Create a binding pair and don't discard the dependencies
instanceBinderWithDeps :: SymPath -> Ty -> Template -> ((String, Binder), [XObj])
instanceBinderWithDeps path@(SymPath _ name) actualType template =
let (x, deps) = instantiateTemplate path actualType template
in ((name, Binder emptyMeta x), deps)
-- | Templates are instructions for the compiler to generate some C-code
-- | based on some template and the names and types to fill into the template.
-- | Templates are generic and need to be given an explicit type to generate the
-- | correct code.
-- | Example:
-- | template1 : ((Array T) -> Int) = "int length__T(<T> xs) { return xs->len; }"
-- | Given the type ((Array Float) -> Int) the following code is produced:
-- | "int length__Float(Array__Float xs) { return xs->len; }"
-- | Create a binding pair used for adding a template definition to an environment.
defineTemplate :: SymPath -> Ty -> [Token] -> [Token] -> (Ty -> [XObj]) -> (String, Binder)
defineTemplate path t declaration definition depsFunc =
let (SymPath _ name) = path
template = Template t (const declaration) (const definition) depsFunc
i = Info 0 0 (show path ++ ".template") Set.empty 0
defLst = [XObj (Deftemplate (TemplateCreator (\_ _ -> template))) Nothing Nothing, XObj (Sym path Symbol) Nothing Nothing]
in (name, Binder emptyMeta (XObj (Lst defLst) (Just i) (Just t)))
-- | The more advanced version of a template, where the code can vary depending on the type.
defineTypeParameterizedTemplate :: TemplateCreator -> SymPath -> Ty -> (String, Binder)
defineTypeParameterizedTemplate templateCreator path t =
let (SymPath _ name) = path
i = Info 0 0 (show path ++ ".parameterizedTemplate") Set.empty 0
defLst = [XObj (Deftemplate templateCreator) Nothing Nothing, XObj (Sym path Symbol) Nothing Nothing]
in (name, Binder emptyMeta (XObj (Lst defLst) (Just i) (Just t)))
-- | Concretizes the types used in @token
-- @cName is the name of the definition, i.e. the "foo" in "void foo() { ... }"
concretizeTypesInToken :: TypeMappings -> String -> [Token] -> Token -> [Token]
concretizeTypesInToken mappings cName decl token =
case token of
TokDecl -> concatMap (concretizeTypesInToken mappings cName (error "Nope.")) decl
TokName -> [TokC cName]
TokTy t mode -> [TokTy (replaceTyVars mappings t) mode]
_ -> [token]
-- | The code needed to correctly call a lambda from C.
templateCodeForCallingLambda :: String -> Ty -> [String] -> String
templateCodeForCallingLambda functionName t args =
let FuncTy argTys retTy = t
castToFnWithEnv = tyToCast (FuncTy (lambdaEnvTy : argTys) retTy)
castToFn = tyToCast t
in
functionName ++ ".env ? " ++
"((" ++ castToFnWithEnv ++ ")" ++ functionName ++ ".callback)(" ++ functionName ++ ".env" ++ (if null args then "" else ", ") ++ joinWithComma args ++ ")" ++
" : " ++
"((" ++ castToFn ++ ")" ++ functionName ++ ".callback)(" ++ joinWithComma args ++ ")"
-- | Must cast a lambda:s .callback member to the correct type to be able to call it.
tyToCast :: Ty -> String
tyToCast t =
let FuncTy argTys retTy = t
in "§(Fn [" ++ joinWithSpace (map show argTys) ++ "] " ++ show retTy ++ ")" -- Note! The '§' means that the emitted type will be "raw" and not converted to 'Lambda'.
----------------------------------------------------------------------------------------------------------
-- ACTUAL TEMPLATES
-- | This function accepts a pointer and will do nothing with it.
templateNoop :: (String, Binder)
templateNoop = defineTemplate
(SymPath [] "noop")
(FuncTy [PointerTy (VarTy "a")] UnitTy)
(toTemplate "void $NAME ($a* a)")
(toTemplate "$DECL { }")
(const [])

90
src/ToTemplate.hs Normal file
View File

@ -0,0 +1,90 @@
module ToTemplate where
import qualified Text.Parsec as Parsec
import Text.Parsec ((<|>))
import Obj
import Types
import Parsing
import Util
-- | High-level helper function for creating templates from strings of C code.
toTemplate :: String -> [Token]
toTemplate text = case Parsec.runParser templateSyntax 0 "(template)" text of
Right ok -> ok
Left err -> error (show err)
where
templateSyntax :: Parsec.Parsec String Int [Token]
templateSyntax = Parsec.many parseTok
parseTok = Parsec.try parseTokDecl <|> --- $DECL
Parsec.try parseTokName <|> --- $NAME
Parsec.try parseTokTyGrouped <|> --- i.e. $(Fn [Int] t)
Parsec.try parseTokTyRawGrouped <|>
Parsec.try parseTokTy <|> --- i.e. $t
parseTokC --- Anything else...
parseTokDecl :: Parsec.Parsec String Int Token
parseTokDecl = do _ <- Parsec.string "$DECL"
return TokDecl
parseTokName :: Parsec.Parsec String Int Token
parseTokName = do _ <- Parsec.string "$NAME"
return TokName
parseTokC :: Parsec.Parsec String Int Token
parseTokC = do s <- Parsec.many1 validInSymbol
return (TokC s)
where validInSymbol = Parsec.choice [Parsec.letter, Parsec.digit, Parsec.oneOf validCharactersInTemplate]
validCharactersInTemplate = " ><{}()[]|;:.,_-+*#/'^!?€%&=@\"\n\t"
parseTokTy :: Parsec.Parsec String Int Token
parseTokTy = do _ <- Parsec.char '$'
s <- Parsec.many1 Parsec.letter
return (toTokTy Normal s)
parseTokTyGrouped :: Parsec.Parsec String Int Token
parseTokTyGrouped = do _ <- Parsec.char '$'
_ <- Parsec.char '('
Parsec.putState 1 -- One paren to close.
s <- fmap ('(' :) (Parsec.many parseCharBalanced)
-- Note: The closing paren is read by parseCharBalanced.
return (toTokTy Normal s)
parseTokTyRawGrouped :: Parsec.Parsec String Int Token
parseTokTyRawGrouped = do _ <- Parsec.char '§'
_ <- Parsec.char '('
Parsec.putState 1 -- One paren to close.
s <- fmap ('(' :) (Parsec.many parseCharBalanced)
-- Note: The closing paren is read by parseCharBalanced.
return (toTokTy Raw s)
parseCharBalanced :: Parsec.Parsec String Int Char
parseCharBalanced = do balanceState <- Parsec.getState
if balanceState > 0
then Parsec.try openParen <|>
Parsec.try closeParen <|>
Parsec.anyChar
else Parsec.char '\0' -- Should always fail which will end the string.
openParen :: Parsec.Parsec String Int Char
openParen = do _ <- Parsec.char '('
Parsec.modifyState (+1)
return '('
closeParen :: Parsec.Parsec String Int Char
closeParen = do _ <- Parsec.char ')'
Parsec.modifyState (\x -> x - 1)
return ')'
-- | Converts a string containing a type to a template token ('TokTy').
-- | i.e. the string "(Array Int)" becomes (TokTy (StructTy "Array" IntTy)).
toTokTy :: TokTyMode -> String -> Token
toTokTy mode s =
case parse s "" of
Left err -> error (show err)
Right [] -> error ("toTokTy got [] when parsing: '" ++ s ++ "'")
Right [xobj] -> case xobjToTy xobj of
Just ok -> TokTy ok mode
Nothing -> error ("toTokTy failed to convert this s-expression to a type: " ++ pretty xobj)
Right xobjs -> error ("toTokTy parsed too many s-expressions: " ++ joinWithSpace (map pretty xobjs))