Merge branch 'devel' of github.com:nim-lang/Nim into devel

This commit is contained in:
Araq
2018-03-12 09:02:21 +01:00
15 changed files with 258 additions and 104 deletions

View File

@@ -22,6 +22,14 @@
### Tool changes
- ``jsondoc2`` has been renamed ``jsondoc``, similar to how ``doc2`` was renamed
``doc``. The old ``jsondoc`` can still be invoked with ``jsondoc0``.
### Compiler changes
### Bugfixes
- The `importcpp` pragma now allows importing the listed fields of generic
C++ types. Support for numeric parameters have also been added through
the use of `static[T]` types.
(#6415)

View File

@@ -617,8 +617,10 @@ proc scanCppGenericSlot(pat: string, cursor, outIdx, outStars: var int): bool =
return false
proc resolveStarsInCppType(typ: PType, idx, stars: int): PType =
# XXX: we should catch this earlier and report it as a semantic error
if idx >= typ.len: internalError "invalid apostrophe type parameter index"
# Make sure the index refers to one of the generic params of the type.
# XXX: we should catch this earlier and report it as a semantic error.
if idx >= typ.len:
internalError "invalid apostrophe type parameter index"
result = typ.sons[idx]
for i in 1..stars:
@@ -820,6 +822,9 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
let typeInSlot = resolveStarsInCppType(origTyp, idx + 1, stars)
if typeInSlot == nil or typeInSlot.kind == tyVoid:
result.add(~"void")
elif typeInSlot.kind == tyStatic:
internalAssert typeInSlot.n != nil
result.add typeInSlot.n.renderTree
else:
result.add getTypeDescAux(m, typeInSlot, check)
else:

View File

@@ -210,14 +210,14 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
gCmd = cmdRst2tex
loadConfigs(DocTexConfig, cache)
commandRst2TeX()
of "jsondoc":
of "jsondoc0":
wantMainModule()
gCmd = cmdDoc
loadConfigs(DocConfig, cache)
wantMainModule()
defineSymbol("nimdoc")
commandJson()
of "jsondoc2":
of "jsondoc2", "jsondoc":
gCmd = cmdDoc
loadConfigs(DocConfig, cache)
wantMainModule()

View File

@@ -255,69 +255,61 @@ proc semCase(c: PContext, n: PNode): PNode =
result.typ = typ
proc semTry(c: PContext, n: PNode): PNode =
var check = initIntSet()
template semExceptBranchType(typeNode: PNode): PNode =
let typ = semTypeNode(c, typeNode, nil).toObject()
if typ.kind != tyObject:
localError(typeNode.info, errExprCannotBeRaised)
if containsOrIncl(check, typ.id):
localError(typeNode.info, errExceptionAlreadyHandled)
newNodeIT(nkType, typeNode.info, typ)
result = n
inc c.p.inTryStmt
checkMinSonsLen(n, 2)
var typ = commonTypeBegin
n.sons[0] = semExprBranchScope(c, n.sons[0])
typ = commonType(typ, n.sons[0].typ)
n[0] = semExprBranchScope(c, n[0])
typ = commonType(typ, n[0].typ)
var check = initIntSet()
var last = sonsLen(n) - 1
for i in countup(1, last):
var a = n.sons[i]
let a = n.sons[i]
checkMinSonsLen(a, 1)
var length = sonsLen(a)
openScope(c)
if a.kind == nkExceptBranch:
# so that ``except [a, b, c]`` is supported:
if length == 2 and a.sons[0].kind == nkBracket:
a.sons[0..0] = a.sons[0].sons
length = a.sonsLen
# Iterate through each exception type in the except branch.
for j in countup(0, length-2):
var typeNode = a.sons[j] # e.g. `Exception`
var symbolNode: PNode = nil # e.g. `foobar`
# Handle the case where the `Exception as foobar` syntax is used.
if typeNode.isInfixAs():
typeNode = a.sons[j].sons[1]
symbolNode = a.sons[j].sons[2]
if a.len == 2 and a[0].kind == nkBracket:
# rewrite ``except [a, b, c]: body`` -> ```except a, b, c: body```
a.sons[0..0] = a[0].sons
if a.len == 2 and a[0].isInfixAs():
# support ``except Exception as ex: body``
a[0][1] = semExceptBranchType(a[0][1])
# Resolve the type ident into a PType.
var typ = semTypeNode(c, typeNode, nil).toObject()
if typ.kind != tyObject:
localError(a.sons[j].info, errExprCannotBeRaised)
let symbol = newSymG(skLet, a[0][2], c)
symbol.typ = a[0][1].typ.toRef()
addDecl(c, symbol)
# Overwrite symbol in AST with the symbol in the symbol table.
a[0][2] = newSymNode(symbol, a[0][2].info)
let newTypeNode = newNodeI(nkType, typeNode.info)
newTypeNode.typ = typ
if symbolNode.isNil:
a.sons[j] = newTypeNode
else:
a.sons[j].sons[1] = newTypeNode
# Add the exception ident to the symbol table.
let symbol = newSymG(skLet, symbolNode, c)
symbol.typ = typ.toRef()
addDecl(c, symbol)
# Overwrite symbol in AST with the symbol in the symbol table.
let symNode = newNodeI(nkSym, typeNode.info)
symNode.sym = symbol
a.sons[j].sons[2] = symNode
if containsOrIncl(check, typ.id):
localError(a.sons[j].info, errExceptionAlreadyHandled)
else:
# support ``except KeyError, ValueError, ... : body``
for j in 0..a.len-2:
a[j] = semExceptBranchType(a[j])
elif a.kind != nkFinally:
illFormedAst(n)
# last child of an nkExcept/nkFinally branch is a statement:
a.sons[length-1] = semExprBranchScope(c, a.sons[length-1])
if a.kind != nkFinally: typ = commonType(typ, a.sons[length-1].typ)
a[^1] = semExprBranchScope(c, a[^1])
if a.kind != nkFinally: typ = commonType(typ, a[^1])
else: dec last
closeScope(c)
dec c.p.inTryStmt
if isEmptyType(typ) or typ.kind == tyNil:
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr}:
discardCheck(c, n.sons[0])
for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon)
if typ == enforceVoidContext:

