importer.nim 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2013 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. # This module implements the symbol importing mechanism.
  10. import
  11. intsets, strutils, os, ast, astalgo, msgs, options, idents, rodread, lookups,
  12. semdata, passes, renderer, gorgeimpl
  13. proc evalImport*(c: PContext, n: PNode): PNode
  14. proc evalFrom*(c: PContext, n: PNode): PNode
  15. proc lookupPackage(pkg, subdir: PNode): string =
  16. let sub = if subdir != nil: renderTree(subdir, {renderNoComments}).replace(" ") else: ""
  17. case pkg.kind
  18. of nkStrLit, nkRStrLit, nkTripleStrLit:
  19. result = scriptableImport(pkg.strVal, sub, pkg.info)
  20. of nkIdent:
  21. result = scriptableImport(pkg.ident.s, sub, pkg.info)
  22. else:
  23. localError(pkg.info, "package name must be an identifier or string literal")
  24. result = ""
  25. proc getModuleName*(n: PNode): string =
  26. # This returns a short relative module name without the nim extension
  27. # e.g. like "system", "importer" or "somepath/module"
  28. # The proc won't perform any checks that the path is actually valid
  29. case n.kind
  30. of nkStrLit, nkRStrLit, nkTripleStrLit:
  31. try:
  32. result = pathSubs(n.strVal, n.info.toFullPath().splitFile().dir)
  33. except ValueError:
  34. localError(n.info, "invalid path: " & n.strVal)
  35. result = n.strVal
  36. of nkIdent:
  37. result = n.ident.s
  38. of nkSym:
  39. result = n.sym.name.s
  40. of nkInfix:
  41. let n0 = n[0]
  42. let n1 = n[1]
  43. if n0.kind == nkIdent and n0.ident.id == getIdent("as").id:
  44. # XXX hack ahead:
  45. n.kind = nkImportAs
  46. n.sons[0] = n.sons[1]
  47. n.sons[1] = n.sons[2]
  48. n.sons.setLen(2)
  49. return getModuleName(n.sons[0])
  50. if n1.kind == nkPrefix and n1[0].kind == nkIdent and n1[0].ident.s == "$":
  51. if n0.kind == nkIdent and n0.ident.s == "/":
  52. result = lookupPackage(n1[1], n[2])
  53. else:
  54. localError(n.info, "only '/' supported with $package notation")
  55. result = ""
  56. else:
  57. # hacky way to implement 'x / y /../ z':
  58. result = getModuleName(n1)
  59. result.add renderTree(n0, {renderNoComments})
  60. result.add getModuleName(n[2])
  61. of nkPrefix:
  62. if n.sons[0].kind == nkIdent and n.sons[0].ident.s == "$":
  63. result = lookupPackage(n[1], nil)
  64. else:
  65. # hacky way to implement 'x / y /../ z':
  66. result = renderTree(n, {renderNoComments}).replace(" ")
  67. of nkDotExpr:
  68. result = renderTree(n, {renderNoComments}).replace(".", "/")
  69. of nkImportAs:
  70. result = getModuleName(n.sons[0])
  71. else:
  72. localError(n.info, errGenerated, "invalid module name: '$1'" % n.renderTree)
  73. result = ""
  74. proc checkModuleName*(n: PNode; doLocalError=true): int32 =
  75. # This returns the full canonical path for a given module import
  76. let modulename = n.getModuleName
  77. let fullPath = findModule(modulename, n.info.toFullPath)
  78. if fullPath.len == 0:
  79. if doLocalError:
  80. localError(n.info, errCannotOpenFile, modulename)
  81. result = InvalidFileIDX
  82. else:
  83. result = fullPath.fileInfoIdx
  84. proc importPureEnumField*(c: PContext; s: PSym) =
  85. var check = strTableGet(c.importTable.symbols, s.name)
  86. if check == nil:
  87. strTableAdd(c.pureEnumFields, s)
  88. proc rawImportSymbol(c: PContext, s: PSym) =
  89. # This does not handle stubs, because otherwise loading on demand would be
  90. # pointless in practice. So importing stubs is fine here!
  91. # check if we have already a symbol of the same name:
  92. var check = strTableGet(c.importTable.symbols, s.name)
  93. if check != nil and check.id != s.id:
  94. if s.kind notin OverloadableSyms:
  95. # s and check need to be qualified:
  96. incl(c.ambiguousSymbols, s.id)
  97. incl(c.ambiguousSymbols, check.id)
  98. # thanks to 'export' feature, it could be we import the same symbol from
  99. # multiple sources, so we need to call 'StrTableAdd' here:
  100. strTableAdd(c.importTable.symbols, s)
  101. if s.kind == skType:
  102. var etyp = s.typ
  103. if etyp.kind in {tyBool, tyEnum}:
  104. for j in countup(0, sonsLen(etyp.n) - 1):
  105. var e = etyp.n.sons[j].sym
  106. if e.kind != skEnumField:
  107. internalError(s.info, "rawImportSymbol")
  108. # BUGFIX: because of aliases for enums the symbol may already
  109. # have been put into the symbol table
  110. # BUGFIX: but only iff they are the same symbols!
  111. var it: TIdentIter
  112. check = initIdentIter(it, c.importTable.symbols, e.name)
  113. while check != nil:
  114. if check.id == e.id:
  115. e = nil
  116. break
  117. check = nextIdentIter(it, c.importTable.symbols)
  118. if e != nil:
  119. if sfPure notin s.flags:
  120. rawImportSymbol(c, e)
  121. else:
  122. importPureEnumField(c, e)
  123. else:
  124. # rodgen assures that converters and patterns are no stubs
  125. if s.kind == skConverter: addConverter(c, s)
  126. if hasPattern(s): addPattern(c, s)
  127. proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
  128. let ident = lookups.considerQuotedIdent(n)
  129. let s = strTableGet(fromMod.tab, ident)
  130. if s == nil:
  131. errorUndeclaredIdentifier(c, n.info, ident.s)
  132. else:
  133. if s.kind == skStub: loadStub(s)
  134. if s.kind notin ExportableSymKinds:
  135. internalError(n.info, "importSymbol: 2")
  136. # for an enumeration we have to add all identifiers
  137. case s.kind
  138. of skProcKinds:
  139. # for a overloadable syms add all overloaded routines
  140. var it: TIdentIter
  141. var e = initIdentIter(it, fromMod.tab, s.name)
  142. while e != nil:
  143. if e.name.id != s.name.id: internalError(n.info, "importSymbol: 3")
  144. rawImportSymbol(c, e)
  145. e = nextIdentIter(it, fromMod.tab)
  146. else: rawImportSymbol(c, s)
  147. proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
  148. var i: TTabIter
  149. var s = initTabIter(i, fromMod.tab)
  150. while s != nil:
  151. if s.kind != skModule:
  152. if s.kind != skEnumField:
  153. if s.kind notin ExportableSymKinds:
  154. internalError(s.info, "importAllSymbols: " & $s.kind)
  155. if exceptSet.isNil or s.name.id notin exceptSet:
  156. rawImportSymbol(c, s)
  157. s = nextIter(i, fromMod.tab)
  158. proc importAllSymbols*(c: PContext, fromMod: PSym) =
  159. var exceptSet: IntSet
  160. importAllSymbolsExcept(c, fromMod, exceptSet)
  161. proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet) =
  162. if n.isNil: return
  163. case n.kind
  164. of nkExportStmt:
  165. for a in n:
  166. assert a.kind == nkSym
  167. let s = a.sym
  168. if s.kind == skModule:
  169. importAllSymbolsExcept(c, s, exceptSet)
  170. elif exceptSet.isNil or s.name.id notin exceptSet:
  171. rawImportSymbol(c, s)
  172. of nkExportExceptStmt:
  173. localError(n.info, errGenerated, "'export except' not implemented")
  174. else:
  175. for i in 0..safeLen(n)-1:
  176. importForwarded(c, n.sons[i], exceptSet)
  177. proc importModuleAs(n: PNode, realModule: PSym): PSym =
  178. result = realModule
  179. if n.kind != nkImportAs: discard
  180. elif n.len != 2 or n.sons[1].kind != nkIdent:
  181. localError(n.info, errGenerated, "module alias must be an identifier")
  182. elif n.sons[1].ident.id != realModule.name.id:
  183. # some misguided guy will write 'import abc.foo as foo' ...
  184. result = createModuleAlias(realModule, n.sons[1].ident, realModule.info)
  185. proc myImportModule(c: PContext, n: PNode): PSym =
  186. var f = checkModuleName(n)
  187. if f != InvalidFileIDX:
  188. let L = c.graph.importStack.len
  189. let recursion = c.graph.importStack.find(f)
  190. c.graph.importStack.add f
  191. #echo "adding ", toFullPath(f), " at ", L+1
  192. if recursion >= 0:
  193. var err = ""
  194. for i in countup(recursion, L-1):
  195. if i > recursion: err.add "\n"
  196. err.add toFullPath(c.graph.importStack[i]) & " imports " &
  197. toFullPath(c.graph.importStack[i+1])
  198. c.recursiveDep = err
  199. result = importModuleAs(n, gImportModule(c.graph, c.module, f, c.cache))
  200. #echo "set back to ", L
  201. c.graph.importStack.setLen(L)
  202. # we cannot perform this check reliably because of
  203. # test: modules/import_in_config)
  204. when true:
  205. if result.info.fileIndex == c.module.info.fileIndex and
  206. result.info.fileIndex == n.info.fileIndex:
  207. localError(n.info, errGenerated, "A module cannot import itself")
  208. if sfDeprecated in result.flags:
  209. message(n.info, warnDeprecated, result.name.s)
  210. #suggestSym(n.info, result, false)
  211. proc impMod(c: PContext; it: PNode) =
  212. let m = myImportModule(c, it)
  213. if m != nil:
  214. var emptySet: IntSet
  215. # ``addDecl`` needs to be done before ``importAllSymbols``!
  216. addDecl(c, m, it.info) # add symbol to symbol table of module
  217. importAllSymbolsExcept(c, m, emptySet)
  218. #importForwarded(c, m.ast, emptySet)
  219. proc evalImport(c: PContext, n: PNode): PNode =
  220. result = n
  221. for i in countup(0, sonsLen(n) - 1):
  222. let it = n.sons[i]
  223. if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket:
  224. let sep = it[0]
  225. let dir = it[1]
  226. let a = newNodeI(nkInfix, it.info)
  227. a.add sep
  228. a.add dir
  229. a.add sep # dummy entry, replaced in the loop
  230. for x in it[2]:
  231. a.sons[2] = x
  232. impMod(c, a)
  233. else:
  234. impMod(c, it)
  235. proc evalFrom(c: PContext, n: PNode): PNode =
  236. result = n
  237. checkMinSonsLen(n, 2)
  238. var m = myImportModule(c, n.sons[0])
  239. if m != nil:
  240. n.sons[0] = newSymNode(m)
  241. addDecl(c, m, n.info) # add symbol to symbol table of module
  242. for i in countup(1, sonsLen(n) - 1):
  243. if n.sons[i].kind != nkNilLit:
  244. importSymbol(c, n.sons[i], m)
  245. proc evalImportExcept*(c: PContext, n: PNode): PNode =
  246. result = n
  247. checkMinSonsLen(n, 2)
  248. var m = myImportModule(c, n.sons[0])
  249. if m != nil:
  250. n.sons[0] = newSymNode(m)
  251. addDecl(c, m, n.info) # add symbol to symbol table of module
  252. var exceptSet = initIntSet()
  253. for i in countup(1, sonsLen(n) - 1):
  254. let ident = lookups.considerQuotedIdent(n.sons[i])
  255. exceptSet.incl(ident.id)
  256. importAllSymbolsExcept(c, m, exceptSet)
  257. #importForwarded(c, m.ast, exceptSet)