nifreader
nimony/src/lib/nifreader.nim
High performance ("zero copies") NIF file reader.
const StringLit: NifKindThe reader's traditional name for the string-literal token kind.
type FilePos = object col: int32 line: int32
type SymbolPart = enum SymDisamb = (0, "SymDisamb") SymDedup = (1, "SymDedup") SymModule = (2, "SymModule")
func dollar`.SymbolPart(e: SymbolPart): stringfunc dollar`.TokenFlag(e: TokenFlag): stringtype ExpandedToken = object tk: NifKind flags: set[TokenFlag] part: SymbolPart suffixes: uint8 kind: uint16 data: StringView pos: FilePos filename: StringView comment: StringView
type Reader = object p: ptr UncheckedArray[char] eof: pointer f: VfsBlob buf: string thisModule: string line: int32 indexAt: int64 unusedNameHint: ExpandedToken splitSyms: bool pending: StringView pendingLeft: int32 pendingTotal: int32 pendingFlags: set[TokenFlag] pendingHasDisamb: bool pendingHasModule: bool
proc $(t: ExpandedToken): stringproc close(r: var Reader)Release the backend's read handle (mmap unmap, LMDB read txn close, …) via the explicit VfsBlob close API.
proc decodeChar(t: ExpandedToken): charproc decodeStr(r: Reader; t: ExpandedToken): stringproc needsDecoding(t: ExpandedToken): boolWhether
decodeStrwould do anything but copyt.data: an escape to expand, or a module suffix to append. When it is false the bytes ARE the value, so a client that interns them (getOrInclFromView) can skip building a string at all.proc decodeComment(t: ExpandedToken): stringDecode the captured
#…#comment, expanding\HHescapes. Returns "" if no comment is attached.proc decodeFilename(t: ExpandedToken): stringproc decodeFloat(t: ExpandedToken): float64proc decodeUInt(t: ExpandedToken): uint64proc decodeInt(t: ExpandedToken): int64proc decodeDisamb(t: ExpandedToken): int64The value of a
SymDisambcomponent:abc.12.modgives 12. Only the leading digits are read (a well-formed symbol has nothing else there).proc splitSymbols(r: var Reader; enable: bool)Turn the split-symbol parsing mode on or off (default: off).
A NIF symbol is not a string but an object:
<name>.<disamb>.<module>, or<name>.<disamb>.<dedup>.<module>for a generic instantiation, or just<name>.<disamb>for a local symbol. Handing the whole thing to a client as one string is what makes the rest of the compiler take it apart again, over and over.In split mode the reader takes it apart ONCE, while it still has the raw bytes: a
Symbol/SymbolDeftoken carries only<name>, and itssuffixesfield says how manyExtendedSuffixtokens follow — one per remaining component, in order, each tagged with itspart. Soabc.12.Ikey.modarrives asSymbol "abc" (suffixes: 3), SymDisamb "12", SymDedup "Ikey", SymModule "mod"
ExtendedSuffixis the binary model's "more bits for the token before me" kind; a symbol component is exactly that, so no new token kind is needed and every existingcaseoverNifKindstill compiles.An atom with no dot to split on at all -- an operator definition such as
[]=-- is left whole:suffixesis 0 and the token is what it always was. A symbol whose disambiguator is not a number (arkham's legacy_exit.sys.mod) still yields its module; only the name/disambiguator boundary needs the digit, so everything but the module is the name.proc symbolsAreSplit(r: Reader): boolproc next(r: var Reader; result: var ExpandedToken)proc next(r: var Reader): ExpandedTokentype DirectivesResult = enum WrongHeader = (0, "WrongHeader") WrongMeta = (1, "WrongMeta") Success = (2, "Success")
func dollar`.DirectivesResult(e: DirectivesResult): stringproc startsWith(r: Reader; prefix: string): boolproc extractModuleSuffix(filename: string): stringproc open(filename: string): Readerproc openFromBuffer(buf: sink string; thisModule: sink string): ReaderThe Reader keeps
bufalive as the owner of the source bytes.bufmay be a short SSO string whose chars live inline in the string object; a plainreadRawDatapointer into it would dangle the moment the Reader is moved into its caller (and into the wrapping Stream), since the inline bytes move with the object.readRawDataStablepinsbufto its heap representation, whose payload address survives those moves, so the cachedr.p/r.eofstay valid for as long as the Reader (and thusbuf) is alive.proc processDirectives(r: var Reader): DirectivesResultproc fileSize(r: var Reader): int64proc offset(r: var Reader): int64proc jumpTo(r: var Reader; offset: int64)proc indexStartsAt(r: Reader): int64proc firstUnusedName(r: Reader): stringReturns the symbol supplied by the
.unusednamedirective, or"".