View File

@@ -632,16 +632,18 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
else:
typ = semTypeNode(c, n.sons[length-2], nil)
propagateToOwner(rectype, typ)
let rec = rectype.sym
var fieldOwner = if c.inGenericContext > 0: c.getCurrOwner
else: rectype.sym
for i in countup(0, sonsLen(n)-3):
var f = semIdentWithPragma(c, skField, n.sons[i], {sfExported})
suggestSym(n.sons[i].info, f, c.graph.usageSym)
f.typ = typ
f.position = pos
if (rec != nil) and ({sfImportc, sfExportc} * rec.flags != {}) and
(f.loc.r == nil):
if fieldOwner != nil and
{sfImportc, sfExportc} * fieldOwner.flags != {} and
f.loc.r == nil:
f.loc.r = rope(f.name.s)
f.flags = f.flags + ({sfImportc, sfExportc} * rec.flags)
f.flags = f.flags + ({sfImportc, sfExportc} * fieldOwner.flags)
inc(pos)
if containsOrIncl(check, f.name.id):
localError(n.sons[i].info, errAttemptToRedefine, f.name.s)

View File

@@ -7,7 +7,6 @@ Advanced commands:
//rst2html convert a reStructuredText file to HTML
//rst2tex convert a reStructuredText file to TeX
//jsondoc extract the documentation to a json file
//jsondoc2 extract the documentation to a json file (uses doc2)
//ctags create a tags file
//buildIndex build an index for the whole documentation
//run run the project (with Tiny C backend; buggy!)

View File

