pipelines.nim 10 KB

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