pipelines.nim 11 KB

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