@@ -84,51 +84,72 @@ Document Types
HTML
----
Generation of HTML documents is done via both the ``doc`` and ``doc2``
commands. These command take either a single .nim file, outputting a single
.html file with the same base filename, or multiple .nim files, outputting
multiple .html files and, optionally, an index file.
Generation of HTML documents is done via the ``doc`` command. This command
takes either a single .nim file, outputting a single .html file with the same
base filename, or multiple .nim files, outputting multiple .html files and,
optionally, an index file.
The ``doc`` command::
nim doc sample
Partial Output::
...
proc helloWorld*(times: int)
...
Output can be viewed in full here: `docgen_sample.html <docgen_sample.html>`_.
The next command, called ``doc2``, is very similar to the ``doc`` command, but
will be run after the compiler performs semantic checking on the input nim
module(s), which allows it to process macros.
The ``doc2`` command::
nim doc2 sample
Partial Output::
...
proc helloWorld(times: int) {.raises: [], tags: [].}
...
The full output can be seen here: `docgen_sample2.html <docgen_sample2.html>`_.
As you can see, the tool has extracted additional information provided to it by
the compiler beyond what the ``doc`` command provides, such as pragmas attached
implicitly by the compiler. This type of information is not available from
looking at the AST (Abstract Syntax Tree) prior to semantic checking, as the
``doc`` command does.
The older version of the ``doc`` command, now renamed ``doc0`` runs before
semantic checking which means it lacks some of the things ``doc`` will output.
The ``doc0`` command::
nim doc0 sample
Partial Output::
...
proc helloWorld*(times: int)
...
Output can be viewed in full here: `docgen_sample.html <docgen_sample.html>`_.
As you can see, the tool has extracted less information than what the ``doc``
command provides, such as pragmas attached implicitly by the compiler. This type
of information is not available from looking at the AST (Abstract Syntax Tree)
prior to semantic checking, which is why ``doc0`` doesn't show it.
JSON
----
Generation of JSON documents is done via the ``jsondoc`` command. This command
takes in a .nim file, and outputs a .json file with the same base filename.
Note that this tool is built off of the ``doc`` command, and therefore is
performed before semantic checking.
takes in a .nim file, and outputs a .json file with the same base filename. Note
that this tool is built off of the ``doc`` command (previously ``doc2``), and
contains the same information.
The ``jsondoc`` command::
nim jsondoc sample
Output::
{
"orig": "docgen_sample.nim",
"nimble": "",
"entries": [
{
"name": "helloWorld",
"type": "skProc",
"line": 5,
"col": 0,
"description": "Takes an integer and outputs as many &quot;hello world!&quot;s",
"code": "proc helloWorld(times: int) {.raises: [], tags: [].}"
}
]
}
Similarly to the old ``doc`` command the old ``jsondoc`` command has been
renamed ``jsondoc0``.
The ``jsondoc0`` command::
nim jsondoc0 sample
Output::
[
{
@@ -142,6 +163,8 @@ Output::
}
]
Note that the ``jsondoc`` command outputs it's JSON without pretty-printing it,
while ``jsondoc0`` outputs pretty-printed JSON.
Related Options
===============

View File

