123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- type VarKind = enum
- Local
- Global
- Threadvar
- Const
- AlwaysConst ## const even on C++
- proc addVarHeader(builder: var Builder, kind: VarKind) =
- ## adds modifiers for given var kind:
- ## Local has no modifier
- ## Global has `static` modifier
- ## Const has `static NIM_CONST` modifier
- ## AlwaysConst has `static const` modifier (NIM_CONST is no-op on C++)
- ## Threadvar is unimplemented
- case kind
- of Local: discard
- of Global:
- builder.add("static ")
- of Const:
- builder.add("static NIM_CONST ")
- of AlwaysConst:
- builder.add("static const ")
- of Threadvar:
- doAssert false, "unimplemented"
- proc addVar(builder: var Builder, kind: VarKind = Local, name: string, typ: Snippet, initializer: Snippet = "") =
- ## adds a variable declaration to the builder
- builder.addVarHeader(kind)
- builder.add(typ)
- builder.add(" ")
- builder.add(name)
- if initializer.len != 0:
- builder.add(" = ")
- builder.add(initializer)
- builder.add(";\n")
- template addVarWithType(builder: var Builder, kind: VarKind = Local, name: string, body: typed) =
- ## adds a variable declaration to the builder, with the `body` building the type
- builder.addVarHeader(kind)
- body
- builder.add(" ")
- builder.add(name)
- builder.add(";\n")
- template addVarWithTypeAndInitializer(builder: var Builder, kind: VarKind = Local, name: string,
- typeBody, initializerBody: typed) =
- ## adds a variable declaration to the builder, with `typeBody` building the type, and
- ## `initializerBody` building the initializer. initializer must be provided
- builder.addVarHeader(kind)
- typeBody
- builder.add(" ")
- builder.add(name)
- builder.add(" = ")
- initializerBody
- builder.add(";\n")
- proc addArrayVar(builder: var Builder, kind: VarKind = Local, name: string, elementType: Snippet, len: int, initializer: Snippet = "") =
- ## adds an array variable declaration to the builder
- builder.addVarHeader(kind)
- builder.add(elementType)
- builder.add(" ")
- builder.add(name)
- builder.add("[")
- builder.addInt(len)
- builder.add("]")
- if initializer.len != 0:
- builder.add(" = ")
- builder.add(initializer)
- builder.add(";\n")
- template addArrayVarWithInitializer(builder: var Builder, kind: VarKind = Local, name: string, elementType: Snippet, len: int, body: typed) =
- ## adds an array variable declaration to the builder with the initializer built according to `body`
- builder.addVarHeader(kind)
- builder.add(elementType)
- builder.add(" ")
- builder.add(name)
- builder.add("[")
- builder.addInt(len)
- builder.add("] = ")
- body
- builder.add(";\n")
- template addTypedef(builder: var Builder, name: string, typeBody: typed) =
- ## adds a typedef declaration to the builder with name `name` and type as
- ## built in `typeBody`
- builder.add("typedef ")
- typeBody
- builder.add(" ")
- builder.add(name)
- builder.add(";\n")
- type
- StructInitializerKind = enum
- siOrderedStruct ## struct constructor, but without named fields on C
- siNamedStruct ## struct constructor, with named fields i.e. C99 designated initializer
- siArray ## array constructor
- siWrapper ## wrapper for a single field, generates it verbatim
- StructInitializer = object
- ## context for building struct initializers, i.e. `{ field1, field2 }`
- kind: StructInitializerKind
- ## if true, fields will not be named, instead values are placed in order
- needsComma: bool
- proc initStructInitializer(builder: var Builder, kind: StructInitializerKind): StructInitializer =
- ## starts building a struct initializer, i.e. braced initializer list
- result = StructInitializer(kind: kind, needsComma: false)
- if kind != siWrapper:
- builder.add("{")
- template addField(builder: var Builder, constr: var StructInitializer, name: string, valueBody: typed) =
- ## adds a field to a struct initializer, with the value built in `valueBody`
- if constr.needsComma:
- assert constr.kind != siWrapper, "wrapper constructor cannot have multiple fields"
- builder.add(", ")
- else:
- constr.needsComma = true
- case constr.kind
- of siArray, siWrapper:
- # no name, can just add value
- valueBody
- of siOrderedStruct:
- # no name, can just add value on C
- assert name.len != 0, "name has to be given for struct initializer field"
- valueBody
- of siNamedStruct:
- assert name.len != 0, "name has to be given for struct initializer field"
- builder.add(".")
- builder.add(name)
- builder.add(" = ")
- valueBody
- proc finishStructInitializer(builder: var Builder, constr: StructInitializer) =
- ## finishes building a struct initializer
- if constr.kind != siWrapper:
- builder.add("}")
- template addStructInitializer(builder: var Builder, constr: out StructInitializer, kind: StructInitializerKind, body: typed) =
- ## builds a struct initializer, i.e. `{ field1, field2 }`
- ## a `var StructInitializer` must be declared and passed as a parameter so
- ## that it can be used with `addField`
- constr = builder.initStructInitializer(kind)
- body
- builder.finishStructInitializer(constr)
- proc addField(obj: var Builder; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
- ## adds a field inside a struct/union type
- obj.add('\t')
- obj.add(typ)
- obj.add(" ")
- obj.add(name)
- if isFlexArray:
- obj.add("[SEQ_DECL_SIZE]")
- if initializer.len != 0:
- obj.add(initializer)
- obj.add(";\n")
- proc addArrayField(obj: var Builder; name, elementType: Snippet; len: int; initializer: Snippet = "") =
- ## adds an array field inside a struct/union type
- obj.add('\t')
- obj.add(elementType)
- obj.add(" ")
- obj.add(name)
- obj.add("[")
- obj.addInt(len)
- obj.add("]")
- if initializer.len != 0:
- obj.add(initializer)
- obj.add(";\n")
- proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
- ## adds an field inside a struct/union type, based on an `skField` symbol
- obj.add('\t')
- if field.alignment > 0:
- obj.add("NIM_ALIGN(")
- obj.addInt(field.alignment)
- obj.add(") ")
- obj.add(typ)
- if sfNoalias in field.flags:
- obj.add(" NIM_NOALIAS")
- obj.add(" ")
- obj.add(name)
- if isFlexArray:
- obj.add("[SEQ_DECL_SIZE]")
- if field.bitsize != 0:
- obj.add(":")
- obj.addInt(field.bitsize)
- if initializer.len != 0:
- obj.add(initializer)
- obj.add(";\n")
- type
- BaseClassKind = enum
- ## denotes how and whether or not the base class/RTTI should be stored
- bcNone, bcCppInherit, bcSupField, bcNoneRtti, bcNoneTinyRtti
- StructBuilderInfo = object
- ## context for building `struct` types
- baseKind: BaseClassKind
- named: bool
- preFieldsLen: int
- proc structOrUnion(t: PType): Snippet =
- let t = t.skipTypes({tyAlias, tySink})
- if tfUnion in t.flags: "union"
- else: "struct"
- proc startSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet): StructBuilderInfo =
- result = StructBuilderInfo(baseKind: bcNone, named: name.len != 0)
- obj.add("struct")
- if result.named:
- obj.add(" ")
- obj.add(name)
- if baseType.len != 0:
- if m.compileToCpp:
- result.baseKind = bcCppInherit
- else:
- result.baseKind = bcSupField
- if result.baseKind == bcCppInherit:
- obj.add(" : public ")
- obj.add(baseType)
- obj.add(" ")
- obj.add("{\n")
- result.preFieldsLen = obj.len
- if result.baseKind == bcSupField:
- obj.addField(name = "Sup", typ = baseType)
- proc finishSimpleStruct(obj: var Builder; m: BModule; info: StructBuilderInfo) =
- if info.baseKind == bcNone and info.preFieldsLen == obj.len:
- # no fields were added, add dummy field
- obj.addField(name = "dummy", typ = "char")
- if info.named:
- obj.add("};\n")
- else:
- obj.add("}")
- template addSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet; body: typed) =
- ## builds a struct type not based on a Nim type with fields according to `body`,
- ## `name` can be empty to build as a type expression and not a statement
- let info = startSimpleStruct(obj, m, name, baseType)
- body
- finishSimpleStruct(obj, m, info)
- proc startStruct(obj: var Builder; m: BModule; t: PType; name: string; baseType: Snippet): StructBuilderInfo =
- result = StructBuilderInfo(baseKind: bcNone, named: name.len != 0)
- if tfPacked in t.flags:
- if hasAttribute in CC[m.config.cCompiler].props:
- obj.add(structOrUnion(t))
- obj.add(" __attribute__((__packed__))")
- else:
- obj.add("#pragma pack(push, 1)\n")
- obj.add(structOrUnion(t))
- else:
- obj.add(structOrUnion(t))
- if result.named:
- obj.add(" ")
- obj.add(name)
- if t.kind == tyObject:
- if t.baseClass == nil:
- if lacksMTypeField(t):
- result.baseKind = bcNone
- elif optTinyRtti in m.config.globalOptions:
- result.baseKind = bcNoneTinyRtti
- else:
- result.baseKind = bcNoneRtti
- elif m.compileToCpp:
- result.baseKind = bcCppInherit
- else:
- result.baseKind = bcSupField
- elif baseType.len != 0:
- if m.compileToCpp:
- result.baseKind = bcCppInherit
- else:
- result.baseKind = bcSupField
- if result.baseKind == bcCppInherit:
- obj.add(" : public ")
- obj.add(baseType)
- obj.add(" ")
- obj.add("{\n")
- result.preFieldsLen = obj.len
- case result.baseKind
- of bcNone:
- # rest of the options add a field or don't need it due to inheritance,
- # we need to add the dummy field for uncheckedarray ahead of time
- # so that it remains trailing
- if t.itemId notin m.g.graph.memberProcsPerType and
- t.n != nil and t.n.len == 1 and t.n[0].kind == nkSym and
- t.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
- # only consists of flexible array field, add *initial* dummy field
- obj.addField(name = "dummy", typ = "char")
- of bcCppInherit: discard
- of bcNoneRtti:
- obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimType")))
- of bcNoneTinyRtti:
- obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimTypeV2")))
- of bcSupField:
- obj.addField(name = "Sup", typ = baseType)
- proc finishStruct(obj: var Builder; m: BModule; t: PType; info: StructBuilderInfo) =
- if info.baseKind == bcNone and info.preFieldsLen == obj.len and
- t.itemId notin m.g.graph.memberProcsPerType:
- # no fields were added, add dummy field
- obj.addField(name = "dummy", typ = "char")
- if info.named:
- obj.add("};\n")
- else:
- obj.add("}")
- if tfPacked in t.flags and hasAttribute notin CC[m.config.cCompiler].props:
- obj.add("#pragma pack(pop)\n")
- template addStruct(obj: var Builder; m: BModule; typ: PType; name: string; baseType: Snippet; body: typed) =
- ## builds a struct type directly based on `typ` with fields according to `body`,
- ## `name` can be empty to build as a type expression and not a statement
- let info = startStruct(obj, m, typ, name, baseType)
- body
- finishStruct(obj, m, typ, info)
- template addFieldWithStructType(obj: var Builder; m: BModule; parentTyp: PType; fieldName: string, body: typed) =
- ## adds a field with a `struct { ... }` type, building the fields according to `body`
- obj.add('\t')
- if tfPacked in parentTyp.flags:
- if hasAttribute in CC[m.config.cCompiler].props:
- obj.add("struct __attribute__((__packed__)) {\n")
- else:
- obj.add("#pragma pack(push, 1)\nstruct {")
- else:
- obj.add("struct {\n")
- body
- obj.add("} ")
- obj.add(fieldName)
- obj.add(";\n")
- if tfPacked in parentTyp.flags and hasAttribute notin CC[m.config.cCompiler].props:
- result.add("#pragma pack(pop)\n")
- template addAnonUnion(obj: var Builder; body: typed) =
- ## adds an anonymous union i.e. `union { ... };` with fields according to `body`
- obj.add "union{\n"
- body
- obj.add("};\n")
|