implements 'export' feature
This commit is contained in:
parent
f503439e81
commit
c98e3d2c27
10 changed files with 137 additions and 40 deletions
|
|
@ -585,8 +585,9 @@ proc StrTableContains(t: TStrTable, n: PSym): bool =
|
||||||
proc StrTableRawInsert(data: var TSymSeq, n: PSym) =
|
proc StrTableRawInsert(data: var TSymSeq, n: PSym) =
|
||||||
var h: THash = n.name.h and high(data)
|
var h: THash = n.name.h and high(data)
|
||||||
while data[h] != nil:
|
while data[h] != nil:
|
||||||
if data[h] == n:
|
if data[h] == n:
|
||||||
InternalError(n.info, "StrTableRawInsert: " & n.name.s)
|
# allowed for 'export' feature:
|
||||||
|
#InternalError(n.info, "StrTableRawInsert: " & n.name.s)
|
||||||
return
|
return
|
||||||
h = nextTry(h, high(data))
|
h = nextTry(h, high(data))
|
||||||
assert(data[h] == nil)
|
assert(data[h] == nil)
|
||||||
|
|
@ -617,23 +618,23 @@ proc StrTableAdd(t: var TStrTable, n: PSym) =
|
||||||
StrTableRawInsert(t.data, n)
|
StrTableRawInsert(t.data, n)
|
||||||
inc(t.counter)
|
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:
|
# returns true if n is already in the string table:
|
||||||
# It is essential that `n` is written nevertheless!
|
# It is essential that `n` is written nevertheless!
|
||||||
# This way the newest redefinition is picked by the semantic analyses!
|
# This way the newest redefinition is picked by the semantic analyses!
|
||||||
assert n.name != nil
|
assert n.name != nil
|
||||||
var h: THash = n.name.h and high(t.data)
|
var h: THash = n.name.h and high(t.data)
|
||||||
while true:
|
while true:
|
||||||
var it = t.data[h]
|
var it = t.data[h]
|
||||||
if it == nil: break
|
if it == nil: break
|
||||||
if it.name.id == n.name.id:
|
if it.name.id == n.name.id:
|
||||||
t.data[h] = n # overwrite it with newer definition!
|
t.data[h] = n # overwrite it with newer definition!
|
||||||
return true # found it
|
return true # found it
|
||||||
h = nextTry(h, high(t.data))
|
h = nextTry(h, high(t.data))
|
||||||
if mustRehash(len(t.data), t.counter):
|
if mustRehash(len(t.data), t.counter):
|
||||||
StrTableEnlarge(t)
|
StrTableEnlarge(t)
|
||||||
StrTableRawInsert(t.data, n)
|
StrTableRawInsert(t.data, n)
|
||||||
else:
|
else:
|
||||||
assert(t.data[h] == nil)
|
assert(t.data[h] == nil)
|
||||||
t.data[h] = n
|
t.data[h] = n
|
||||||
inc(t.counter)
|
inc(t.counter)
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ import
|
||||||
|
|
||||||
proc evalImport*(c: PContext, n: PNode): PNode
|
proc evalImport*(c: PContext, n: PNode): PNode
|
||||||
proc evalFrom*(c: PContext, n: PNode): PNode
|
proc evalFrom*(c: PContext, n: PNode): PNode
|
||||||
proc importAllSymbols*(c: PContext, fromMod: PSym)
|
|
||||||
|
|
||||||
proc getModuleName*(n: PNode): string =
|
proc getModuleName*(n: PNode): string =
|
||||||
# This returns a short relative module name without the nim extension
|
# This returns a short relative module name without the nim extension
|
||||||
|
|
@ -42,42 +41,43 @@ proc checkModuleName*(n: PNode): string =
|
||||||
if result.len == 0:
|
if result.len == 0:
|
||||||
LocalError(n.info, errCannotOpenFile, modulename)
|
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
|
# This does not handle stubs, because otherwise loading on demand would be
|
||||||
# pointless in practice. So importing stubs is fine here!
|
# 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:
|
# check if we have already a symbol of the same name:
|
||||||
var check = StrTableGet(c.tab.stack[importTablePos], s.name)
|
var check = StrTableGet(c.tab.stack[importTablePos], s.name)
|
||||||
if check != nil and check.id != copy.id:
|
if check != nil and check.id != s.id:
|
||||||
if s.kind notin OverloadableSyms:
|
if s.kind notin OverloadableSyms:
|
||||||
# s and check need to be qualified:
|
# s and check need to be qualified:
|
||||||
Incl(c.AmbiguousSymbols, copy.id)
|
Incl(c.AmbiguousSymbols, s.id)
|
||||||
Incl(c.AmbiguousSymbols, check.id)
|
Incl(c.AmbiguousSymbols, check.id)
|
||||||
StrTableAdd(c.tab.stack[importTablePos], copy)
|
# thanks to 'export' feature, it could be we import the same symbol from
|
||||||
if s.kind == skType:
|
# multiple sources, so we need to call 'StrTableAdd' here:
|
||||||
|
StrTableAdd(c.tab.stack[importTablePos], s)
|
||||||
|
if s.kind == skType:
|
||||||
var etyp = s.typ
|
var etyp = s.typ
|
||||||
if etyp.kind in {tyBool, tyEnum} and sfPure notin s.flags:
|
if etyp.kind in {tyBool, tyEnum} and sfPure notin s.flags:
|
||||||
for j in countup(0, sonsLen(etyp.n) - 1):
|
for j in countup(0, sonsLen(etyp.n) - 1):
|
||||||
var e = etyp.n.sons[j].sym
|
var e = etyp.n.sons[j].sym
|
||||||
if (e.Kind != skEnumField):
|
if e.Kind != skEnumField:
|
||||||
InternalError(s.info, "rawImportSymbol")
|
InternalError(s.info, "rawImportSymbol")
|
||||||
# BUGFIX: because of aliases for enums the symbol may already
|
# BUGFIX: because of aliases for enums the symbol may already
|
||||||
# have been put into the symbol table
|
# have been put into the symbol table
|
||||||
# BUGFIX: but only iff they are the same symbols!
|
# BUGFIX: but only iff they are the same symbols!
|
||||||
var it: TIdentIter
|
var it: TIdentIter
|
||||||
check = InitIdentIter(it, c.tab.stack[importTablePos], e.name)
|
check = InitIdentIter(it, c.tab.stack[importTablePos], e.name)
|
||||||
while check != nil:
|
while check != nil:
|
||||||
if check.id == e.id:
|
if check.id == e.id:
|
||||||
e = nil
|
e = nil
|
||||||
break
|
break
|
||||||
check = NextIdentIter(it, c.tab.stack[importTablePos])
|
check = NextIdentIter(it, c.tab.stack[importTablePos])
|
||||||
if e != nil:
|
if e != nil:
|
||||||
rawImportSymbol(c, e)
|
rawImportSymbol(c, e)
|
||||||
else:
|
else:
|
||||||
# rodgen assures that converters and patterns are no stubs
|
# rodgen assures that converters and patterns are no stubs
|
||||||
if s.kind == skConverter: addConverter(c, s)
|
if s.kind == skConverter: addConverter(c, s)
|
||||||
if hasPattern(s): addPattern(c, s)
|
if hasPattern(s): addPattern(c, s)
|
||||||
|
|
||||||
proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
|
proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
|
||||||
let ident = lookups.considerAcc(n)
|
let ident = lookups.considerAcc(n)
|
||||||
let s = StrTableGet(fromMod.tab, ident)
|
let s = StrTableGet(fromMod.tab, ident)
|
||||||
|
|
@ -98,17 +98,6 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
|
||||||
rawImportSymbol(c, e)
|
rawImportSymbol(c, e)
|
||||||
e = NextIdentIter(it, fromMod.tab)
|
e = NextIdentIter(it, fromMod.tab)
|
||||||
else: rawImportSymbol(c, s)
|
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) =
|
proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: TIntSet) =
|
||||||
var i: TTabIter
|
var i: TTabIter
|
||||||
|
|
@ -118,12 +107,34 @@ proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: TIntSet) =
|
||||||
if s.kind != skEnumField:
|
if s.kind != skEnumField:
|
||||||
if s.Kind notin ExportableSymKinds:
|
if s.Kind notin ExportableSymKinds:
|
||||||
InternalError(s.info, "importAllSymbols: " & $s.kind)
|
InternalError(s.info, "importAllSymbols: " & $s.kind)
|
||||||
if s.name.id notin exceptSet:
|
if exceptSet.empty or s.name.id notin exceptSet:
|
||||||
rawImportSymbol(c, s)
|
rawImportSymbol(c, s)
|
||||||
s = NextIter(i, fromMod.tab)
|
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 =
|
proc evalImport(c: PContext, n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
|
var emptySet: TIntSet
|
||||||
for i in countup(0, sonsLen(n) - 1):
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
var f = checkModuleName(n.sons[i])
|
var f = checkModuleName(n.sons[i])
|
||||||
if f.len > 0:
|
if f.len > 0:
|
||||||
|
|
@ -132,7 +143,8 @@ proc evalImport(c: PContext, n: PNode): PNode =
|
||||||
Message(n.sons[i].info, warnDeprecated, m.name.s)
|
Message(n.sons[i].info, warnDeprecated, m.name.s)
|
||||||
# ``addDecl`` needs to be done before ``importAllSymbols``!
|
# ``addDecl`` needs to be done before ``importAllSymbols``!
|
||||||
addDecl(c, m) # add symbol to symbol table of module
|
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 =
|
proc evalFrom(c: PContext, n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
|
|
@ -157,3 +169,4 @@ proc evalImportExcept*(c: PContext, n: PNode): PNode =
|
||||||
let ident = lookups.considerAcc(n.sons[i])
|
let ident = lookups.considerAcc(n.sons[i])
|
||||||
exceptSet.incl(ident.id)
|
exceptSet.incl(ident.id)
|
||||||
importAllSymbolsExcept(c, m, exceptSet)
|
importAllSymbolsExcept(c, m, exceptSet)
|
||||||
|
importForwarded(c, m.ast, exceptSet)
|
||||||
|
|
|
||||||
|
|
@ -1657,6 +1657,26 @@ proc fixImmediateParams(n: PNode): PNode =
|
||||||
|
|
||||||
result = n
|
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 =
|
proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
result = n
|
result = n
|
||||||
if gCmd == cmdIdeTools: suggestExpr(c, n)
|
if gCmd == cmdIdeTools: suggestExpr(c, n)
|
||||||
|
|
@ -1859,6 +1879,9 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
of nkIncludeStmt:
|
of nkIncludeStmt:
|
||||||
if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "include")
|
if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "include")
|
||||||
result = evalInclude(c, n)
|
result = evalInclude(c, n)
|
||||||
|
of nkExportStmt, nkExportExceptStmt:
|
||||||
|
if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "export")
|
||||||
|
result = semExport(c, n)
|
||||||
of nkPragmaBlock:
|
of nkPragmaBlock:
|
||||||
result = semPragmaBlock(c, n)
|
result = semPragmaBlock(c, n)
|
||||||
of nkStaticStmt:
|
of nkStaticStmt:
|
||||||
|
|
|
||||||
|
|
@ -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
|
manipulated using the unified internal symbol representation. In such context
|
||||||
typedesc acts as any other type. You can create variables, store typedesc
|
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
|
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
|
.. code-block:: nimrod
|
||||||
macro safePrintF(formatString: string{lit}, args: vararg[expr]): expr =
|
macro safePrintF(formatString: string{lit}, args: vararg[expr]): expr =
|
||||||
|
|
@ -3744,7 +3744,7 @@ This is best illustrated by an example:
|
||||||
Import statement
|
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:
|
module name followed by an ``except`` to prevent some symbols to be imported:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nimrod
|
||||||
|
|
@ -3754,6 +3754,33 @@ module name followed by an ``except`` to prevent some symbols to be imported:
|
||||||
echo "$1" % "abc"
|
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
|
Scope rules
|
||||||
-----------
|
-----------
|
||||||
Identifiers are valid from the point of their declaration until the end of
|
Identifiers are valid from the point of their declaration until the end of
|
||||||
|
|
|
||||||
|
|
@ -190,6 +190,11 @@ proc `$`*(s: TIntSet): string =
|
||||||
## The `$` operator for int sets.
|
## The `$` operator for int sets.
|
||||||
dollarImpl()
|
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:
|
when isMainModule:
|
||||||
var x = initIntSet()
|
var x = initIntSet()
|
||||||
x.incl(1)
|
x.incl(1)
|
||||||
|
|
|
||||||
8
tests/compile/mexporta.nim
Normal file
8
tests/compile/mexporta.nim
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
# module A
|
||||||
|
import mexportb
|
||||||
|
export mexportb.TMyObject, mexportb.xyz
|
||||||
|
|
||||||
|
export mexportb.q
|
||||||
|
|
||||||
|
proc `$`*(x: TMyObject): string = "my object"
|
||||||
|
|
||||||
7
tests/compile/mexportb.nim
Normal file
7
tests/compile/mexportb.nim
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# module B
|
||||||
|
type TMyObject* = object
|
||||||
|
|
||||||
|
const xyz* = 13
|
||||||
|
|
||||||
|
proc q*(x: int): int = 6
|
||||||
|
proc q*(x: string): string = "8"
|
||||||
10
tests/compile/texport.nim
Normal file
10
tests/compile/texport.nim
Normal file
|
|
@ -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")
|
||||||
|
|
||||||
2
todo.txt
2
todo.txt
|
|
@ -1,7 +1,7 @@
|
||||||
version 0.9.2
|
version 0.9.2
|
||||||
=============
|
=============
|
||||||
|
|
||||||
- 'export' feature
|
- fix tfShared and tfNotNil
|
||||||
- test&finish first class iterators:
|
- test&finish first class iterators:
|
||||||
* nested iterators
|
* nested iterators
|
||||||
* test generic iterators
|
* test generic iterators
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,9 @@ Language Additions
|
||||||
that ``nil`` is not allowed. However currently the compiler performs no
|
that ``nil`` is not allowed. However currently the compiler performs no
|
||||||
advanced static checking for this; for now it's merely for documentation
|
advanced static checking for this; for now it's merely for documentation
|
||||||
purposes.
|
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
|
2012-09-23 Version 0.9.0 released
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue