pipelines.nim 11 KB

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