pipelines.nim 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. import sem, cgen, modulegraphs, ast, llstream, parser, msgs,
  2. lineinfos, reorder, options, semdata, cgendata, modules, pathutils,
  3. packages, syntaxes, depends, vm, pragmas, idents, lookups
  4. import pipelineutils
  5. when not defined(leanCompiler):
  6. import jsgen, docgen2
  7. import std/[syncio, objectdollar, assertions, tables, strutils]
  8. import renderer
  9. import ic/replayer
  10. proc setPipeLinePass*(graph: ModuleGraph; pass: PipelinePass) =
  11. graph.pipelinePass = pass
  12. proc processPipeline(graph: ModuleGraph; semNode: PNode; bModule: PPassContext): PNode =
  13. case graph.pipelinePass
  14. of CgenPass:
  15. result = semNode
  16. if bModule != nil:
  17. genTopLevelStmt(BModule(bModule), result)
  18. of JSgenPass:
  19. when not defined(leanCompiler):
  20. result = processJSCodeGen(bModule, semNode)
  21. of GenDependPass:
  22. result = addDotDependency(bModule, semNode)
  23. of SemPass:
  24. result = graph.emptyNode
  25. of Docgen2Pass, Docgen2TexPass:
  26. when not defined(leanCompiler):
  27. result = processNode(bModule, semNode)
  28. of Docgen2JsonPass:
  29. when not defined(leanCompiler):
  30. result = processNodeJson(bModule, semNode)
  31. of EvalPass, InterpreterPass:
  32. result = interpreterCode(bModule, semNode)
  33. of NonePass:
  34. doAssert false, "use setPipeLinePass to set a proper PipelinePass"
  35. proc processImplicitImports(graph: ModuleGraph; implicits: seq[string], nodeKind: TNodeKind,
  36. m: PSym, ctx: PContext, bModule: PPassContext, idgen: IdGenerator,
  37. ) =
  38. # XXX fixme this should actually be relative to the config file!
  39. let relativeTo = toFullPath(graph.config, m.info)
  40. for module in items(implicits):
  41. # implicit imports should not lead to a module importing itself
  42. if m.position != resolveMod(graph.config, module, relativeTo).int32:
  43. var importStmt = newNodeI(nodeKind, m.info)
  44. var str = newStrNode(nkStrLit, module)
  45. str.info = m.info
  46. importStmt.add str
  47. message(graph.config, importStmt.info, hintProcessingStmt, $idgen[])
  48. let semNode = semWithPContext(ctx, importStmt)
  49. if semNode == nil or processPipeline(graph, semNode, bModule) == nil:
  50. break
  51. proc prePass(c: PContext; n: PNode) =
  52. for son in n:
  53. if son.kind == nkPragma:
  54. for s in son:
  55. var key = if s.kind in nkPragmaCallKinds and s.len > 1: s[0] else: s
  56. if key.kind in {nkBracketExpr, nkCast} or key.kind notin nkIdentKinds:
  57. continue
  58. let ident = whichKeyword(considerQuotedIdent(c, key))
  59. case ident
  60. of wReorder:
  61. pragmaNoForward(c, s, flag = sfReorder)
  62. of wExperimental:
  63. if isTopLevel(c) and s.kind in nkPragmaCallKinds and s.len == 2:
  64. let name = c.semConstExpr(c, s[1])
  65. case name.kind
  66. of nkStrLit, nkRStrLit, nkTripleStrLit:
  67. try:
  68. let feature = parseEnum[Feature](name.strVal)
  69. if feature == codeReordering:
  70. c.features.incl feature
  71. c.module.flags.incl sfReorder
  72. except ValueError:
  73. discard
  74. else:
  75. discard
  76. else:
  77. discard
  78. proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
  79. stream: PLLStream): bool =
  80. if graph.stopCompile(): return true
  81. var
  82. p: Parser
  83. s: PLLStream
  84. fileIdx = module.fileIdx
  85. prepareConfigNotes(graph, module)
  86. let ctx = preparePContext(graph, module, idgen)
  87. let bModule: PPassContext =
  88. case graph.pipelinePass
  89. of CgenPass:
  90. setupCgen(graph, module, idgen)
  91. of JSgenPass:
  92. when not defined(leanCompiler):
  93. setupJSgen(graph, module, idgen)
  94. else:
  95. nil
  96. of EvalPass, InterpreterPass:
  97. setupEvalGen(graph, module, idgen)
  98. of GenDependPass:
  99. setupDependPass(graph, module, idgen)
  100. of Docgen2Pass:
  101. when not defined(leanCompiler):
  102. openHtml(graph, module, idgen)
  103. else:
  104. nil
  105. of Docgen2TexPass:
  106. when not defined(leanCompiler):
  107. openTex(graph, module, idgen)
  108. else:
  109. nil
  110. of Docgen2JsonPass:
  111. when not defined(leanCompiler):
  112. openJson(graph, module, idgen)
  113. else:
  114. nil
  115. of SemPass:
  116. nil
  117. of NonePass:
  118. doAssert false, "use setPipeLinePass to set a proper PipelinePass"
  119. nil
  120. if stream == nil:
  121. let filename = toFullPathConsiderDirty(graph.config, fileIdx)
  122. s = llStreamOpen(filename, fmRead)
  123. if s == nil:
  124. rawMessage(graph.config, errCannotOpenFile, filename.string)
  125. return false
  126. else:
  127. s = stream
  128. while true:
  129. syntaxes.openParser(p, fileIdx, s, graph.cache, graph.config)
  130. if not belongsToStdlib(graph, module) or (belongsToStdlib(graph, module) and module.name.s == "distros"):
  131. # XXX what about caching? no processing then? what if I change the
  132. # modules to include between compilation runs? we'd need to track that
  133. # in ROD files. I think we should enable this feature only
  134. # for the interactive mode.
  135. if module.name.s != "nimscriptapi":
  136. processImplicitImports graph, graph.config.implicitImports, nkImportStmt, module, ctx, bModule, idgen
  137. processImplicitImports graph, graph.config.implicitIncludes, nkIncludeStmt, module, ctx, bModule, idgen
  138. checkFirstLineIndentation(p)
  139. block processCode:
  140. if graph.stopCompile(): break processCode
  141. var n = parseTopLevelStmt(p)
  142. if n.kind == nkEmpty: break processCode
  143. # read everything, no streaming possible
  144. var sl = newNodeI(nkStmtList, n.info)
  145. sl.add n
  146. while true:
  147. var n = parseTopLevelStmt(p)
  148. if n.kind == nkEmpty: break
  149. sl.add n
  150. prePass(ctx, sl)
  151. if sfReorder in module.flags or codeReordering in graph.config.features:
  152. sl = reorder(graph, sl, module)
  153. if graph.pipelinePass != EvalPass:
  154. message(graph.config, sl.info, hintProcessingStmt, $idgen[])
  155. var semNode = semWithPContext(ctx, sl)
  156. discard processPipeline(graph, semNode, bModule)
  157. closeParser(p)
  158. if s.kind != llsStdIn: break
  159. let finalNode = closePContext(graph, ctx, nil)
  160. case graph.pipelinePass
  161. of CgenPass:
  162. if bModule != nil:
  163. finalCodegenActions(graph, BModule(bModule), finalNode)
  164. of JSgenPass:
  165. when not defined(leanCompiler):
  166. discard finalJSCodeGen(graph, bModule, finalNode)
  167. of EvalPass, InterpreterPass:
  168. discard interpreterCode(bModule, finalNode)
  169. of SemPass, GenDependPass:
  170. discard
  171. of Docgen2Pass, Docgen2TexPass:
  172. when not defined(leanCompiler):
  173. discard closeDoc(graph, bModule, finalNode)
  174. of Docgen2JsonPass:
  175. when not defined(leanCompiler):
  176. discard closeJson(graph, bModule, finalNode)
  177. of NonePass:
  178. doAssert false, "use setPipeLinePass to set a proper PipelinePass"
  179. if graph.config.backend notin {backendC, backendCpp, backendObjc}:
  180. # We only write rod files here if no C-like backend is active.
  181. # The C-like backends have been patched to support the IC mechanism.
  182. # They are responsible for closing the rod files. See `cbackend.nim`.
  183. closeRodFile(graph, module)
  184. result = true
  185. proc compilePipelineModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags, fromModule: PSym = nil): PSym =
  186. var flags = flags
  187. if fileIdx == graph.config.projectMainIdx2: flags.incl sfMainModule
  188. result = graph.getModule(fileIdx)
  189. template processModuleAux(moduleStatus) =
  190. onProcessing(graph, fileIdx, moduleStatus, fromModule = fromModule)
  191. var s: PLLStream
  192. if sfMainModule in flags:
  193. if graph.config.projectIsStdin: s = stdin.llStreamOpen
  194. elif graph.config.projectIsCmd: s = llStreamOpen(graph.config.cmdInput)
  195. discard processPipelineModule(graph, result, idGeneratorFromModule(result), s)
  196. if result == nil:
  197. var cachedModules: seq[FileIndex]
  198. result = moduleFromRodFile(graph, fileIdx, cachedModules)
  199. let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
  200. if result == nil:
  201. result = newModule(graph, fileIdx)
  202. result.flags.incl flags
  203. registerModule(graph, result)
  204. processModuleAux("import")
  205. else:
  206. if sfSystemModule in flags:
  207. graph.systemModule = result
  208. partialInitModule(result, graph, fileIdx, filename)
  209. for m in cachedModules:
  210. registerModuleById(graph, m)
  211. replayStateChanges(graph.packed[m.int].module, graph)
  212. replayGenericCacheInformation(graph, m.int)
  213. elif graph.isDirty(result):
  214. result.flags.excl sfDirty
  215. # reset module fields:
  216. initStrTables(graph, result)
  217. result.ast = nil
  218. processModuleAux("import(dirty)")
  219. graph.markClientsDirty(fileIdx)
  220. proc importPipelineModule(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym =
  221. # this is called by the semantic checking phase
  222. assert graph.config != nil
  223. result = compilePipelineModule(graph, fileIdx, {}, s)
  224. graph.addDep(s, fileIdx)
  225. # keep track of import relationships
  226. if graph.config.hcrOn:
  227. graph.importDeps.mgetOrPut(FileIndex(s.position), @[]).add(fileIdx)
  228. #if sfSystemModule in result.flags:
  229. # localError(result.info, errAttemptToRedefine, result.name.s)
  230. # restore the notes for outer module:
  231. graph.config.notes =
  232. if graph.config.belongsToProjectPackage(s) or isDefined(graph.config, "booting"): graph.config.mainPackageNotes
  233. else: graph.config.foreignPackageNotes
  234. proc connectPipelineCallbacks*(graph: ModuleGraph) =
  235. graph.includeFileCallback = modules.includeModule
  236. graph.importModuleCallback = importPipelineModule
  237. proc compilePipelineSystemModule*(graph: ModuleGraph) =
  238. if graph.systemModule == nil:
  239. connectPipelineCallbacks(graph)
  240. graph.config.m.systemFileIdx = fileInfoIdx(graph.config,
  241. graph.config.libpath / RelativeFile"system.nim")
  242. discard graph.compilePipelineModule(graph.config.m.systemFileIdx, {sfSystemModule})
  243. proc compilePipelineProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) =
  244. connectPipelineCallbacks(graph)
  245. let conf = graph.config
  246. wantMainModule(conf)
  247. configComplete(graph)
  248. let systemFileIdx = fileInfoIdx(conf, conf.libpath / RelativeFile"system.nim")
  249. let projectFile = if projectFileIdx == InvalidFileIdx: conf.projectMainIdx else: projectFileIdx
  250. conf.projectMainIdx2 = projectFile
  251. let packSym = getPackage(graph, projectFile)
  252. graph.config.mainPackageId = packSym.getPackageId
  253. graph.importStack.add projectFile
  254. if projectFile == systemFileIdx:
  255. discard graph.compilePipelineModule(projectFile, {sfMainModule, sfSystemModule})
  256. else:
  257. graph.compilePipelineSystemModule()
  258. discard graph.compilePipelineModule(projectFile, {sfMainModule})