mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-08 08:10:56 +00:00
406 lines
12 KiB
Nim
406 lines
12 KiB
Nim
import std / [assertions, tables]
|
|
import "../../dist/nimony/src/lib" / [bitabs, nifreader, nifstreams, nifcursors, lineinfos]
|
|
import ".." / [ast, idents, lineinfos, options, modules, modulegraphs, msgs, pathutils]
|
|
import enum2nif, icniftags, nifbasics
|
|
|
|
type
|
|
NifProgram* = ref object
|
|
nifSymIdToPSym: Table[SymId, PSym]
|
|
moduleToNifSuffix: Table[FileIndex, string] # FileIndex (PSym.position) -> module suffix
|
|
|
|
DecodeContext = object
|
|
graph: ModuleGraph
|
|
symbols: Table[ItemId, PSym]
|
|
types: Table[ItemId, PType]
|
|
modules: Table[int, FileIndex] # maps module id in NIF to FileIndex of the module
|
|
prog: NifProgram
|
|
|
|
proc nodeKind(n: Cursor): TNodeKind {.inline.} =
|
|
assert n.kind == ParLe
|
|
pool.tags[n.tagId].parseNodeKind()
|
|
|
|
proc expect(n: Cursor; k: set[NifKind]) =
|
|
if n.kind notin k:
|
|
when defined(debug):
|
|
writeStackTrace()
|
|
quit "[NIF decoder] expected: " & $k & " but got: " & $n.kind & toString n
|
|
|
|
proc expect(n: Cursor; k: NifKind) {.inline.} =
|
|
expect n, {k}
|
|
|
|
proc incExpect(n: var Cursor; k: set[NifKind]) =
|
|
inc n
|
|
expect n, k
|
|
|
|
proc incExpect(n: var Cursor; k: NifKind) {.inline.} =
|
|
incExpect n, {k}
|
|
|
|
proc skipParRi(n: var Cursor) =
|
|
expect n, {ParRi}
|
|
inc n
|
|
|
|
proc expectTag(n: Cursor; tagId: TagId) =
|
|
if n.kind == ParLe and n.tagId == tagId:
|
|
discard
|
|
else:
|
|
when defined(debug):
|
|
writeStackTrace()
|
|
if n.kind != ParLe:
|
|
quit "[NIF decoder] expected: ParLe but got: " & $n.kind & toString n
|
|
else:
|
|
quit "[NIF decoder] expected: " & pool.tags[tagId] & " but got: " & pool.tags[n.tagId] & toString n
|
|
|
|
proc incExpectTag(n: var Cursor; tagId: TagId) =
|
|
inc n
|
|
expectTag(n, tagId)
|
|
|
|
when false:
|
|
proc expectTag(n: Cursor; tag: string) =
|
|
let id = pool.tags.getKeyId(tag)
|
|
if id == TagId(0):
|
|
when defined(debug):
|
|
writeStackTrace()
|
|
quit "[NIF decoder] expected: " & tag & " but doesn't exist" & toString n
|
|
else:
|
|
expectTag(n, id)
|
|
|
|
proc fromNifLineInfo(c: var DecodeContext; n: Cursor): TLineInfo =
|
|
if n.info == NoLineInfo:
|
|
unknownLineInfo
|
|
else:
|
|
let info = pool.man.unpack(n.info)
|
|
c.graph.config.newLineInfo(pool.files[info.file].AbsoluteFile, info.line, info.col)
|
|
|
|
proc fromNifModuleId(c: var DecodeContext; n: var Cursor): (FileIndex, int32) =
|
|
expect n, {ParLe, IntLit}
|
|
if n.kind == ParLe:
|
|
expectTag n, modIdTag
|
|
incExpect n, IntLit
|
|
let id = pool.integers[n.intId]
|
|
incExpect n, StringLit
|
|
let path = pool.strings[n.litId].AbsoluteFile
|
|
result = (fileInfoIdx(c.graph.config, path), id.int32)
|
|
assert id notin c.modules
|
|
c.modules[id] = result[0]
|
|
inc n
|
|
skipParRi n
|
|
elif n.kind == IntLit:
|
|
let id = pool.integers[n.intId]
|
|
result = (c.modules[id], id.int32)
|
|
inc n
|
|
|
|
proc fromNifSymbol(c: var DecodeContext; n: var Cursor): PSym
|
|
proc fromNifType(c: var DecodeContext; n: var Cursor): PType
|
|
proc fromNif(c: var DecodeContext; n: var Cursor): PNode
|
|
|
|
proc fromNifSymDef(c: var DecodeContext; n: var Cursor): PSym =
|
|
expectTag n, symIdTag
|
|
let info = c.fromNifLineInfo n
|
|
inc n
|
|
let (itemIdModule, nifModId) = c.fromNifModuleId(n)
|
|
expect n, IntLit
|
|
let itemId = pool.integers[n.intId].int32
|
|
incExpect n, Ident
|
|
let ident = c.graph.cache.getIdent(pool.strings[n.litId])
|
|
incExpect n, {Ident, DotToken}
|
|
let magic = if n.kind == Ident: pool.strings[n.litId].parseMagic else: mNone
|
|
incExpect n, {Ident, DotToken}
|
|
let flags = if n.kind == Ident: pool.strings[n.litId].parseSymFlags else: {}
|
|
incExpect n, {Ident, DotToken}
|
|
let options = if n.kind == Ident: pool.strings[n.litId].parseOptions else: {}
|
|
incExpect n, IntLit
|
|
let offset = pool.integers[n.intId].int32
|
|
incExpect n, IntLit
|
|
let disamb = pool.integers[n.intId].int32
|
|
incExpect n, ParLe
|
|
let kind = parseSymKind(pool.tags[n.tagId])
|
|
inc n
|
|
|
|
result = PSym(itemId: ItemId(module: itemIdModule.int32, item: itemId),
|
|
kind: kind,
|
|
magic: magic,
|
|
name: ident,
|
|
info: info,
|
|
flags: flags,
|
|
options: options,
|
|
offset: offset,
|
|
disamb: disamb)
|
|
|
|
# PNode, PSym or PType type fields in PSym can have cycles.
|
|
# Add PSym to `c.symbols` before parsing these fields so that
|
|
# they can refer this PSym.
|
|
let nifItemId = ItemId(module: itemIdModule.int32, item: itemId)
|
|
assert nifItemId notin c.symbols
|
|
c.symbols[nifItemId] = result
|
|
|
|
case kind
|
|
of skLet, skVar, skField, skForVar:
|
|
result.guard = c.fromNifSymbol n
|
|
expect n, IntLit
|
|
result.bitsize = pool.integers[n.intId]
|
|
incExpect n, IntLit
|
|
result.alignment = pool.integers[n.intId]
|
|
inc n
|
|
else:
|
|
discard
|
|
skipParRi n
|
|
result.position = if kind == skModule:
|
|
c.fromNifModuleId(n)[0].int
|
|
else:
|
|
expect n, IntLit
|
|
let p = pool.integers[n.intId]
|
|
inc n
|
|
p
|
|
|
|
result.typ = c.fromNifType n
|
|
result.setOwner(c.fromNifSymbol n)
|
|
result.ast = c.fromNif n
|
|
|
|
expect n, Ident
|
|
result.loc.k = pool.strings[n.litId].parseLocKind()
|
|
incExpect n, StringLit
|
|
result.loc.snippet.add pool.strings[n.litId]
|
|
inc n
|
|
result.constraint = c.fromNif n
|
|
result.instantiatedFrom = c.fromNifSymbol n
|
|
skipParRi n
|
|
|
|
if sfExported in flags or kind == skModule:
|
|
let nifSym = toNifSym(result, c.prog.moduleToNifSuffix, c.graph.config)
|
|
let symId = pool.syms.getOrIncl(nifSym)
|
|
c.prog.nifSymIdToPSym[symId] = result
|
|
|
|
proc fromNifTypeDef(c: var DecodeContext; n: var Cursor): PType =
|
|
expectTag n, typeIdTag
|
|
inc n
|
|
let (itemIdModule, nifModId) = c.fromNifModuleId(n)
|
|
expect n, IntLit
|
|
let itemId = pool.integers[n.intId].int32
|
|
incExpect n, Ident
|
|
let kind = parseTypeKind(pool.strings[n.litId])
|
|
incExpect n, {Ident, DotToken}
|
|
let flags = if n.kind == Ident: pool.strings[n.litId].parseTypeFlags else: {}
|
|
inc n
|
|
|
|
result = PType(itemId: ItemId(module: itemIdModule.int32, item: itemId),
|
|
kind: kind,
|
|
flags: flags)
|
|
let nifItemId = ItemId(module: nifModId, item: itemId)
|
|
assert nifItemId notin c.types
|
|
c.types[nifItemId] = result
|
|
|
|
expect n, {DotToken, ParLe}
|
|
if n.kind == DotToken:
|
|
inc n
|
|
else:
|
|
expectTag n, sonsTag
|
|
inc n
|
|
while n.kind != ParRi:
|
|
result.addAllowNil c.fromNifType n
|
|
inc n
|
|
result.n = c.fromNif n
|
|
result.setOwner c.fromNifSymbol n
|
|
result.sym = c.fromNifSymbol n
|
|
skipParRi n
|
|
|
|
proc fromNifNodeFlags(n: var Cursor): set[TNodeFlag] =
|
|
if n.kind == DotToken:
|
|
result = {}
|
|
inc n
|
|
elif n.kind == Ident:
|
|
result = parseNodeFlags(pool.strings[n.litId])
|
|
inc n
|
|
else:
|
|
assert false, "expected Node flag but got " & $n.kind
|
|
|
|
proc fromNifSymbol(c: var DecodeContext; n: var Cursor): PSym =
|
|
if n.kind == DotToken:
|
|
result = nil
|
|
inc n
|
|
elif n.kind == Symbol:
|
|
if n.symId notin c.prog.nifSymIdToPSym:
|
|
# TODO: Support import statement and remove this branch
|
|
#echo pool.syms[n.symId], " is not found"
|
|
result = nil
|
|
else:
|
|
result = c.prog.nifSymIdToPSym[n.symId]
|
|
inc n
|
|
else:
|
|
expect n, ParLe
|
|
if n.tagId == symIdTag:
|
|
result = c.fromNifSymDef n
|
|
elif n.tagId == symTag:
|
|
incExpect n, IntLit
|
|
let nifModId = c.modules[pool.integers[n.intId].int32]
|
|
incExpect n, IntLit
|
|
let item = pool.integers[n.intId].int32
|
|
let nifItemId = ItemId(module: nifModId.int32, item: item)
|
|
result = c.symbols[nifItemId]
|
|
inc n
|
|
skipParRi n
|
|
else:
|
|
assert false, "expected symbol tag but got " & pool.tags[n.tagId]
|
|
|
|
proc fromNifType(c: var DecodeContext; n: var Cursor): PType =
|
|
if n.kind == DotToken:
|
|
result = nil
|
|
inc n
|
|
else:
|
|
expect n, ParLe
|
|
if n.tagId == typeIdTag:
|
|
result = c.fromNifTypeDef n
|
|
elif n.tagId == typeTag:
|
|
incExpect n, IntLit
|
|
let nifModId = pool.integers[n.intId].int32
|
|
incExpect n, IntLit
|
|
let item = pool.integers[n.intId].int32
|
|
let nifItemId = ItemId(module: nifModId, item: item)
|
|
result = c.types[nifItemId]
|
|
inc n
|
|
skipParRi n
|
|
else:
|
|
assert false, "expected type tag but got " & pool.tags[n.tagId]
|
|
|
|
template withNode(c: var DecodeContext; n: var Cursor; result: PNode; kind: TNodeKind; body: untyped) =
|
|
let info = c.fromNifLineInfo(n)
|
|
incExpect n, {DotToken, Ident}
|
|
let flags = fromNifNodeFlags n
|
|
result = newNodeI(kind, info)
|
|
result.flags = flags
|
|
result.typ = c.fromNifType n
|
|
body
|
|
skipParRi n
|
|
|
|
proc fromNif(c: var DecodeContext; n: var Cursor): PNode =
|
|
result = nil
|
|
case n.kind:
|
|
of DotToken:
|
|
result = nil
|
|
inc n
|
|
of ParLe:
|
|
let kind = n.nodeKind
|
|
case kind:
|
|
of nkEmpty:
|
|
result = newNodeI(nkEmpty, c.fromNifLineInfo(n))
|
|
incExpect n, {Ident, DotToken}
|
|
let flags = fromNifNodeFlags n
|
|
result.flags = flags
|
|
skipParRi n
|
|
of nkIdent:
|
|
let info = c.fromNifLineInfo(n)
|
|
incExpect n, {DotToken, Ident}
|
|
let flags = fromNifNodeFlags n
|
|
let typ = c.fromNifType n
|
|
expect n, Ident
|
|
result = newIdentNode(c.graph.cache.getIdent(pool.strings[n.litId]), info)
|
|
inc n
|
|
result.flags = flags
|
|
result.typ = typ
|
|
skipParRi n
|
|
of nkSym:
|
|
c.withNode n, result, kind:
|
|
result.sym = c.fromNifSymbol n
|
|
of nkCharLit:
|
|
c.withNode n, result, kind:
|
|
expect n, CharLit
|
|
result.intVal = n.charLit.int
|
|
inc n
|
|
of nkIntLit .. nkInt64Lit:
|
|
c.withNode n, result, kind:
|
|
expect n, IntLit
|
|
result.intVal = pool.integers[n.intId]
|
|
inc n
|
|
of nkUIntLit .. nkUInt64Lit:
|
|
c.withNode n, result, kind:
|
|
expect n, UIntLit
|
|
result.intVal = cast[BiggestInt](pool.uintegers[n.uintId])
|
|
inc n
|
|
of nkFloatLit .. nkFloat128Lit:
|
|
c.withNode n, result, kind:
|
|
if n.kind == FloatLit:
|
|
result.floatVal = pool.floats[n.floatId]
|
|
inc n
|
|
elif n.kind == ParLe:
|
|
case pool.tags[n.tagId]
|
|
of "inf":
|
|
result.floatVal = Inf
|
|
of "nan":
|
|
result.floatVal = NaN
|
|
of "neginf":
|
|
result.floatVal = NegInf
|
|
else:
|
|
assert false, "expected float literal but got " & pool.tags[n.tagId]
|
|
inc n
|
|
skipParRi n
|
|
else:
|
|
assert false, "expected float literal but got " & $n.kind
|
|
of nkStrLit .. nkTripleStrLit:
|
|
c.withNode n, result, kind:
|
|
expect n, StringLit
|
|
result.strVal = pool.strings[n.litId]
|
|
inc n
|
|
of nkNilLit:
|
|
c.withNode n, result, kind:
|
|
discard
|
|
of nkNone:
|
|
assert false, "Unknown tag " & pool.tags[n.tagId]
|
|
else:
|
|
c.withNode n, result, kind:
|
|
while n.kind != ParRi:
|
|
result.addAllowNil c.fromNif n
|
|
else:
|
|
assert false, "Not yet implemented " & $n.kind
|
|
|
|
proc loadNif(stream: var Stream; graph: ModuleGraph; prog: NifProgram): PNode =
|
|
discard processDirectives(stream.r)
|
|
|
|
var buf = fromStream(stream)
|
|
var n = beginRead(buf)
|
|
var c = DecodeContext(graph: graph, prog: prog)
|
|
|
|
var n2 = n
|
|
var nested = 0
|
|
while true:
|
|
if n2.info != NoLineInfo:
|
|
break
|
|
elif n2.kind == EofToken:
|
|
break
|
|
elif n2.kind == ParLe:
|
|
inc nested
|
|
elif n2.kind == ParRi:
|
|
dec nested
|
|
if nested == 0: break
|
|
inc n2
|
|
assert n2.info != NoLineInfo
|
|
let info = pool.man.unpack(n2.info)
|
|
let fileIdx = c.graph.config.fileInfoIdx(pool.files[info.file].AbsoluteFile)
|
|
var currentModule = graph.newModule(fileIdx)
|
|
if currentModule.itemId.module == 0'i32:
|
|
currentModule.flags = {sfMainModule, sfSystemModule}
|
|
|
|
c.symbols[currentModule.itemId] = currentModule
|
|
let nifSym = toNifSym(currentModule, c.prog.moduleToNifSuffix, c.graph.config)
|
|
let symId = pool.syms.getOrIncl(nifSym)
|
|
c.prog.nifSymIdToPSym[symId] = currentModule
|
|
|
|
result = fromNif(c, n)
|
|
|
|
endRead(buf)
|
|
|
|
proc loadNifFile*(infile: AbsoluteFile; graph: ModuleGraph; prog: NifProgram): PNode =
|
|
var stream = nifstreams.open(infile.string)
|
|
result = loadNif(stream, graph, prog)
|
|
stream.close
|
|
|
|
proc loadNifFromBuffer*(strbuf: sink string; graph: ModuleGraph; prog: NifProgram): PNode =
|
|
var stream = nifstreams.openFromBuffer(strbuf)
|
|
result = loadNif(stream, graph, prog)
|
|
|
|
when isMainModule:
|
|
import std/cmdline
|
|
|
|
if paramCount() > 0:
|
|
var graph = newModuleGraph(newIdentCache(), newConfigRef())
|
|
var node = loadNifFile(paramStr(1).toAbsolute(toAbsoluteDir(".")), graph)
|
|
debug(node)
|