mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
313 lines
12 KiB
Nim
313 lines
12 KiB
Nim
import sem, cgen, modulegraphs, ast, llstream, parser, msgs,
|
|
lineinfos, reorder, options, semdata, cgendata, modules, pathutils,
|
|
packages, syntaxes, depends, vm, pragmas, idents, lookups, wordrecg,
|
|
liftdestructors
|
|
|
|
import pipelineutils
|
|
|
|
import ../dist/checksums/src/checksums/sha1
|
|
|
|
when not defined(leanCompiler):
|
|
import jsgen, docgen2
|
|
|
|
import std/[syncio, objectdollar, assertions, tables, strutils, strtabs]
|
|
import renderer
|
|
import ic/replayer
|
|
|
|
proc setPipeLinePass*(graph: ModuleGraph; pass: PipelinePass) =
|
|
graph.pipelinePass = pass
|
|
|
|
proc processPipeline(graph: ModuleGraph; semNode: PNode; bModule: PPassContext): PNode =
|
|
case graph.pipelinePass
|
|
of CgenPass:
|
|
result = semNode
|
|
if bModule != nil:
|
|
genTopLevelStmt(BModule(bModule), result)
|
|
of JSgenPass:
|
|
when not defined(leanCompiler):
|
|
result = processJSCodeGen(bModule, semNode)
|
|
else:
|
|
result = nil
|
|
of GenDependPass:
|
|
result = addDotDependency(bModule, semNode)
|
|
of SemPass:
|
|
result = graph.emptyNode
|
|
of Docgen2Pass, Docgen2TexPass:
|
|
when not defined(leanCompiler):
|
|
result = processNode(bModule, semNode)
|
|
else:
|
|
result = nil
|
|
of Docgen2JsonPass:
|
|
when not defined(leanCompiler):
|
|
result = processNodeJson(bModule, semNode)
|
|
else:
|
|
result = nil
|
|
of EvalPass, InterpreterPass:
|
|
result = interpreterCode(bModule, semNode)
|
|
of NonePass:
|
|
raiseAssert "use setPipeLinePass to set a proper PipelinePass"
|
|
|
|
proc processImplicitImports(graph: ModuleGraph; implicits: seq[string], nodeKind: TNodeKind,
|
|
m: PSym, ctx: PContext, bModule: PPassContext, idgen: IdGenerator,
|
|
) =
|
|
# XXX fixme this should actually be relative to the config file!
|
|
let relativeTo = toFullPath(graph.config, m.info)
|
|
for module in items(implicits):
|
|
# implicit imports should not lead to a module importing itself
|
|
if m.position != resolveMod(graph.config, module, relativeTo).int32:
|
|
var importStmt = newNodeI(nodeKind, m.info)
|
|
var str = newStrNode(nkStrLit, module)
|
|
str.info = m.info
|
|
importStmt.add str
|
|
message(graph.config, importStmt.info, hintProcessingStmt, $idgen[])
|
|
let semNode = semWithPContext(ctx, importStmt)
|
|
if semNode == nil or processPipeline(graph, semNode, bModule) == nil:
|
|
break
|
|
|
|
proc prePass(c: PContext; n: PNode) =
|
|
for son in n:
|
|
if son.kind == nkPragma:
|
|
for s in son:
|
|
var key = if s.kind in nkPragmaCallKinds and s.len > 1: s[0] else: s
|
|
if key.kind in {nkBracketExpr, nkCast} or key.kind notin nkIdentKinds:
|
|
continue
|
|
let ident = whichKeyword(considerQuotedIdent(c, key))
|
|
case ident
|
|
of wReorder:
|
|
pragmaNoForward(c, s, flag = sfReorder)
|
|
of wExperimental:
|
|
if isTopLevel(c) and s.kind in nkPragmaCallKinds and s.len == 2:
|
|
let name = c.semConstExpr(c, s[1])
|
|
case name.kind
|
|
of nkStrLit, nkRStrLit, nkTripleStrLit:
|
|
try:
|
|
let feature = parseEnum[Feature](name.strVal)
|
|
if feature == codeReordering:
|
|
c.features.incl feature
|
|
c.module.flags.incl sfReorder
|
|
except ValueError:
|
|
discard
|
|
else:
|
|
discard
|
|
else:
|
|
discard
|
|
|
|
proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
|
|
stream: PLLStream): bool =
|
|
if graph.stopCompile(): return true
|
|
var
|
|
p: Parser = default(Parser)
|
|
s: PLLStream
|
|
fileIdx = module.fileIdx
|
|
|
|
prepareConfigNotes(graph, module)
|
|
let ctx = preparePContext(graph, module, idgen)
|
|
let bModule: PPassContext =
|
|
case graph.pipelinePass
|
|
of CgenPass:
|
|
setupCgen(graph, module, idgen)
|
|
of JSgenPass:
|
|
when not defined(leanCompiler):
|
|
setupJSgen(graph, module, idgen)
|
|
else:
|
|
nil
|
|
of EvalPass, InterpreterPass:
|
|
setupEvalGen(graph, module, idgen)
|
|
of GenDependPass:
|
|
setupDependPass(graph, module, idgen)
|
|
of Docgen2Pass:
|
|
when not defined(leanCompiler):
|
|
openHtml(graph, module, idgen)
|
|
else:
|
|
nil
|
|
of Docgen2TexPass:
|
|
when not defined(leanCompiler):
|
|
openTex(graph, module, idgen)
|
|
else:
|
|
nil
|
|
of Docgen2JsonPass:
|
|
when not defined(leanCompiler):
|
|
openJson(graph, module, idgen)
|
|
else:
|
|
nil
|
|
of SemPass:
|
|
nil
|
|
of NonePass:
|
|
raiseAssert "use setPipeLinePass to set a proper PipelinePass"
|
|
|
|
if stream == nil:
|
|
let filename = toFullPathConsiderDirty(graph.config, fileIdx)
|
|
s = llStreamOpen(filename, fmRead)
|
|
if s == nil:
|
|
rawMessage(graph.config, errCannotOpenFile, filename.string)
|
|
return false
|
|
graph.interactive = false
|
|
else:
|
|
s = stream
|
|
graph.interactive = stream.kind == llsStdIn
|
|
while true:
|
|
syntaxes.openParser(p, fileIdx, s, graph.cache, graph.config)
|
|
|
|
if not belongsToStdlib(graph, module) or (belongsToStdlib(graph, module) and module.name.s == "distros"):
|
|
# XXX what about caching? no processing then? what if I change the
|
|
# modules to include between compilation runs? we'd need to track that
|
|
# in ROD files. I think we should enable this feature only
|
|
# for the interactive mode.
|
|
if module.name.s != "nimscriptapi":
|
|
processImplicitImports graph, graph.config.implicitImports, nkImportStmt, module, ctx, bModule, idgen
|
|
processImplicitImports graph, graph.config.implicitIncludes, nkIncludeStmt, module, ctx, bModule, idgen
|
|
|
|
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
|
|
|
|
prePass(ctx, sl)
|
|
if sfReorder in module.flags or codeReordering in graph.config.features:
|
|
sl = reorder(graph, sl, module)
|
|
if graph.pipelinePass != EvalPass:
|
|
message(graph.config, sl.info, hintProcessingStmt, $idgen[])
|
|
var semNode = semWithPContext(ctx, sl)
|
|
discard processPipeline(graph, semNode, bModule)
|
|
|
|
closeParser(p)
|
|
if s.kind != llsStdIn: break
|
|
let finalNode = closePContext(graph, ctx, nil)
|
|
case graph.pipelinePass
|
|
of CgenPass:
|
|
if bModule != nil:
|
|
let m = BModule(bModule)
|
|
finalCodegenActions(graph, m, finalNode)
|
|
if graph.dispatchers.len > 0:
|
|
let ctx = preparePContext(graph, module, idgen)
|
|
for disp in getDispatchers(graph):
|
|
let retTyp = disp.typ.returnType
|
|
if retTyp != nil:
|
|
# TODO: properly semcheck the code of dispatcher?
|
|
createTypeBoundOps(graph, ctx, retTyp, disp.ast.info, idgen)
|
|
genProcAux(m, disp)
|
|
discard closePContext(graph, ctx, nil)
|
|
of JSgenPass:
|
|
when not defined(leanCompiler):
|
|
discard finalJSCodeGen(graph, bModule, finalNode)
|
|
of EvalPass, InterpreterPass:
|
|
discard interpreterCode(bModule, finalNode)
|
|
of SemPass, GenDependPass:
|
|
discard
|
|
of Docgen2Pass, Docgen2TexPass:
|
|
when not defined(leanCompiler):
|
|
discard closeDoc(graph, bModule, finalNode)
|
|
of Docgen2JsonPass:
|
|
when not defined(leanCompiler):
|
|
discard closeJson(graph, bModule, finalNode)
|
|
of NonePass:
|
|
raiseAssert "use setPipeLinePass to set a proper PipelinePass"
|
|
|
|
if graph.config.backend notin {backendC, backendCpp, backendObjc}:
|
|
# We only write rod files here if no C-like backend is active.
|
|
# The C-like backends have been patched to support the IC mechanism.
|
|
# They are responsible for closing the rod files. See `cbackend.nim`.
|
|
closeRodFile(graph, module)
|
|
result = true
|
|
|
|
proc compilePipelineModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags; fromModule: PSym = nil): PSym =
|
|
var flags = flags
|
|
if fileIdx == graph.config.projectMainIdx2: flags.incl sfMainModule
|
|
result = graph.getModule(fileIdx)
|
|
|
|
template processModuleAux(moduleStatus) =
|
|
onProcessing(graph, fileIdx, moduleStatus, fromModule = fromModule)
|
|
var s: PLLStream = nil
|
|
if sfMainModule in flags:
|
|
if graph.config.projectIsStdin: s = stdin.llStreamOpen
|
|
elif graph.config.projectIsCmd: s = llStreamOpen(graph.config.cmdInput)
|
|
discard processPipelineModule(graph, result, idGeneratorFromModule(result), s)
|
|
if result == nil:
|
|
var cachedModules: seq[FileIndex] = @[]
|
|
result = moduleFromRodFile(graph, fileIdx, cachedModules)
|
|
let path = toFullPath(graph.config, fileIdx)
|
|
let filename = AbsoluteFile path
|
|
if fileExists(filename): # it could be a stdinfile
|
|
graph.cachedFiles[path] = $secureHashFile(path)
|
|
if result == nil:
|
|
result = newModule(graph, fileIdx)
|
|
result.flags.incl flags
|
|
registerModule(graph, result)
|
|
processModuleAux("import")
|
|
else:
|
|
if sfSystemModule in flags:
|
|
graph.systemModule = result
|
|
if sfMainModule in flags and graph.config.cmd == cmdM:
|
|
result.flags.incl flags
|
|
registerModule(graph, result)
|
|
processModuleAux("import")
|
|
partialInitModule(result, graph, fileIdx, filename)
|
|
for m in cachedModules:
|
|
registerModuleById(graph, m)
|
|
if sfMainModule in flags and graph.config.cmd == cmdM:
|
|
discard
|
|
else:
|
|
replayStateChanges(graph.packed.pm[m.int].module, graph)
|
|
replayGenericCacheInformation(graph, m.int)
|
|
elif graph.isDirty(result):
|
|
result.flags.excl sfDirty
|
|
# reset module fields:
|
|
initStrTables(graph, result)
|
|
result.ast = nil
|
|
processModuleAux("import(dirty)")
|
|
graph.markClientsDirty(fileIdx)
|
|
|
|
proc importPipelineModule(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym =
|
|
# this is called by the semantic checking phase
|
|
assert graph.config != nil
|
|
result = compilePipelineModule(graph, fileIdx, {}, s)
|
|
graph.addDep(s, fileIdx)
|
|
# keep track of import relationships
|
|
if graph.config.hcrOn:
|
|
graph.importDeps.mgetOrPut(FileIndex(s.position), @[]).add(fileIdx)
|
|
#if sfSystemModule in result.flags:
|
|
# localError(result.info, errAttemptToRedefine, result.name.s)
|
|
# restore the notes for outer module:
|
|
graph.config.notes =
|
|
if graph.config.belongsToProjectPackage(s) or isDefined(graph.config, "booting"): graph.config.mainPackageNotes
|
|
else: graph.config.foreignPackageNotes
|
|
|
|
proc connectPipelineCallbacks*(graph: ModuleGraph) =
|
|
graph.includeFileCallback = modules.includeModule
|
|
graph.importModuleCallback = importPipelineModule
|
|
|
|
proc compilePipelineSystemModule*(graph: ModuleGraph) =
|
|
if graph.systemModule == nil:
|
|
connectPipelineCallbacks(graph)
|
|
graph.config.m.systemFileIdx = fileInfoIdx(graph.config,
|
|
graph.config.libpath / RelativeFile"system.nim")
|
|
discard graph.compilePipelineModule(graph.config.m.systemFileIdx, {sfSystemModule})
|
|
|
|
proc compilePipelineProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) =
|
|
connectPipelineCallbacks(graph)
|
|
let conf = graph.config
|
|
wantMainModule(conf)
|
|
configComplete(graph)
|
|
|
|
let systemFileIdx = fileInfoIdx(conf, conf.libpath / RelativeFile"system.nim")
|
|
let projectFile = if projectFileIdx == InvalidFileIdx: conf.projectMainIdx else: projectFileIdx
|
|
conf.projectMainIdx2 = projectFile
|
|
|
|
let packSym = getPackage(graph, projectFile)
|
|
graph.config.mainPackageId = packSym.getPackageId
|
|
graph.importStack.add projectFile
|
|
|
|
if projectFile == systemFileIdx:
|
|
discard graph.compilePipelineModule(projectFile, {sfMainModule, sfSystemModule})
|
|
else:
|
|
graph.compilePipelineSystemModule()
|
|
discard graph.compilePipelineModule(projectFile, {sfMainModule})
|