123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- 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})
|