importer.nim 8.5 KB

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