pipelines.nim 11 KB

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