forked from carp-lang/Carp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Deftype.hs
598 lines (538 loc) · 28.6 KB
/
Deftype.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
{-# LANGUAGE NamedFieldPuns #-}
module Deftype
( moduleForDeftype,
moduleForDeftypeInContext,
bindingsForRegisteredType,
fieldArg,
memberArg,
)
where
import Concretize
import Context
import Data.Maybe
import Env (addListOfBindings, new)
import Info
import Managed
import Obj
import StructUtils
import Template
import TemplateGenerator as TG
import ToTemplate
import qualified TypeCandidate as TC
import TypeError
import TypePredicates
import Types
import TypesToC
import Util
import Validate
{-# ANN module "HLint: ignore Reduce duplication" #-}
moduleForDeftypeInContext :: Context -> String -> [Ty] -> [XObj] -> Maybe Info -> Either TypeError (String, XObj, [XObj])
moduleForDeftypeInContext ctx name vars members info =
let global = contextGlobalEnv ctx
types = contextTypeEnv ctx
path = contextPath ctx
inner = either (const Nothing) Just (innermostModuleEnv ctx)
previous =
either
(const Nothing)
Just
( (lookupBinderInInternalEnv ctx (SymPath path name))
<> (lookupBinderInGlobalEnv ctx (SymPath path name))
>>= \b ->
replaceLeft
(NotFoundGlobal (SymPath path name))
( case binderXObj b of
XObj (Mod ev et) _ _ -> Right (ev, et)
_ -> Left "Non module"
)
)
in moduleForDeftype inner types global path name vars members info previous
-- | 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 :: Maybe Env -> TypeEnv -> Env -> [String] -> String -> [Ty] -> [XObj] -> Maybe Info -> Maybe (Env, TypeEnv) -> Either TypeError (String, XObj, [XObj])
moduleForDeftype innerEnv typeEnv env pathStrings typeName typeVariables rest i existingEnv =
let moduleValueEnv = fromMaybe (new innerEnv (Just typeName)) (fmap fst existingEnv)
moduleTypeEnv = fromMaybe (new (Just typeEnv) (Just typeName)) (fmap snd existingEnv)
initmembers = case rest of
-- ANSI C does not allow empty structs. We add a dummy member here to account for this.
-- Note that we *don't* add this member for external types--we leave those definitions up to the user.
-- The corresponding field is emitted for the struct definition in Emit.hs
[(XObj (Arr []) ii t)] -> [(XObj (Arr [(XObj (Sym (SymPath [] "__dummy") Symbol) Nothing Nothing), (XObj (Sym (SymPath [] "Char") Symbol) Nothing Nothing)]) ii t)]
_ -> rest
in do
let mems = case initmembers of
[(XObj (Arr ms) _ _)] -> ms
_ -> []
-- Check that this is a valid type definition.
candidate <- TC.mkStructCandidate typeName typeVariables typeEnv env mems pathStrings
validateType candidate
-- Generate standard function bindings for the type.
(funcs, deps) <- generateTypeBindings candidate
-- Add the type and bindings to the environment.
let moduleEnvWithBindings = addListOfBindings moduleValueEnv funcs
typeModuleXObj = XObj (Mod moduleEnvWithBindings moduleTypeEnv) i (Just ModuleTy)
pure (typeName, 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, TypeEnv) -> Either TypeError (String, XObj, [XObj])
bindingsForRegisteredType typeEnv env pathStrings typeName rest i existingEnv =
let moduleValueEnv = fromMaybe (new (Just env) (Just typeName)) (fmap fst existingEnv)
moduleTypeEnv = fromMaybe (new (Just typeEnv) (Just typeName)) (fmap snd existingEnv)
in do
let mems = case rest of
[(XObj (Arr ms) _ _)] -> ms
_ -> []
-- Check that this is a valid type definition.
candidate <- TC.mkStructCandidate typeName [] typeEnv env mems pathStrings
validateType candidate
-- Generate function bindings for the registered type.
(binders, deps) <- templatesForMembers candidate
okInit <- binderForInit candidate
(okStr, strDeps) <- binderForStrOrPrn "str" candidate
(okPrn, _) <- binderForStrOrPrn "prn" candidate
-- Add the type and bindings to the environment.
let moduleEnvWithBindings = addListOfBindings moduleValueEnv (okInit : okStr : okPrn : binders)
typeModuleXObj = XObj (Mod moduleEnvWithBindings moduleTypeEnv) i (Just ModuleTy)
pure (typeName, typeModuleXObj, deps ++ strDeps)
--------------------------------------------------------------------------------
-- Binding creators
-- | Generate the standard set of functions for a new type.
generateTypeBindings :: TC.TypeCandidate -> Either TypeError ([(String, Binder)], [XObj])
generateTypeBindings candidate =
do
(okMembers, membersDeps) <- templatesForMembers candidate
okInit <- binderForInit candidate
(okStr, strDeps) <- binderForStrOrPrn "str" candidate
(okPrn, _) <- binderForStrOrPrn "prn" candidate
(okDelete, deleteDeps) <- binderForDelete candidate
(okCopy, copyDeps) <- binderForCopy candidate
pure
( (okInit : okStr : okPrn : okDelete : okCopy : okMembers),
(deleteDeps ++ membersDeps ++ copyDeps ++ strDeps)
)
-- | Generate all the templates for ALL the member variables in a deftype declaration.
templatesForMembers :: TC.TypeCandidate -> Either TypeError ([(String, Binder)], [XObj])
templatesForMembers candidate =
let bindersAndDeps = concatMap (templatesForSingleMember candidate) (TC.getFields candidate)
in Right (map fst bindersAndDeps, concatMap snd bindersAndDeps)
-- | Generate the templates for a single member in a deftype declaration.
templatesForSingleMember :: TC.TypeCandidate -> TC.TypeField -> [((String, Binder), [XObj])]
templatesForSingleMember _ (TC.StructField "__dummy" _) = []
templatesForSingleMember candidate field@(TC.StructField _ t) =
case t of
-- Unit member types are special since we do not represent them in emitted c.
-- Instead, members of type Unit are executed for their side effects and silently omitted
-- from the produced C structs.
UnitTy ->
binders
(FuncTy [RefTy p (VarTy "q")] UnitTy StaticLifetimeTy)
(FuncTy [p, t] p StaticLifetimeTy)
(FuncTy [RefTy p (VarTy "q"), t] UnitTy StaticLifetimeTy)
(FuncTy [p, RefTy (FuncTy [] UnitTy (VarTy "fq")) (VarTy "q")] p StaticLifetimeTy)
_ ->
binders
(FuncTy [RefTy p (VarTy "q")] (RefTy t (VarTy "q")) StaticLifetimeTy)
(FuncTy [p, t] p StaticLifetimeTy)
(FuncTy [RefTy p (VarTy "q"), t] UnitTy StaticLifetimeTy)
(FuncTy [p, RefTy (FuncTy [t] t (VarTy "fq")) (VarTy "q")] p StaticLifetimeTy)
where
p = TC.toType candidate
memberName = TC.fieldName field
binders getterSig setterSig mutatorSig updaterSig =
[ getter getterSig,
setter setterSig,
mutator mutatorSig,
updater updaterSig
]
getter :: Ty -> ((String, Binder), [XObj])
getter sig =
let doc = "gets the `" ++ (TC.fieldName field) ++ "` property of a `" ++ (TC.getName candidate) ++ "`."
binderT = sig
binderP = SymPath (TC.getFullPath candidate) (TC.fieldName field)
temp = TG.generateConcreteFieldTemplate candidate field getterGenerator
in instanceBinderWithDeps binderP binderT temp doc
setter :: Ty -> ((String, Binder), [XObj])
setter sig =
let doc = "sets the `" ++ (TC.fieldName field) ++ "` property of a `" ++ (TC.getName candidate) ++ "`."
binderT = sig
binderP = SymPath (TC.getFullPath candidate) ("set-" ++ (TC.fieldName field))
concrete = (TG.generateConcreteFieldTemplate candidate field setterGenerator)
generic = (TG.generateGenericFieldTemplate candidate field setterGenerator)
in if isTypeGeneric t
then (defineTypeParameterizedTemplate generic binderP binderT doc, [])
else instanceBinderWithDeps binderP binderT concrete doc
mutator :: Ty -> ((String, Binder), [XObj])
mutator sig =
let doc = "sets the `" ++ (TC.fieldName field) ++ "` property of a `" ++ (TC.getName candidate) ++ "` in place."
binderT = sig
binderP = SymPath (TC.getFullPath candidate) ("set-" ++ (TC.fieldName field) ++ "!")
concrete = (TG.generateConcreteFieldTemplate candidate field mutatorGenerator)
generic = (TG.generateGenericFieldTemplate candidate field mutatorGenerator)
in if isTypeGeneric t
then (defineTypeParameterizedTemplate generic binderP binderT doc, [])
else instanceBinderWithDeps binderP binderT concrete doc
updater :: Ty -> ((String, Binder), [XObj])
updater sig =
let doc = "updates the `" ++ memberName ++ "` property of a `" ++ show p ++ "` using a function `f`."
binderT = sig
binderP = SymPath (TC.getFullPath candidate) ("update-" ++ (TC.fieldName field))
temp = TG.generateConcreteFieldTemplate candidate field updateGenerator
in instanceBinderWithDeps binderP binderT temp doc
templatesForSingleMember _ _ = error "templatesforsinglemember"
-- | Helper function to create the binder for the 'init' template.
binderForInit :: TC.TypeCandidate -> Either TypeError (String, Binder)
binderForInit candidate =
-- Remove the __dummy field from the members array to ensure we can call the initializer with no arguments.
-- See the implementation of moduleForDeftype for more details.
let nodummy = remove ((== "__dummy") . TC.fieldName) (TC.getFields candidate)
doc = "creates a `" ++ (TC.getName candidate) ++ "`."
binderP = (SymPath (TC.getFullPath candidate) "init")
binderT = (FuncTy (concatMap TC.fieldTypes nodummy) (TC.toType candidate) StaticLifetimeTy)
gen = (initGenerator StackAlloc)
in if isTypeGeneric (TC.toType candidate)
then Right (defineTypeParameterizedTemplate (generateGenericTypeTemplate candidate gen) binderP binderT doc)
else Right (instanceBinder binderP binderT (generateConcreteTypeTemplate candidate gen) doc)
-- | Helper function to create the binder for the 'str' template.
binderForStrOrPrn :: String -> TC.TypeCandidate -> Either TypeError ((String, Binder), [XObj])
binderForStrOrPrn strOrPrn candidate =
let binderP = SymPath (TC.getFullPath candidate) strOrPrn
binderT = (FuncTy [RefTy (TC.toType candidate) (VarTy "q")] StringTy StaticLifetimeTy)
doc = "converts a `" ++ TC.getName candidate ++ "` to a string."
in if isTypeGeneric (TC.toType candidate)
then Right $ (defineTypeParameterizedTemplate (TG.generateGenericTypeTemplate candidate strGenerator) binderP binderT doc, [])
else Right $ instanceBinderWithDeps binderP binderT (TG.generateConcreteTypeTemplate candidate strGenerator) doc
-- | Helper function to create the binder for the 'delete' template.
binderForDelete :: TC.TypeCandidate -> Either TypeError ((String, Binder), [XObj])
binderForDelete candidate =
let doc = "deletes a `" ++ TC.getName candidate ++ "`. Should usually not be called manually."
binderP = SymPath (TC.getFullPath candidate) "delete"
binderT = FuncTy [(TC.toType candidate)] UnitTy StaticLifetimeTy
in if isTypeGeneric (TC.toType candidate)
then Right $ (defineTypeParameterizedTemplate (TG.generateGenericTypeTemplate candidate deleteGenerator) binderP binderT doc, [])
else Right $ instanceBinderWithDeps binderP binderT (TG.generateConcreteTypeTemplate candidate deleteGenerator) doc
-- | Helper function to create the binder for the 'copy' template.
binderForCopy :: TC.TypeCandidate -> Either TypeError ((String, Binder), [XObj])
binderForCopy candidate =
let doc = "copies a `" ++ TC.getName candidate ++ "`."
binderP = SymPath (TC.getFullPath candidate) "copy"
binderT = FuncTy [RefTy (TC.toType candidate) (VarTy "q")] (TC.toType candidate) StaticLifetimeTy
in if isTypeGeneric (TC.toType candidate)
then Right $ (defineTypeParameterizedTemplate (TG.generateGenericTypeTemplate candidate copyGenerator) binderP binderT doc, [])
else Right $ instanceBinderWithDeps binderP binderT (TG.generateConcreteTypeTemplate candidate copyGenerator) doc
--------------------------------------------------------------------------------
-- Template generators
--
-- These functions declaratively specify how C code should be emitted for a
-- type. Binder helpers use these to generate an appropriate template for a
-- bound function name for the type.
-- | getterGenerator returns a template generator for struct property getters.
getterGenerator :: TG.TemplateGenerator TC.TypeField
getterGenerator = TG.mkTemplateGenerator tgen decl body deps
where
tgen :: TG.TypeGenerator TC.TypeField
tgen _ = (FuncTy [RefTy (VarTy "p") (VarTy "q")] (VarTy "t") StaticLifetimeTy)
decl :: TG.TokenGenerator TC.TypeField
decl TG.GeneratorArg {instanceT = UnitTy} = toTemplate "void $NAME($(Ref p) p)"
decl _ = toTemplate "$t $NAME($(Ref p) p)"
body :: TG.TokenGenerator TC.TypeField
body TG.GeneratorArg {value = (TC.StructField _ UnitTy)} = toTemplate "$DECL { return; }\n"
body TG.GeneratorArg {instanceT = (FuncTy _ (RefTy UnitTy _) _)} = toTemplate " $DECL { void* ptr = NULL; return ptr; }\n"
body TG.GeneratorArg {value = (TC.StructField name ty)} =
let fixForVoidStarMembers =
if isFunctionType ty && not (isTypeGeneric ty)
then "(" ++ tyToCLambdaFix (RefTy ty (VarTy "q")) ++ ")"
else ""
in toTemplate ("$DECL { return " ++ fixForVoidStarMembers ++ "(&(p->" ++ (mangle name) ++ ")); }\n")
body TG.GeneratorArg {} = toTemplate "/* template error! */"
deps :: TG.DepenGenerator TC.TypeField
deps = const []
-- | setterGenerator returns a template generator for struct property setters.
setterGenerator :: TG.TemplateGenerator TC.TypeField
setterGenerator = TG.mkTemplateGenerator tgen decl body deps
where
tgen :: TG.TypeGenerator TC.TypeField
tgen _ = (FuncTy [VarTy "p", VarTy "t"] (VarTy "p") StaticLifetimeTy)
decl :: TG.TokenGenerator TC.TypeField
decl GeneratorArg {instanceT = (FuncTy [_, UnitTy] _ _)} = toTemplate "$p $NAME($p p)"
decl _ = toTemplate "$p $NAME($p p, $t newValue)"
body :: TG.TokenGenerator TC.TypeField
body GeneratorArg {instanceT = (FuncTy [_, UnitTy] _ _)} = toTemplate "$DECL { return p; }\n"
body GeneratorArg {tenv, env, instanceT = (FuncTy [_, ty] _ _), value = (TC.StructField name _)} =
multilineTemplate
[ "$DECL {",
memberDeletion tenv env (mangle name, ty),
" p." ++ (mangle name) ++ " = newValue;",
" return p;",
"}\n"
]
body _ = toTemplate "/* template error! */"
deps :: TG.DepenGenerator TC.TypeField
deps GeneratorArg {tenv, env, TG.instanceT = (FuncTy [_, ty] _ _)}
| isManaged tenv env ty = depsOfPolymorphicFunction tenv env [] "delete" (typesDeleterFunctionType ty)
| isFunctionType ty = [defineFunctionTypeAlias ty]
| otherwise = []
deps _ = []
-- | mutatorGenerator returns a template generator for struct property setters (in-place).
mutatorGenerator :: TG.TemplateGenerator TC.TypeField
mutatorGenerator = TG.mkTemplateGenerator tgen decl body deps
where
tgen :: TG.TypeGenerator TC.TypeField
tgen _ = (FuncTy [RefTy (VarTy "p") (VarTy "q"), VarTy "t"] UnitTy StaticLifetimeTy)
decl :: TG.TokenGenerator TC.TypeField
decl GeneratorArg {instanceT = (FuncTy [_, UnitTy] _ _)} = toTemplate "void $NAME($p* pRef)"
decl _ = toTemplate "void $NAME($p* pRef, $t newValue)"
body :: TG.TokenGenerator TC.TypeField
-- Execution of the action passed as an argument is handled in Emit.hs.
body GeneratorArg {instanceT = (FuncTy [_, UnitTy] _ _)} = toTemplate "$DECL { return; }\n"
body GeneratorArg {tenv, env, instanceT = (FuncTy [_, ty] _ _), value = (TC.StructField name _)} =
multilineTemplate
[ "$DECL {",
memberRefDeletion tenv env (mangle name, ty),
" pRef->" ++ mangle name ++ " = newValue;",
"}\n"
]
body _ = toTemplate "/* template error! */"
deps :: TG.DepenGenerator TC.TypeField
deps GeneratorArg {tenv, env, instanceT = (FuncTy [_, ty] _ _)} =
if isManaged tenv env ty
then depsOfPolymorphicFunction tenv env [] "delete" (typesDeleterFunctionType ty)
else []
deps _ = []
-- | Returns a template generator for updating struct properties with a function.
updateGenerator :: TG.TemplateGenerator TC.TypeField
updateGenerator = TG.mkTemplateGenerator tgen decl body deps
where
tgen :: TG.TypeGenerator TC.TypeField
tgen GeneratorArg {value = (TC.StructField _ UnitTy)} =
(FuncTy [VarTy "p", RefTy (FuncTy [] UnitTy (VarTy "fq")) (VarTy "q")] (VarTy "p") StaticLifetimeTy)
tgen _ = (FuncTy [VarTy "p", RefTy (FuncTy [VarTy "t"] (VarTy "t") (VarTy "fq")) (VarTy "q")] (VarTy "p") StaticLifetimeTy)
decl :: TG.TokenGenerator TC.TypeField
decl _ = toTemplate "$p $NAME($p p, Lambda *updater)" -- Lambda used to be (Fn [t] t)
body :: TG.TokenGenerator TC.TypeField
body GeneratorArg {value = (TC.StructField _ UnitTy)} =
toTemplate ("$DECL { " ++ templateCodeForCallingLambda "(*updater)" (FuncTy [] UnitTy (VarTy "fq")) [] ++ "; return p;}\n")
body GeneratorArg {value = (TC.StructField name _)} =
multilineTemplate
[ "$DECL {",
" p." ++ mangle name ++ " = " ++ templateCodeForCallingLambda "(*updater)" (FuncTy [VarTy "t"] (VarTy "t") (VarTy "fq")) ["p." ++ mangle name] ++ ";",
" return p;",
"}\n"
]
body _ = toTemplate "/* template error! */"
deps :: TG.DepenGenerator TC.TypeField
deps GeneratorArg {instanceT = (FuncTy [_, RefTy t@(FuncTy fArgTys fRetTy _) _] _ _)} =
if isTypeGeneric fRetTy
then []
else [defineFunctionTypeAlias t, defineFunctionTypeAlias (FuncTy (lambdaEnvTy : fArgTys) fRetTy StaticLifetimeTy)]
deps _ = []
-- | Returns a template generator for a types initializer function.
initGenerator :: AllocationMode -> TG.TemplateGenerator TC.TypeCandidate
initGenerator alloc = TG.mkTemplateGenerator genT decl body deps
where
genT :: TG.TypeGenerator TC.TypeCandidate
genT GeneratorArg {value} =
(FuncTy (concatMap TC.fieldTypes (TC.getFields value)) (VarTy "p") StaticLifetimeTy)
decl :: TG.TokenGenerator TC.TypeCandidate
decl GeneratorArg {originalT, instanceT = (FuncTy _ concreteT _), value} =
let mappings = unifySignatures originalT concreteT
concreteFields = replaceGenericTypeSymbolsOnFields mappings (TC.getFields value)
cFields = remove isUnitT (remove isDummy concreteFields)
in toTemplate ("$p $NAME(" ++ joinWithComma (map fieldArg cFields) ++ ")")
decl _ = toTemplate "/* template error! */"
body :: TG.TokenGenerator TC.TypeCandidate
body GeneratorArg {originalT, instanceT = (FuncTy _ concreteT _), value} =
let mappings = unifySignatures originalT concreteT
concreteFields = replaceGenericTypeSymbolsOnFields mappings (TC.getFields value)
in tokensForInit alloc (show originalT) (remove isUnitT concreteFields)
body _ = toTemplate "/* template error! */"
deps :: TG.DepenGenerator TC.TypeCandidate
deps GeneratorArg {tenv, env, instanceT = (FuncTy _ concreteT _)} =
case concretizeType tenv env concreteT of
Left _ -> []
Right ok -> ok
deps _ = []
tokensForInit :: AllocationMode -> String -> [TC.TypeField] -> [Token]
-- if this is truly a memberless struct, init it to 0;
-- This can happen in cases where *all* members of the struct are of type Unit.
-- Since we do not generate members for Unit types.
tokensForInit StackAlloc _ [] =
multilineTemplate
[ "$DECL {",
" $p instance = {};",
" return instance;",
"}"
]
tokensForInit StackAlloc _ fields =
multilineTemplate
[ "$DECL {",
" $p instance;",
assignments fields,
" return instance;",
"}"
]
tokensForInit HeapAlloc typeName fields =
multilineTemplate
[ "$DECL {",
" $p instance = CARP_MALLOC(sizeof(" ++ typeName ++ "));",
assignments fields,
" return instance;",
"}"
]
assignments :: [TC.TypeField] -> String
assignments [] = ""
assignments fields = joinLines $ fmap (memberAssignment alloc) fields
isDummy field = TC.fieldName field == "__dummy"
isUnitT (TC.StructField _ UnitTy) = True
isUnitT _ = False
-- | Generate C code for assigning to a member variable.
-- Needs to know if the instance is a pointer or stack variable.
-- Also handles the special dummy member we add for empty structs to be ANSI C compatible.
memberAssignment :: AllocationMode -> TC.TypeField -> String
memberAssignment allocationMode field =
case (TC.fieldName field) of
"__dummy" -> " instance" ++ sep ++ mangle name ++ " = " ++ "0" ++ ";"
_ -> " instance" ++ sep ++ mangle name ++ " = " ++ mangle name ++ ";"
where
name = (TC.fieldName field)
sep = case allocationMode of
StackAlloc -> "."
HeapAlloc -> "->"
-- | 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)"
fieldArg :: TC.TypeField -> String
fieldArg (TC.StructField name ty) =
tyToCLambdaFix (templatizeTy ty) ++ " " ++ mangle name
fieldArg _ = ""
---- | 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 (templatizeTy memberTy) ++ " " ++ memberName
-- | If the type is just a type variable; create a template type variable by appending $ in front of it's name
templatizeTy :: Ty -> Ty
templatizeTy (VarTy vt) = VarTy ("$" ++ vt)
templatizeTy (FuncTy argTys retTy ltTy) = FuncTy (map templatizeTy argTys) (templatizeTy retTy) (templatizeTy ltTy)
templatizeTy (StructTy name tys) = StructTy name (map templatizeTy tys)
templatizeTy (RefTy t lt) = RefTy (templatizeTy t) (templatizeTy lt)
templatizeTy (PointerTy t) = PointerTy (templatizeTy t)
templatizeTy t = t
-- | Returns a template generator for a type's str and prn functions.
strGenerator :: TG.TemplateGenerator TC.TypeCandidate
strGenerator = TG.mkTemplateGenerator genT decl body deps
where
genT :: TG.TypeGenerator TC.TypeCandidate
genT GeneratorArg {originalT} =
FuncTy [RefTy originalT (VarTy "q")] StringTy StaticLifetimeTy
decl :: TG.TokenGenerator TC.TypeCandidate
decl GeneratorArg {instanceT = (FuncTy [RefTy structT _] _ _)} =
toTemplate $ "String $NAME(" ++ tyToCLambdaFix structT ++ " *p)"
decl _ = toTemplate "/* template error! */"
body :: TG.TokenGenerator TC.TypeCandidate
body GeneratorArg {tenv, env, originalT, instanceT = (FuncTy [RefTy structT _] _ _), value} =
let mappings = unifySignatures originalT structT
concreteFields = replaceGenericTypeSymbolsOnFields mappings (TC.getFields value)
in tokensForStr tenv env (getStructName structT) concreteFields structT
body _ = toTemplate "/* template error! */"
deps :: TG.DepenGenerator TC.TypeCandidate
deps arg@GeneratorArg {tenv, env, originalT, instanceT = (FuncTy [RefTy structT _] _ _), value} =
let mappings = unifySignatures originalT structT
concreteFields = replaceGenericTypeSymbolsOnFields mappings (TC.getFields value)
in concatMap
(depsOfPolymorphicFunction tenv env [] "prn" . typesStrFunctionType tenv env)
(remove isFullyGenericType (concatMap TC.fieldTypes concreteFields))
++ [defineFunctionTypeAlias (instanceT arg) | not (isTypeGeneric structT)]
deps _ = []
tokensForStr :: TypeEnv -> Env -> String -> [TC.TypeField] -> Ty -> [Token]
tokensForStr typeEnv env typeName fields concreteStructTy =
let members = remove ((== "__dummy") . fst) (map fieldToTuple fields)
in multilineTemplate
[ "$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 members concreteStructTy,
" String buffer = CARP_MALLOC(size);",
" String bufferPtr = buffer;",
"",
" sprintf(bufferPtr, \"(%s \", \"" ++ typeName ++ "\");",
" bufferPtr += strlen(\"" ++ typeName ++ "\") + 2;\n",
joinLines (map (memberPrn typeEnv env) members),
" bufferPtr--;",
" sprintf(bufferPtr, \")\");",
" return buffer;",
"}"
]
calculateStructStrSize :: TypeEnv -> Env -> [(String, Ty)] -> Ty -> String
calculateStructStrSize typeEnv env fields s =
" int size = snprintf(NULL, 0, \"(%s )\", \"" ++ show s ++ "\");\n"
++ unlines (map (memberPrnSize typeEnv env) fields)
-- | Returns a template generator for a type's delete function.
deleteGenerator :: TG.TemplateGenerator TC.TypeCandidate
deleteGenerator = TG.mkTemplateGenerator genT decl body deps
where
genT :: TG.TypeGenerator TC.TypeCandidate
genT _ = FuncTy [VarTy "p"] UnitTy StaticLifetimeTy
decl :: TG.TokenGenerator TC.TypeCandidate
decl _ = toTemplate "void $NAME($p p)"
body :: TG.TokenGenerator TC.TypeCandidate
body GeneratorArg {tenv, env, originalT, instanceT = (FuncTy [structT] _ _), value} =
let mappings = unifySignatures originalT structT
concreteFields = replaceGenericTypeSymbolsOnFields mappings (TC.getFields value)
members = map fieldToTuple concreteFields
in multilineTemplate
[ "$DECL {",
joinLines (map (memberDeletion tenv env) members),
"}"
]
body _ = toTemplate "/* template error! */"
deps :: TG.DepenGenerator TC.TypeCandidate
deps GeneratorArg {tenv, env, originalT, instanceT = (FuncTy [structT] _ _), value}
| isTypeGeneric structT = []
| otherwise =
let mappings = unifySignatures originalT structT
concreteFields = replaceGenericTypeSymbolsOnFields mappings (TC.getFields value)
in concatMap
(depsOfPolymorphicFunction tenv env [] "delete" . typesDeleterFunctionType)
(filter (isManaged tenv env) (concatMap TC.fieldTypes concreteFields))
deps _ = []
-- | Returns a template generator for a type's copy function.
copyGenerator :: TG.TemplateGenerator TC.TypeCandidate
copyGenerator = TG.mkTemplateGenerator genT decl body deps
where
genT :: TG.TypeGenerator TC.TypeCandidate
genT _ = FuncTy [RefTy (VarTy "p") (VarTy "q")] (VarTy "p") StaticLifetimeTy
decl :: TG.TokenGenerator TC.TypeCandidate
decl _ = toTemplate "$p $NAME($p* pRef)"
body :: TG.TokenGenerator TC.TypeCandidate
body GeneratorArg {tenv, env, originalT, instanceT = (FuncTy [RefTy structT _] _ _), value} =
let mappings = unifySignatures originalT structT
concreteFields = replaceGenericTypeSymbolsOnFields mappings (TC.getFields value)
members = map fieldToTuple concreteFields
in tokensForCopy tenv env members
body _ = toTemplate "/* template error! */"
deps :: TG.DepenGenerator TC.TypeCandidate
deps GeneratorArg {tenv, env, originalT, instanceT = (FuncTy [RefTy structT _] _ _), value}
| isTypeGeneric structT = []
| otherwise =
let mappings = unifySignatures originalT structT
concreteFields = replaceGenericTypeSymbolsOnFields mappings (TC.getFields value)
members = map fieldToTuple concreteFields
in concatMap
(depsOfPolymorphicFunction tenv env [] "copy" . typesCopyFunctionType)
(filter (isManaged tenv env) (map snd members))
deps _ = []
--------------------------------------------------------------------------------
-- Utilities
-- | Converts a type field to a tuple of its name and primary type.
-- This is a convenience function for interop with the old tuple based
-- functions for handling type members and it should eventually be deprecated
-- once these functions work on type fields directly.
fieldToTuple :: TC.TypeField -> (String, Ty)
fieldToTuple (TC.StructField name t) = (mangle name, t)
fieldToTuple (TC.SumField name (t : _)) = (mangle name, t) -- note: not actually used.
fieldToTuple (TC.SumField name []) = (mangle name, TypeTy) -- note: not actually used.