Files
Nim/tests/icnif/tencode_node2node.nim
2025-11-06 04:50:19 +09:00

284 lines
8.3 KiB
Nim

import std/assertions
import "../../compiler/icnif" / [nifencoder, nifdecoder]
import "../../compiler" / [idents, ast, astalgo, options, pathutils, modulegraphs, modules, msgs, pipelines, syntaxes, sem, llstream, lineinfos]
# This test generates PNode by semchecks test code.
# Then it is used to test icnif/nifencoder and nifdecoder.
const TestCodeDir = currentSourcePath().AbsoluteFile.splitFile().dir / RelativeDir"testcode"
proc newConfigRefForTest(): ConfigRef =
var conf = newConfigRef()
conf.setDefaultLibpath()
conf.searchPaths.add(conf.libpath)
excl(conf.notes, hintProcessing)
excl(conf.mainPackageNotes, hintProcessing)
result = conf
proc newModuleGraphForSem(cache: IdentCache; conf: ConfigRef): ModuleGraph =
var graph = newModuleGraph(cache, conf)
graph.setPipeLinePass(SemPass)
graph.compilePipelineSystemModule()
result = graph
proc sem(graph: ModuleGraph; path: AbsoluteFile): PNode =
result = nil
let fileIdx = fileInfoIdx(graph.config, path)
var module = newModule(graph, fileIdx)
registerModule(graph, module)
var idgen = idGeneratorFromModule(module)
let ctx = preparePContext(graph, module, idgen)
var stream = llStreamOpen(path, fmRead)
if stream == nil:
rawMessage(graph.config, errCannotOpenFile, path.string)
return nil
var p: Parser = default(Parser)
syntaxes.openParser(p, fileIdx, stream, graph.cache, graph.config)
checkFirstLineIndentation(p)
block processCode:
if graph.stopCompile(): break processCode
var n = parseTopLevelStmt(p)
if n.kind == nkEmpty: break processCode
# read everything, no streaming possible
var sl = newNodeI(nkStmtList, n.info)
sl.add n
while true:
var n = parseTopLevelStmt(p)
if n.kind == nkEmpty: break
sl.add n
var semNode = semWithPContext(ctx, sl)
return semNode
type
# Nim's AST has cycles that causes infinite recursive loop in eql procs.
# this is used to prevent that happen.
EqlContext = object
nodeStack: seq[PNode]
symStack: seq[PSym]
typStack: seq[PType]
confX, confY: ConfigRef # used to print the line info when there is a mismatch
# and get path from FileIndex
# Compare PType, PSym and PNode but ignores fields nifencoder and nifdecoder doesn't support
# `x` is generated by sem.nim and `y` is decoded by icnif/nifdecoder.
proc eql(x, y: PNode; c: var EqlContext): bool
proc eql(x, y: PType; c: var EqlContext): bool
proc eql(x, y: TLoc): bool =
if x.k != y.k:
echo "loc kind mismatch: ", x.k, "/", y.k
result = false
elif x.snippet != y.snippet:
echo "loc snippet mismatch: ", x.snippet, "/", y.snippet
result = false
else:
result = true
proc eqlFileIndex(x, y: int; c: EqlContext): bool =
let xpath = c.confX.toFullPath(x.FileIndex)
let ypath = c.confY.toFullPath(y.FileIndex)
if xpath != ypath:
echo "file index mismatch: ", xpath, "/", ypath
result = false
else:
result = true
proc eqlSymPos(x, y: PSym; c: EqlContext): bool =
if x.kind == skModule:
result = eqlFileIndex(x.position, y.position, c)
elif x.position != y.position:
echo "symbol position mismatch: ", x.position, "/", y.position
result = false
else:
result = true
proc eqlItemId(x, y: ItemId; c: EqlContext): bool =
if x.item != y.item:
echo "itemId.item mismatch: ", x.item, "/", y.item
result = false
elif not eqlFileIndex(x.module, y.module, c):
result = false
else:
result = true
proc eql(x, y: PSym; c: var EqlContext): bool =
if x == nil and y == nil:
result = true
elif x == nil or y == nil:
echo "symbol is missing"
result = false
elif x.name.s != y.name.s:
echo "symbol name mismatch: ", x.name.s, "/", y.name.s
result = false
elif not eqlItemId(x.itemId, y.itemId, c):
echo "symbol itemId mismatch"
result = false
elif x.kind != y.kind:
echo "symbol kind mismatch: ", x.kind, "/", y.kind
result = false
elif x.flags != y.flags:
echo "symbol flag mismatch: ", x.flags, "/", y.flags
result = false
elif not eqlSymPos(x, y, c):
result = false
elif x.disamb != y.disamb:
echo "symbol disamb mismatch: ", x.disamb, "/", y.disamb
result = false
elif not eql(x.loc, y.loc):
echo "symbol.loc mismatch"
result = false
else:
if c.symStack.len != 0:
for i in countDown(c.symStack.len - 1, 0):
if x == c.symStack[i]:
return true
c.symStack.add x
if not eql(x.typ, y.typ, c):
echo "symbol type mismatch:"
result = false
elif not eql(x.owner, y.owner, c):
echo "Symbol owner mismatch:"
debug(x.owner)
debug(y.owner)
result = false
else:
result = true
discard c.symStack.pop
proc eql(x, y: PType; c: var EqlContext): bool =
if x == nil and y == nil:
result = true
elif x == nil or y == nil:
echo "type is missing"
result = false
elif not eqlItemId(x.itemId, y.itemId, c):
echo "type itemId mismatch"
result = false
elif x.kind != y.kind:
echo "type kind mismatch: ", x.kind, "/", y.kind
result = false
elif x.flags != y.flags:
echo "type flag mismatch: ", x.flags, "/", y.flags
result = false
else:
if c.typStack.len != 0:
for i in countDown(c.typStack.len - 1, 0):
if x == c.typStack[i]:
# echo "cycle is detected in PType"
return true
c.typStack.add x
if not eql(x.n, y.n, c):
echo "type.n mismatch"
debug(x.n)
debug(y.n)
result = false
elif not eql(x.owner, y.owner, c):
echo "type owner mismatch: "
debug(x.owner)
debug(y.owner)
result = false
elif not eql(x.sym, y.sym, c):
echo "type sym mismatch:"
debug(x.sym)
debug(y.sym)
result = false
elif x.kidsLen != y.kidsLen:
echo "type kidsLen mismatch"
result = false
else:
result = true
for i in 0 ..< x.kidsLen:
if not eql(x[i], y[i], c):
echo "type kids mismatch: "
debug(x[i])
debug(y[i])
result = false
break
discard c.typStack.pop
proc eql(x, y: PNode; c: var EqlContext): bool =
if x == nil and y == nil:
result = true
elif x == nil or y == nil:
result = false
elif x.kind != y.kind:
echo "node kind mismatch: ", x.kind, "/", y.kind
result = false
elif x.flags != y.flags:
echo "node flag mismatch: ", x.flags, "/", y.flags
debug(x)
debug(y)
result = false
elif x.safeLen == y.safeLen:
if c.nodeStack.len != 0:
for i in countDown(c.nodeStack.len - 1, 0):
if x == c.nodeStack[i]:
# echo "cycle is detected in PNode"
return true
c.nodeStack.add x
if not eql(x.typ, y.typ, c):
echo "PNode type mismatch at ", `$`(c.confX, x.info), ":"
debug(x)
debug(y)
debug(x.typ)
debug(y.typ)
result = false
else:
case x.kind:
of nkIdent:
# these idents are generated from different IdentCache
result = x.ident.s == y.ident.s
if not result:
echo "PNode identifier mismatch: ", `$`(c.confX, x.info), x.ident.s, "/", y.ident.s
of nkSym:
result = eql(x.sym, y.sym, c)
if not result:
echo "Symbol mismatch:"
debug(x.sym)
debug(y.sym)
debug(x.sym.typ)
debug(y.sym.typ)
of nkCharLit .. nkTripleStrLit:
result = sameValue(x, y)
else:
result = true
for i in 0 ..< x.safeLen:
if not eql(x[i], y[i], c):
result = false
break
discard c.nodeStack.pop
else:
echo "node length mismatch"
debug(x)
debug(y)
result = false
proc testNifEncDec(graph: ModuleGraph; src: string) =
let fullPath = TestCodeDir / RelativeFile(src)
let n = sem(graph, fullPath)
#debug(n)
let nif = saveNifToBuffer(n, graph.config)
#echo nif
# Don't reuse the ModuleGraph used for semcheck when load NIF.
var graphForLoad = newModuleGraph(newIdentCache(), newConfigRefForTest())
let n2 = loadNifFromBuffer(nif, graphForLoad)
#debug(n2)
var c = EqlContext(confX: graph.config, confY: graphForLoad.config)
assert eql(n, n2, c)
var conf = newConfigRefForTest()
var cache = newIdentCache()
var graph = newModuleGraphForSem(cache, conf)
testNifEncDec(graph, "modtest1.nim")
testNifEncDec(graph, "modtestliterals.nim")
testNifEncDec(graph, "modtesttypesections.nim")
testNifEncDec(graph, "modtestpragmas.nim")