Follows up #24423, needed more refactoring than I expected, sorry for
ugly diff.
With this pretty much all of the raw C code generating parts of the
codegen are abstracted into the cbuilder API (to my knowledge at least).
The current design of NIFC does not implement everything the codegen
generates, such things have mostly not been adapted, they are the
following along with how I'm guessing they could be implemented:
* C++ specific codegen: Maybe a dialect of NIFC for generating C++?
* `codegenDecl` pragma: Could be passed as a pragma to NIFC
* C macros, currently only used for line info IIRC i.e. `nimln_(123)`:
Just inline them when generating NIFC
* Other C defines & `#line`: Maybe as NIFC directives or line infos?
* There is also [this
`#ifndef`](21420d8b09/compiler/cgen.nim (L2249))
when generating headers but NIFC shouldn't need it
* `alignof`/`offsetof`: Is in `cbuilder` but not implemented in NIFC,
should be easy
For now we can disable C++ and the `codegenDecl` pragma when generating
NIFC but since cbuilder is mostly designed to generate NIFC as a flag
when booting the compiler, this hinders the ability to run the CI
against NIFC. Maybe we could also make cbuilder able to generate both C
and NIFC at runtime, this would be a large refactor but wouldn't be too
difficult.
Other missing abstractions before being able to generate NIFC are:
* Primitive types and symbols i.e. `int`, `void*`, `NI`, `NIM_NULL` are
currently still constant string literals, `NU8`, `NU16` etc are also
sometimes generated like `"NU" & $bits`.
* NIFC identifiers, i.e. adding `.c` to imported symbols and properly
mangling generated ones. Not sure how difficult this is going to be.
Doing this early is useful so we can move the indentation logic into
`Builder` itself rather than mix it with the block logic in `ccgstmts`
(the `if` statements in #24381 have not been indented properly either).
However it also means `Builder` is now used for code that still
generates raw C code, so the diff won't be as clean when these get
updated.
Most of what ccgexprs uses is now ported to cbuilder, so this PR makes
around ~25% of ccgexprs use it, along with adding `if` stmts (no
`while`/`switch` and `for` which is only used as `for (tmp = a; tmp < b;
tmp++)`). The `if` builder does not add indents for blocks since we
can't make `Builder` an object yet rather than an alias to `string`,
this will likely be one of the last refactors.
Somewhat unrelated but `ccgtypes` is not ready yet because proc
signatures are not implemented.
Different from `intLiteral`, we add procs that just try to generate
integer values, assuming they're not an edge case like `<= low(int32)`
or `> high(int32)`.
`cbuilder` is now split into `cbuilderbase`, `cbuilderexprs`,
`cbuilderdecls`, with all the struct builder code up to this point going
in `cbuilderdecls`.
Variable builders are added, with local, global and constant variables
implemented, but not threadvars.
A builder for struct (braced) initializers is added. The field names
have to be passed to build each field (so they can be used in `oconstr`
in nifc), but they're not used in the output code if a flag
`orderCompliant` is enabled, which means the initializer list is
generated in order of the built fields. The version which uses the names
on C is not implemented (C99 designated initializers), so this flag has
to be enabled for now.
The struct builders now generate the struct as an inline expression if a
name isn't provided rather than a statement. This means we can now use
`addSimpleStruct` etc for the type of fields, but we can't replace
`addFieldWithStructType` because of `#pragma pack(pop)`.
Doc comments are added to every usable proc but may still not be
sufficient.