nifcore-based IC serialization helpers — Stage 1 of porting the IC backend from the old nifstreams/nifcursors NIF stack to nifcore (see doc/ic_nifcore_port.md).
It hosts:
- the process-wide shared Pool/TagPool that stands in for the old global nifstreams.pool,
- writeFileStable, the content-stable file writer mirroring nifcursors.writeFile(..., OnlyIfChanged),
- the first ported writer (writeSemDeps), used as the migration spike.
No nifstreams/nifcursors types cross this module's boundary: callers pass plain Nim values (config, ids, string lists), so it can coexist with the still-old-API ast2nif.nim during the migration.
Types
CookieKind = enum ckParLe, ckParRi, ckSym, ckSymDef, ckIdent, ckStr, ckInt, ckUInt, ckFloat, ckChar, ckDot
- Source Edit
CookieTok = object kind*: CookieKind tag*: string name*: string sym*: uint32 str*: string ival*: int64 uval*: uint64 fval*: float64 cval*: uint32
- Source Edit
IcBuilder = object buf*: TokenBuf
- A thin nifcore TokenBuf builder whose surface is primitive types only (strings/ints/floats). It lets the still-old-API ast2nif.nim drive a nifcore buffer without any nifcore type crossing the module boundary — the bridge that routes IC output onto the nifcore serializer (Stage 2). Source Edit
Procs
proc addCharLit(b: var IcBuilder; c: char) {.inline, ...raises: [], tags: [], forbids: [].}
- Source Edit
proc addDotToken(b: var IcBuilder) {.inline, ...raises: [], tags: [], forbids: [].}
- Source Edit
proc addFloatLit(b: var IcBuilder; v: float64) {.inline, ...raises: [], tags: [], forbids: [].}
- Source Edit
proc addStmtsBody(dest: var IcBuilder; src: var IcBuilder) {....raises: [], tags: [], forbids: [].}
- Append the BODY of a (stmts . . <body> ) builder into dest, dropping the wrapper tag and its two leading dot slots (flags/type) — the nifcore analogue of the old for i in 3 ..< content.len-1: dest.add content[i]. Source Edit
proc addUIntLit(b: var IcBuilder; v: uint64) {.inline, ...raises: [], tags: [], forbids: [].}
- Source Edit
proc collectBifStrLits(path: string): seq[string] {....raises: [IOError], tags: [ReadIOEffect], forbids: [].}
-
Read a small (tag "s" "s" …) bif sidecar (semdeps/edges) and return every string literal it holds, in order — the binary analogue of the old nifstreams scan that collected StrLits. Keeps nifcore types out of deps.nim, which only needs the recorded string list.
Uses loadFromFile (a full read into owned memory) rather than the mmap-backed bif.load, then CLOSES the handle. bif.load intentionally leaves the mapping resident for the process lifetime; for the nim ic driver that reads these sidecars while nim m children rewrite them, a lingering read mapping is a Windows sharing violation: the child's open(path, fmWrite) fails with IOError: cannot open. These sidecars are tiny, so the zero-copy mmap buys nothing here anyway.
Source Edit proc createIcBuf(cap = 16): TokenBuf {.inline, ...raises: [], tags: [], forbids: [].}
- A TokenBuf bound to the shared IC pools. Source Edit
proc cursorPool(c: Cursor): Pool {.inline, ...raises: [], tags: [], forbids: [].}
- The literals pool the cursor's buffer was built against. ast2nif.nim imports nifcore with except pool (to keep nifstreams' global pool var the writer uses), so the reader reaches a cursor's pool through here — needed once bif-loaded buffers carry their OWN fresh pool rather than the shared icPool. Source Edit
proc flattenForCookie(b: var IcBuilder): seq[CookieTok] {....raises: [], tags: [], forbids: [].}
- Flatten the nifcore module buffer to the cookie hashers' flat token list. Source Edit
proc lineInfo(b: var IcBuilder; file: string; line, col: int32; comment = "") {. ...raises: [], tags: [], forbids: [].}
- Attach line info (+ optional #comment#) to the head just emitted. No-op when file is empty (matches the old "emit only when info is valid"). Strings are interned in the shared pools; the file/comment ids reproduce the old pool.files/pool.strings entries by string value. Source Edit
proc newIcBuilder(cap = 16): IcBuilder {....raises: [], tags: [], forbids: [].}
- Source Edit
proc storeBifStable(b: var IcBuilder; path: string; dottedSuffix: string) {. ...raises: [IOError, OSError, Exception], tags: [WriteIOEffect, ReadIOEffect, WriteDirEffect, ReadDirEffect], forbids: [].}
- Content-stable bif write — the binary analogue of writeFileStable's onlyIfChanged: only replace path when the encoded bytes differ, so an unchanged sidecar keeps its mtime and nifmake prunes the dependent rebuild cascade. Used for the iface/impl cookies + dep sidecars whose byte-stability gates incremental builds. (bif encoding is deterministic for a given buffer under fresh pools, so equal content ⇒ equal bytes.) Source Edit
proc writeFileStable(b: var TokenBuf; path: string; onlyIfChanged = false) {. ...raises: [Exception, IOError], tags: [RootEffect, ReadIOEffect, WriteIOEffect], forbids: [].}
- Serialize b to canonical module NIF text and write it. Mirrors nifcursors.writeFile: the module suffix is derived from path ("." & extractModuleSuffix), and onlyIfChanged skips the write when the on-disk bytes already match — the content-stability nifmake's incremental rebuild depends on. Source Edit
proc writeSemDeps(config: ConfigRef; thisModule: int32; importPaths: seq[string]) {. ...raises: [OSError, IOError, Exception], tags: [ReadEnvEffect, ReadIOEffect, WriteIOEffect, WriteDirEffect, ReadDirEffect], forbids: [].}
- Stage 1 spike: the nifcore port of ast2nif.writeSemDeps. Serializes the module's resolved direct imports as (semdeps "path" ...). Byte-identical to the old writer (verified), so nim ic build graphs are unaffected. Source Edit
proc writeStable(b: var IcBuilder; path: string; onlyIfChanged = false) {. inline, ...raises: [Exception, IOError], tags: [RootEffect, ReadIOEffect, WriteIOEffect], forbids: [].}
- Source Edit