mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-08 16:20:58 +00:00
219 lines
6.8 KiB
Nim
219 lines
6.8 KiB
Nim
import std / [assertions, sets, tables]
|
|
import ".." / [ast, idents, lineinfos, msgs, options]
|
|
import "../../dist/nimony/src/lib" / [bitabs, nifstreams, nifcursors, lineinfos]
|
|
import enum2nif, icniftags, nifbasics
|
|
|
|
type
|
|
EncodeContext = object
|
|
conf: ConfigRef
|
|
currentModule: PSym
|
|
decodedSyms: HashSet[ItemId]
|
|
decodedTypes: HashSet[ItemId]
|
|
decodedFileIndices: HashSet[FileIndex]
|
|
dest: TokenBuf
|
|
moduleToNifSuffix: Table[FileIndex, string] # FileIndex (PSym.position) -> module suffix
|
|
|
|
proc initEncodeContext(conf: ConfigRef; currentModule: PSym): EncodeContext =
|
|
result = EncodeContext(conf: conf,
|
|
currentModule: currentModule,
|
|
dest: createTokenBuf())
|
|
result.decodedSyms.incl(currentModule.itemId)
|
|
|
|
template buildTree(dest: var TokenBuf; tag: TagId; body: untyped) =
|
|
dest.addParLe tag
|
|
body
|
|
dest.addParRi
|
|
|
|
template buildTree(dest: var TokenBuf; tag: string; body: untyped) =
|
|
buildTree dest, pool.tags.getOrIncl(tag), body
|
|
|
|
proc writeFlags[E](dest: var TokenBuf; flags: set[E]) =
|
|
var flagsAsIdent = ""
|
|
genFlags(flags, flagsAsIdent)
|
|
if flagsAsIdent.len > 0:
|
|
dest.addIdent flagsAsIdent
|
|
else:
|
|
dest.addDotToken
|
|
|
|
proc toNif(c: var EncodeContext; info: TLineInfo): PackedLineInfo =
|
|
if info == unknownLineInfo:
|
|
NoLineInfo
|
|
else:
|
|
let fileId = pool.files.getOrIncl(c.conf.toFullPath(info.fileIndex))
|
|
pack(pool.man, fileId, info.line.int32, info.col)
|
|
|
|
proc toNifModuleId(c: var EncodeContext; moduleId: int) =
|
|
# `ItemId.module` in PType and PSym (and `PSym.position` when it is skModule) are module's FileIndex
|
|
# but it cannot be directly encoded as the uniqueness of it can broke
|
|
# if any import/include statements are changed.
|
|
if not c.decodedFileIndices.containsOrIncl(moduleId.FileIndex):
|
|
c.dest.buildTree modIdTag:
|
|
c.dest.addIntLit moduleId
|
|
let path = toFullPath(c.conf, moduleId.FileIndex)
|
|
c.dest.addStrLit path
|
|
else:
|
|
c.dest.addIntLit moduleId
|
|
|
|
proc toNif(c: var EncodeContext; sym: PSym)
|
|
proc toNif(c: var EncodeContext; typ: PType)
|
|
proc toNif(c: var EncodeContext; n: PNode)
|
|
|
|
proc toNifDef(c: var EncodeContext; sym: PSym) =
|
|
c.dest.addParLe symIdTag, c.toNif sym.info
|
|
c.toNifModuleId sym.itemId.module
|
|
c.dest.addIntLit sym.itemId.item
|
|
c.dest.addIdent sym.name.s
|
|
if sym.magic == mNone:
|
|
c.dest.addDotToken
|
|
else:
|
|
c.dest.addIdent toNifTag(sym.magic)
|
|
c.dest.writeFlags sym.flags
|
|
c.dest.writeFlags sym.options
|
|
c.dest.addIntLit sym.offset
|
|
c.dest.addIntLit sym.disamb
|
|
c.dest.buildTree sym.kind.toNifTag:
|
|
case sym.kind
|
|
of skLet, skVar, skField, skForVar:
|
|
c.toNif sym.guard
|
|
c.dest.addIntLit sym.bitsize
|
|
c.dest.addIntLit sym.alignment
|
|
else:
|
|
discard
|
|
if sym.kind == skModule:
|
|
c.toNifModuleId sym.position
|
|
else:
|
|
c.dest.addIntLit sym.position
|
|
c.toNif sym.typ
|
|
c.toNif sym.owner
|
|
c.toNif sym.ast # drastically increase output NIF size!
|
|
c.dest.addIdent toNifTag(sym.loc.k)
|
|
c.dest.addStrLit sym.loc.snippet
|
|
c.toNif sym.constraint
|
|
c.toNif sym.instantiatedFrom
|
|
c.dest.addParRi
|
|
|
|
proc toNifDef(c: var EncodeContext; typ: PType) =
|
|
c.dest.buildTree typeIdTag:
|
|
c.toNifModuleId typ.itemId.module
|
|
c.dest.addIntLit typ.itemId.item
|
|
c.dest.addIdent toNifTag(typ.kind)
|
|
c.dest.writeFlags typ.flags
|
|
# following PType or PSym type field can have cycles but this proc should not called recursively
|
|
# as c.decodedTypes prevents it.
|
|
if typ.len == 0:
|
|
c.dest.addDotToken
|
|
else:
|
|
c.dest.buildTree sonsTag:
|
|
for ch in typ.kids:
|
|
c.toNif ch
|
|
|
|
c.toNif typ.n
|
|
c.toNif typ.owner
|
|
c.toNif typ.sym
|
|
|
|
proc toNif(c: var EncodeContext; sym: PSym) =
|
|
if sym == nil:
|
|
c.dest.addDotToken()
|
|
elif sym.owner != nil and sym.originatingModule != c.currentModule:
|
|
let nifSym = toNifSym(sym, c.moduleToNifSuffix, c.conf)
|
|
c.dest.addSymUse(pool.syms.getOrIncl(nifSym), NoLineInfo)
|
|
else:
|
|
if not c.decodedSyms.containsOrIncl(sym.itemId):
|
|
c.toNifDef sym
|
|
else:
|
|
c.dest.buildTree symTag:
|
|
c.dest.addIntLit sym.itemId.module
|
|
c.dest.addIntLit sym.itemId.item
|
|
|
|
proc toNif(c: var EncodeContext; typ: PType) =
|
|
if typ == nil:
|
|
c.dest.addDotToken()
|
|
else:
|
|
if not c.decodedTypes.containsOrIncl(typ.itemId):
|
|
c.toNifDef typ
|
|
else:
|
|
c.dest.buildTree typeTag:
|
|
c.dest.addIntLit typ.itemId.module
|
|
c.dest.addIntLit typ.itemId.item
|
|
|
|
proc writeNodeFlags(dest: var TokenBuf; flags: set[TNodeFlag]) {.inline.} =
|
|
writeFlags dest, flags
|
|
|
|
template withNode(c: var EncodeContext; n: PNode; body: untyped) =
|
|
c.dest.addParLe pool.tags.getOrIncl(toNifTag(n.kind)), c.toNif n.info
|
|
writeNodeFlags(c.dest, n.flags)
|
|
c.toNif n.typ
|
|
body
|
|
c.dest.addParRi
|
|
|
|
proc toNif(c: var EncodeContext; n: PNode) =
|
|
if n == nil:
|
|
c.dest.addDotToken
|
|
else:
|
|
case n.kind:
|
|
of nkEmpty:
|
|
let info = c.toNif n.info
|
|
c.dest.addParLe pool.tags.getOrIncl(toNifTag(nkEmpty)), info
|
|
c.dest.writeNodeFlags(n.flags)
|
|
c.dest.addParRi
|
|
of nkIdent:
|
|
# nkIdent uses flags and typ when it is a generic parameter
|
|
c.withNode n:
|
|
c.dest.addIdent n.ident.s
|
|
of nkSym:
|
|
when false:
|
|
echo "nkSym: ", n.sym.name.s
|
|
if n.sym.kind == skModule:
|
|
echo "position = ", n.sym.position
|
|
debug(n.sym)
|
|
var o = n.sym.owner
|
|
for i in 0 .. 20:
|
|
if o == nil:
|
|
break
|
|
echo "owner ", i, ":"
|
|
if o.kind == skModule:
|
|
echo "position = ", o.position
|
|
debug(o)
|
|
o = o.owner
|
|
# PNode.typ and PNode.sym.typ are different in `int` nkSym Node in following statement:
|
|
# type TestInt = int
|
|
c.withNode n:
|
|
c.toNif n.sym
|
|
of nkCharLit:
|
|
c.withNode n:
|
|
c.dest.add charToken(n.intVal.char, NoLineInfo)
|
|
of nkIntLit .. nkInt64Lit:
|
|
c.withNode n:
|
|
c.dest.addIntLit n.intVal
|
|
of nkUIntLit .. nkUInt64Lit:
|
|
c.withNode n:
|
|
c.dest.addUIntLit cast[BiggestUInt](n.intVal)
|
|
of nkFloatLit .. nkFloat128Lit:
|
|
c.withNode n:
|
|
c.dest.add floatToken(pool.floats.getOrIncl(n.floatVal), NoLineInfo)
|
|
of nkStrLit .. nkTripleStrLit:
|
|
c.withNode n:
|
|
c.dest.addStrLit n.strVal
|
|
of nkNilLit:
|
|
c.withNode n:
|
|
discard
|
|
else:
|
|
#assert n.kind in {nkArgList, nkBracket, nkRecList, nkPragma, nkType} or n.len > 0, $n.kind
|
|
c.withNode(n):
|
|
for i in 0 ..< n.len:
|
|
c.toNif n[i]
|
|
|
|
proc saveNif(c: var EncodeContext; n: PNode): string =
|
|
toNif c, n
|
|
|
|
result = "(.nif24)\n" & toString(c.dest)
|
|
|
|
proc saveNifFile*(n: PNode; conf: ConfigRef; module: PSym) =
|
|
let outfile = module.name.s & ".nif"
|
|
var c = initEncodeContext(conf, module)
|
|
writeFile outfile, saveNif(c, n)
|
|
|
|
proc saveNifToBuffer*(n: PNode; conf: ConfigRef; module: PSym): string =
|
|
var c = initEncodeContext(conf, module)
|
|
result = saveNif(c, n)
|