diff options
author | Araq <rumpf_a@web.de> | 2012-12-01 19:10:47 +0100 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2012-12-01 19:10:47 +0100 |
commit | c98e3d2c274ac4bd4227d4e3f7e0d2636827f88c (patch) | |
tree | c9b624562ae38f56d19e7ed190307caea8c61241 | |
parent | f503439e811d822f19daa9591cb3bd9e90edabe7 (diff) | |
download | Nim-c98e3d2c274ac4bd4227d4e3f7e0d2636827f88c.tar.gz |
implements 'export' feature
-rwxr-xr-x | compiler/astalgo.nim | 17 | ||||
-rwxr-xr-x | compiler/importer.nim | 71 | ||||
-rwxr-xr-x | compiler/semexprs.nim | 23 | ||||
-rwxr-xr-x | doc/manual.txt | 31 | ||||
-rwxr-xr-x | lib/pure/collections/intsets.nim | 5 | ||||
-rw-r--r-- | tests/compile/mexporta.nim | 8 | ||||
-rw-r--r-- | tests/compile/mexportb.nim | 7 | ||||
-rw-r--r-- | tests/compile/texport.nim | 10 | ||||
-rwxr-xr-x | todo.txt | 2 | ||||
-rwxr-xr-x | web/news.txt | 3 |
10 files changed, 137 insertions, 40 deletions
diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index da0de3e94..9a2bebab4 100755 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -585,8 +585,9 @@ proc StrTableContains(t: TStrTable, n: PSym): bool = proc StrTableRawInsert(data: var TSymSeq, n: PSym) = var h: THash = n.name.h and high(data) while data[h] != nil: - if data[h] == n: - InternalError(n.info, "StrTableRawInsert: " & n.name.s) + if data[h] == n: + # allowed for 'export' feature: + #InternalError(n.info, "StrTableRawInsert: " & n.name.s) return h = nextTry(h, high(data)) assert(data[h] == nil) @@ -617,23 +618,23 @@ proc StrTableAdd(t: var TStrTable, n: PSym) = StrTableRawInsert(t.data, n) inc(t.counter) -proc StrTableIncl*(t: var TStrTable, n: PSym): bool = +proc StrTableIncl*(t: var TStrTable, n: PSym): bool {.discardable.} = # returns true if n is already in the string table: # It is essential that `n` is written nevertheless! # This way the newest redefinition is picked by the semantic analyses! assert n.name != nil var h: THash = n.name.h and high(t.data) - while true: + while true: var it = t.data[h] - if it == nil: break - if it.name.id == n.name.id: + if it == nil: break + if it.name.id == n.name.id: t.data[h] = n # overwrite it with newer definition! return true # found it h = nextTry(h, high(t.data)) - if mustRehash(len(t.data), t.counter): + if mustRehash(len(t.data), t.counter): StrTableEnlarge(t) StrTableRawInsert(t.data, n) - else: + else: assert(t.data[h] == nil) t.data[h] = n inc(t.counter) diff --git a/compiler/importer.nim b/compiler/importer.nim index 11fc45601..774bcea6a 100755 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -15,7 +15,6 @@ import proc evalImport*(c: PContext, n: PNode): PNode proc evalFrom*(c: PContext, n: PNode): PNode -proc importAllSymbols*(c: PContext, fromMod: PSym) proc getModuleName*(n: PNode): string = # This returns a short relative module name without the nim extension @@ -42,42 +41,43 @@ proc checkModuleName*(n: PNode): string = if result.len == 0: LocalError(n.info, errCannotOpenFile, modulename) -proc rawImportSymbol(c: PContext, s: PSym) = +proc rawImportSymbol(c: PContext, s: PSym) = # This does not handle stubs, because otherwise loading on demand would be # pointless in practice. So importing stubs is fine here! - var copy = s # do not copy symbols when importing! # check if we have already a symbol of the same name: var check = StrTableGet(c.tab.stack[importTablePos], s.name) - if check != nil and check.id != copy.id: - if s.kind notin OverloadableSyms: + if check != nil and check.id != s.id: + if s.kind notin OverloadableSyms: # s and check need to be qualified: - Incl(c.AmbiguousSymbols, copy.id) + Incl(c.AmbiguousSymbols, s.id) Incl(c.AmbiguousSymbols, check.id) - StrTableAdd(c.tab.stack[importTablePos], copy) - if s.kind == skType: + # thanks to 'export' feature, it could be we import the same symbol from + # multiple sources, so we need to call 'StrTableAdd' here: + StrTableAdd(c.tab.stack[importTablePos], s) + if s.kind == skType: var etyp = s.typ - if etyp.kind in {tyBool, tyEnum} and sfPure notin s.flags: - for j in countup(0, sonsLen(etyp.n) - 1): + if etyp.kind in {tyBool, tyEnum} and sfPure notin s.flags: + for j in countup(0, sonsLen(etyp.n) - 1): var e = etyp.n.sons[j].sym - if (e.Kind != skEnumField): + if e.Kind != skEnumField: InternalError(s.info, "rawImportSymbol") # BUGFIX: because of aliases for enums the symbol may already # have been put into the symbol table # BUGFIX: but only iff they are the same symbols! var it: TIdentIter check = InitIdentIter(it, c.tab.stack[importTablePos], e.name) - while check != nil: - if check.id == e.id: + while check != nil: + if check.id == e.id: e = nil - break + break check = NextIdentIter(it, c.tab.stack[importTablePos]) - if e != nil: + if e != nil: rawImportSymbol(c, e) else: # rodgen assures that converters and patterns are no stubs if s.kind == skConverter: addConverter(c, s) if hasPattern(s): addPattern(c, s) - + proc importSymbol(c: PContext, n: PNode, fromMod: PSym) = let ident = lookups.considerAcc(n) let s = StrTableGet(fromMod.tab, ident) @@ -98,17 +98,6 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) = rawImportSymbol(c, e) e = NextIdentIter(it, fromMod.tab) else: rawImportSymbol(c, s) - -proc importAllSymbols(c: PContext, fromMod: PSym) = - var i: TTabIter - var s = InitTabIter(i, fromMod.tab) - while s != nil: - if s.kind != skModule: - if s.kind != skEnumField: - if s.Kind notin ExportableSymKinds: - InternalError(s.info, "importAllSymbols: " & $s.kind) - rawImportSymbol(c, s) # this is correct! - s = NextIter(i, fromMod.tab) proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: TIntSet) = var i: TTabIter @@ -118,12 +107,34 @@ proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: TIntSet) = if s.kind != skEnumField: if s.Kind notin ExportableSymKinds: InternalError(s.info, "importAllSymbols: " & $s.kind) - if s.name.id notin exceptSet: + if exceptSet.empty or s.name.id notin exceptSet: rawImportSymbol(c, s) s = NextIter(i, fromMod.tab) +proc importAllSymbols*(c: PContext, fromMod: PSym) = + var exceptSet: TIntSet + importAllSymbolsExcept(c, fromMod, exceptSet) + +proc importForwarded(c: PContext, n: PNode, exceptSet: TIntSet) = + if n.isNil: return + case n.kind + of nkExportStmt: + for a in n: + assert a.kind == nkSym + let s = a.sym + if s.kind == skModule: + importAllSymbolsExcept(c, s, exceptSet) + elif exceptSet.empty or s.name.id notin exceptSet: + rawImportSymbol(c, s) + of nkExportExceptStmt: + localError(n.info, errGenerated, "'export except' not implemented") + else: + for i in 0 ..safeLen(n)-1: + importForwarded(c, n.sons[i], exceptSet) + proc evalImport(c: PContext, n: PNode): PNode = result = n + var emptySet: TIntSet for i in countup(0, sonsLen(n) - 1): var f = checkModuleName(n.sons[i]) if f.len > 0: @@ -132,7 +143,8 @@ proc evalImport(c: PContext, n: PNode): PNode = Message(n.sons[i].info, warnDeprecated, m.name.s) # ``addDecl`` needs to be done before ``importAllSymbols``! addDecl(c, m) # add symbol to symbol table of module - importAllSymbols(c, m) + importAllSymbolsExcept(c, m, emptySet) + importForwarded(c, m.ast, emptySet) proc evalFrom(c: PContext, n: PNode): PNode = result = n @@ -157,3 +169,4 @@ proc evalImportExcept*(c: PContext, n: PNode): PNode = let ident = lookups.considerAcc(n.sons[i]) exceptSet.incl(ident.id) importAllSymbolsExcept(c, m, exceptSet) + importForwarded(c, m.ast, exceptSet) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 9f2b431f6..7acfb830f 100755 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1657,6 +1657,26 @@ proc fixImmediateParams(n: PNode): PNode = result = n +proc semExport(c: PContext, n: PNode): PNode = + var x = newNodeI(n.kind, n.info) + #let L = if n.kind == nkExportExceptStmt: L = 1 else: n.len + for i in 0.. <n.len: + let a = n.sons[i] + var o: TOverloadIter + var s = initOverloadIter(o, c, a) + if s == nil: + localError(a.info, errGenerated, "invalid expr for 'export': " & + renderTree(a)) + while s != nil: + if s.kind in ExportableSymKinds+{skModule}: + x.add(newSymNode(s, a.info)) + s = nextOverloadIter(o, c, a) + if c.module.ast.isNil: + c.module.ast = newNodeI(nkStmtList, n.info) + assert c.module.ast.kind == nkStmtList + c.module.ast.add x + result = n + proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result = n if gCmd == cmdIdeTools: suggestExpr(c, n) @@ -1859,6 +1879,9 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkIncludeStmt: if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "include") result = evalInclude(c, n) + of nkExportStmt, nkExportExceptStmt: + if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "export") + result = semExport(c, n) of nkPragmaBlock: result = semPragmaBlock(c, n) of nkStaticStmt: diff --git a/doc/manual.txt b/doc/manual.txt index a5bce775b..5ea3575ee 100755 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -3656,7 +3656,7 @@ don't need to instantiate the code multiple times, because types then can be manipulated using the unified internal symbol representation. In such context typedesc acts as any other type. You can create variables, store typedesc values inside containers and so on. For example, here is how we can create -a type-safe wrapper for the unsafe `printf` function form C: +a type-safe wrapper for the unsafe `printf` function from C: .. code-block:: nimrod macro safePrintF(formatString: string{lit}, args: vararg[expr]): expr = @@ -3744,7 +3744,7 @@ This is best illustrated by an example: Import statement ~~~~~~~~~~~~~~~~ -After the import statement a list of module names can follow or a single +After the `import`:idx: statement a list of module names can follow or a single module name followed by an ``except`` to prevent some symbols to be imported: .. code-block:: nimrod @@ -3754,6 +3754,33 @@ module name followed by an ``except`` to prevent some symbols to be imported: echo "$1" % "abc" +Export statement +~~~~~~~~~~~~~~~~ + +An `export`:idx: statement can be used for symbol fowarding so that client +modules don't need to import a module's dependencies: + +.. code-block:: nimrod + # module B + type TMyObject* = object + +.. code-block:: nimrod + # module A + import B + export B.TMyObject + + proc `$`*(x: TMyObject): string = "my object" + + +.. code-block:: nimrod + # module C + import A + + # B.TMyObject has been imported implicitly here: + var x: TMyObject + echo($x) + + Scope rules ----------- Identifiers are valid from the point of their declaration until the end of diff --git a/lib/pure/collections/intsets.nim b/lib/pure/collections/intsets.nim index fcaf7b212..2a8d7eec2 100755 --- a/lib/pure/collections/intsets.nim +++ b/lib/pure/collections/intsets.nim @@ -190,6 +190,11 @@ proc `$`*(s: TIntSet): string = ## The `$` operator for int sets. dollarImpl() +proc empty*(s: TIntSet): bool {.inline.} = + ## returns true if `s` is empty. This is safe to call even before + ## the set has been initialized with `initIntSet`. + result = s.counter == 0 + when isMainModule: var x = initIntSet() x.incl(1) diff --git a/tests/compile/mexporta.nim b/tests/compile/mexporta.nim new file mode 100644 index 000000000..b7d4ddec9 --- /dev/null +++ b/tests/compile/mexporta.nim @@ -0,0 +1,8 @@ +# module A +import mexportb +export mexportb.TMyObject, mexportb.xyz + +export mexportb.q + +proc `$`*(x: TMyObject): string = "my object" + diff --git a/tests/compile/mexportb.nim b/tests/compile/mexportb.nim new file mode 100644 index 000000000..10d89f388 --- /dev/null +++ b/tests/compile/mexportb.nim @@ -0,0 +1,7 @@ +# module B +type TMyObject* = object + +const xyz* = 13 + +proc q*(x: int): int = 6 +proc q*(x: string): string = "8" diff --git a/tests/compile/texport.nim b/tests/compile/texport.nim new file mode 100644 index 000000000..99228dfce --- /dev/null +++ b/tests/compile/texport.nim @@ -0,0 +1,10 @@ +discard """ + output: "my object68" +""" + +import mexporta + +# B.TMyObject has been imported implicitly here: +var x: TMyObject +echo($x, q(0), q"0") + diff --git a/todo.txt b/todo.txt index e6731582f..61b178a23 100755 --- a/todo.txt +++ b/todo.txt @@ -1,7 +1,7 @@ version 0.9.2 ============= -- 'export' feature +- fix tfShared and tfNotNil - test&finish first class iterators: * nested iterators * test generic iterators diff --git a/web/news.txt b/web/news.txt index 6cd120e39..fc54bbce2 100755 --- a/web/news.txt +++ b/web/news.txt @@ -51,6 +51,9 @@ Language Additions that ``nil`` is not allowed. However currently the compiler performs no advanced static checking for this; for now it's merely for documentation purposes. +- An ``export`` statement has been added to the language: It can be used for + symbol forwarding so client modules don't have to import a module's + dependencies explicitly. 2012-09-23 Version 0.9.0 released |