1313# # distinctly-named codec emitted by the cbor_helpers template.
1414
1515import std/ [os, strutils, tables, sets]
16- import ./ meta, ./ string_helpers, ./ c_cpp_common
16+ import ./ meta, ./ string_helpers, ./ c_cpp_common, ./ types_ir
1717
1818# # Wire-format C type for any Nim `ptr T` / `pointer`. Fixed 64-bit so the CBOR
1919# # payload size is stable regardless of host architecture (mirrors CppPtrType).
@@ -30,81 +30,43 @@ const
3030 PreludeHeaderName * = " nim_ffi_prelude.h"
3131 CborHeaderName * = " nim_ffi_cbor.h"
3232
33- type LeafInfo = tuple [ok: bool , cType: string , suffix: string , owns: bool ]
34-
35- func leafCType (t: string ): LeafInfo =
36- # # Maps a Nim leaf type to its C type, codec suffix and whether a decoded
37- # # value owns heap memory. `ok` is false for composite types (seq/Option/
38- # # user structs), which are monomorphised separately.
39- case t
40- of " int" , " int64" :
41- (true , " int64_t" , " i64" , false )
42- of " int32" :
43- (true , " int32_t" , " i32" , false )
44- of " int16" :
45- (true , " int16_t" , " i16" , false )
46- of " int8" :
47- (true , " int8_t" , " i8" , false )
48- of " uint" , " uint64" :
49- (true , " uint64_t" , " u64" , false )
50- of " uint32" :
51- (true , " uint32_t" , " u32" , false )
52- of " uint16" :
53- (true , " uint16_t" , " u16" , false )
54- of " uint8" , " byte" :
55- (true , " uint8_t" , " u8" , false )
56- of " bool" :
57- (true , " bool" , " bool" , false )
58- of " float" , " float64" :
59- (true , " double" , " f64" , false )
60- of " float32" :
61- (true , " float" , " f32" , false )
62- of " pointer" :
63- (true , CPtrType , " u64" , false )
64- of " string" , " cstring" :
65- (true , " NimFfiStr" , " str" , true )
66- else :
67- (false , " " , " " , false )
68-
69- func cToken (cType: string ): string =
70- # # Short PascalCase token used to build monomorphised container names and
71- # # codec-adapter symbols. Composite C type names are already unique C
72- # # identifiers, so they pass through verbatim.
73- case cType
74- of " int64_t" : " I64"
75- of " int32_t" : " I32"
76- of " int16_t" : " I16"
77- of " int8_t" : " I8"
78- of " uint64_t" : " U64"
79- of " uint32_t" : " U32"
80- of " uint16_t" : " U16"
81- of " uint8_t" : " U8"
82- of " bool" : " Bool"
83- of " double" : " F64"
84- of " float" : " F32"
85- of " NimFfiStr" : " Str"
86- of " NimFfiBytes" : " Bytes"
87- else : cType
33+ const scalarCInfoTable: array [ScalarKind , tuple [cType, suffix: string ]] = [
34+ skBool: (" bool" , " bool" ),
35+ skI8: (" int8_t" , " i8" ),
36+ skI16: (" int16_t" , " i16" ),
37+ skI32: (" int32_t" , " i32" ),
38+ skI64: (" int64_t" , " i64" ),
39+ skU8: (" uint8_t" , " u8" ),
40+ skU16: (" uint16_t" , " u16" ),
41+ skU32: (" uint32_t" , " u32" ),
42+ skU64: (" uint64_t" , " u64" ),
43+ skF32: (" float" , " f32" ),
44+ skF64: (" double" , " f64" ),
45+ ]
8846
8947func leafSuffix (cType: string ): string =
90- # # Inverse of leafCType's cType→suffix for the leaf codecs the template
91- # # provides; empty string for composite types.
48+ # # C type name → leaf codec suffix for the leaf codecs the template provides;
49+ # # empty string for composite types. Driven off the shared scalar table so it
50+ # # can't drift from the IR's scalar set.
51+ for s in ScalarKind :
52+ if scalarCInfoTable[s].cType == cType:
53+ return scalarCInfoTable[s].suffix
9254 case cType
93- of " int64_t" : " i64"
94- of " int32_t" : " i32"
95- of " int16_t" : " i16"
96- of " int8_t" : " i8"
97- of " uint64_t" : " u64"
98- of " uint32_t" : " u32"
99- of " uint16_t" : " u16"
100- of " uint8_t" : " u8"
101- of " bool" : " bool"
102- of " double" : " f64"
103- of " float" : " f32"
10455 of " NimFfiStr" : " str"
10556 of " NimFfiBytes" : " bytes"
10657 else : " "
10758
59+ func cToken (cType: string ): string =
60+ # # Short PascalCase token used to build monomorphised container names and
61+ # # codec-adapter symbols. Leaf types reuse their codec suffix (e.g.
62+ # # `int64_t`→`I64`); composite C type names are already unique C identifiers,
63+ # # so they pass through verbatim.
64+ let suffix = leafSuffix (cType)
65+ if suffix.len > 0 :
66+ capitalizeFirstLetter (suffix)
67+ else :
68+ cType
69+
10870type CTypeReg = object
10971 libName: string # # snake_case symbol prefix, e.g. "my_timer"
11072 libType: string # # PascalCase container-name prefix, e.g. "MyTimer"
@@ -293,44 +255,44 @@ proc emitStructType(reg: var CTypeReg, t: FFITypeMeta) =
293255 reg.codecs.add (body.join (" \n " ))
294256 reg.owns[t.name] = owns
295257
296- proc ensureCType (reg: var CTypeReg , nimType: string ): tuple [cType: string , owns: bool ] =
297- let t = nimType. strip ()
298- if t. startsWith ( " ptr " ) or t == " pointer " :
299- return ( CPtrType , false )
300- let leaf = leafCType (t)
301- if leaf.ok:
302- return (leaf.cType, leaf.owns)
303-
304- let seqInner = genericInnerType (t, " seq[ " )
305- if seqInner.len > 0 :
306- let inner = seqInner. strip ()
307- if inner == " byte " or inner == " uint8 " :
308- return ( " NimFfiBytes " , true )
309- let (elemC, _) = ensureCType (reg, inner )
258+ proc ensureCType (reg: var CTypeReg , t: FFIType ): tuple [cType: string , owns: bool ] =
259+ # # Walks the shared type IR into a C type, monomorphising each distinct
260+ # # `seq[T]` / `Option[T]` into its own struct + codec triple on first sight.
261+ case t.kind
262+ of ftPtr:
263+ ( CPtrType , false )
264+ of ftScalar:
265+ (scalarCInfoTable[t.scalar].cType, false )
266+ of ftStr:
267+ ( " NimFfiStr " , true )
268+ of ftBytes:
269+ ( " NimFfiBytes " , true )
270+ of ftSeq:
271+ let (elemC, _) = ensureCType (reg, t.elem )
310272 let name = reg.libType & " Seq_" & cToken (elemC)
311273 if name notin reg.emitted:
312274 reg.emitted.incl (name)
313275 emitSeqType (reg, name, elemC)
314- return (name, true )
315-
316- var optInner = genericInnerType (t, " Option[" )
317- if optInner.len == 0 :
318- optInner = genericInnerType (t, " Maybe[" )
319- if optInner.len > 0 :
320- let (elemC, elemOwns) = ensureCType (reg, optInner.strip ())
276+ (name, true )
277+ of ftOpt:
278+ let (elemC, elemOwns) = ensureCType (reg, t.elem)
321279 let name = reg.libType & " Opt_" & cToken (elemC)
322280 if name notin reg.emitted:
323281 reg.emitted.incl (name)
324282 emitOptType (reg, name, elemC, elemOwns)
325- return (name, reg.owns.getOrDefault (name, false ))
283+ (name, reg.owns.getOrDefault (name, false ))
284+ of ftStruct:
285+ let name = t.name
286+ if name notin reg.emitted:
287+ reg.emitted.incl (name)
288+ if name in reg.typeTable:
289+ emitStructType (reg, reg.typeTable[name])
290+ else :
291+ reg.decls.add (" /* unknown type referenced: " & name & " */" )
292+ (name, reg.owns.getOrDefault (name, false ))
326293
327- if t notin reg.emitted:
328- reg.emitted.incl (t)
329- if t in reg.typeTable:
330- emitStructType (reg, reg.typeTable[t])
331- else :
332- reg.decls.add (" /* unknown type referenced: " & t & " */" )
333- (t, reg.owns.getOrDefault (t, false ))
294+ proc ensureCType (reg: var CTypeReg , nimType: string ): tuple [cType: string , owns: bool ] =
295+ ensureCType (reg, parseFFIType (nimType))
334296
335297proc reqTypeMeta (p: FFIProcMeta ): FFITypeMeta =
336298 # # Synthesises the per-proc Req struct as an FFITypeMeta so it flows through
@@ -347,7 +309,7 @@ func paramByValue(nimType: string, ridesAsPtr: bool): bool =
347309 # # aggregates (seq, Option, user structs) pass by const pointer.
348310 if ridesAsPtr:
349311 return true
350- leafCType (nimType. strip ()).ok
312+ parseFFIType (nimType).kind in {ftScalar, ftStr, ftPtr}
351313
352314proc cReturnType (reg: var CTypeReg , p: FFIProcMeta ): string =
353315 if p.returnRidesAsPtr ():
0 commit comments