@@ -11,11 +11,23 @@
## It supports one convenience iterator over all command line options and some
## lower-level features.
##
## Supported syntax:
## Supported syntax with default empty ``shortNoVal``/``longNoVal``:
##
## 1. short options - ``-abcd``, where a, b, c, d are names
## 2. long option - ``--foo:bar``, ``--foo=bar`` or ``--foo``
## 3. argument - everything else
##
## When ``shortNoVal``/``longNoVal`` are non-empty then the ':' and '=' above
## are still accepted, but become optional. Note that these option key sets
## must be updated along with the set of option keys taking no value, but
## keys which do take values need no special updates as their set evolves.
##
## When option values begin with ':' or '=' they need to be doubled up (as in
## ``--delim::``) or alternated (as in ``--delim=:``).
##
## The common ``--`` non-option argument delimiter appears as an empty string
## long option key. ``OptParser.cmd``, ``OptParser.pos``, and
## ``os.parseCmdLine`` may be used to complete parsing in that case.
{.push debugger: off.}
@@ -32,9 +44,11 @@ type
cmdShortOption ## a short option ``-c`` detected
OptParser* =
object of RootObj ## this object implements the command line parser
cmd: string
pos: int
cmd*: string # cmd,pos exported so caller can catch "--" as..
pos*: int # ..empty key or subcmd cmdArg & handle specially
inShortState: bool
shortNoVal: set[char]
longNoVal: seq[string]
kind*: CmdLineKind ## the dected command line token
key*, val*: TaintedString ## key and value pair; ``key`` is the option
## or the argument, ``value`` is not "" if
@@ -78,11 +92,19 @@ when declared(os.paramCount):
# we cannot provide this for NimRtl creation on Posix, because we can't
# access the command line arguments then!
proc initOptParser*(cmdline = ""): OptParser =
proc initOptParser*(cmdline = "", shortNoVal: set[char]={},
longNoVal: seq[string] = @[]): OptParser =
## inits the option parser. If ``cmdline == ""``, the real command line
## (as provided by the ``OS`` module) is taken.
## (as provided by the ``OS`` module) is taken. If ``shortNoVal`` is
## provided command users do not need to delimit short option keys and
## values with a ':' or '='. If ``longNoVal`` is provided command users do
## not need to delimit long option keys and values with a ':' or '='
## (though they still need at least a space). In both cases, ':' or '='
## may still be used if desired. They just become optional.
result.pos = 0
result.inShortState = false
result.shortNoVal = shortNoVal
result.longNoVal = longNoVal
if cmdline != "":
result.cmd = cmdline
else:
@@ -94,15 +116,19 @@ when declared(os.paramCount):
result.key = TaintedString""
result.val = TaintedString""
proc initOptParser*(cmdline: seq[string]): OptParser =
proc initOptParser*(cmdline: seq[TaintedString], shortNoVal: set[char]={},
longNoVal: seq[string] = @[]): OptParser =
## inits the option parser. If ``cmdline.len == 0``, the real command line
## (as provided by the ``OS`` module) is taken.
## (as provided by the ``OS`` module) is taken. ``shortNoVal`` and
## ``longNoVal`` behavior is the same as for ``initOptParser(string,...)``.
result.pos = 0
result.inShortState = false
result.shortNoVal = shortNoVal
result.longNoVal = longNoVal
result.cmd = ""
if cmdline.len != 0:
for i in 0..<cmdline.len:
result.cmd.add quote(cmdline[i])
result.cmd.add quote(cmdline[i].string)
result.cmd.add ' '
else:
for i in countup(1, paramCount()):
@@ -121,8 +147,9 @@ proc handleShortOption(p: var OptParser) =
while p.cmd[i] in {'\x09', ' '}:
inc(i)
p.inShortState = false
if p.cmd[i] in {':', '='}:
inc(i)
if p.cmd[i] in {':', '='} or card(p.shortNoVal) > 0 and p.key.string[0] notin p.shortNoVal:
if p.cmd[i] in {':', '='}:
inc(i)
p.inShortState = false
while p.cmd[i] in {'\x09', ' '}: inc(i)
i = parseWord(p.cmd, i, p.val.string)
@@ -146,12 +173,13 @@ proc next*(p: var OptParser) {.rtl, extern: "npo$1".} =
of '-':
inc(i)
if p.cmd[i] == '-':
p.kind = cmdLongoption
p.kind = cmdLongOption
inc(i)
i = parseWord(p.cmd, i, p.key.string, {'\0', ' ', '\x09', ':', '='})
while p.cmd[i] in {'\x09', ' '}: inc(i)
if p.cmd[i] in {':', '='}:
inc(i)
if p.cmd[i] in {':', '='} or len(p.longNoVal) > 0 and p.key.string notin p.longNoVal:
if p.cmd[i] in {':', '='}:
inc(i)
while p.cmd[i] in {'\x09', ' '}: inc(i)
p.pos = parseWord(p.cmd, i, p.val.string)
else:
@@ -172,7 +200,7 @@ iterator getopt*(p: var OptParser): tuple[kind: CmdLineKind, key, val: TaintedSt
## Example:
##
## .. code-block:: nim
## var p = initOptParser("--left --debug:3 -l=4 -r:2")
## var p = initOptParser("--left --debug:3 -l -r:2")
## for kind, key, val in p.getopt():
## case kind
## of cmdArgument:
@@ -192,17 +220,30 @@ iterator getopt*(p: var OptParser): tuple[kind: CmdLineKind, key, val: TaintedSt
yield (p.kind, p.key, p.val)
when declared(initOptParser):
iterator getopt*(): tuple[kind: CmdLineKind, key, val: TaintedString] =
## This is an convenience iterator for iterating over the command line arguments.
## This create a new OptParser object.
## See above for a more detailed example
iterator getopt*(cmdline: seq[TaintedString] = commandLineParams(),
shortNoVal: set[char]={}, longNoVal: seq[string] = @[]):
tuple[kind: CmdLineKind, key, val: TaintedString] =
## This is an convenience iterator for iterating over command line arguments.
## This creates a new OptParser. See the above ``getopt(var OptParser)``
## example for using default empty ``NoVal`` parameters. This example is
## for the same option keys as that example but here option key-value
## separators become optional for command users:
##
## .. code-block:: nim
## for kind, key, val in getopt():
## # this will iterate over all arguments passed to the cmdline.
## continue
## for kind, key, val in getopt(shortNoVal = { 'l' },
## longNoVal = @[ "left" ]):
## case kind
## of cmdArgument:
## filename = key
## of cmdLongOption, cmdShortOption:
## case key
## of "help", "h": writeHelp()
## of "version", "v": writeVersion()
## of cmdEnd: assert(false) # cannot happen
## if filename == "":
## writeHelp()
##
var p = initOptParser()
var p = initOptParser(cmdline, shortNoVal=shortNoVal, longNoVal=longNoVal)
while true:
next(p)
if p.kind == cmdEnd: break

View File

@@ -1250,7 +1250,7 @@ proc wordWrap*(s: string, maxLineWidth = 80,
if len(word) > spaceLeft:
if splitLongWords and len(word) > maxLineWidth:
result.add(substr(word, 0, spaceLeft-1))
var w = spaceLeft+1
var w = spaceLeft
var wordLeft = len(word) - spaceLeft
while wordLeft > 0:
result.add(newLine)
@@ -2514,6 +2514,11 @@ when isMainModule:
outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes"
doAssert wordWrap(inp, 10, false) == outp
let
longInp = """ThisIsOneVeryLongStringWhichWeWillSplitIntoEightSeparatePartsNow"""
longOutp = "ThisIsOn\neVeryLon\ngStringW\nhichWeWi\nllSplitI\nntoEight\nSeparate\nPartsNow"
doAssert wordWrap(longInp, 8, true) == longOutp
doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000"
doAssert formatBiggestFloat(1234.567, ffDecimal, 0) == "1235."
doAssert formatBiggestFloat(1234.567, ffDecimal, 1) == "1234.6"

View File

@@ -1461,9 +1461,9 @@ when defined(nodejs):
programResult = 0
else:
var programResult* {.exportc: "nim_program_result".}: int
## modify this variable to specify the exit code of the program
## under normal circumstances. When the program is terminated
## prematurely using ``quit``, this value is ignored.
## modify this variable to specify the exit code of the program
## under normal circumstances. When the program is terminated
## prematurely using ``quit``, this value is ignored.
when defined(nimdoc):
proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn.}

View File

@@ -32,3 +32,11 @@ proc testTryAsExpr(i: int) =
test[Exception]()
test2()
testTryAsExpr(5)
# see bug #7115
doAssert(not compiles(
try:
echo 1
except [KeyError as ex1, ValueError as ex2]:
echo 2
))

View File

@@ -9,6 +9,17 @@ kind: cmdLongOption key:val -- left:
kind: cmdLongOption key:val -- debug:3
kind: cmdShortOption key:val -- l:4
kind: cmdShortOption key:val -- r:2
parseoptNoVal
kind: cmdLongOption key:val -- left:
kind: cmdLongOption key:val -- debug:3
kind: cmdShortOption key:val -- l:
kind: cmdShortOption key:val -- r:2
kind: cmdLongOption key:val -- debug:2
kind: cmdLongOption key:val -- debug:1
kind: cmdShortOption key:val -- r:1
kind: cmdShortOption key:val -- r:0
kind: cmdShortOption key:val -- l:
kind: cmdShortOption key:val -- r:4
parseopt2
first round
kind: cmdLongOption key:val -- left:
@@ -39,6 +50,15 @@ block:
for kind, key, val in parseopt.getopt(p):
echo "kind: ", kind, "\tkey:val -- ", key, ":", val
block:
echo "parseoptNoVal"
# test NoVal mode with custom cmdline arguments
var argv = "--left --debug:3 -l -r:2 --debug 2 --debug=1 -r1 -r=0 -lr4"
var p = parseopt.initOptParser(argv,
shortNoVal = {'l'}, longNoVal = @["left"])
for kind, key, val in parseopt.getopt(p):
echo "kind: ", kind, "\tkey:val -- ", key, ":", val
block:
echo "parseopt2"
for kind, key, val in parseopt2.getopt():

View File

@@ -0,0 +1,53 @@
discard """
targets: "cpp"
output: "[0, 0, 10, 0]\n5\n1.2\n15\ntest"
"""
{.emit: """
template <int N, class T>
struct GenericIntType {
T data[N];
};
template <class T>
struct GenericTType {
T field;
};
struct SimpleStruct {
int field;
};
""" .}
type
GenericIntType {.importcpp: "GenericIntType<'0, '1>".} [N: static[int]; T] = object
data: array[N, T]
GenericTType {.importcpp: "GenericTType<'0>".} [T] = object
field: T
GenInt4 = GenericIntType[4, int]
SimpleStruct {.importcpp: "SimpleStruct"} = object
field: int
var
a = GenInt4()
b = SimpleStruct()
c = GenericTType[float]()
d = SimpleStruct(field: 15)
e = GenericTType[string](field: "test")
a.data[2] = 10
b.field = 5
c.field = 1.2
echo a.data
echo b.field
echo c.field
echo d.field
echo e.field

View File

@@ -10,7 +10,7 @@ _nim()
if [ $COMP_CWORD -eq 1 ] ; then
# first item - suggest commands
kw="compile c doc doc2 compileToC cc compileToCpp cpp compileToOC objc js e rst2html rst2tex jsondoc jsondoc2 buildIndex genDepend dump check"
kw="compile c doc compileToC cc compileToCpp cpp compileToOC objc js e rst2html rst2tex jsondoc buildIndex genDepend dump check"
COMPREPLY=( $( compgen -W "${kw}" -- $cur ) )
return 0
fi

View File

@@ -5,7 +5,6 @@ _nim() {
':command:((
{compile,c}\:compile\ project\ with\ default\ code\ generator\ C
doc\:generate\ the\ documentation\ for\ inputfile
doc2\:generate\ the\ documentation\ for\ inputfile
{compileToC,cc}\:compile\ project\ with\ C\ code\ generator
{compileToCpp,cpp}\:compile\ project\ to\ C++\ code
{compileToOC,objc}\:compile\ project\ to\ Objective\ C\ code
@@ -14,7 +13,6 @@ _nim() {
rst2html\:convert\ a\ reStructuredText\ file\ to\ HTML
rst2tex\:convert\ a\ reStructuredText\ file\ to\ TeX
jsondoc\:extract\ the\ documentation\ to\ a\ json\ file
jsondoc2\:extract\ documentation\ to\ a\ json\ file\ using\ doc2
buildIndex\:build\ an\ index\ for\ the\ whole\ documentation
genDepend\:generate\ a\ DOT\ file\ containing\ the\ module\ dependency\ graph
dump\:dump\ all\ defined\ conditionals\ and\ search\ paths