Merge branch 'araq-misc' into devel

This commit is contained in:
Araq 2018-08-16 12:30:00 +02:00
commit 2870ca5d71
122 changed files with 772 additions and 882 deletions

View file

@ -51,5 +51,3 @@ script:
- ./koch csource - ./koch csource
- ./koch nimsuggest - ./koch nimsuggest
# - nim c -r nimsuggest/tester # - nim c -r nimsuggest/tester
- ( ! grep -F '.. code-block' -l -r --include '*.html' --exclude contributing.html --exclude docgen.html --exclude tut2.html )
- ( ! grep -F '..code-block' -l -r --include '*.html' --exclude contributing.html --exclude docgen.html --exclude tut2.html )

View file

@ -63,6 +63,10 @@
- ``lineInfoObj`` now returns absolute path instead of project path. - ``lineInfoObj`` now returns absolute path instead of project path.
It's used by ``lineInfo``, ``check``, ``expect``, ``require``, etc. It's used by ``lineInfo``, ``check``, ``expect``, ``require``, etc.
- `threadpool`'s `await` and derivatives have been renamed to `blockUntil`
to avoid confusions with `await` from the `async` macro.
#### Breaking changes in the compiler #### Breaking changes in the compiler
- The undocumented ``#? braces`` parsing mode was removed. - The undocumented ``#? braces`` parsing mode was removed.
@ -91,7 +95,7 @@
- Added the procs ``rationals.`div```, ``rationals.`mod```, ``rationals.floorDiv`` and ``rationals.floorMod`` for rationals. - Added the procs ``rationals.`div```, ``rationals.`mod```, ``rationals.floorDiv`` and ``rationals.floorMod`` for rationals.
- Added the proc ``math.prod`` for product of elements in openArray. - Added the proc ``math.prod`` for product of elements in openArray.
- Added the proc ``parseBinInt`` to parse a binary integer from a string, which returns the value. - Added the proc ``parseBinInt`` to parse a binary integer from a string, which returns the value.
- ``parseOct`` and ``parseBin`` in parseutils now also support the ``maxLen`` argument similar to ``parseHexInt`` - ``parseOct`` and ``parseBin`` in parseutils now also support the ``maxLen`` argument similar to ``parseHexInt``.
- Added the proc ``flush`` for memory mapped files. - Added the proc ``flush`` for memory mapped files.
- Added the ``MemMapFileStream``. - Added the ``MemMapFileStream``.
- Added ``macros.copyLineInfo`` to copy lineInfo from other node. - Added ``macros.copyLineInfo`` to copy lineInfo from other node.
@ -134,7 +138,13 @@
- ``func`` is now an alias for ``proc {.noSideEffect.}``. - ``func`` is now an alias for ``proc {.noSideEffect.}``.
- In order to make ``for`` loops and iterators more flexible to use Nim now - In order to make ``for`` loops and iterators more flexible to use Nim now
supports so called "for-loop macros". See supports so called "for-loop macros". See
the `manual <manual.html#macros-for-loop-macros>`_ for more details. the [manual](manual.html#macros-for-loop-macros) for more details.
This feature enables a Python-like generic ``enumerate`` implementation.
- Case statements can now be rewritten via macros. See the [manual](manual.html#macros-case-statement-macros) for more information.
This feature enables custom pattern matchers.
- the `typedesc` special type has been renamed to just `type`. - the `typedesc` special type has been renamed to just `type`.
- `static` and `type` are now also modifiers similar to `ref` and `ptr`. - `static` and `type` are now also modifiers similar to `ref` and `ptr`.
They denote the special types `static[T]` and `type[T]`. They denote the special types `static[T]` and `type[T]`.
@ -155,7 +165,7 @@
More details in language manual. More details in language manual.
- ``nil`` for strings/seqs is finally gone. Instead the default value for - ``nil`` for strings/seqs is finally gone. Instead the default value for
these is ``"" / @[]``. these is ``"" / @[]``. Use ``--nilseqs:on`` for a transition period.
- Accessing the binary zero terminator in Nim's native strings - Accessing the binary zero terminator in Nim's native strings
is now invalid. Internally a Nim string still has the trailing zero for is now invalid. Internally a Nim string still has the trailing zero for
@ -167,9 +177,20 @@
- Thread-local variables can now be declared inside procs. This implies all - Thread-local variables can now be declared inside procs. This implies all
the effects of the ``global`` pragma. the effects of the ``global`` pragma.
- Nim now supports ``except`` clause in the export statement. - Nim now supports the ``except`` clause in the export statement.
- Range float types, example ``range[0.0 .. Inf]``. More details in language manual. - Range float types, example ``range[0.0 .. Inf]``. More details in language manual.
- The ``{.this.}`` pragma has been deprecated. It never worked within generics and
we found the resulting code harder to read than the more explicit ``obj.field``
syntax.
- "Memory regions" for pointer types have been deprecated, they were hardly used
anywhere. Note that this has **nothing** to do with the ``--gc:regions`` switch
of managing memory.
- The exception hierarchy was slightly reworked, ``SystemError`` was renamed to
``CatchableError`` and is the new base class for any exception that is guaranteed to
be catchable. This change should have minimal impact on most existing Nim code.
### Tool changes ### Tool changes
@ -178,7 +199,7 @@
### Compiler changes ### Compiler changes
- The VM's instruction count limit was raised to 1 billion instructions in - The VM's instruction count limit was raised to 3 million instructions in
order to support more complex computations at compile-time. order to support more complex computations at compile-time.
- Support for hot code reloading has been implemented for the JavaScript - Support for hot code reloading has been implemented for the JavaScript
@ -204,6 +225,6 @@
- macros.bindSym now capable to accepts not only literal string or string constant expression. - macros.bindSym now capable to accepts not only literal string or string constant expression.
bindSym enhancement make it also can accepts computed string or ident node inside macros / bindSym enhancement make it also can accepts computed string or ident node inside macros /
compile time functions / static blocks. Only in templates / regular code it retains it's old behavior. compile time functions / static blocks. Only in templates / regular code it retains it's old behavior.
This new feature can be accessed via {.experimental: "dynamicBindSym".} pragma/switch This new feature can be accessed via {.experimental: "dynamicBindSym".} pragma/switch.
### Bugfixes ### Bugfixes

View file

@ -971,8 +971,8 @@ const
tyFloat..tyFloat128, tyUInt..tyUInt64} tyFloat..tyFloat128, tyUInt..tyUInt64}
ConstantDataTypes*: TTypeKinds = {tyArray, tySet, ConstantDataTypes*: TTypeKinds = {tyArray, tySet,
tyTuple, tySequence} tyTuple, tySequence}
NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tySequence, NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr,
tyProc, tyString, tyError} tyProc, tyError}
ExportableSymKinds* = {skVar, skConst, skProc, skFunc, skMethod, skType, ExportableSymKinds* = {skVar, skConst, skProc, skFunc, skMethod, skType,
skIterator, skIterator,
skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias} skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias}
@ -1150,7 +1150,10 @@ proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) =
for i in countup(0, high(src.data)): dest.data[i] = src.data[i] for i in countup(0, high(src.data)): dest.data[i] = src.data[i]
proc discardSons*(father: PNode) = proc discardSons*(father: PNode) =
father.sons = nil when defined(nimNoNilSeqs):
father.sons = @[]
else:
father.sons = nil
proc withInfo*(n: PNode, info: TLineInfo): PNode = proc withInfo*(n: PNode, info: TLineInfo): PNode =
n.info = info n.info = info
@ -1367,7 +1370,7 @@ proc createModuleAlias*(s: PSym, newIdent: PIdent, info: TLineInfo;
result.loc = s.loc result.loc = s.loc
result.annex = s.annex result.annex = s.annex
# XXX once usedGenerics is used, ensure module aliases keep working! # XXX once usedGenerics is used, ensure module aliases keep working!
assert s.usedGenerics == nil assert s.usedGenerics.len == 0
proc initStrTable*(x: var TStrTable) = proc initStrTable*(x: var TStrTable) =
x.counter = 0 x.counter = 0
@ -1592,7 +1595,10 @@ proc getStr*(a: PNode): string =
of nkStrLit..nkTripleStrLit: result = a.strVal of nkStrLit..nkTripleStrLit: result = a.strVal
of nkNilLit: of nkNilLit:
# let's hope this fixes more problems than it creates: # let's hope this fixes more problems than it creates:
result = nil when defined(nimNoNilSeqs):
result = ""
else:
result = nil
else: else:
doAssert false, "getStr" doAssert false, "getStr"
#internalError(a.info, "getStr") #internalError(a.info, "getStr")

View file

@ -431,7 +431,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
# don't call '$' here for efficiency: # don't call '$' here for efficiency:
let pat = ri.sons[0].sym.loc.r.data let pat = ri.sons[0].sym.loc.r.data
internalAssert p.config, pat != nil internalAssert p.config, pat.len > 0
if pat.contains({'#', '(', '@', '\''}): if pat.contains({'#', '(', '@', '\''}):
var pl = genPatternCall(p, ri, pat, typ) var pl = genPatternCall(p, ri, pat, typ)
# simpler version of 'fixupCall' that works with the pl+params combination: # simpler version of 'fixupCall' that works with the pl+params combination:
@ -480,7 +480,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
# don't call '$' here for efficiency: # don't call '$' here for efficiency:
let pat = ri.sons[0].sym.loc.r.data let pat = ri.sons[0].sym.loc.r.data
internalAssert p.config, pat != nil internalAssert p.config, pat.len > 0
var start = 3 var start = 3
if ' ' in pat: if ' ' in pat:
start = 1 start = 1

View file

@ -65,9 +65,10 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
of tyString: of tyString:
# with the new semantics for 'nil' strings, we can map "" to nil and # with the new semantics for 'nil' strings, we can map "" to nil and
# save tons of allocations: # save tons of allocations:
#if n.strVal.len == 0: result = genNilStringLiteral(p.module, n.info) if n.strVal.len == 0 and optNilSeqs notin p.options:
#else: result = genNilStringLiteral(p.module, n.info)
result = genStringLiteral(p.module, n) else:
result = genStringLiteral(p.module, n)
else: else:
if n.strVal.isNil: result = rope("NIM_NIL") if n.strVal.isNil: result = rope("NIM_NIL")
else: result = makeCString(n.strVal) else: result = makeCString(n.strVal)

View file

@ -58,7 +58,7 @@ proc genSectionEnd*(fs: TCFileSection; conf: ConfigRef): Rope =
proc genSectionStart*(ps: TCProcSection; conf: ConfigRef): Rope = proc genSectionStart*(ps: TCProcSection; conf: ConfigRef): Rope =
if compilationCachePresent(conf): if compilationCachePresent(conf):
result = rope(nil) result = rope("")
add(result, "\n/*\t") add(result, "\n/*\t")
add(result, CProcSectionNames[ps]) add(result, CProcSectionNames[ps])
add(result, ":*/\n") add(result, ":*/\n")

View file

@ -278,6 +278,7 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
of "implicitstatic": result = contains(conf.options, optImplicitStatic) of "implicitstatic": result = contains(conf.options, optImplicitStatic)
of "patterns": result = contains(conf.options, optPatterns) of "patterns": result = contains(conf.options, optPatterns)
of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace) of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace)
of "nilseqs": result = contains(conf.options, optNilSeqs)
else: invalidCmdLineOption(conf, passCmd1, switch, info) else: invalidCmdLineOption(conf, passCmd1, switch, info)
proc processPath(conf: ConfigRef; path: string, info: TLineInfo, proc processPath(conf: ConfigRef; path: string, info: TLineInfo,
@ -496,6 +497,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
else: else:
localError(conf, info, errOnOrOffExpectedButXFound % arg) localError(conf, info, errOnOrOffExpectedButXFound % arg)
of "laxstrings": processOnOffSwitch(conf, {optLaxStrings}, arg, pass, info) of "laxstrings": processOnOffSwitch(conf, {optLaxStrings}, arg, pass, info)
of "nilseqs": processOnOffSwitch(conf, {optNilSeqs}, arg, pass, info)
of "checks", "x": processOnOffSwitch(conf, ChecksOptions, arg, pass, info) of "checks", "x": processOnOffSwitch(conf, ChecksOptions, arg, pass, info)
of "floatchecks": of "floatchecks":
processOnOffSwitch(conf, {optNaNCheck, optInfCheck}, arg, pass, info) processOnOffSwitch(conf, {optNaNCheck, optInfCheck}, arg, pass, info)

View file

@ -74,3 +74,5 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimVmExportFixed") defineSymbol("nimVmExportFixed")
defineSymbol("nimIncrSeqV3") defineSymbol("nimIncrSeqV3")
defineSymbol("nimAshr") defineSymbol("nimAshr")
defineSymbol("nimNoNilSeqs")
defineSymbol("nimNoNilSeqs2")

View file

@ -191,7 +191,7 @@ proc ropeFormatNamedVars(conf: ConfigRef; frmt: FormatStr,
proc genComment(d: PDoc, n: PNode): string = proc genComment(d: PDoc, n: PNode): string =
result = "" result = ""
var dummyHasToc: bool var dummyHasToc: bool
if n.comment != nil: if n.comment.len > 0:
renderRstToOut(d[], parseRst(n.comment, toFilename(d.conf, n.info), renderRstToOut(d[], parseRst(n.comment, toFilename(d.conf, n.info),
toLinenumber(n.info), toColumn(n.info), toLinenumber(n.info), toColumn(n.info),
dummyHasToc, d.options, d.conf), result) dummyHasToc, d.options, d.conf), result)
@ -205,7 +205,8 @@ proc genRecComment(d: PDoc, n: PNode): Rope =
result = genRecComment(d, n.sons[i]) result = genRecComment(d, n.sons[i])
if result != nil: return if result != nil: return
else: else:
n.comment = nil when defined(nimNoNilSeqs): n.comment = ""
else: n.comment = nil
proc getPlainDocstring(n: PNode): string = proc getPlainDocstring(n: PNode): string =
## Gets the plain text docstring of a node non destructively. ## Gets the plain text docstring of a node non destructively.
@ -215,7 +216,7 @@ proc getPlainDocstring(n: PNode): string =
## the concatenated ``##`` comments of the node. ## the concatenated ``##`` comments of the node.
result = "" result = ""
if n == nil: return if n == nil: return
if n.comment != nil and startsWith(n.comment, "##"): if startsWith(n.comment, "##"):
result = n.comment result = n.comment
if result.len < 1: if result.len < 1:
for i in countup(0, safeLen(n)-1): for i in countup(0, safeLen(n)-1):
@ -564,9 +565,9 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode =
result = %{ "name": %name, "type": %($k), "line": %n.info.line.int, result = %{ "name": %name, "type": %($k), "line": %n.info.line.int,
"col": %n.info.col} "col": %n.info.col}
if comm != nil and comm != "": if comm.len > 0:
result["description"] = %comm result["description"] = %comm
if r.buf != nil: if r.buf.len > 0:
result["code"] = %r.buf result["code"] = %r.buf
proc checkForFalse(n: PNode): bool = proc checkForFalse(n: PNode): bool =
@ -634,7 +635,7 @@ proc add(d: PDoc; j: JsonNode) =
proc generateJson*(d: PDoc, n: PNode) = proc generateJson*(d: PDoc, n: PNode) =
case n.kind case n.kind
of nkCommentStmt: of nkCommentStmt:
if n.comment != nil and startsWith(n.comment, "##"): if startsWith(n.comment, "##"):
let stripped = n.comment.substr(2).strip let stripped = n.comment.substr(2).strip
d.add %{ "comment": %stripped, "line": %n.info.line.int, d.add %{ "comment": %stripped, "line": %n.info.line.int,
"col": %n.info.col } "col": %n.info.col }
@ -678,7 +679,7 @@ proc genTagsItem(d: PDoc, n, nameNode: PNode, k: TSymKind): string =
proc generateTags*(d: PDoc, n: PNode, r: var Rope) = proc generateTags*(d: PDoc, n: PNode, r: var Rope) =
case n.kind case n.kind
of nkCommentStmt: of nkCommentStmt:
if n.comment != nil and startsWith(n.comment, "##"): if startsWith(n.comment, "##"):
let stripped = n.comment.substr(2).strip let stripped = n.comment.substr(2).strip
r.add stripped r.add stripped
of nkProcDef: of nkProcDef:

View file

@ -30,14 +30,7 @@ type
wordCounter: int wordCounter: int
idAnon*, idDelegator*, emptyIdent*: PIdent idAnon*, idDelegator*, emptyIdent*: PIdent
when false: proc resetIdentCache*() = discard
var
legacy: IdentCache
proc resetIdentCache*() =
when false:
for i in low(legacy.buckets)..high(legacy.buckets):
legacy.buckets[i] = nil
proc cmpIgnoreStyle*(a, b: cstring, blen: int): int = proc cmpIgnoreStyle*(a, b: cstring, blen: int): int =
if a[0] != b[0]: return 1 if a[0] != b[0]: return 1
@ -73,11 +66,9 @@ proc cmpExact(a, b: cstring, blen: int): int =
if result == 0: if result == 0:
if a[i] != '\0': result = 1 if a[i] != '\0': result = 1
{.this: self.} proc getIdent*(ic: IdentCache; identifier: cstring, length: int, h: Hash): PIdent =
var idx = h and high(ic.buckets)
proc getIdent*(self: IdentCache; identifier: cstring, length: int, h: Hash): PIdent = result = ic.buckets[idx]
var idx = h and high(buckets)
result = buckets[idx]
var last: PIdent = nil var last: PIdent = nil
var id = 0 var id = 0
while result != nil: while result != nil:
@ -85,8 +76,8 @@ proc getIdent*(self: IdentCache; identifier: cstring, length: int, h: Hash): PId
if last != nil: if last != nil:
# make access to last looked up identifier faster: # make access to last looked up identifier faster:
last.next = result.next last.next = result.next
result.next = buckets[idx] result.next = ic.buckets[idx]
buckets[idx] = result ic.buckets[idx] = result
return return
elif cmpIgnoreStyle(cstring(result.s), identifier, length) == 0: elif cmpIgnoreStyle(cstring(result.s), identifier, length) == 0:
assert((id == 0) or (id == result.id)) assert((id == 0) or (id == result.id))
@ -97,20 +88,20 @@ proc getIdent*(self: IdentCache; identifier: cstring, length: int, h: Hash): PId
result.h = h result.h = h
result.s = newString(length) result.s = newString(length)
for i in countup(0, length - 1): result.s[i] = identifier[i] for i in countup(0, length - 1): result.s[i] = identifier[i]
result.next = buckets[idx] result.next = ic.buckets[idx]
buckets[idx] = result ic.buckets[idx] = result
if id == 0: if id == 0:
inc(wordCounter) inc(ic.wordCounter)
result.id = -wordCounter result.id = -ic.wordCounter
else: else:
result.id = id result.id = id
proc getIdent*(self: IdentCache; identifier: string): PIdent = proc getIdent*(ic: IdentCache; identifier: string): PIdent =
result = getIdent(cstring(identifier), len(identifier), result = getIdent(ic, cstring(identifier), len(identifier),
hashIgnoreStyle(identifier)) hashIgnoreStyle(identifier))
proc getIdent*(self: IdentCache; identifier: string, h: Hash): PIdent = proc getIdent*(ic: IdentCache; identifier: string, h: Hash): PIdent =
result = getIdent(cstring(identifier), len(identifier), h) result = getIdent(ic, cstring(identifier), len(identifier), h)
proc newIdentCache*(): IdentCache = proc newIdentCache*(): IdentCache =
result = IdentCache() result = IdentCache()

View file

@ -1267,7 +1267,7 @@ proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) =
if f.loc.r == nil: f.loc.r = mangleName(p.module, f) if f.loc.r == nil: f.loc.r = mangleName(p.module, f)
if sfInfixCall in f.flags: if sfInfixCall in f.flags:
let pat = n.sons[0].sym.loc.r.data let pat = n.sons[0].sym.loc.r.data
internalAssert p.config, pat != nil internalAssert p.config, pat.len > 0
if pat.contains({'#', '(', '@'}): if pat.contains({'#', '(', '@'}):
var typ = skipTypes(n.sons[0].typ, abstractInst) var typ = skipTypes(n.sons[0].typ, abstractInst)
assert(typ.kind == tyProc) assert(typ.kind == tyProc)
@ -1350,7 +1350,7 @@ proc arrayTypeForElemType(typ: PType): string =
of tyUint8: "Uint8Array" of tyUint8: "Uint8Array"
of tyFloat32: "Float32Array" of tyFloat32: "Float32Array"
of tyFloat64, tyFloat: "Float64Array" of tyFloat64, tyFloat: "Float64Array"
else: nil else: ""
proc createVar(p: PProc, typ: PType, indirect: bool): Rope = proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
var t = skipTypes(typ, abstractInst) var t = skipTypes(typ, abstractInst)

View file

@ -164,13 +164,13 @@ type
TNoteKinds* = set[TNoteKind] TNoteKinds* = set[TNoteKind]
proc computeNotesVerbosity(): array[0..3, TNoteKinds] = proc computeNotesVerbosity(): array[0..3, TNoteKinds] =
result[3] = {low(TNoteKind)..high(TNoteKind)} - {} result[3] = {low(TNoteKind)..high(TNoteKind)} - {}
result[2] = result[3] - {hintStackTrace, warnUninit, hintExtendedContext} result[2] = result[3] - {hintStackTrace, warnUninit, hintExtendedContext}
result[1] = result[2] - {warnShadowIdent, warnProveField, warnProveIndex, result[1] = result[2] - {warnShadowIdent, warnProveField, warnProveIndex,
warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd, warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
hintSource, hintGlobalVar, hintGCStats} hintSource, hintGlobalVar, hintGCStats}
result[0] = result[1] - {hintSuccessX, hintSuccess, hintConf, result[0] = result[1] - {hintSuccessX, hintSuccess, hintConf,
hintProcessing, hintPattern, hintExecuting, hintLinking} hintProcessing, hintPattern, hintExecuting, hintLinking}
const const
NotesVerbosity* = computeNotesVerbosity() NotesVerbosity* = computeNotesVerbosity()

View file

@ -262,7 +262,7 @@ proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string) =
err.add "\nThis might be caused by a recursive module dependency: " err.add "\nThis might be caused by a recursive module dependency: "
err.add c.recursiveDep err.add c.recursiveDep
# prevent excessive errors for 'nim check' # prevent excessive errors for 'nim check'
c.recursiveDep = nil c.recursiveDep = ""
localError(c.config, info, errGenerated, err) localError(c.config, info, errGenerated, err)
proc lookUp*(c: PContext, n: PNode): PSym = proc lookUp*(c: PContext, n: PNode): PSym =

View file

@ -65,10 +65,8 @@ type
proc hash*(x: FileIndex): Hash {.borrow.} proc hash*(x: FileIndex): Hash {.borrow.}
{.this: g.}
proc stopCompile*(g: ModuleGraph): bool {.inline.} = proc stopCompile*(g: ModuleGraph): bool {.inline.} =
result = doStopCompile != nil and doStopCompile() result = g.doStopCompile != nil and g.doStopCompile()
proc createMagic*(g: ModuleGraph; name: string, m: TMagic): PSym = proc createMagic*(g: ModuleGraph; name: string, m: TMagic): PSym =
result = newSym(skProc, getIdent(g.cache, name), nil, unknownLineInfo(), {}) result = newSym(skProc, getIdent(g.cache, name), nil, unknownLineInfo(), {})
@ -98,44 +96,44 @@ proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
result.cacheTables = initTable[string, BTree[string, PNode]]() result.cacheTables = initTable[string, BTree[string, PNode]]()
proc resetAllModules*(g: ModuleGraph) = proc resetAllModules*(g: ModuleGraph) =
initStrTable(packageSyms) initStrTable(g.packageSyms)
deps = initIntSet() g.deps = initIntSet()
modules = @[] g.modules = @[]
importStack = @[] g.importStack = @[]
inclToMod = initTable[FileIndex, FileIndex]() g.inclToMod = initTable[FileIndex, FileIndex]()
usageSym = nil g.usageSym = nil
owners = @[] g.owners = @[]
methods = @[] g.methods = @[]
initStrTable(compilerprocs) initStrTable(g.compilerprocs)
initStrTable(exposed) initStrTable(g.exposed)
proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym = proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym =
if fileIdx.int32 >= 0 and fileIdx.int32 < modules.len: if fileIdx.int32 >= 0 and fileIdx.int32 < g.modules.len:
result = modules[fileIdx.int32] result = g.modules[fileIdx.int32]
proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b
proc addDep*(g: ModuleGraph; m: PSym, dep: FileIndex) = proc addDep*(g: ModuleGraph; m: PSym, dep: FileIndex) =
assert m.position == m.info.fileIndex.int32 assert m.position == m.info.fileIndex.int32
addModuleDep(g.incr, g.config, m.info.fileIndex, dep, isIncludeFile = false) addModuleDep(g.incr, g.config, m.info.fileIndex, dep, isIncludeFile = false)
if suggestMode: if g.suggestMode:
deps.incl m.position.dependsOn(dep.int) g.deps.incl m.position.dependsOn(dep.int)
# we compute the transitive closure later when quering the graph lazily. # we compute the transitive closure later when quering the graph lazily.
# this improves efficiency quite a lot: # this improves efficiency quite a lot:
#invalidTransitiveClosure = true #invalidTransitiveClosure = true
proc addIncludeDep*(g: ModuleGraph; module, includeFile: FileIndex) = proc addIncludeDep*(g: ModuleGraph; module, includeFile: FileIndex) =
addModuleDep(g.incr, g.config, module, includeFile, isIncludeFile = true) addModuleDep(g.incr, g.config, module, includeFile, isIncludeFile = true)
discard hasKeyOrPut(inclToMod, includeFile, module) discard hasKeyOrPut(g.inclToMod, includeFile, module)
proc parentModule*(g: ModuleGraph; fileIdx: FileIndex): FileIndex = proc parentModule*(g: ModuleGraph; fileIdx: FileIndex): FileIndex =
## returns 'fileIdx' if the file belonging to this index is ## returns 'fileIdx' if the file belonging to this index is
## directly used as a module or else the module that first ## directly used as a module or else the module that first
## references this include file. ## references this include file.
if fileIdx.int32 >= 0 and fileIdx.int32 < modules.len and modules[fileIdx.int32] != nil: if fileIdx.int32 >= 0 and fileIdx.int32 < g.modules.len and g.modules[fileIdx.int32] != nil:
result = fileIdx result = fileIdx
else: else:
result = inclToMod.getOrDefault(fileIdx) result = g.inclToMod.getOrDefault(fileIdx)
proc transitiveClosure(g: var IntSet; n: int) = proc transitiveClosure(g: var IntSet; n: int) =
# warshall's algorithm # warshall's algorithm
@ -147,22 +145,22 @@ proc transitiveClosure(g: var IntSet; n: int) =
g.incl i.dependsOn(j) g.incl i.dependsOn(j)
proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) = proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) =
let m = getModule fileIdx let m = g.getModule fileIdx
if m != nil: incl m.flags, sfDirty if m != nil: incl m.flags, sfDirty
proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) = proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) =
# we need to mark its dependent modules D as dirty right away because after # we need to mark its dependent modules D as dirty right away because after
# nimsuggest is done with this module, the module's dirty flag will be # nimsuggest is done with this module, the module's dirty flag will be
# cleared but D still needs to be remembered as 'dirty'. # cleared but D still needs to be remembered as 'dirty'.
if invalidTransitiveClosure: if g.invalidTransitiveClosure:
invalidTransitiveClosure = false g.invalidTransitiveClosure = false
transitiveClosure(deps, modules.len) transitiveClosure(g.deps, g.modules.len)
# every module that *depends* on this file is also dirty: # every module that *depends* on this file is also dirty:
for i in 0i32..<modules.len.int32: for i in 0i32..<g.modules.len.int32:
let m = modules[i] let m = g.modules[i]
if m != nil and deps.contains(i.dependsOn(fileIdx.int)): if m != nil and g.deps.contains(i.dependsOn(fileIdx.int)):
incl m.flags, sfDirty incl m.flags, sfDirty
proc isDirty*(g: ModuleGraph; m: PSym): bool = proc isDirty*(g: ModuleGraph; m: PSym): bool =
result = suggestMode and sfDirty in m.flags result = g.suggestMode and sfDirty in m.flags

View file

@ -391,10 +391,10 @@ proc rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) =
if conf.structuredErrorHook != nil: if conf.structuredErrorHook != nil:
conf.structuredErrorHook(conf, unknownLineInfo(), conf.structuredErrorHook(conf, unknownLineInfo(),
s & (if kind != nil: KindFormat % kind else: ""), sev) s & (if kind.len > 0: KindFormat % kind else: ""), sev)
if not ignoreMsgBecauseOfIdeTools(conf, msg): if not ignoreMsgBecauseOfIdeTools(conf, msg):
if kind != nil: if kind.len > 0:
styledMsgWriteln(color, title, resetStyle, s, styledMsgWriteln(color, title, resetStyle, s,
KindColor, `%`(KindFormat, kind)) KindColor, `%`(KindFormat, kind))
else: else:
@ -483,9 +483,9 @@ proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
if not ignoreMsg: if not ignoreMsg:
if conf.structuredErrorHook != nil: if conf.structuredErrorHook != nil:
conf.structuredErrorHook(conf, info, s & (if kind != nil: KindFormat % kind else: ""), sev) conf.structuredErrorHook(conf, info, s & (if kind.len > 0: KindFormat % kind else: ""), sev)
if not ignoreMsgBecauseOfIdeTools(conf, msg): if not ignoreMsgBecauseOfIdeTools(conf, msg):
if kind != nil: if kind.len > 0:
styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s, styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s,
KindColor, `%`(KindFormat, kind)) KindColor, `%`(KindFormat, kind))
else: else:

View file

@ -38,7 +38,8 @@ type # please make sure we have under 32 options
optPatterns, # en/disable pattern matching optPatterns, # en/disable pattern matching
optMemTracker, optMemTracker,
optHotCodeReloading, optHotCodeReloading,
optLaxStrings optLaxStrings,
optNilSeqs
TOptions* = set[TOption] TOptions* = set[TOption]
TGlobalOption* = enum # **keep binary compatible** TGlobalOption* = enum # **keep binary compatible**
@ -110,14 +111,16 @@ type
ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod, ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod,
ideHighlight, ideOutline, ideKnown, ideMsg ideHighlight, ideOutline, ideKnown, ideMsg
Feature* = enum ## experimental features Feature* = enum ## experimental features; DO NOT RENAME THESE!
implicitDeref, implicitDeref,
dotOperators, dotOperators,
callOperator, callOperator,
parallel, parallel,
destructor, destructor,
notnil, notnil,
dynamicBindSym dynamicBindSym,
forLoopMacros,
caseStmtMacros
SymbolFilesOption* = enum SymbolFilesOption* = enum
disabledSf, writeOnlySf, readOnlySf, v2Sf disabledSf, writeOnlySf, readOnlySf, v2Sf
@ -482,7 +485,7 @@ include packagehandling
proc getOsCacheDir(): string = proc getOsCacheDir(): string =
when defined(posix): when defined(posix):
result = string getEnv("XDG_CACHE_HOME", getHomeDir() / ".cache") result = getEnv("XDG_CACHE_HOME", getHomeDir() / ".cache") / "nim"
else: else:
result = getHomeDir() / genSubDir result = getHomeDir() / genSubDir
@ -600,18 +603,22 @@ proc findModule*(conf: ConfigRef; modulename, currentModule: string): string =
proc findProjectNimFile*(conf: ConfigRef; pkg: string): string = proc findProjectNimFile*(conf: ConfigRef; pkg: string): string =
const extensions = [".nims", ".cfg", ".nimcfg", ".nimble"] const extensions = [".nims", ".cfg", ".nimcfg", ".nimble"]
var candidates: seq[string] = @[] var candidates: seq[string] = @[]
for k, f in os.walkDir(pkg, relative=true): var dir = pkg
if k == pcFile and f != "config.nims": while true:
let (_, name, ext) = splitFile(f) for k, f in os.walkDir(dir, relative=true):
if ext in extensions: if k == pcFile and f != "config.nims":
let x = changeFileExt(pkg / name, ".nim") let (_, name, ext) = splitFile(f)
if fileExists(x): if ext in extensions:
candidates.add x let x = changeFileExt(dir / name, ".nim")
for c in candidates: if fileExists(x):
# nim-foo foo or foo nfoo candidates.add x
if (pkg in c) or (c in pkg): return c for c in candidates:
if candidates.len >= 1: # nim-foo foo or foo nfoo
return candidates[0] if (pkg in c) or (c in pkg): return c
if candidates.len >= 1:
return candidates[0]
dir = parentDir(dir)
if dir == "": break
return "" return ""
proc canonDynlibName(s: string): string = proc canonDynlibName(s: string): string =

View file

@ -150,7 +150,8 @@ template sameOrNoInd(p): bool = p.tok.indent == p.currInd or p.tok.indent < 0
proc rawSkipComment(p: var TParser, node: PNode) = proc rawSkipComment(p: var TParser, node: PNode) =
if p.tok.tokType == tkComment: if p.tok.tokType == tkComment:
if node != nil: if node != nil:
if node.comment == nil: node.comment = "" when not defined(nimNoNilSeqs):
if node.comment == nil: node.comment = ""
when defined(nimpretty): when defined(nimpretty):
if p.tok.commentOffsetB > p.tok.commentOffsetA: if p.tok.commentOffsetB > p.tok.commentOffsetA:
add node.comment, fileSection(p.lex.config, p.lex.fileIdx, p.tok.commentOffsetA, p.tok.commentOffsetB) add node.comment, fileSection(p.lex.config, p.lex.fileIdx, p.tok.commentOffsetA, p.tok.commentOffsetB)

View file

@ -21,14 +21,17 @@ type
formals: int formals: int
c: PContext c: PContext
subMatch: bool # subnode matches are special subMatch: bool # subnode matches are special
mappingIsFull: bool
PPatternContext = var TPatternContext PPatternContext = var TPatternContext
proc getLazy(c: PPatternContext, sym: PSym): PNode = proc getLazy(c: PPatternContext, sym: PSym): PNode =
if not isNil(c.mapping): if c.mappingIsFull:
result = c.mapping[sym.position] result = c.mapping[sym.position]
proc putLazy(c: PPatternContext, sym: PSym, n: PNode) = proc putLazy(c: PPatternContext, sym: PSym, n: PNode) =
if isNil(c.mapping): newSeq(c.mapping, c.formals) if not c.mappingIsFull:
newSeq(c.mapping, c.formals)
c.mappingIsFull = true
c.mapping[sym.position] = n c.mapping[sym.position] = n
proc matches(c: PPatternContext, p, n: PNode): bool proc matches(c: PPatternContext, p, n: PNode): bool
@ -209,7 +212,11 @@ proc matchStmtList(c: PPatternContext, p, n: PNode): PNode =
for j in 0 ..< p.len: for j in 0 ..< p.len:
if not matches(c, p.sons[j], n.sons[i+j]): if not matches(c, p.sons[j], n.sons[i+j]):
# we need to undo any bindings: # we need to undo any bindings:
if not isNil(c.mapping): c.mapping = nil when defined(nimNoNilSeqs):
c.mapping = @[]
c.mappingIsFull = false
else:
if not isNil(c.mapping): c.mapping = nil
return false return false
result = true result = true

View file

@ -1066,8 +1066,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
of wThis: of wThis:
if it.kind in nkPragmaCallKinds and it.len == 2: if it.kind in nkPragmaCallKinds and it.len == 2:
c.selfName = considerQuotedIdent(c, it[1]) c.selfName = considerQuotedIdent(c, it[1])
message(c.config, n.info, warnDeprecated, "the '.this' pragma")
elif it.kind == nkIdent or it.len == 1: elif it.kind == nkIdent or it.len == 1:
c.selfName = getIdent(c.cache, "self") c.selfName = getIdent(c.cache, "self")
message(c.config, n.info, warnDeprecated, "the '.this' pragma")
else: else:
localError(c.config, it.info, "'this' pragma is allowed to have zero or one arguments") localError(c.config, it.info, "'this' pragma is allowed to have zero or one arguments")
of wNoRewrite: of wNoRewrite:

View file

@ -281,7 +281,7 @@ const
proc shouldRenderComment(g: var TSrcGen, n: PNode): bool = proc shouldRenderComment(g: var TSrcGen, n: PNode): bool =
result = false result = false
if n.comment != nil: if n.comment.len > 0:
result = (renderNoComments notin g.flags) or result = (renderNoComments notin g.flags) or
(renderDocComments in g.flags) (renderDocComments in g.flags)
@ -402,7 +402,7 @@ proc lsons(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int =
proc lsub(g: TSrcGen; n: PNode): int = proc lsub(g: TSrcGen; n: PNode): int =
# computes the length of a tree # computes the length of a tree
if isNil(n): return 0 if isNil(n): return 0
if n.comment != nil: return MaxLineLen + 1 if n.comment.len > 0: return MaxLineLen + 1
case n.kind case n.kind
of nkEmpty: result = 0 of nkEmpty: result = 0
of nkTripleStrLit: of nkTripleStrLit:
@ -500,7 +500,7 @@ proc lsub(g: TSrcGen; n: PNode): int =
of nkBreakStmt: result = lsub(g, n.sons[0]) + len("break_") of nkBreakStmt: result = lsub(g, n.sons[0]) + len("break_")
of nkContinueStmt: result = lsub(g, n.sons[0]) + len("continue_") of nkContinueStmt: result = lsub(g, n.sons[0]) + len("continue_")
of nkPragma: result = lcomma(g, n) + 4 of nkPragma: result = lcomma(g, n) + 4
of nkCommentStmt: result = if n.comment.isNil: 0 else: len(n.comment) of nkCommentStmt: result = len(n.comment)
of nkOfBranch: result = lcomma(g, n, 0, - 2) + lsub(g, lastSon(n)) + len("of_:_") of nkOfBranch: result = lcomma(g, n, 0, - 2) + lsub(g, lastSon(n)) + len("of_:_")
of nkImportAs: result = lsub(g, n.sons[0]) + len("_as_") + lsub(g, n.sons[1]) of nkImportAs: result = lsub(g, n.sons[0]) + len("_as_") + lsub(g, n.sons[1])
of nkElifBranch: result = lsons(g, n) + len("elif_:_") of nkElifBranch: result = lsons(g, n) + len("elif_:_")
@ -539,7 +539,7 @@ proc gsub(g: var TSrcGen, n: PNode) =
proc hasCom(n: PNode): bool = proc hasCom(n: PNode): bool =
result = false result = false
if n.isNil: return false if n.isNil: return false
if n.comment != nil: return true if n.comment.len > 0: return true
case n.kind case n.kind
of nkEmpty..nkNilLit: discard of nkEmpty..nkNilLit: discard
else: else:
@ -602,7 +602,7 @@ proc gsection(g: var TSrcGen, n: PNode, c: TContext, kind: TTokType,
dedent(g) dedent(g)
proc longMode(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): bool = proc longMode(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): bool =
result = n.comment != nil result = n.comment.len > 0
if not result: if not result:
# check further # check further
for i in countup(start, sonsLen(n) + theEnd): for i in countup(start, sonsLen(n) + theEnd):
@ -637,7 +637,7 @@ proc gstmts(g: var TSrcGen, n: PNode, c: TContext, doIndent=true) =
proc gcond(g: var TSrcGen, n: PNode) = proc gcond(g: var TSrcGen, n: PNode) =
if n.kind == nkStmtListExpr: if n.kind == nkStmtListExpr:
put(g, tkParLe, "(") put(g, tkParLe, "(")
gsub(g, n) gsub(g, n)
if n.kind == nkStmtListExpr: if n.kind == nkStmtListExpr:
put(g, tkParRi, ")") put(g, tkParRi, ")")
@ -864,7 +864,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
if isNil(n): return if isNil(n): return
var var
a: TContext a: TContext
if n.comment != nil: pushCom(g, n) if n.comment.len > 0: pushCom(g, n)
case n.kind # atoms: case n.kind # atoms:
of nkTripleStrLit: put(g, tkTripleStrLit, atom(g, n)) of nkTripleStrLit: put(g, tkTripleStrLit, atom(g, n))
of nkEmpty: discard of nkEmpty: discard
@ -1079,7 +1079,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
elif n[0].kind in {nkOpenSymChoice, nkClosedSymChoice}: n[0][0].sym.name elif n[0].kind in {nkOpenSymChoice, nkClosedSymChoice}: n[0][0].sym.name
else: nil else: nil
var n_next = n[1] var n_next = n[1]
while n_next.kind in {nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref, while n_next.kind in {nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref,
nkStringToCString, nkCStringToString} and n_next.len > 0: nkStringToCString, nkCStringToString} and n_next.len > 0:
n_next = n_next[0] n_next = n_next[0]
if n_next.kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)): if n_next.kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):

View file

@ -66,29 +66,19 @@ type
Rope* = ref RopeObj Rope* = ref RopeObj
RopeObj*{.acyclic.} = object of RootObj # the empty rope is represented RopeObj*{.acyclic.} = object of RootObj # the empty rope is represented
# by nil to safe space # by nil to safe space
left*, right*: Rope left, right: Rope
length*: int L: int # <= 0 if a leaf
data*: string # != nil if a leaf data*: string
proc len*(a: Rope): int = proc len*(a: Rope): int =
## the rope's length ## the rope's length
if a == nil: result = 0 if a == nil: result = 0
else: result = a.length else: result = abs a.L
proc newRope(data: string = nil): Rope = proc newRope(data: string = ""): Rope =
new(result) new(result)
if data != nil: result.L = -len(data)
result.length = len(data) result.data = data
result.data = data
proc newMutableRope*(capacity = 30): Rope =
## creates a new rope that supports direct modifications of the rope's
## 'data' and 'length' fields.
new(result)
result.data = newStringOfCap(capacity)
proc freezeMutableRope*(r: Rope) {.inline.} =
r.length = r.data.len
var var
cache: array[0..2048*2 - 1, Rope] # XXX Global here! cache: array[0..2048*2 - 1, Rope] # XXX Global here!
@ -147,7 +137,7 @@ proc `&`*(a, b: Rope): Rope =
result = a result = a
else: else:
result = newRope() result = newRope()
result.length = a.length + b.length result.L = abs(a.L) + abs(b.L)
result.left = a result.left = a
result.right = b result.right = b

View file

@ -45,7 +45,7 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
template cbos(name, body) {.dirty.} = template cbos(name, body) {.dirty.} =
result.registerCallback "stdlib.system." & astToStr(name), result.registerCallback "stdlib.system." & astToStr(name),
proc (a: VmArgs) = proc (a: VmArgs) =
errorMsg = nil errorMsg = ""
try: try:
body body
except OSError: except OSError:

View file

@ -119,7 +119,7 @@ proc commonType*(x, y: PType): PType =
elif b.kind == tyStmt: result = b elif b.kind == tyStmt: result = b
elif a.kind == tyTypeDesc: elif a.kind == tyTypeDesc:
# turn any concrete typedesc into the abstract typedesc type # turn any concrete typedesc into the abstract typedesc type
if a.sons == nil: result = a if a.len == 0: result = a
else: else:
result = newType(tyTypeDesc, a.owner) result = newType(tyTypeDesc, a.owner)
rawAddSon(result, newType(tyNone, a.owner)) rawAddSon(result, newType(tyNone, a.owner))

View file

@ -89,7 +89,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
continue continue
determineType(c, sym) determineType(c, sym)
initCandidate(c, z, sym, initialBinding, scope, diagnosticsFlag) initCandidate(c, z, sym, initialBinding, scope, diagnosticsFlag)
if c.currentScope.symbols.counter == counterInitial or syms != nil: if c.currentScope.symbols.counter == counterInitial or syms.len != 0:
matches(c, n, orig, z) matches(c, n, orig, z)
if z.state == csMatch: if z.state == csMatch:
#if sym.name.s == "==" and (n.info ?? "temp3"): #if sym.name.s == "==" and (n.info ?? "temp3"):
@ -237,7 +237,7 @@ proc bracketNotFoundError(c: PContext; n: PNode) =
if symx.kind in routineKinds: if symx.kind in routineKinds:
errors.add(CandidateError(sym: symx, errors.add(CandidateError(sym: symx,
unmatchedVarParam: 0, firstMismatch: 0, unmatchedVarParam: 0, firstMismatch: 0,
diagnostics: nil, diagnostics: @[],
enabled: false)) enabled: false))
symx = nextOverloadIter(o, c, headSymbol) symx = nextOverloadIter(o, c, headSymbol)
if errors.len == 0: if errors.len == 0:
@ -455,7 +455,7 @@ proc tryDeref(n: PNode): PNode =
proc semOverloadedCall(c: PContext, n, nOrig: PNode, proc semOverloadedCall(c: PContext, n, nOrig: PNode,
filter: TSymKinds, flags: TExprFlags): PNode = filter: TSymKinds, flags: TExprFlags): PNode =
var errors: CandidateErrors = if efExplain in flags: @[] else: nil var errors: CandidateErrors = @[] # if efExplain in flags: @[] else: nil
var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags) var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags)
if r.state == csMatch: if r.state == csMatch:
# this may be triggered, when the explain pragma is used # this may be triggered, when the explain pragma is used

View file

@ -37,6 +37,7 @@ type
# in standalone ``except`` and ``finally`` # in standalone ``except`` and ``finally``
next*: PProcCon # used for stacking procedure contexts next*: PProcCon # used for stacking procedure contexts
wasForwarded*: bool # whether the current proc has a separate header wasForwarded*: bool # whether the current proc has a separate header
mappingExists*: bool
mapping*: TIdTable mapping*: TIdTable
TMatchedConcept* = object TMatchedConcept* = object
@ -176,12 +177,14 @@ proc lastOptionEntry*(c: PContext): POptionEntry =
proc popProcCon*(c: PContext) {.inline.} = c.p = c.p.next proc popProcCon*(c: PContext) {.inline.} = c.p = c.p.next
proc put*(p: PProcCon; key, val: PSym) = proc put*(p: PProcCon; key, val: PSym) =
if p.mapping.data == nil: initIdTable(p.mapping) if not p.mappingExists:
initIdTable(p.mapping)
p.mappingExists = true
#echo "put into table ", key.info #echo "put into table ", key.info
p.mapping.idTablePut(key, val) p.mapping.idTablePut(key, val)
proc get*(p: PProcCon; key: PSym): PSym = proc get*(p: PProcCon; key: PSym): PSym =
if p.mapping.data == nil: return nil if not p.mappingExists: return nil
result = PSym(p.mapping.idTableGet(key)) result = PSym(p.mapping.idTableGet(key))
proc getGenSym*(c: PContext; s: PSym): PSym = proc getGenSym*(c: PContext; s: PSym): PSym =

View file

@ -97,10 +97,9 @@ proc sameInstantiation(a, b: TInstantiation): bool =
proc genericCacheGet(genericSym: PSym, entry: TInstantiation; proc genericCacheGet(genericSym: PSym, entry: TInstantiation;
id: CompilesId): PSym = id: CompilesId): PSym =
if genericSym.procInstCache != nil: for inst in genericSym.procInstCache:
for inst in genericSym.procInstCache: if inst.compilesId == id and sameInstantiation(entry, inst[]):
if inst.compilesId == id and sameInstantiation(entry, inst[]): return inst.sym
return inst.sym
when false: when false:
proc `$`(x: PSym): string = proc `$`(x: PSym): string =

View file

@ -88,7 +88,7 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
else: else:
globalError(conf, n.info, "string literal must be of some string type") globalError(conf, n.info, "string literal must be of some string type")
of nkNilLit: of nkNilLit:
if x.kind in NilableTypes: if x.kind in NilableTypes+{tyString, tySequence}:
n.typ = t n.typ = t
else: else:
globalError(conf, n.info, "nil literal must be of some pointer type") globalError(conf, n.info, "nil literal must be of some pointer type")

View file

@ -121,7 +121,7 @@ proc missingMandatoryFields(c: PContext, fieldsRecList, initExpr: PNode): string
if {tfNotNil, tfNeedsInit} * r.sym.typ.flags != {}: if {tfNotNil, tfNeedsInit} * r.sym.typ.flags != {}:
let assignment = locateFieldInInitExpr(c, r.sym, initExpr) let assignment = locateFieldInInitExpr(c, r.sym, initExpr)
if assignment == nil: if assignment == nil:
if result == nil: if result.len == 0:
result = r.sym.name.s result = r.sym.name.s
else: else:
result.add ", " result.add ", "
@ -129,7 +129,7 @@ proc missingMandatoryFields(c: PContext, fieldsRecList, initExpr: PNode): string
proc checkForMissingFields(c: PContext, recList, initExpr: PNode) = proc checkForMissingFields(c: PContext, recList, initExpr: PNode) =
let missing = missingMandatoryFields(c, recList, initExpr) let missing = missingMandatoryFields(c, recList, initExpr)
if missing != nil: if missing.len > 0:
localError(c.config, initExpr.info, "fields not initialized: $1.", [missing]) localError(c.config, initExpr.info, "fields not initialized: $1.", [missing])
proc semConstructFields(c: PContext, recNode: PNode, proc semConstructFields(c: PContext, recNode: PNode,

View file

@ -178,71 +178,6 @@ proc semIf(c: PContext, n: PNode): PNode =
result.kind = nkIfExpr result.kind = nkIfExpr
result.typ = typ result.typ = typ
proc semCase(c: PContext, n: PNode): PNode =
result = n
checkMinSonsLen(n, 2, c.config)
openScope(c)
n.sons[0] = semExprWithType(c, n.sons[0])
var chckCovered = false
var covered: BiggestInt = 0
var typ = commonTypeBegin
var hasElse = false
let caseTyp = skipTypes(n.sons[0].typ, abstractVarRange-{tyTypeDesc})
case caseTyp.kind
of tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32, tyBool:
chckCovered = true
of tyFloat..tyFloat128, tyString, tyError:
discard
else:
localError(c.config, n.info, errSelectorMustBeOfCertainTypes)
return
for i in countup(1, sonsLen(n) - 1):
var x = n.sons[i]
when defined(nimsuggest):
if c.config.ideCmd == ideSug and exactEquals(c.config.m.trackPos, x.info) and caseTyp.kind == tyEnum:
suggestEnum(c, x, caseTyp)
case x.kind
of nkOfBranch:
checkMinSonsLen(x, 2, c.config)
semCaseBranch(c, n, x, i, covered)
var last = sonsLen(x)-1
x.sons[last] = semExprBranchScope(c, x.sons[last])
typ = commonType(typ, x.sons[last])
of nkElifBranch:
chckCovered = false
checkSonsLen(x, 2, c.config)
openScope(c)
x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0]))
x.sons[1] = semExprBranch(c, x.sons[1])
typ = commonType(typ, x.sons[1])
closeScope(c)
of nkElse:
chckCovered = false
checkSonsLen(x, 1, c.config)
x.sons[0] = semExprBranchScope(c, x.sons[0])
typ = commonType(typ, x.sons[0])
hasElse = true
else:
illFormedAst(x, c.config)
if chckCovered:
if covered == toCover(c, n.sons[0].typ):
hasElse = true
else:
localError(c.config, n.info, "not all cases are covered")
closeScope(c)
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse:
for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon)
# propagate any enforced VoidContext:
if typ == c.enforceVoidContext:
result.typ = c.enforceVoidContext
else:
for i in 1..n.len-1:
var it = n.sons[i]
let j = it.len-1
if not endsInNoReturn(it.sons[j]):
it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
result.typ = typ
proc semTry(c: PContext, n: PNode): PNode = proc semTry(c: PContext, n: PNode): PNode =
var check = initIntSet() var check = initIntSet()
@ -683,29 +618,28 @@ proc isTrivalStmtExpr(n: PNode): bool =
return false return false
result = true result = true
proc handleForLoopMacro(c: PContext; n: PNode): PNode = proc handleStmtMacro(c: PContext; n, selector: PNode; magicType: string): PNode =
let iterExpr = n[^2] if selector.kind in nkCallKinds:
if iterExpr.kind in nkCallKinds:
# we transform # we transform
# n := for a, b, c in m(x, y, z): Y # n := for a, b, c in m(x, y, z): Y
# to # to
# m(n) # m(n)
let forLoopStmt = magicsys.getCompilerProc(c.graph, "ForLoopStmt") let maType = magicsys.getCompilerProc(c.graph, magicType)
if forLoopStmt == nil: return if maType == nil: return
let headSymbol = iterExpr[0] let headSymbol = selector[0]
var o: TOverloadIter var o: TOverloadIter
var match: PSym = nil var match: PSym = nil
var symx = initOverloadIter(o, c, headSymbol) var symx = initOverloadIter(o, c, headSymbol)
while symx != nil: while symx != nil:
if symx.kind in {skTemplate, skMacro}: if symx.kind in {skTemplate, skMacro}:
if symx.typ.len == 2 and symx.typ[1] == forLoopStmt.typ: if symx.typ.len == 2 and symx.typ[1] == maType.typ:
if match == nil: if match == nil:
match = symx match = symx
else: else:
localError(c.config, n.info, errAmbiguousCallXYZ % [ localError(c.config, n.info, errAmbiguousCallXYZ % [
getProcHeader(c.config, match), getProcHeader(c.config, match),
getProcHeader(c.config, symx), $iterExpr]) getProcHeader(c.config, symx), $selector])
symx = nextOverloadIter(o, c, headSymbol) symx = nextOverloadIter(o, c, headSymbol)
if match == nil: return if match == nil: return
@ -717,11 +651,44 @@ proc handleForLoopMacro(c: PContext; n: PNode): PNode =
of skTemplate: result = semTemplateExpr(c, callExpr, match, {}) of skTemplate: result = semTemplateExpr(c, callExpr, match, {})
else: result = nil else: result = nil
proc handleForLoopMacro(c: PContext; n: PNode): PNode =
result = handleStmtMacro(c, n, n[^2], "ForLoopStmt")
proc handleCaseStmtMacro(c: PContext; n: PNode): PNode =
# n[0] has been sem'checked and has a type. We use this to resolve
# 'match(n[0])' but then we pass 'n' to the 'match' macro. This seems to
# be the best solution.
var toResolve = newNodeI(nkCall, n.info)
toResolve.add newIdentNode(getIdent(c.cache, "match"), n.info)
toResolve.add n[0]
var errors: CandidateErrors
var r = resolveOverloads(c, toResolve, toResolve, {skTemplate, skMacro}, {},
errors, false)
if r.state == csMatch:
var match = r.calleeSym
markUsed(c.config, n[0].info, match, c.graph.usageSym)
styleCheckUse(n[0].info, match)
# but pass 'n' to the 'match' macro, not 'n[0]':
r.call.sons[1] = n
let toExpand = semResolvedCall(c, r, r.call, {})
case match.kind
of skMacro: result = semMacroExpr(c, toExpand, toExpand, match, {})
of skTemplate: result = semTemplateExpr(c, toExpand, match, {})
else: result = nil
# this would be the perfectly consistent solution with 'for loop macros',
# but it kinda sucks for pattern matching as the matcher is not attached to
# a type then:
when false:
result = handleStmtMacro(c, n, n[0], "CaseStmt")
proc semFor(c: PContext, n: PNode): PNode = proc semFor(c: PContext, n: PNode): PNode =
checkMinSonsLen(n, 3, c.config) checkMinSonsLen(n, 3, c.config)
var length = sonsLen(n) var length = sonsLen(n)
result = handleForLoopMacro(c, n) if forLoopMacros in c.features:
if result != nil: return result result = handleForLoopMacro(c, n)
if result != nil: return result
openScope(c) openScope(c)
result = n result = n
n.sons[length-2] = semExprNoDeref(c, n.sons[length-2], {efWantIterator}) n.sons[length-2] = semExprNoDeref(c, n.sons[length-2], {efWantIterator})
@ -757,6 +724,75 @@ proc semFor(c: PContext, n: PNode): PNode =
result.typ = c.enforceVoidContext result.typ = c.enforceVoidContext
closeScope(c) closeScope(c)
proc semCase(c: PContext, n: PNode): PNode =
result = n
checkMinSonsLen(n, 2, c.config)
openScope(c)
n.sons[0] = semExprWithType(c, n.sons[0])
var chckCovered = false
var covered: BiggestInt = 0
var typ = commonTypeBegin
var hasElse = false
let caseTyp = skipTypes(n.sons[0].typ, abstractVarRange-{tyTypeDesc})
case caseTyp.kind
of tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32, tyBool:
chckCovered = true
of tyFloat..tyFloat128, tyString, tyError:
discard
else:
if caseStmtMacros in c.features:
result = handleCaseStmtMacro(c, n)
if result != nil: return result
localError(c.config, n.info, errSelectorMustBeOfCertainTypes)
return
for i in countup(1, sonsLen(n) - 1):
var x = n.sons[i]
when defined(nimsuggest):
if c.config.ideCmd == ideSug and exactEquals(c.config.m.trackPos, x.info) and caseTyp.kind == tyEnum:
suggestEnum(c, x, caseTyp)
case x.kind
of nkOfBranch:
checkMinSonsLen(x, 2, c.config)
semCaseBranch(c, n, x, i, covered)
var last = sonsLen(x)-1
x.sons[last] = semExprBranchScope(c, x.sons[last])
typ = commonType(typ, x.sons[last])
of nkElifBranch:
chckCovered = false
checkSonsLen(x, 2, c.config)
openScope(c)
x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0]))
x.sons[1] = semExprBranch(c, x.sons[1])
typ = commonType(typ, x.sons[1])
closeScope(c)
of nkElse:
chckCovered = false
checkSonsLen(x, 1, c.config)
x.sons[0] = semExprBranchScope(c, x.sons[0])
typ = commonType(typ, x.sons[0])
hasElse = true
else:
illFormedAst(x, c.config)
if chckCovered:
if covered == toCover(c, n.sons[0].typ):
hasElse = true
else:
localError(c.config, n.info, "not all cases are covered")
closeScope(c)
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse:
for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon)
# propagate any enforced VoidContext:
if typ == c.enforceVoidContext:
result.typ = c.enforceVoidContext
else:
for i in 1..n.len-1:
var it = n.sons[i]
let j = it.len-1
if not endsInNoReturn(it.sons[j]):
it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
result.typ = typ
proc semRaise(c: PContext, n: PNode): PNode = proc semRaise(c: PContext, n: PNode): PNode =
result = n result = n
checkSonsLen(n, 1, c.config) checkSonsLen(n, 1, c.config)
@ -970,7 +1006,10 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
var body = s.typ.lastSon var body = s.typ.lastSon
if body.kind == tyObject: if body.kind == tyObject:
# erases all declared fields # erases all declared fields
body.n.sons = nil when defined(nimNoNilSeqs):
body.n.sons = @[]
else:
body.n.sons = nil
popOwner(c) popOwner(c)
closeScope(c) closeScope(c)

View file

@ -193,6 +193,8 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
if region.skipTypes({tyGenericInst, tyAlias, tySink}).kind notin { if region.skipTypes({tyGenericInst, tyAlias, tySink}).kind notin {
tyError, tyObject}: tyError, tyObject}:
message c.config, n[i].info, errGenerated, "region needs to be an object type" message c.config, n[i].info, errGenerated, "region needs to be an object type"
else:
message(c.config, n.info, warnDeprecated, "region for pointer types")
addSonSkipIntLit(result, region) addSonSkipIntLit(result, region)
addSonSkipIntLit(result, t) addSonSkipIntLit(result, t)
if tfPartial in result.flags: if tfPartial in result.flags:

View file

@ -40,8 +40,8 @@ proc searchInstTypes*(key: PType): PType =
if not (genericTyp.kind == tyGenericBody and if not (genericTyp.kind == tyGenericBody and
key.sons[0] == genericTyp and genericTyp.sym != nil): return key.sons[0] == genericTyp and genericTyp.sym != nil): return
if genericTyp.sym.typeInstCache == nil: when not defined(nimNoNilSeqs):
return if genericTyp.sym.typeInstCache == nil: return
for inst in genericTyp.sym.typeInstCache: for inst in genericTyp.sym.typeInstCache:
if inst.id == key.id: return inst if inst.id == key.id: return inst

View file

@ -143,7 +143,7 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
c.calleeScope = 1 c.calleeScope = 1
else: else:
c.calleeScope = calleeScope c.calleeScope = calleeScope
c.diagnostics = if diagnosticsEnabled: @[] else: nil c.diagnostics = @[] # if diagnosticsEnabled: @[] else: nil
c.diagnosticsEnabled = diagnosticsEnabled c.diagnosticsEnabled = diagnosticsEnabled
c.magic = c.calleeSym.magic c.magic = c.calleeSym.magic
initIdTable(c.bindings) initIdTable(c.bindings)
@ -535,6 +535,12 @@ proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
proc allowsNil(f: PType): TTypeRelation {.inline.} = proc allowsNil(f: PType): TTypeRelation {.inline.} =
result = if tfNotNil notin f.flags: isSubtype else: isNone result = if tfNotNil notin f.flags: isSubtype else: isNone
proc allowsNilDeprecated(c: TCandidate, f: PType): TTypeRelation =
if optNilSeqs in c.c.config.options:
result = allowsNil(f)
else:
result = isNone
proc inconsistentVarTypes(f, a: PType): bool {.inline.} = proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
result = f.kind != a.kind and (f.kind in {tyVar, tyLent} or a.kind in {tyVar, tyLent}) result = f.kind != a.kind and (f.kind in {tyVar, tyLent} or a.kind in {tyVar, tyLent})
@ -741,7 +747,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
diagnostics = @[] diagnostics = @[]
flags = {efExplain} flags = {efExplain}
m.c.config.writelnHook = proc (s: string) = m.c.config.writelnHook = proc (s: string) =
if errorPrefix == nil: errorPrefix = typeClass.sym.name.s & ":" if errorPrefix.len == 0: errorPrefix = typeClass.sym.name.s & ":"
let msg = s.replace("Error:", errorPrefix) let msg = s.replace("Error:", errorPrefix)
if oldWriteHook != nil: oldWriteHook msg if oldWriteHook != nil: oldWriteHook msg
diagnostics.add msg diagnostics.add msg
@ -1253,7 +1259,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
result = isNone result = isNone
elif tfNotNil in f.flags and tfNotNil notin a.flags: elif tfNotNil in f.flags and tfNotNil notin a.flags:
result = isNilConversion result = isNilConversion
of tyNil: result = f.allowsNil of tyNil: result = allowsNilDeprecated(c, f)
else: discard else: discard
of tyOrdinal: of tyOrdinal:
if isOrdinalType(a): if isOrdinalType(a):
@ -1338,7 +1344,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
result = isNilConversion result = isNilConversion
else: else:
result = isEqual result = isEqual
of tyNil: result = f.allowsNil of tyNil: result = allowsNilDeprecated(c, f)
else: discard else: discard
of tyCString: of tyCString:
# conversion from string to cstring is automatic: # conversion from string to cstring is automatic:
@ -1612,7 +1618,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
if f.sonsLen == 0: if f.sonsLen == 0:
result = isGeneric result = isGeneric
else: else:
internalAssert c.c.graph.config, a.sons != nil and a.sons.len > 0 internalAssert c.c.graph.config, a.len > 0
c.typedescMatched = true c.typedescMatched = true
var aa = a var aa = a
while aa.kind in {tyTypeDesc, tyGenericParam} and aa.len > 0: while aa.kind in {tyTypeDesc, tyGenericParam} and aa.len > 0:
@ -1809,7 +1815,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
# see tests/tgenericconverter: # see tests/tgenericconverter:
let srca = typeRel(m, src, a) let srca = typeRel(m, src, a)
if srca notin {isEqual, isGeneric, isSubtype}: continue if srca notin {isEqual, isGeneric, isSubtype}: continue
let constraint = c.converters[i].typ.n[1].sym.constraint let constraint = c.converters[i].typ.n[1].sym.constraint
if not constraint.isNil and not matchNodeKinds(constraint, arg): if not constraint.isNil and not matchNodeKinds(constraint, arg):
continue continue

View file

@ -428,7 +428,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
result = t.sym.name.s & " literal(" & $t.n.intVal & ")" result = t.sym.name.s & " literal(" & $t.n.intVal & ")"
elif prefer in {preferName, preferTypeName} or t.sym.owner.isNil: elif prefer in {preferName, preferTypeName} or t.sym.owner.isNil:
result = t.sym.name.s result = t.sym.name.s
if t.kind == tyGenericParam and t.sons != nil and t.sonsLen > 0: if t.kind == tyGenericParam and t.sonsLen > 0:
result.add ": " result.add ": "
var first = true var first = true
for son in t.sons: for son in t.sons:
@ -1200,7 +1200,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
tyNone, tyForward, tyFromExpr: tyNone, tyForward, tyFromExpr:
result = t result = t
of tyNil: of tyNil:
if kind != skConst: result = t if kind != skConst and kind != skParam: result = t
of tyString, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString, tyPointer: of tyString, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString, tyPointer:
result = nil result = nil
of tyOrdinal: of tyOrdinal:
@ -1528,10 +1528,9 @@ proc isCompileTimeOnly*(t: PType): bool {.inline.} =
proc containsCompileTimeOnly*(t: PType): bool = proc containsCompileTimeOnly*(t: PType): bool =
if isCompileTimeOnly(t): return true if isCompileTimeOnly(t): return true
if t.sons != nil: for i in 0 ..< t.sonsLen:
for i in 0 ..< t.sonsLen: if t.sons[i] != nil and isCompileTimeOnly(t.sons[i]):
if t.sons[i] != nil and isCompileTimeOnly(t.sons[i]): return true
return true
return false return false
type type

View file

@ -17,7 +17,7 @@ const
byteExcess* = 128 # we use excess-K for immediates byteExcess* = 128 # we use excess-K for immediates
wordExcess* = 32768 wordExcess* = 32768
MaxLoopIterations* = 1_000_000_000 # max iterations of all loops MaxLoopIterations* = 3_000_000 # max iterations of all loops
type type

View file

@ -68,6 +68,8 @@ Advanced options:
--oldNewlines:on|off turn on|off the old behaviour of "\n" --oldNewlines:on|off turn on|off the old behaviour of "\n"
--laxStrings:on|off when turned on, accessing the zero terminator in --laxStrings:on|off when turned on, accessing the zero terminator in
strings is allowed; only for backwards compatibility strings is allowed; only for backwards compatibility
--nilseqs:on|off allow 'nil' for strings/seqs for
backwards compatibility
--skipCfg do not read the general configuration file --skipCfg do not read the general configuration file
--skipUserCfg do not read the user's configuration file --skipUserCfg do not read the user's configuration file
--skipParentCfg do not read the parent dirs' configuration files --skipParentCfg do not read the parent dirs' configuration files

View file

@ -1,28 +0,0 @@
* `Exception <system.html#Exception>`_
* `AccessViolationError <system.html#AccessViolationError>`_
* `ArithmeticError <system.html#ArithmeticError>`_
* `DivByZeroError <system.html#DivByZeroError>`_
* `OverflowError <system.html#OverflowError>`_
* `AssertionError <system.html#AssertionError>`_
* `DeadThreadError <system.html#DeadThreadError>`_
* `FloatingPointError <system.html#FloatingPointError>`_
* `FloatDivByZeroError <system.html#FloatDivByZeroError>`_
* `FloatInexactError <system.html#FloatInexactError>`_
* `FloatInvalidOpError <system.html#FloatInvalidOpError>`_
* `FloatOverflowError <system.html#FloatOverflowError>`_
* `FloatUnderflowError <system.html#FloatUnderflowError>`_
* `FieldError <system.html#FieldError>`_
* `IndexError <system.html#IndexError>`_
* `ObjectAssignmentError <system.html#ObjectAssignmentError>`_
* `ObjectConversionError <system.html#ObjectConversionError>`_
* `ValueError <system.html#ValueError>`_
* `KeyError <system.html#KeyError>`_
* `ReraiseError <system.html#ReraiseError>`_
* `RangeError <system.html#RangeError>`_
* `OutOfMemoryError <system.html#OutOfMemoryError>`_
* `ResourceExhaustedError <system.html#ResourceExhaustedError>`_
* `StackOverflowError <system.html#StackOverflowError>`_
* `SystemError <system.html#SystemError>`_
* `IOError <system.html#IOError>`_
* `OSError <system.html#OSError>`_
* `LibraryError <system.html#LibraryError>`_

View file

@ -769,8 +769,8 @@ Pre-defined floating point types
The following floating point types are pre-defined: The following floating point types are pre-defined:
``float`` ``float``
the generic floating point type; its size is platform dependent the generic floating point type; its size used to be platform dependent,
(the compiler chooses the processor's fastest floating point type). but now it is always mapped to ``float64``.
This type should be used in general. This type should be used in general.
floatXX floatXX
@ -1507,68 +1507,6 @@ non nilable pointers. The details of this analysis are still to be specified
here. here.
Memory regions
--------------
The types ``ref`` and ``ptr`` can get an optional ``region`` annotation.
A region has to be an object type.
Regions are very useful to separate user space and kernel memory in the
development of OS kernels:
.. code-block:: nim
type
Kernel = object
Userspace = object
var a: Kernel ptr Stat
var b: Userspace ptr Stat
# the following does not compile as the pointer types are incompatible:
a = b
As the example shows ``ptr`` can also be used as a binary
operator, ``region ptr T`` is a shortcut for ``ptr[region, T]``.
In order to make generic code easier to write ``ptr T`` is a subtype
of ``ptr[R, T]`` for any ``R``.
Furthermore the subtype relation of the region object types is lifted to
the pointer types: If ``A <: B`` then ``ptr[A, T] <: ptr[B, T]``. This can be
used to model subregions of memory. As a special typing rule ``ptr[R, T]`` is
not compatible to ``pointer`` to prevent the following from compiling:
.. code-block:: nim
# from system
proc dealloc(p: pointer)
# wrap some scripting language
type
PythonsHeap = object
PyObjectHeader = object
rc: int
typ: pointer
PyObject = ptr[PythonsHeap, PyObjectHeader]
proc createPyObject(): PyObject {.importc: "...".}
proc destroyPyObject(x: PyObject) {.importc: "...".}
var foo = createPyObject()
# type error here, how convenient:
dealloc(foo)
Future directions:
* Memory regions might become available for ``string`` and ``seq`` too.
* Builtin regions like ``private``, ``global`` and ``local`` might be
useful for an OpenCL target.
* Builtin "regions" can model ``lent`` and ``unique`` pointers.
* An assignment operator can be attached to a region so that proper write
barriers can be generated. This would imply that the GC can be implemented
completely in user-space.
Procedural type Procedural type
--------------- ---------------
A procedural type is internally a pointer to a procedure. ``nil`` is A procedural type is internally a pointer to a procedure. ``nil`` is
@ -2337,6 +2275,8 @@ pointer type and overloading resolution is tried with ``a[]`` instead.
Automatic self insertions Automatic self insertions
------------------------- -------------------------
**Note**: The ``.this`` pragma is deprecated and should not be used anymore.
Starting with version 0.14 of the language, Nim supports ``field`` as a Starting with version 0.14 of the language, Nim supports ``field`` as a
shortcut for ``self.field`` comparable to the `this`:idx: keyword in Java shortcut for ``self.field`` comparable to the `this`:idx: keyword in Java
or C++. This feature has to be explicitly enabled via a ``{.this: self.}`` or C++. This feature has to be explicitly enabled via a ``{.this: self.}``
@ -4029,9 +3969,13 @@ exception.
Exception hierarchy Exception hierarchy
------------------- -------------------
The exception tree is defined in the `system <system.html>`_ module: The exception tree is defined in the `system <system.html>`_ module.
Every exception inherits from ``system.Exception``. Exceptions that indicate
.. include:: exception_hierarchy_fragment.txt programming bugs inherit from ``system.Defect`` (which is a subtype of ``Exception``)
and are stricly speaking not catchable as they can also be mapped to an operation
that terminates the whole process. Exceptions that indicate any other runtime error
that can be caught inherit from ``system.CatchableError``
(which is a subtype of ``Exception``).
Imported exceptions Imported exceptions
@ -5436,6 +5380,7 @@ type ``system.ForLoopStmt`` can rewrite the entirety of a ``for`` loop:
:test: "nim c $1" :test: "nim c $1"
import macros import macros
{.experimental: "forLoopMacros".}
macro enumerate(x: ForLoopStmt): untyped = macro enumerate(x: ForLoopStmt): untyped =
expectKind x, nnkForStmt expectKind x, nnkForStmt
@ -5462,6 +5407,62 @@ type ``system.ForLoopStmt`` can rewrite the entirety of a ``for`` loop:
echo a2, " ", b2 echo a2, " ", b2
Currently for loop macros must be enabled explicitly
via ``{.experimental: "forLoopMacros".}``.
Case statement macros
---------------------
A macro that needs to be called `match`:idx: can be used to
rewrite ``case`` statements in order to
implement `pattern matching`:idx: for certain types. The following
example implements a simplistic form of pattern matching for tuples,
leveraging the existing equality operator for tuples (as provided in
``system.==``):
.. code-block:: nim
:test: "nim c $1"
{.experimental: "caseStmtMacros".}
import macros
macro match(n: tuple): untyped =
result = newTree(nnkIfStmt)
let selector = n[0]
for i in 1 ..< n.len:
let it = n[i]
case it.kind
of nnkElse, nnkElifBranch, nnkElifExpr, nnkElseExpr:
result.add it
of nnkOfBranch:
for j in 0..it.len-2:
let cond = newCall("==", selector, it[j])
result.add newTree(nnkElifBranch, cond, it[^1])
else:
error "'match' cannot handle this node", it
echo repr result
case ("foo", 78)
of ("foo", 78): echo "yes"
of ("bar", 88): echo "no"
else: discard
Currently case statement macros must be enabled explicitly
via ``{.experimental: "caseStmtMacros".}``.
``match`` macros are subject to overload resolution. First the
``case``'s selector expression is used to determine which ``match``
macro to call. To this macro is then the complete ``case`` statement
body is passed and the macro is evaluated.
In other words, the macro needs to transform the full ``case`` statement
but only the statement's selector expression is used to determine which
``macro`` to call.
Special Types Special Types
============= =============
@ -7933,7 +7934,7 @@ that ``spawn`` takes is restricted:
``spawn`` executes the passed expression on the thread pool and returns ``spawn`` executes the passed expression on the thread pool and returns
a `data flow variable`:idx: ``FlowVar[T]`` that can be read from. The reading a `data flow variable`:idx: ``FlowVar[T]`` that can be read from. The reading
with the ``^`` operator is **blocking**. However, one can use ``awaitAny`` to with the ``^`` operator is **blocking**. However, one can use ``blockUntilAny`` to
wait on multiple flow variables at the same time: wait on multiple flow variables at the same time:
.. code-block:: nim .. code-block:: nim
@ -7944,10 +7945,10 @@ wait on multiple flow variables at the same time:
var responses = newSeq[FlowVarBase](3) var responses = newSeq[FlowVarBase](3)
for i in 0..2: for i in 0..2:
responses[i] = spawn tellServer(Update, "key", "value") responses[i] = spawn tellServer(Update, "key", "value")
var index = awaitAny(responses) var index = blockUntilAny(responses)
assert index >= 0 assert index >= 0
responses.del(index) responses.del(index)
discard awaitAny(responses) discard blockUntilAny(responses)
Data flow variables ensure that no data races Data flow variables ensure that no data races
are possible. Due to technical limitations not every type ``T`` is possible in are possible. Due to technical limitations not every type ``T`` is possible in

View file

@ -199,7 +199,7 @@ Generated C code directory
The generated files that Nim produces all go into a subdirectory called The generated files that Nim produces all go into a subdirectory called
``nimcache``. Its full path is ``nimcache``. Its full path is
- ``$XDG_CACHE_HOME/$projectname(_r|_d)`` or ``~/.cache/$projectname(_r|_d)`` - ``$XDG_CACHE_HOME/nim/$projectname(_r|_d)`` or ``~/.cache/nim/$projectname(_r|_d)``
on Posix on Posix
- ``$HOME/nimcache/$projectname(_r|_d)`` on Windows. - ``$HOME/nimcache/$projectname(_r|_d)`` on Windows.

View file

@ -25,7 +25,7 @@ Spawn statement
A standalone ``spawn`` statement is a simple construct. It executes A standalone ``spawn`` statement is a simple construct. It executes
the passed expression on the thread pool and returns a `data flow variable`:idx: the passed expression on the thread pool and returns a `data flow variable`:idx:
``FlowVar[T]`` that can be read from. The reading with the ``^`` operator is ``FlowVar[T]`` that can be read from. The reading with the ``^`` operator is
**blocking**. However, one can use ``awaitAny`` to wait on multiple flow **blocking**. However, one can use ``blockUntilAny`` to wait on multiple flow
variables at the same time: variables at the same time:
.. code-block:: nim .. code-block:: nim
@ -36,10 +36,10 @@ variables at the same time:
var responses = newSeq[FlowVarBase](3) var responses = newSeq[FlowVarBase](3)
for i in 0..2: for i in 0..2:
responses[i] = spawn tellServer(Update, "key", "value") responses[i] = spawn tellServer(Update, "key", "value")
var index = awaitAny(responses) var index = blockUntilAny(responses)
assert index >= 0 assert index >= 0
responses.del(index) responses.del(index)
discard awaitAny(responses) discard blockUntilAny(responses)
Data flow variables ensure that no data races Data flow variables ensure that no data races
are possible. Due to technical limitations not every type ``T`` is possible in are possible. Due to technical limitations not every type ``T`` is possible in

View file

@ -42,6 +42,6 @@ test "arithmetic failure":
expect(ArithmeticError): expect(ArithmeticError):
err() err()
expect(ArithmeticError, SystemError): expect(ArithmeticError, CatchableError):
discard foo() discard foo()

View file

@ -183,7 +183,7 @@ proc bundleNimbleExe() =
bundleNimbleSrc() bundleNimbleSrc()
# now compile Nimble and copy it to $nim/bin for the installer.ini # now compile Nimble and copy it to $nim/bin for the installer.ini
# to pick it up: # to pick it up:
nimexec("c -d:release dist/nimble/src/nimble.nim") nimexec("c -d:release --nilseqs:on dist/nimble/src/nimble.nim")
copyExe("dist/nimble/src/nimble".exe, "bin/nimble".exe) copyExe("dist/nimble/src/nimble".exe, "bin/nimble".exe)
proc buildNimble(latest: bool) = proc buildNimble(latest: bool) =
@ -210,7 +210,7 @@ proc buildNimble(latest: bool) =
else: else:
exec("git checkout -f stable") exec("git checkout -f stable")
exec("git pull") exec("git pull")
nimexec("c --noNimblePath -p:compiler -d:release " & installDir / "src/nimble.nim") nimexec("c --noNimblePath -p:compiler --nilseqs:on -d:release " & installDir / "src/nimble.nim")
copyExe(installDir / "src/nimble".exe, "bin/nimble".exe) copyExe(installDir / "src/nimble".exe, "bin/nimble".exe)
proc bundleNimsuggest(buildExe: bool) = proc bundleNimsuggest(buildExe: bool) =

View file

@ -43,7 +43,6 @@ type
t: Thread[ptr Actor[In, Out]] t: Thread[ptr Actor[In, Out]]
PActor*[In, Out] = ptr Actor[In, Out] ## an actor PActor*[In, Out] = ptr Actor[In, Out] ## an actor
{.deprecated: [TTask: Task, TActor: Actor].}
proc spawn*[In, Out](action: proc( proc spawn*[In, Out](action: proc(
self: PActor[In, Out]){.thread.}): PActor[In, Out] = self: PActor[In, Out]){.thread.}): PActor[In, Out] =
@ -168,7 +167,7 @@ proc terminate*[In, Out](a: var ActorPool[In, Out]) =
for i in 0..<a.actors.len: join(a.actors[i]) for i in 0..<a.actors.len: join(a.actors[i])
when Out isnot void: when Out isnot void:
close(a.outputs) close(a.outputs)
a.actors = nil a.actors = @[]
proc join*[In, Out](a: var ActorPool[In, Out]) = proc join*[In, Out](a: var ActorPool[In, Out]) =
## short-cut for `sync` and then `terminate`. ## short-cut for `sync` and then `terminate`.

View file

@ -128,10 +128,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
var a = 0 var a = 0
for c in items(string(formatstr)): for c in items(string(formatstr)):
if c == '?': if c == '?':
if args[a].isNil: add(result, dbQuote(args[a]))
add(result, "NULL")
else:
add(result, dbQuote(args[a]))
inc(a) inc(a)
else: else:
add(result, c) add(result, c)
@ -183,24 +180,8 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
row = mysql.fetchRow(sqlres) row = mysql.fetchRow(sqlres)
if row == nil: break if row == nil: break
for i in 0..L-1: for i in 0..L-1:
if row[i] == nil: setLen(result[i], 0)
if backup == nil: result[i].add row[i]
newSeq(backup, L)
if backup[i] == nil and result[i] != nil:
shallowCopy(backup[i], result[i])
result[i] = nil
else:
if result[i] == nil:
if backup != nil:
if backup[i] == nil:
backup[i] = ""
shallowCopy(result[i], backup[i])
setLen(result[i], 0)
else:
result[i] = ""
else:
setLen(result[i], 0)
add(result[i], row[i])
yield result yield result
properFreeResult(sqlres, row) properFreeResult(sqlres, row)
@ -323,10 +304,7 @@ proc getRow*(db: DbConn, query: SqlQuery,
if row != nil: if row != nil:
for i in 0..L-1: for i in 0..L-1:
setLen(result[i], 0) setLen(result[i], 0)
if row[i] == nil: add(result[i], row[i])
result[i] = nil
else:
add(result[i], row[i])
properFreeResult(sqlres, row) properFreeResult(sqlres, row)
proc getAllRows*(db: DbConn, query: SqlQuery, proc getAllRows*(db: DbConn, query: SqlQuery,
@ -345,10 +323,7 @@ proc getAllRows*(db: DbConn, query: SqlQuery,
setLen(result, j+1) setLen(result, j+1)
newSeq(result[j], L) newSeq(result[j], L)
for i in 0..L-1: for i in 0..L-1:
if row[i] == nil: result[j][i] = $row[i]
result[j][i] = nil
else:
result[j][i] = $row[i]
inc(j) inc(j)
mysql.freeResult(sqlres) mysql.freeResult(sqlres)

View file

@ -103,10 +103,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
else: else:
for c in items(string(formatstr)): for c in items(string(formatstr)):
if c == '?': if c == '?':
if args[a] == nil: add(result, dbQuote(args[a]))
add(result, "NULL")
else:
add(result, dbQuote(args[a]))
inc(a) inc(a)
else: else:
add(result, c) add(result, c)
@ -179,7 +176,7 @@ proc setRow(res: PPGresult, r: var Row, line, cols: int32) =
setLen(r[col], 0) setLen(r[col], 0)
let x = pqgetvalue(res, line, col) let x = pqgetvalue(res, line, col)
if x.isNil: if x.isNil:
r[col] = nil r[col] = ""
else: else:
add(r[col], x) add(r[col], x)

View file

@ -105,7 +105,6 @@ proc dbError*(db: DbConn) {.noreturn.} =
proc dbQuote*(s: string): string = proc dbQuote*(s: string): string =
## DB quotes the string. ## DB quotes the string.
if s.isNil: return "NULL"
result = "'" result = "'"
for c in items(s): for c in items(s):
if c == '\'': add(result, "''") if c == '\'': add(result, "''")

View file

@ -267,7 +267,7 @@ proc `[]`*(pattern: Captures, i: int): string =
let bounds = bounds.get let bounds = bounds.get
return pattern.str.substr(bounds.a, bounds.b) return pattern.str.substr(bounds.a, bounds.b)
else: else:
return nil return ""
proc match*(pattern: RegexMatch): string = proc match*(pattern: RegexMatch): string =
return pattern.captures[-1] return pattern.captures[-1]
@ -291,9 +291,9 @@ template toTableImpl(cond: untyped) {.dirty.} =
else: else:
result[key] = nextVal result[key] = nextVal
proc toTable*(pattern: Captures, default: string = nil): Table[string, string] = proc toTable*(pattern: Captures, default: string = ""): Table[string, string] =
result = initTable[string, string]() result = initTable[string, string]()
toTableImpl(nextVal == nil) toTableImpl(nextVal.len == 0)
proc toTable*(pattern: CaptureBounds, default = none(HSlice[int, int])): proc toTable*(pattern: CaptureBounds, default = none(HSlice[int, int])):
Table[string, Option[HSlice[int, int]]] = Table[string, Option[HSlice[int, int]]] =
@ -312,13 +312,13 @@ template itemsImpl(cond: untyped) {.dirty.} =
iterator items*(pattern: CaptureBounds, default = none(HSlice[int, int])): Option[HSlice[int, int]] = iterator items*(pattern: CaptureBounds, default = none(HSlice[int, int])): Option[HSlice[int, int]] =
itemsImpl(nextVal.isNone) itemsImpl(nextVal.isNone)
iterator items*(pattern: Captures, default: string = nil): string = iterator items*(pattern: Captures, default: string = ""): string =
itemsImpl(nextVal == nil) itemsImpl(nextVal.len == 0)
proc toSeq*(pattern: CaptureBounds, default = none(HSlice[int, int])): seq[Option[HSlice[int, int]]] = proc toSeq*(pattern: CaptureBounds, default = none(HSlice[int, int])): seq[Option[HSlice[int, int]]] =
accumulateResult(pattern.items(default)) accumulateResult(pattern.items(default))
proc toSeq*(pattern: Captures, default: string = nil): seq[string] = proc toSeq*(pattern: Captures, default: string = ""): seq[string] =
accumulateResult(pattern.items(default)) accumulateResult(pattern.items(default))
proc `$`*(pattern: RegexMatch): string = proc `$`*(pattern: RegexMatch): string =

View file

@ -10,11 +10,7 @@ proc fget*[K, V](self: Table[K, V], key: K): V =
const Ident = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'} const Ident = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'}
const StartIdent = Ident - {'0'..'9'} const StartIdent = Ident - {'0'..'9'}
proc checkNil(arg: string): string = template checkNil(arg: string): string = arg
if arg == nil:
raise newException(ValueError, "Cannot use nil capture")
else:
return arg
template formatStr*(howExpr, namegetter, idgetter): untyped = template formatStr*(howExpr, namegetter, idgetter): untyped =
let how = howExpr let how = howExpr

View file

@ -113,7 +113,7 @@ proc matchOrFind(buf: cstring, pattern: Regex, matches: var openArray[string],
var b = rawMatches[i * 2 + 1] var b = rawMatches[i * 2 + 1]
if a >= 0'i32: if a >= 0'i32:
matches[i-1] = bufSubstr(buf, int(a), int(b)) matches[i-1] = bufSubstr(buf, int(a), int(b))
else: matches[i-1] = nil else: matches[i-1] = ""
return rawMatches[1] - rawMatches[0] return rawMatches[1] - rawMatches[0]
proc findBounds*(buf: cstring, pattern: Regex, matches: var openArray[string], proc findBounds*(buf: cstring, pattern: Regex, matches: var openArray[string],
@ -133,7 +133,7 @@ proc findBounds*(buf: cstring, pattern: Regex, matches: var openArray[string],
var a = rawMatches[i * 2] var a = rawMatches[i * 2]
var b = rawMatches[i * 2 + 1] var b = rawMatches[i * 2 + 1]
if a >= 0'i32: matches[i-1] = bufSubstr(buf, int(a), int(b)) if a >= 0'i32: matches[i-1] = bufSubstr(buf, int(a), int(b))
else: matches[i-1] = nil else: matches[i-1] = ""
return (rawMatches[0].int, rawMatches[1].int - 1) return (rawMatches[0].int, rawMatches[1].int - 1)
proc findBounds*(s: string, pattern: Regex, matches: var openArray[string], proc findBounds*(s: string, pattern: Regex, matches: var openArray[string],
@ -287,7 +287,7 @@ proc find*(buf: cstring, pattern: Regex, matches: var openArray[string],
var a = rawMatches[i * 2] var a = rawMatches[i * 2]
var b = rawMatches[i * 2 + 1] var b = rawMatches[i * 2 + 1]
if a >= 0'i32: matches[i-1] = bufSubstr(buf, int(a), int(b)) if a >= 0'i32: matches[i-1] = bufSubstr(buf, int(a), int(b))
else: matches[i-1] = nil else: matches[i-1] = ""
return rawMatches[0] return rawMatches[0]
proc find*(s: string, pattern: Regex, matches: var openArray[string], proc find*(s: string, pattern: Regex, matches: var openArray[string],
@ -456,8 +456,6 @@ proc replacef*(s: string, sub: Regex, by: string): string =
while true: while true:
var match = findBounds(s, sub, caps, prev) var match = findBounds(s, sub, caps, prev)
if match.first < 0: break if match.first < 0: break
assert result != nil
assert s != nil
add(result, substr(s, prev, match.first-1)) add(result, substr(s, prev, match.first-1))
addf(result, by, caps) addf(result, by, caps)
prev = match.last + 1 prev = match.last + 1
@ -615,7 +613,7 @@ when isMainModule:
doAssert false doAssert false
if "abc" =~ re"(cba)?.*": if "abc" =~ re"(cba)?.*":
doAssert matches[0] == nil doAssert matches[0] == ""
else: doAssert false else: doAssert false
if "abc" =~ re"().*": if "abc" =~ re"().*":

View file

@ -293,9 +293,9 @@ proc renderRstToJsonNode(node: PRstNode): JsonNode =
(key: "kind", val: %($node.kind)), (key: "kind", val: %($node.kind)),
(key: "level", val: %BiggestInt(node.level)) (key: "level", val: %BiggestInt(node.level))
] ]
if node.text != nil: if node.text.len > 0:
result.add("text", %node.text) result.add("text", %node.text)
if node.sons != nil and len(node.sons) > 0: if len(node.sons) > 0:
var accm = newSeq[JsonNode](len(node.sons)) var accm = newSeq[JsonNode](len(node.sons))
for i, son in node.sons: for i, son in node.sons:
accm[i] = renderRstToJsonNode(son) accm[i] = renderRstToJsonNode(son)

View file

@ -312,7 +312,6 @@ proc setIndexTerm*(d: var RstGenerator, id, term: string,
## The index won't be written to disk unless you call `writeIndexFile() ## The index won't be written to disk unless you call `writeIndexFile()
## <#writeIndexFile>`_. The purpose of the index is documented in the `docgen ## <#writeIndexFile>`_. The purpose of the index is documented in the `docgen
## tools guide <docgen.html#index-switch>`_. ## tools guide <docgen.html#index-switch>`_.
assert(not d.theIndex.isNil)
var var
entry = term entry = term
isTitle = false isTitle = false
@ -337,7 +336,7 @@ proc hash(n: PRstNode): int =
result = hash(n.text) result = hash(n.text)
elif n.len > 0: elif n.len > 0:
result = hash(n.sons[0]) result = hash(n.sons[0])
for i in 1 .. <len(n): for i in 1 ..< len(n):
result = result !& hash(n.sons[i]) result = result !& hash(n.sons[i])
result = !$result result = !$result
@ -398,9 +397,9 @@ proc hash(x: IndexEntry): Hash =
proc `<-`(a: var IndexEntry, b: IndexEntry) = proc `<-`(a: var IndexEntry, b: IndexEntry) =
shallowCopy a.keyword, b.keyword shallowCopy a.keyword, b.keyword
shallowCopy a.link, b.link shallowCopy a.link, b.link
if b.linkTitle.isNil: a.linkTitle = nil if b.linkTitle.isNil: a.linkTitle = ""
else: shallowCopy a.linkTitle, b.linkTitle else: shallowCopy a.linkTitle, b.linkTitle
if b.linkDesc.isNil: a.linkDesc = nil if b.linkDesc.isNil: a.linkDesc = ""
else: shallowCopy a.linkDesc, b.linkDesc else: shallowCopy a.linkDesc, b.linkDesc
proc sortIndex(a: var openArray[IndexEntry]) = proc sortIndex(a: var openArray[IndexEntry]) =
@ -452,7 +451,7 @@ proc generateSymbolIndex(symbols: seq[IndexEntry]): string =
title="$3" data-doc-search-tag="$2" href="$1">$2</a></li> title="$3" data-doc-search-tag="$2" href="$1">$2</a></li>
""", [url, text, desc]) """, [url, text, desc])
else: else:
result.addf("""<li><a class="reference external" result.addf("""<li><a class="reference external"
data-doc-search-tag="$2" href="$1">$2</a></li> data-doc-search-tag="$2" href="$1">$2</a></li>
""", [url, text]) """, [url, text])
inc j inc j
@ -524,7 +523,7 @@ proc generateDocumentationTOC(entries: seq[IndexEntry]): string =
titleTag = levels[L].text titleTag = levels[L].text
else: else:
result.add(level.indentToLevel(levels[L].level)) result.add(level.indentToLevel(levels[L].level))
result.addf("""<li><a class="reference" data-doc-search-tag="$1" href="$2"> result.addf("""<li><a class="reference" data-doc-search-tag="$1" href="$2">
$3</a></li> $3</a></li>
""", [titleTag & " : " & levels[L].text, link, levels[L].text]) """, [titleTag & " : " & levels[L].text, link, levels[L].text])
inc L inc L
@ -608,8 +607,8 @@ proc readIndexDir(dir: string):
fileEntries[F].linkTitle = extraCols[1].unquoteIndexColumn fileEntries[F].linkTitle = extraCols[1].unquoteIndexColumn
fileEntries[F].linkDesc = extraCols[2].unquoteIndexColumn fileEntries[F].linkDesc = extraCols[2].unquoteIndexColumn
else: else:
fileEntries[F].linkTitle = nil fileEntries[F].linkTitle = ""
fileEntries[F].linkDesc = nil fileEntries[F].linkDesc = ""
inc F inc F
# Depending on type add this to the list of symbols or table of APIs. # Depending on type add this to the list of symbols or table of APIs.
if title.keyword.isNil: if title.keyword.isNil:
@ -657,7 +656,6 @@ proc mergeIndexes*(dir: string): string =
## Returns the merged and sorted indices into a single HTML block which can ## Returns the merged and sorted indices into a single HTML block which can
## be further embedded into nimdoc templates. ## be further embedded into nimdoc templates.
var (modules, symbols, docs) = readIndexDir(dir) var (modules, symbols, docs) = readIndexDir(dir)
assert(not symbols.isNil)
result = "" result = ""
# Generate a quick jump list of documents. # Generate a quick jump list of documents.

View file

@ -219,10 +219,10 @@ proc getHint(entry: StackTraceEntry): string =
## We try to provide some hints about stack trace entries that the user ## We try to provide some hints about stack trace entries that the user
## may not be familiar with, in particular calls inside the stdlib. ## may not be familiar with, in particular calls inside the stdlib.
result = "" result = ""
if entry.procname == "processPendingCallbacks": if entry.procname == cstring"processPendingCallbacks":
if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0: if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0:
return "Executes pending callbacks" return "Executes pending callbacks"
elif entry.procname == "poll": elif entry.procname == cstring"poll":
if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0: if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0:
return "Processes asynchronous completion events" return "Processes asynchronous completion events"

View file

@ -493,8 +493,6 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
## **Warning**: ``recvLineInto`` on unbuffered sockets assumes that the ## **Warning**: ``recvLineInto`` on unbuffered sockets assumes that the
## protocol uses ``\r\L`` to delimit a new line. ## protocol uses ``\r\L`` to delimit a new line.
assert SocketFlag.Peek notin flags ## TODO: assert SocketFlag.Peek notin flags ## TODO:
assert(not resString.mget.isNil(),
"String inside resString future needs to be initialised")
result = newFuture[void]("asyncnet.recvLineInto") result = newFuture[void]("asyncnet.recvLineInto")
# TODO: Make the async transformation check for FutureVar params and complete # TODO: Make the async transformation check for FutureVar params and complete
@ -657,7 +655,7 @@ when defineSsl:
proc wrapConnectedSocket*(ctx: SslContext, socket: AsyncSocket, proc wrapConnectedSocket*(ctx: SslContext, socket: AsyncSocket,
handshake: SslHandshakeType, handshake: SslHandshakeType,
hostname: string = nil) = hostname: string = "") =
## Wraps a connected socket in an SSL context. This function effectively ## Wraps a connected socket in an SSL context. This function effectively
## turns ``socket`` into an SSL socket. ## turns ``socket`` into an SSL socket.
## ``hostname`` should be specified so that the client knows which hostname ## ``hostname`` should be specified so that the client knows which hostname
@ -672,7 +670,7 @@ when defineSsl:
case handshake case handshake
of handshakeAsClient: of handshakeAsClient:
if not hostname.isNil and not isIpAddress(hostname): if hostname.len > 0 and not isIpAddress(hostname):
# Set the SNI address for this connection. This call can fail if # Set the SNI address for this connection. This call can fail if
# we're not using TLSv1+. # we're not using TLSv1+.
discard SSL_set_tlsext_host_name(socket.sslHandle, hostname) discard SSL_set_tlsext_host_name(socket.sslHandle, hostname)

View file

@ -184,7 +184,7 @@ proc missingOrExcl*(s: var IntSet, key: int) : bool =
## `key` is removed from `s` and false is returned. ## `key` is removed from `s` and false is returned.
var count = s.elems var count = s.elems
exclImpl(s, key) exclImpl(s, key)
result = count == s.elems result = count == s.elems
proc containsOrIncl*(s: var IntSet, key: int): bool = proc containsOrIncl*(s: var IntSet, key: int): bool =
## returns true if `s` contains `key`, otherwise `key` is included in `s` ## returns true if `s` contains `key`, otherwise `key` is included in `s`
@ -212,7 +212,10 @@ proc initIntSet*: IntSet =
#newSeq(result.data, InitIntSetSize) #newSeq(result.data, InitIntSetSize)
#result.max = InitIntSetSize-1 #result.max = InitIntSetSize-1
result.data = nil when defined(nimNoNilSeqs):
result.data = @[]
else:
result.data = nil
result.max = 0 result.max = 0
result.counter = 0 result.counter = 0
result.head = nil result.head = nil
@ -222,7 +225,10 @@ proc clear*(result: var IntSet) =
#setLen(result.data, InitIntSetSize) #setLen(result.data, InitIntSetSize)
#for i in 0..InitIntSetSize-1: result.data[i] = nil #for i in 0..InitIntSetSize-1: result.data[i] = nil
#result.max = InitIntSetSize-1 #result.max = InitIntSetSize-1
result.data = nil when defined(nimNoNilSeqs):
result.data = @[]
else:
result.data = nil
result.max = 0 result.max = 0
result.counter = 0 result.counter = 0
result.head = nil result.head = nil
@ -234,7 +240,10 @@ proc assign*(dest: var IntSet, src: IntSet) =
## copies `src` to `dest`. `dest` does not need to be initialized by ## copies `src` to `dest`. `dest` does not need to be initialized by
## `initIntSet`. ## `initIntSet`.
if src.elems <= src.a.len: if src.elems <= src.a.len:
dest.data = nil when defined(nimNoNilSeqs):
dest.data = @[]
else:
dest.data = nil
dest.max = 0 dest.max = 0
dest.counter = src.counter dest.counter = src.counter
dest.head = nil dest.head = nil
@ -247,11 +256,9 @@ proc assign*(dest: var IntSet, src: IntSet) =
var it = src.head var it = src.head
while it != nil: while it != nil:
var h = it.key and dest.max var h = it.key and dest.max
while dest.data[h] != nil: h = nextTry(h, dest.max) while dest.data[h] != nil: h = nextTry(h, dest.max)
assert(dest.data[h] == nil) assert(dest.data[h] == nil)
var n: PTrunk var n: PTrunk
new(n) new(n)
n.next = dest.head n.next = dest.head
@ -259,7 +266,6 @@ proc assign*(dest: var IntSet, src: IntSet) =
n.bits = it.bits n.bits = it.bits
dest.head = n dest.head = n
dest.data[h] = n dest.data[h] = n
it = it.next it = it.next
proc union*(s1, s2: IntSet): IntSet = proc union*(s1, s2: IntSet): IntSet =
@ -315,7 +321,7 @@ proc len*(s: IntSet): int {.inline.} =
for _ in s: for _ in s:
inc(result) inc(result)
proc card*(s: IntSet): int {.inline.} = proc card*(s: IntSet): int {.inline.} =
## alias for `len() <#len>` _. ## alias for `len() <#len>` _.
result = s.len() result = s.len()
@ -361,7 +367,7 @@ when isMainModule:
x.incl(1056) x.incl(1056)
x.incl(1044) x.incl(1044)
x.excl(1044) x.excl(1044)
assert x.containsOrIncl(888) == false assert x.containsOrIncl(888) == false
assert 888 in x assert 888 in x

View file

@ -1329,7 +1329,7 @@ when isMainModule:
doAssert clearTable[42] == "asd" doAssert clearTable[42] == "asd"
clearTable.clear() clearTable.clear()
doAssert(not clearTable.hasKey(123123)) doAssert(not clearTable.hasKey(123123))
doAssert clearTable.getOrDefault(42) == nil doAssert clearTable.getOrDefault(42) == ""
block: #5482 block: #5482
var a = [("wrong?","foo"), ("wrong?", "foo2")].newOrderedTable() var a = [("wrong?","foo"), ("wrong?", "foo2")].newOrderedTable()

View file

@ -30,7 +30,7 @@ proc destroySemaphore(cv: var Semaphore) {.inline.} =
deinitCond(cv.c) deinitCond(cv.c)
deinitLock(cv.L) deinitLock(cv.L)
proc await(cv: var Semaphore) = proc blockUntil(cv: var Semaphore) =
acquire(cv.L) acquire(cv.L)
while cv.counter <= 0: while cv.counter <= 0:
wait(cv.c, cv.L) wait(cv.c, cv.L)
@ -81,7 +81,7 @@ proc closeBarrier(b: ptr Barrier) {.compilerProc.} =
fence() fence()
b.interest = true b.interest = true
fence() fence()
while b.left != b.entered: await(b.cv) while b.left != b.entered: blockUntil(b.cv)
destroySemaphore(b.cv) destroySemaphore(b.cv)
{.pop.} {.pop.}
@ -89,8 +89,6 @@ proc closeBarrier(b: ptr Barrier) {.compilerProc.} =
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
type type
foreign* = object ## a region that indicates the pointer comes from a
## foreign thread heap.
AwaitInfo = object AwaitInfo = object
cv: Semaphore cv: Semaphore
idx: int idx: int
@ -99,7 +97,7 @@ type
FlowVarBaseObj = object of RootObj FlowVarBaseObj = object of RootObj
ready, usesSemaphore, awaited: bool ready, usesSemaphore, awaited: bool
cv: Semaphore #\ cv: Semaphore #\
# for 'awaitAny' support # for 'blockUntilAny' support
ai: ptr AwaitInfo ai: ptr AwaitInfo
idx: int idx: int
data: pointer # we incRef and unref it to keep it alive; note this MUST NOT data: pointer # we incRef and unref it to keep it alive; note this MUST NOT
@ -130,12 +128,12 @@ type
q: ToFreeQueue q: ToFreeQueue
readyForTask: Semaphore readyForTask: Semaphore
proc await*(fv: FlowVarBase) = proc blockUntil*(fv: FlowVarBase) =
## waits until the value for the flowVar arrives. Usually it is not necessary ## waits until the value for the flowVar arrives. Usually it is not necessary
## to call this explicitly. ## to call this explicitly.
if fv.usesSemaphore and not fv.awaited: if fv.usesSemaphore and not fv.awaited:
fv.awaited = true fv.awaited = true
await(fv.cv) blockUntil(fv.cv)
destroySemaphore(fv.cv) destroySemaphore(fv.cv)
proc selectWorker(w: ptr Worker; fn: WorkerProc; data: pointer): bool = proc selectWorker(w: ptr Worker; fn: WorkerProc; data: pointer): bool =
@ -143,7 +141,7 @@ proc selectWorker(w: ptr Worker; fn: WorkerProc; data: pointer): bool =
w.data = data w.data = data
w.f = fn w.f = fn
signal(w.taskArrived) signal(w.taskArrived)
await(w.taskStarted) blockUntil(w.taskStarted)
result = true result = true
proc cleanFlowVars(w: ptr Worker) = proc cleanFlowVars(w: ptr Worker) =
@ -178,11 +176,11 @@ proc attach(fv: FlowVarBase; i: int): bool =
release(fv.cv.L) release(fv.cv.L)
proc finished(fv: FlowVarBase) = proc finished(fv: FlowVarBase) =
doAssert fv.ai.isNil, "flowVar is still attached to an 'awaitAny'" doAssert fv.ai.isNil, "flowVar is still attached to an 'blockUntilAny'"
# we have to protect against the rare cases where the owner of the flowVar # we have to protect against the rare cases where the owner of the flowVar
# simply disregards the flowVar and yet the "flowVar" has not yet written # simply disregards the flowVar and yet the "flowVar" has not yet written
# anything to it: # anything to it:
await(fv) blockUntil(fv)
if fv.data.isNil: return if fv.data.isNil: return
let owner = cast[ptr Worker](fv.owner) let owner = cast[ptr Worker](fv.owner)
let q = addr(owner.q) let q = addr(owner.q)
@ -191,7 +189,7 @@ proc finished(fv: FlowVarBase) =
#echo "EXHAUSTED!" #echo "EXHAUSTED!"
release(q.lock) release(q.lock)
wakeupWorkerToProcessQueue(owner) wakeupWorkerToProcessQueue(owner)
await(q.empty) blockUntil(q.empty)
acquire(q.lock) acquire(q.lock)
q.data[q.len] = cast[pointer](fv.data) q.data[q.len] = cast[pointer](fv.data)
inc q.len inc q.len
@ -222,7 +220,7 @@ proc awaitAndThen*[T](fv: FlowVar[T]; action: proc (x: T) {.closure.}) =
## to ``action``. Note that due to Nim's parameter passing semantics this ## to ``action``. Note that due to Nim's parameter passing semantics this
## means that ``T`` doesn't need to be copied and so ``awaitAndThen`` can ## means that ``T`` doesn't need to be copied and so ``awaitAndThen`` can
## sometimes be more efficient than ``^``. ## sometimes be more efficient than ``^``.
await(fv) blockUntil(fv)
when T is string or T is seq: when T is string or T is seq:
action(cast[T](fv.data)) action(cast[T](fv.data))
elif T is ref: elif T is ref:
@ -231,31 +229,31 @@ proc awaitAndThen*[T](fv: FlowVar[T]; action: proc (x: T) {.closure.}) =
action(fv.blob) action(fv.blob)
finished(fv) finished(fv)
proc unsafeRead*[T](fv: FlowVar[ref T]): foreign ptr T = proc unsafeRead*[T](fv: FlowVar[ref T]): ptr T =
## blocks until the value is available and then returns this value. ## blocks until the value is available and then returns this value.
await(fv) blockUntil(fv)
result = cast[foreign ptr T](fv.data) result = cast[ptr T](fv.data)
proc `^`*[T](fv: FlowVar[ref T]): ref T = proc `^`*[T](fv: FlowVar[ref T]): ref T =
## blocks until the value is available and then returns this value. ## blocks until the value is available and then returns this value.
await(fv) blockUntil(fv)
let src = cast[ref T](fv.data) let src = cast[ref T](fv.data)
deepCopy result, src deepCopy result, src
proc `^`*[T](fv: FlowVar[T]): T = proc `^`*[T](fv: FlowVar[T]): T =
## blocks until the value is available and then returns this value. ## blocks until the value is available and then returns this value.
await(fv) blockUntil(fv)
when T is string or T is seq: when T is string or T is seq:
# XXX closures? deepCopy? # XXX closures? deepCopy?
result = cast[T](fv.data) result = cast[T](fv.data)
else: else:
result = fv.blob result = fv.blob
proc awaitAny*(flowVars: openArray[FlowVarBase]): int = proc blockUntilAny*(flowVars: openArray[FlowVarBase]): int =
## awaits any of the given flowVars. Returns the index of one flowVar for ## awaits any of the given flowVars. Returns the index of one flowVar for
## which a value arrived. A flowVar only supports one call to 'awaitAny' at ## which a value arrived. A flowVar only supports one call to 'blockUntilAny' at
## the same time. That means if you awaitAny([a,b]) and awaitAny([b,c]) the second ## the same time. That means if you blockUntilAny([a,b]) and blockUntilAny([b,c]) the second
## call will only await 'c'. If there is no flowVar left to be able to wait ## call will only blockUntil 'c'. If there is no flowVar left to be able to wait
## on, -1 is returned. ## on, -1 is returned.
## **Note**: This results in non-deterministic behaviour and should be avoided. ## **Note**: This results in non-deterministic behaviour and should be avoided.
var ai: AwaitInfo var ai: AwaitInfo
@ -271,7 +269,7 @@ proc awaitAny*(flowVars: openArray[FlowVarBase]): int =
inc conflicts inc conflicts
if conflicts < flowVars.len: if conflicts < flowVars.len:
if result < 0: if result < 0:
await(ai.cv) blockUntil(ai.cv)
result = ai.idx result = ai.idx
for i in 0 .. flowVars.high: for i in 0 .. flowVars.high:
discard cas(addr flowVars[i].ai, addr ai, nil) discard cas(addr flowVars[i].ai, addr ai, nil)
@ -328,7 +326,7 @@ proc slave(w: ptr Worker) {.thread.} =
w.ready = true w.ready = true
readyWorker = w readyWorker = w
signal(gSomeReady) signal(gSomeReady)
await(w.taskArrived) blockUntil(w.taskArrived)
# XXX Somebody needs to look into this (why does this assertion fail # XXX Somebody needs to look into this (why does this assertion fail
# in Visual Studio?) # in Visual Studio?)
when not defined(vcc) and not defined(tcc): assert(not w.ready) when not defined(vcc) and not defined(tcc): assert(not w.ready)
@ -353,7 +351,7 @@ proc distinguishedSlave(w: ptr Worker) {.thread.} =
else: else:
w.ready = true w.ready = true
signal(w.readyForTask) signal(w.readyForTask)
await(w.taskArrived) blockUntil(w.taskArrived)
assert(not w.ready) assert(not w.ready)
w.f(w, w.data) w.f(w, w.data)
if w.q.len != 0: w.cleanFlowVars if w.q.len != 0: w.cleanFlowVars
@ -501,7 +499,7 @@ proc nimSpawn3(fn: WorkerProc; data: pointer) {.compilerProc.} =
# on the current thread instead. # on the current thread instead.
var self = addr(workersData[localThreadId-1]) var self = addr(workersData[localThreadId-1])
fn(self, data) fn(self, data)
await(self.taskStarted) blockUntil(self.taskStarted)
return return
if isSlave: if isSlave:
@ -526,7 +524,7 @@ proc nimSpawn3(fn: WorkerProc; data: pointer) {.compilerProc.} =
inc numSlavesWaiting inc numSlavesWaiting
await(gSomeReady) blockUntil(gSomeReady)
if isSlave: if isSlave:
withLock numSlavesLock: withLock numSlavesLock:
@ -544,7 +542,7 @@ proc nimSpawn4(fn: WorkerProc; data: pointer; id: ThreadId) {.compilerProc.} =
release(distinguishedLock) release(distinguishedLock)
while true: while true:
if selectWorker(addr(distinguishedData[id]), fn, data): break if selectWorker(addr(distinguishedData[id]), fn, data): break
await(distinguishedData[id].readyForTask) blockUntil(distinguishedData[id].readyForTask)
proc sync*() = proc sync*() =
@ -557,7 +555,7 @@ proc sync*() =
if not allReady: break if not allReady: break
allReady = allReady and workersData[i].ready allReady = allReady and workersData[i].ready
if allReady: break if allReady: break
await(gSomeReady) blockUntil(gSomeReady)
inc toRelease inc toRelease
for i in 0 ..< toRelease: for i in 0 ..< toRelease:

View file

@ -1869,7 +1869,6 @@ proc entityToUtf8*(entity: string): string =
## "" is returned if the entity name is unknown. The HTML parser ## "" is returned if the entity name is unknown. The HTML parser
## already converts entities to UTF-8. ## already converts entities to UTF-8.
runnableExamples: runnableExamples:
doAssert entityToUtf8(nil) == ""
doAssert entityToUtf8("") == "" doAssert entityToUtf8("") == ""
doAssert entityToUtf8("a") == "" doAssert entityToUtf8("a") == ""
doAssert entityToUtf8("gt") == ">" doAssert entityToUtf8("gt") == ">"

View file

@ -378,23 +378,23 @@ proc newMultipartData*: MultipartData =
## Constructs a new ``MultipartData`` object. ## Constructs a new ``MultipartData`` object.
MultipartData(content: @[]) MultipartData(content: @[])
proc add*(p: var MultipartData, name, content: string, filename: string = nil, proc add*(p: var MultipartData, name, content: string, filename: string = "",
contentType: string = nil) = contentType: string = "") =
## Add a value to the multipart data. Raises a `ValueError` exception if ## Add a value to the multipart data. Raises a `ValueError` exception if
## `name`, `filename` or `contentType` contain newline characters. ## `name`, `filename` or `contentType` contain newline characters.
if {'\c','\L'} in name: if {'\c','\L'} in name:
raise newException(ValueError, "name contains a newline character") raise newException(ValueError, "name contains a newline character")
if filename != nil and {'\c','\L'} in filename: if {'\c','\L'} in filename:
raise newException(ValueError, "filename contains a newline character") raise newException(ValueError, "filename contains a newline character")
if contentType != nil and {'\c','\L'} in contentType: if {'\c','\L'} in contentType:
raise newException(ValueError, "contentType contains a newline character") raise newException(ValueError, "contentType contains a newline character")
var str = "Content-Disposition: form-data; name=\"" & name & "\"" var str = "Content-Disposition: form-data; name=\"" & name & "\""
if filename != nil: if filename.len > 0:
str.add("; filename=\"" & filename & "\"") str.add("; filename=\"" & filename & "\"")
str.add("\c\L") str.add("\c\L")
if contentType != nil: if contentType.len > 0:
str.add("Content-Type: " & contentType & "\c\L") str.add("Content-Type: " & contentType & "\c\L")
str.add("\c\L" & content & "\c\L") str.add("\c\L" & content & "\c\L")
@ -434,7 +434,7 @@ proc addFiles*(p: var MultipartData, xs: openarray[tuple[name, file: string]]):
var contentType: string var contentType: string
let (_, fName, ext) = splitFile(file) let (_, fName, ext) = splitFile(file)
if ext.len > 0: if ext.len > 0:
contentType = m.getMimetype(ext[1..ext.high], nil) contentType = m.getMimetype(ext[1..ext.high], "")
p.add(name, readFile(file), fName & ext, contentType) p.add(name, readFile(file), fName & ext, contentType)
result = p result = p
@ -457,7 +457,7 @@ proc `[]=`*(p: var MultipartData, name: string,
p.add(name, file.content, file.name, file.contentType) p.add(name, file.content, file.name, file.contentType)
proc format(p: MultipartData): tuple[contentType, body: string] = proc format(p: MultipartData): tuple[contentType, body: string] =
if p == nil or p.content == nil or p.content.len == 0: if p == nil or p.content.len == 0:
return ("", "") return ("", "")
# Create boundary that is not in the data to be formatted # Create boundary that is not in the data to be formatted

View file

@ -1144,7 +1144,7 @@ proc processType(typeName: NimNode, obj: NimNode,
result = quote do: result = quote do:
( (
verifyJsonKind(`jsonNode`, {JString, JNull}, astToStr(`jsonNode`)); verifyJsonKind(`jsonNode`, {JString, JNull}, astToStr(`jsonNode`));
if `jsonNode`.kind == JNull: nil else: `jsonNode`.str if `jsonNode`.kind == JNull: "" else: `jsonNode`.str
) )
of "biggestint": of "biggestint":
result = quote do: result = quote do:

View file

@ -231,7 +231,7 @@ proc newSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM,
raiseOSError(osLastError()) raiseOSError(osLastError())
result = newSocket(fd, domain, sockType, protocol, buffered) result = newSocket(fd, domain, sockType, protocol, buffered)
proc parseIPv4Address(address_str: string): IpAddress = proc parseIPv4Address(addressStr: string): IpAddress =
## Parses IPv4 adresses ## Parses IPv4 adresses
## Raises EInvalidValue on errors ## Raises EInvalidValue on errors
var var
@ -241,15 +241,15 @@ proc parseIPv4Address(address_str: string): IpAddress =
result.family = IpAddressFamily.IPv4 result.family = IpAddressFamily.IPv4
for i in 0 .. high(address_str): for i in 0 .. high(addressStr):
if address_str[i] in strutils.Digits: # Character is a number if addressStr[i] in strutils.Digits: # Character is a number
currentByte = currentByte * 10 + currentByte = currentByte * 10 +
cast[uint16](ord(address_str[i]) - ord('0')) cast[uint16](ord(addressStr[i]) - ord('0'))
if currentByte > 255'u16: if currentByte > 255'u16:
raise newException(ValueError, raise newException(ValueError,
"Invalid IP Address. Value is out of range") "Invalid IP Address. Value is out of range")
seperatorValid = true seperatorValid = true
elif address_str[i] == '.': # IPv4 address separator elif addressStr[i] == '.': # IPv4 address separator
if not seperatorValid or byteCount >= 3: if not seperatorValid or byteCount >= 3:
raise newException(ValueError, raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups") "Invalid IP Address. The address consists of too many groups")
@ -265,11 +265,11 @@ proc parseIPv4Address(address_str: string): IpAddress =
raise newException(ValueError, "Invalid IP Address") raise newException(ValueError, "Invalid IP Address")
result.address_v4[byteCount] = cast[uint8](currentByte) result.address_v4[byteCount] = cast[uint8](currentByte)
proc parseIPv6Address(address_str: string): IpAddress = proc parseIPv6Address(addressStr: string): IpAddress =
## Parses IPv6 adresses ## Parses IPv6 adresses
## Raises EInvalidValue on errors ## Raises EInvalidValue on errors
result.family = IpAddressFamily.IPv6 result.family = IpAddressFamily.IPv6
if address_str.len < 2: if addressStr.len < 2:
raise newException(ValueError, "Invalid IP Address") raise newException(ValueError, "Invalid IP Address")
var var
@ -282,7 +282,7 @@ proc parseIPv6Address(address_str: string): IpAddress =
v4StartPos = -1 v4StartPos = -1
byteCount = 0 byteCount = 0
for i,c in address_str: for i,c in addressStr:
if c == ':': if c == ':':
if not seperatorValid: if not seperatorValid:
raise newException(ValueError, raise newException(ValueError,
@ -293,7 +293,7 @@ proc parseIPv6Address(address_str: string): IpAddress =
"Invalid IP Address. Address contains more than one \"::\" seperator") "Invalid IP Address. Address contains more than one \"::\" seperator")
dualColonGroup = groupCount dualColonGroup = groupCount
seperatorValid = false seperatorValid = false
elif i != 0 and i != high(address_str): elif i != 0 and i != high(addressStr):
if groupCount >= 8: if groupCount >= 8:
raise newException(ValueError, raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups") "Invalid IP Address. The address consists of too many groups")
@ -303,11 +303,11 @@ proc parseIPv6Address(address_str: string): IpAddress =
groupCount.inc() groupCount.inc()
if dualColonGroup != -1: seperatorValid = false if dualColonGroup != -1: seperatorValid = false
elif i == 0: # only valid if address starts with :: elif i == 0: # only valid if address starts with ::
if address_str[1] != ':': if addressStr[1] != ':':
raise newException(ValueError, raise newException(ValueError,
"Invalid IP Address. Address may not start with \":\"") "Invalid IP Address. Address may not start with \":\"")
else: # i == high(address_str) - only valid if address ends with :: else: # i == high(addressStr) - only valid if address ends with ::
if address_str[high(address_str)-1] != ':': if addressStr[high(addressStr)-1] != ':':
raise newException(ValueError, raise newException(ValueError,
"Invalid IP Address. Address may not end with \":\"") "Invalid IP Address. Address may not end with \":\"")
lastWasColon = true lastWasColon = true
@ -345,7 +345,7 @@ proc parseIPv6Address(address_str: string): IpAddress =
result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF) result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
groupCount.inc() groupCount.inc()
else: # Must parse IPv4 address else: # Must parse IPv4 address
for i,c in address_str[v4StartPos..high(address_str)]: for i,c in addressStr[v4StartPos..high(addressStr)]:
if c in strutils.Digits: # Character is a number if c in strutils.Digits: # Character is a number
currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0')) currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0'))
if currentShort > 255'u32: if currentShort > 255'u32:
@ -386,21 +386,21 @@ proc parseIPv6Address(address_str: string): IpAddress =
raise newException(ValueError, raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups") "Invalid IP Address. The address consists of too many groups")
proc parseIpAddress*(address_str: string): IpAddress = proc parseIpAddress*(addressStr: string): IpAddress =
## Parses an IP address ## Parses an IP address
## Raises EInvalidValue on error ## Raises EInvalidValue on error
if address_str == nil: if addressStr.len == 0:
raise newException(ValueError, "IP Address string is nil") raise newException(ValueError, "IP Address string is empty")
if address_str.contains(':'): if addressStr.contains(':'):
return parseIPv6Address(address_str) return parseIPv6Address(addressStr)
else: else:
return parseIPv4Address(address_str) return parseIPv4Address(addressStr)
proc isIpAddress*(address_str: string): bool {.tags: [].} = proc isIpAddress*(addressStr: string): bool {.tags: [].} =
## Checks if a string is an IP address ## Checks if a string is an IP address
## Returns true if it is, false otherwise ## Returns true if it is, false otherwise
try: try:
discard parseIpAddress(address_str) discard parseIpAddress(addressStr)
except ValueError: except ValueError:
return false return false
return true return true
@ -587,7 +587,7 @@ when defineSsl:
proc pskClientCallback(ssl: SslPtr; hint: cstring; identity: cstring; max_identity_len: cuint; psk: ptr cuchar; proc pskClientCallback(ssl: SslPtr; hint: cstring; identity: cstring; max_identity_len: cuint; psk: ptr cuchar;
max_psk_len: cuint): cuint {.cdecl.} = max_psk_len: cuint): cuint {.cdecl.} =
let ctx = SSLContext(context: ssl.SSL_get_SSL_CTX) let ctx = SSLContext(context: ssl.SSL_get_SSL_CTX)
let hintString = if hint == nil: nil else: $hint let hintString = if hint == nil: "" else: $hint
let (identityString, pskString) = (ctx.clientGetPskFunc)(hintString) let (identityString, pskString) = (ctx.clientGetPskFunc)(hintString)
if psk.len.cuint > max_psk_len: if psk.len.cuint > max_psk_len:
return 0 return 0
@ -657,7 +657,7 @@ when defineSsl:
proc wrapConnectedSocket*(ctx: SSLContext, socket: Socket, proc wrapConnectedSocket*(ctx: SSLContext, socket: Socket,
handshake: SslHandshakeType, handshake: SslHandshakeType,
hostname: string = nil) = hostname: string = "") =
## Wraps a connected socket in an SSL context. This function effectively ## Wraps a connected socket in an SSL context. This function effectively
## turns ``socket`` into an SSL socket. ## turns ``socket`` into an SSL socket.
## ``hostname`` should be specified so that the client knows which hostname ## ``hostname`` should be specified so that the client knows which hostname
@ -671,7 +671,7 @@ when defineSsl:
wrapSocket(ctx, socket) wrapSocket(ctx, socket)
case handshake case handshake
of handshakeAsClient: of handshakeAsClient:
if not hostname.isNil and not isIpAddress(hostname): if hostname.len > 0 and not isIpAddress(hostname):
# Discard result in case OpenSSL version doesn't support SNI, or we're # Discard result in case OpenSSL version doesn't support SNI, or we're
# not using TLSv1+ # not using TLSv1+
discard SSL_set_tlsext_host_name(socket.sslHandle, hostname) discard SSL_set_tlsext_host_name(socket.sslHandle, hostname)

View file

@ -44,10 +44,10 @@ type
proc initOptParser*(cmdline: seq[string]): OptParser {.rtl.} = proc initOptParser*(cmdline: seq[string]): OptParser {.rtl.} =
## Initalizes option parses with cmdline. cmdline should not contain ## Initalizes option parses with cmdline. cmdline should not contain
## argument 0 - program name. ## argument 0 - program name.
## If cmdline == nil default to current command line arguments. ## If cmdline.len == 0 default to current command line arguments.
result.remainingShortOptions = "" result.remainingShortOptions = ""
when not defined(createNimRtl): when not defined(createNimRtl):
if cmdline == nil: if cmdline.len == 0:
result.cmd = commandLineParams() result.cmd = commandLineParams()
return return
else: else:
@ -60,7 +60,7 @@ proc initOptParser*(cmdline: string): OptParser {.rtl, deprecated.} =
## and calls initOptParser(openarray[string]) ## and calls initOptParser(openarray[string])
## Do not use. ## Do not use.
if cmdline == "": # backward compatibility if cmdline == "": # backward compatibility
return initOptParser(seq[string](nil)) return initOptParser(@[])
else: else:
return initOptParser(cmdline.split) return initOptParser(cmdline.split)

View file

@ -95,6 +95,7 @@ type
kind: XmlEventKind kind: XmlEventKind
err: XmlErrorKind err: XmlErrorKind
state: ParserState state: ParserState
cIsEmpty: bool
filename: string filename: string
options: set[XmlParseOption] options: set[XmlParseOption]
@ -125,7 +126,8 @@ proc open*(my: var XmlParser, input: Stream, filename: string,
my.kind = xmlError my.kind = xmlError
my.a = "" my.a = ""
my.b = "" my.b = ""
my.c = nil my.c = ""
my.cIsEmpty = true
my.options = options my.options = options
proc close*(my: var XmlParser) {.inline.} = proc close*(my: var XmlParser) {.inline.} =
@ -482,6 +484,7 @@ proc parseTag(my: var XmlParser) =
my.kind = xmlElementOpen my.kind = xmlElementOpen
my.state = stateAttr my.state = stateAttr
my.c = my.a # save for later my.c = my.a # save for later
my.cIsEmpty = false
else: else:
my.kind = xmlElementStart my.kind = xmlElementStart
let slash = my.buf[my.bufpos] == '/' let slash = my.buf[my.bufpos] == '/'
@ -490,7 +493,8 @@ proc parseTag(my: var XmlParser) =
if slash and my.buf[my.bufpos] == '>': if slash and my.buf[my.bufpos] == '>':
inc(my.bufpos) inc(my.bufpos)
my.state = stateEmptyElementTag my.state = stateEmptyElementTag
my.c = nil my.c = ""
my.cIsEmpty = true
elif my.buf[my.bufpos] == '>': elif my.buf[my.bufpos] == '>':
inc(my.bufpos) inc(my.bufpos)
else: else:
@ -678,7 +682,7 @@ proc next*(my: var XmlParser) =
of stateEmptyElementTag: of stateEmptyElementTag:
my.state = stateNormal my.state = stateNormal
my.kind = xmlElementEnd my.kind = xmlElementEnd
if not my.c.isNil: if not my.cIsEmpty:
my.a = my.c my.a = my.c
of stateError: of stateError:
my.kind = xmlError my.kind = xmlError

View file

@ -765,7 +765,7 @@ template fillMatches(s, caps, c) =
if startIdx != -1: if startIdx != -1:
caps[k] = substr(s, startIdx, endIdx) caps[k] = substr(s, startIdx, endIdx)
else: else:
caps[k] = nil caps[k] = ""
proc matchLen*(s: string, pattern: Peg, matches: var openArray[string], proc matchLen*(s: string, pattern: Peg, matches: var openArray[string],
start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} = start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} =
@ -1854,7 +1854,7 @@ when isMainModule:
assert match("prefix/start", peg"^start$", 7) assert match("prefix/start", peg"^start$", 7)
if "foo" =~ peg"{'a'}?.*": if "foo" =~ peg"{'a'}?.*":
assert matches[0] == nil assert matches[0].len == 0
else: assert false else: assert false
if "foo" =~ peg"{''}.*": if "foo" =~ peg"{''}.*":

View file

@ -37,7 +37,7 @@ type
length: int length: int
data: string # != nil if a leaf data: string # != nil if a leaf
proc isConc(r: Rope): bool {.inline.} = return isNil(r.data) proc isConc(r: Rope): bool {.inline.} = return r.length > 0
# Note that the left and right pointers are not needed for leafs. # Note that the left and right pointers are not needed for leafs.
# Leaves have relatively high memory overhead (~30 bytes on a 32 # Leaves have relatively high memory overhead (~30 bytes on a 32
@ -50,12 +50,12 @@ proc isConc(r: Rope): bool {.inline.} = return isNil(r.data)
proc len*(a: Rope): int {.rtl, extern: "nro$1".} = proc len*(a: Rope): int {.rtl, extern: "nro$1".} =
## the rope's length ## the rope's length
if a == nil: result = 0 if a == nil: result = 0
else: result = a.length else: result = abs a.length
proc newRope(): Rope = new(result) proc newRope(): Rope = new(result)
proc newRope(data: string): Rope = proc newRope(data: string): Rope =
new(result) new(result)
result.length = len(data) result.length = -len(data)
result.data = data result.data = data
var var
@ -129,7 +129,7 @@ proc insertInCache(s: string, tree: Rope): Rope =
result.left = t result.left = t
t.right = nil t.right = nil
proc rope*(s: string = nil): Rope {.rtl, extern: "nro$1Str".} = proc rope*(s: string = ""): Rope {.rtl, extern: "nro$1Str".} =
## Converts a string to a rope. ## Converts a string to a rope.
if s.len == 0: if s.len == 0:
result = nil result = nil
@ -170,17 +170,7 @@ proc `&`*(a, b: Rope): Rope {.rtl, extern: "nroConcRopeRope".} =
result = a result = a
else: else:
result = newRope() result = newRope()
result.length = a.length + b.length result.length = abs(a.length) + abs(b.length)
when false:
# XXX rebalancing would be nice, but is too expensive.
result.left = a.left
var x = newRope()
x.left = a.right
x.right = b
result.right = x
else:
result.left = a
result.right = b
proc `&`*(a: Rope, b: string): Rope {.rtl, extern: "nroConcRopeStr".} = proc `&`*(a: Rope, b: string): Rope {.rtl, extern: "nroConcRopeStr".} =
## the concatenation operator for ropes. ## the concatenation operator for ropes.
@ -229,7 +219,6 @@ iterator leaves*(r: Rope): string =
stack.add(it.right) stack.add(it.right)
it = it.left it = it.left
assert(it != nil) assert(it != nil)
assert(it.data != nil)
yield it.data yield it.data
iterator items*(r: Rope): char = iterator items*(r: Rope): char =
@ -250,54 +239,6 @@ proc `$`*(r: Rope): string {.rtl, extern: "nroToString".}=
result = newStringOfCap(r.len) result = newStringOfCap(r.len)
for s in leaves(r): add(result, s) for s in leaves(r): add(result, s)
when false:
# Format string caching seems reasonable: All leaves can be shared and format
# string parsing has to be done only once. A compiled format string is stored
# as a rope. A negative length is used for the index into the args array.
proc compiledArg(idx: int): Rope =
new(result)
result.length = -idx
proc compileFrmt(frmt: string): Rope =
var i = 0
var length = len(frmt)
result = nil
var num = 0
while i < length:
if frmt[i] == '$':
inc(i)
case frmt[i]
of '$':
add(result, "$")
inc(i)
of '#':
inc(i)
add(result, compiledArg(num+1))
inc(num)
of '0'..'9':
var j = 0
while true:
j = j * 10 + ord(frmt[i]) - ord('0')
inc(i)
if frmt[i] notin {'0'..'9'}: break
add(s, compiledArg(j))
of '{':
inc(i)
var j = 0
while frmt[i] in {'0'..'9'}:
j = j * 10 + ord(frmt[i]) - ord('0')
inc(i)
if frmt[i] == '}': inc(i)
else: raise newException(EInvalidValue, "invalid format string")
add(s, compiledArg(j))
else: raise newException(EInvalidValue, "invalid format string")
var start = i
while i < length:
if frmt[i] != '$': inc(i)
else: break
if i - 1 >= start:
add(result, substr(frmt, start, i-1))
proc `%`*(frmt: string, args: openArray[Rope]): Rope {. proc `%`*(frmt: string, args: openArray[Rope]): Rope {.
rtl, extern: "nroFormat".} = rtl, extern: "nroFormat".} =
## `%` substitution operator for ropes. Does not support the ``$identifier`` ## `%` substitution operator for ropes. Does not support the ``$identifier``

View file

@ -119,8 +119,7 @@ proc newSmtp*(useSsl = false, debug=false,
when compiledWithSsl: when compiledWithSsl:
sslContext.wrapSocket(result.sock) sslContext.wrapSocket(result.sock)
else: else:
raise newException(SystemError, {.error: "SMTP module compiled without SSL support".}
"SMTP module compiled without SSL support")
proc newAsyncSmtp*(useSsl = false, debug=false, proc newAsyncSmtp*(useSsl = false, debug=false,
sslContext = defaultSslContext): AsyncSmtp = sslContext = defaultSslContext): AsyncSmtp =
@ -133,8 +132,7 @@ proc newAsyncSmtp*(useSsl = false, debug=false,
when compiledWithSsl: when compiledWithSsl:
sslContext.wrapSocket(result.sock) sslContext.wrapSocket(result.sock)
else: else:
raise newException(SystemError, {.error: "SMTP module compiled without SSL support".}
"SMTP module compiled without SSL support")
proc quitExcpt(smtp: AsyncSmtp, msg: string): Future[void] = proc quitExcpt(smtp: AsyncSmtp, msg: string): Future[void] =
var retFuture = newFuture[void]() var retFuture = newFuture[void]()

View file

@ -377,7 +377,10 @@ when not defined(js):
proc ssClose(s: Stream) = proc ssClose(s: Stream) =
var s = StringStream(s) var s = StringStream(s)
s.data = nil when defined(nimNoNilSeqs):
s.data = ""
else:
s.data = nil
proc newStringStream*(s: string = ""): StringStream = proc newStringStream*(s: string = ""): StringStream =
## creates a new stream from the string `s`. ## creates a new stream from the string `s`.

View file

@ -358,9 +358,6 @@ proc isNilOrEmpty*(s: string): bool {.noSideEffect, procvar, rtl,
proc isNilOrWhitespace*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsNilOrWhitespace".} = proc isNilOrWhitespace*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsNilOrWhitespace".} =
## Checks if `s` is nil or consists entirely of whitespace characters. ## Checks if `s` is nil or consists entirely of whitespace characters.
if len(s) == 0:
return true
result = true result = true
for c in s: for c in s:
if not c.isSpaceAscii(): if not c.isSpaceAscii():
@ -1378,9 +1375,11 @@ proc find*(s: string, sub: char, start: Natural = 0, last: Natural = 0): int {.n
if sub == s[i]: return i if sub == s[i]: return i
else: else:
when hasCStringBuiltin: when hasCStringBuiltin:
let found = c_memchr(s[start].unsafeAddr, sub, last-start+1) let L = last-start+1
if not found.isNil: if L > 0:
return cast[ByteAddress](found) -% cast[ByteAddress](s.cstring) let found = c_memchr(s[start].unsafeAddr, sub, L)
if not found.isNil:
return cast[ByteAddress](found) -% cast[ByteAddress](s.cstring)
else: else:
for i in start..last: for i in start..last:
if sub == s[i]: return i if sub == s[i]: return i
@ -1527,7 +1526,7 @@ proc replace*(s, sub: string, by = ""): string {.noSideEffect,
elif subLen == 1: elif subLen == 1:
# when the pattern is a single char, we use a faster # when the pattern is a single char, we use a faster
# char-based search that doesn't need a skip table: # char-based search that doesn't need a skip table:
var c = sub[0] let c = sub[0]
let last = s.high let last = s.high
var i = 0 var i = 0
while true: while true:

View file

@ -176,10 +176,7 @@ method suiteEnded*(formatter: OutputFormatter) {.base, gcsafe.} =
discard discard
proc addOutputFormatter*(formatter: OutputFormatter) = proc addOutputFormatter*(formatter: OutputFormatter) =
if formatters == nil: formatters.add(formatter)
formatters = @[formatter]
else:
formatters.add(formatter)
proc newConsoleOutputFormatter*(outputLevel: OutputLevel = PRINT_ALL, proc newConsoleOutputFormatter*(outputLevel: OutputLevel = PRINT_ALL,
colorOutput = true): ConsoleOutputFormatter = colorOutput = true): ConsoleOutputFormatter =
@ -225,7 +222,7 @@ method testStarted*(formatter: ConsoleOutputFormatter, testName: string) =
formatter.isInTest = true formatter.isInTest = true
method failureOccurred*(formatter: ConsoleOutputFormatter, checkpoints: seq[string], stackTrace: string) = method failureOccurred*(formatter: ConsoleOutputFormatter, checkpoints: seq[string], stackTrace: string) =
if stackTrace != nil: if stackTrace.len > 0:
echo stackTrace echo stackTrace
let prefix = if formatter.isInSuite: " " else: "" let prefix = if formatter.isInSuite: " " else: ""
for msg in items(checkpoints): for msg in items(checkpoints):
@ -236,7 +233,7 @@ method testEnded*(formatter: ConsoleOutputFormatter, testResult: TestResult) =
if formatter.outputLevel != PRINT_NONE and if formatter.outputLevel != PRINT_NONE and
(formatter.outputLevel == PRINT_ALL or testResult.status == FAILED): (formatter.outputLevel == PRINT_ALL or testResult.status == FAILED):
let prefix = if testResult.suiteName != nil: " " else: "" let prefix = if testResult.suiteName.len > 0: " " else: ""
template rawPrint() = echo(prefix, "[", $testResult.status, "] ", testResult.testName) template rawPrint() = echo(prefix, "[", $testResult.status, "] ", testResult.testName)
when not defined(ECMAScript): when not defined(ECMAScript):
if formatter.colorOutput and not defined(ECMAScript): if formatter.colorOutput and not defined(ECMAScript):
@ -301,7 +298,7 @@ method failureOccurred*(formatter: JUnitOutputFormatter, checkpoints: seq[string
## ``stackTrace`` is provided only if the failure occurred due to an exception. ## ``stackTrace`` is provided only if the failure occurred due to an exception.
## ``checkpoints`` is never ``nil``. ## ``checkpoints`` is never ``nil``.
formatter.testErrors.add(checkpoints) formatter.testErrors.add(checkpoints)
if stackTrace != nil: if stackTrace.len > 0:
formatter.testStackTrace = stackTrace formatter.testStackTrace = stackTrace
method testEnded*(formatter: JUnitOutputFormatter, testResult: TestResult) = method testEnded*(formatter: JUnitOutputFormatter, testResult: TestResult) =
@ -392,7 +389,7 @@ proc shouldRun(currentSuiteName, testName: string): bool =
return false return false
proc ensureInitialized() = proc ensureInitialized() =
if formatters == nil: if formatters.len == 0:
formatters = @[OutputFormatter(defaultConsoleFormatter())] formatters = @[OutputFormatter(defaultConsoleFormatter())]
if not disabledParamFiltering and not testsFilters.isValid: if not disabledParamFiltering and not testsFilters.isValid:
@ -507,7 +504,7 @@ template test*(name, body) {.dirty.} =
if testStatusIMPL == FAILED: if testStatusIMPL == FAILED:
programResult += 1 programResult += 1
let testResult = TestResult( let testResult = TestResult(
suiteName: when declared(testSuiteName): testSuiteName else: nil, suiteName: when declared(testSuiteName): testSuiteName else: "",
testName: name, testName: name,
status: testStatusIMPL status: testStatusIMPL
) )
@ -555,7 +552,7 @@ template fail* =
when declared(stackTrace): when declared(stackTrace):
formatter.failureOccurred(checkpoints, stackTrace) formatter.failureOccurred(checkpoints, stackTrace)
else: else:
formatter.failureOccurred(checkpoints, nil) formatter.failureOccurred(checkpoints, "")
when not defined(ECMAScript): when not defined(ECMAScript):
if abortOnError: quit(programResult) if abortOnError: quit(programResult)

View file

@ -217,9 +217,9 @@ proc createAttribute*(doc: PDocument, name: string): PAttr =
new(attrNode) new(attrNode)
attrNode.fName = name attrNode.fName = name
attrNode.fNodeName = name attrNode.fNodeName = name
attrNode.fLocalName = nil attrNode.fLocalName = ""
attrNode.prefix = nil attrNode.prefix = ""
attrNode.fNamespaceURI = nil attrNode.fNamespaceURI = ""
attrNode.value = "" attrNode.value = ""
attrNode.fSpecified = false attrNode.fSpecified = false
return attrNode return attrNode
@ -254,7 +254,7 @@ proc createAttributeNS*(doc: PDocument, namespaceURI: string, qualifiedName: str
attrNode.prefix = qualifiedName.split(':')[0] attrNode.prefix = qualifiedName.split(':')[0]
attrNode.fLocalName = qualifiedName.split(':')[1] attrNode.fLocalName = qualifiedName.split(':')[1]
else: else:
attrNode.prefix = nil attrNode.prefix = ""
attrNode.fLocalName = qualifiedName attrNode.fLocalName = qualifiedName
attrNode.value = "" attrNode.value = ""
@ -298,9 +298,9 @@ proc createElement*(doc: PDocument, tagName: string): PElement =
new(elNode) new(elNode)
elNode.fTagName = tagName elNode.fTagName = tagName
elNode.fNodeName = tagName elNode.fNodeName = tagName
elNode.fLocalName = nil elNode.fLocalName = ""
elNode.prefix = nil elNode.prefix = ""
elNode.fNamespaceURI = nil elNode.fNamespaceURI = ""
elNode.childNodes = @[] elNode.childNodes = @[]
elNode.attributes = @[] elNode.attributes = @[]
@ -332,7 +332,7 @@ proc createElementNS*(doc: PDocument, namespaceURI: string, qualifiedName: strin
elNode.prefix = qualifiedName.split(':')[0] elNode.prefix = qualifiedName.split(':')[0]
elNode.fLocalName = qualifiedName.split(':')[1] elNode.fLocalName = qualifiedName.split(':')[1]
else: else:
elNode.prefix = nil elNode.prefix = ""
elNode.fLocalName = qualifiedName elNode.fLocalName = qualifiedName
elNode.fNamespaceURI = namespaceURI elNode.fNamespaceURI = namespaceURI
elNode.childNodes = @[] elNode.childNodes = @[]
@ -893,22 +893,22 @@ proc tagName*(el: PElement): string =
proc getAttribute*(el: PNode, name: string): string = proc getAttribute*(el: PNode, name: string): string =
## Retrieves an attribute value by ``name`` ## Retrieves an attribute value by ``name``
if isNil(el.attributes): if isNil(el.attributes):
return nil return ""
var attribute = el.attributes.getNamedItem(name) var attribute = el.attributes.getNamedItem(name)
if not isNil(attribute): if not isNil(attribute):
return attribute.value return attribute.value
else: else:
return nil return ""
proc getAttributeNS*(el: PNode, namespaceURI: string, localName: string): string = proc getAttributeNS*(el: PNode, namespaceURI: string, localName: string): string =
## Retrieves an attribute value by ``localName`` and ``namespaceURI`` ## Retrieves an attribute value by ``localName`` and ``namespaceURI``
if isNil(el.attributes): if isNil(el.attributes):
return nil return ""
var attribute = el.attributes.getNamedItemNS(namespaceURI, localName) var attribute = el.attributes.getNamedItemNS(namespaceURI, localName)
if not isNil(attribute): if not isNil(attribute):
return attribute.value return attribute.value
else: else:
return nil return ""
proc getAttributeNode*(el: PElement, name: string): PAttr = proc getAttributeNode*(el: PElement, name: string): PAttr =
## Retrieves an attribute node by ``name`` ## Retrieves an attribute node by ``name``

View file

@ -119,7 +119,7 @@ proc loadXMLStream*(stream: Stream): PDocument =
## a ``PDocument`` ## a ``PDocument``
var x: XmlParser var x: XmlParser
open(x, stream, nil, {reportComments}) open(x, stream, "", {reportComments})
var xmlDoc: PDocument var xmlDoc: PDocument
var dom: PDOMImplementation = getDOM() var dom: PDOMImplementation = getDOM()
@ -161,7 +161,7 @@ when not defined(testing) and isMainModule:
#echo(xml.getElementsByTagName("bla:test")[0].namespaceURI) #echo(xml.getElementsByTagName("bla:test")[0].namespaceURI)
#echo(xml.getElementsByTagName("test")[0].namespaceURI) #echo(xml.getElementsByTagName("test")[0].namespaceURI)
for i in items(xml.getElementsByTagName("*")): for i in items(xml.getElementsByTagName("*")):
if i.namespaceURI != nil: if i.namespaceURI.len > 0:
echo(i.nodeName, "=", i.namespaceURI) echo(i.nodeName, "=", i.namespaceURI)

View file

@ -484,141 +484,103 @@ type
raise_id: uint # set when exception is raised raise_id: uint # set when exception is raised
up: ref Exception # used for stacking exceptions. Not exported! up: ref Exception # used for stacking exceptions. Not exported!
SystemError* = object of Exception ## \ Defect* = object of Exception ## \
## Abstract class for exceptions that the runtime system raises. ## Abstract base class for all exceptions that Nim's runtime raises
## ## but that are strictly uncatchable as they can also be mapped to
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. ## a ``quit`` / ``trap`` / ``exit`` operation.
IOError* = object of SystemError ## \
CatchableError* = object of Exception ## \
## Abstract class for all exceptions that are catchable.
IOError* = object of CatchableError ## \
## Raised if an IO error occurred. ## Raised if an IO error occurred.
##
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
EOFError* = object of IOError ## \ EOFError* = object of IOError ## \
## Raised if an IO "end of file" error occurred. ## Raised if an IO "end of file" error occurred.
## OSError* = object of CatchableError ## \
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
OSError* = object of SystemError ## \
## Raised if an operating system service failed. ## Raised if an operating system service failed.
##
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
errorCode*: int32 ## OS-defined error code describing this error. errorCode*: int32 ## OS-defined error code describing this error.
LibraryError* = object of OSError ## \ LibraryError* = object of OSError ## \
## Raised if a dynamic library could not be loaded. ## Raised if a dynamic library could not be loaded.
## ResourceExhaustedError* = object of CatchableError ## \
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
ResourceExhaustedError* = object of SystemError ## \
## Raised if a resource request could not be fulfilled. ## Raised if a resource request could not be fulfilled.
## ArithmeticError* = object of Defect ## \
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
ArithmeticError* = object of Exception ## \
## Raised if any kind of arithmetic error occurred. ## Raised if any kind of arithmetic error occurred.
##
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
DivByZeroError* = object of ArithmeticError ## \ DivByZeroError* = object of ArithmeticError ## \
## Raised for runtime integer divide-by-zero errors. ## Raised for runtime integer divide-by-zero errors.
##
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
OverflowError* = object of ArithmeticError ## \ OverflowError* = object of ArithmeticError ## \
## Raised for runtime integer overflows. ## Raised for runtime integer overflows.
## ##
## This happens for calculations whose results are too large to fit in the ## This happens for calculations whose results are too large to fit in the
## provided bits. See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. ## provided bits.
AccessViolationError* = object of Exception ## \ AccessViolationError* = object of Defect ## \
## Raised for invalid memory access errors ## Raised for invalid memory access errors
## AssertionError* = object of Defect ## \
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
AssertionError* = object of Exception ## \
## Raised when assertion is proved wrong. ## Raised when assertion is proved wrong.
## ##
## Usually the result of using the `assert() template <#assert>`_. See the ## Usually the result of using the `assert() template <#assert>`_.
## full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. ValueError* = object of Defect ## \
ValueError* = object of Exception ## \
## Raised for string and object conversion errors. ## Raised for string and object conversion errors.
KeyError* = object of ValueError ## \ KeyError* = object of ValueError ## \
## Raised if a key cannot be found in a table. ## Raised if a key cannot be found in a table.
## ##
## Mostly used by the `tables <tables.html>`_ module, it can also be raised ## Mostly used by the `tables <tables.html>`_ module, it can also be raised
## by other collection modules like `sets <sets.html>`_ or `strtabs ## by other collection modules like `sets <sets.html>`_ or `strtabs
## <strtabs.html>`_. See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. ## <strtabs.html>`_.
OutOfMemError* = object of SystemError ## \ OutOfMemError* = object of Defect ## \
## Raised for unsuccessful attempts to allocate memory. ## Raised for unsuccessful attempts to allocate memory.
## IndexError* = object of Defect ## \
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
IndexError* = object of Exception ## \
## Raised if an array index is out of bounds. ## Raised if an array index is out of bounds.
##
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
FieldError* = object of Exception ## \ FieldError* = object of Defect ## \
## Raised if a record field is not accessible because its dicriminant's ## Raised if a record field is not accessible because its dicriminant's
## value does not fit. ## value does not fit.
## RangeError* = object of Defect ## \
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
RangeError* = object of Exception ## \
## Raised if a range check error occurred. ## Raised if a range check error occurred.
## StackOverflowError* = object of Defect ## \
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
StackOverflowError* = object of SystemError ## \
## Raised if the hardware stack used for subroutine calls overflowed. ## Raised if the hardware stack used for subroutine calls overflowed.
## ReraiseError* = object of Defect ## \
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
ReraiseError* = object of Exception ## \
## Raised if there is no exception to reraise. ## Raised if there is no exception to reraise.
## ObjectAssignmentError* = object of Defect ## \
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
ObjectAssignmentError* = object of Exception ## \
## Raised if an object gets assigned to its parent's object. ## Raised if an object gets assigned to its parent's object.
## ObjectConversionError* = object of Defect ## \
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
ObjectConversionError* = object of Exception ## \
## Raised if an object is converted to an incompatible object type. ## Raised if an object is converted to an incompatible object type.
## You can use ``of`` operator to check if conversion will succeed. ## You can use ``of`` operator to check if conversion will succeed.
## FloatingPointError* = object of Defect ## \
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
FloatingPointError* = object of Exception ## \
## Base class for floating point exceptions. ## Base class for floating point exceptions.
##
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
FloatInvalidOpError* = object of FloatingPointError ## \ FloatInvalidOpError* = object of FloatingPointError ## \
## Raised by invalid operations according to IEEE. ## Raised by invalid operations according to IEEE.
## ##
## Raised by ``0.0/0.0``, for example. See the full `exception ## Raised by ``0.0/0.0``, for example.
## hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
FloatDivByZeroError* = object of FloatingPointError ## \ FloatDivByZeroError* = object of FloatingPointError ## \
## Raised by division by zero. ## Raised by division by zero.
## ##
## Divisor is zero and dividend is a finite nonzero number. See the full ## Divisor is zero and dividend is a finite nonzero number.
## `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
FloatOverflowError* = object of FloatingPointError ## \ FloatOverflowError* = object of FloatingPointError ## \
## Raised for overflows. ## Raised for overflows.
## ##
## The operation produced a result that exceeds the range of the exponent. ## The operation produced a result that exceeds the range of the exponent.
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
FloatUnderflowError* = object of FloatingPointError ## \ FloatUnderflowError* = object of FloatingPointError ## \
## Raised for underflows. ## Raised for underflows.
## ##
## The operation produced a result that is too small to be represented as a ## The operation produced a result that is too small to be represented as a
## normal number. See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. ## normal number.
FloatInexactError* = object of FloatingPointError ## \ FloatInexactError* = object of FloatingPointError ## \
## Raised for inexact results. ## Raised for inexact results.
## ##
## The operation produced a result that cannot be represented with infinite ## The operation produced a result that cannot be represented with infinite
## precision -- for example: ``2.0 / 3.0, log(1.1)`` ## precision -- for example: ``2.0 / 3.0, log(1.1)``
## ##
## **NOTE**: Nim currently does not detect these! See the full ## **NOTE**: Nim currently does not detect these!
## `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. DeadThreadError* = object of Defect ## \
DeadThreadError* = object of Exception ## \
## Raised if it is attempted to send a message to a dead thread. ## Raised if it is attempted to send a message to a dead thread.
## NilAccessError* = object of Defect ## \
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
NilAccessError* = object of SystemError ## \
## Raised on dereferences of ``nil`` pointers. ## Raised on dereferences of ``nil`` pointers.
## ##
## This is only raised if the ``segfaults.nim`` module was imported! ## This is only raised if the ``segfaults.nim`` module was imported!
when defined(nimNewRuntime): when defined(nimNewRuntime):
type type
MoveError* = object of SystemError ## \ MoveError* = object of Defect ## \
## Raised on attempts to re-sink an already consumed ``sink`` parameter. ## Raised on attempts to re-sink an already consumed ``sink`` parameter.
when defined(js) or defined(nimdoc): when defined(js) or defined(nimdoc):
@ -2321,9 +2283,9 @@ iterator mpairs*(a: var cstring): tuple[key: int, val: var char] {.inline.} =
inc(i) inc(i)
proc isNil*[T](x: seq[T]): bool {.noSideEffect, magic: "IsNil".} proc isNil*[T](x: seq[T]): bool {.noSideEffect, magic: "IsNil", deprecated.}
proc isNil*[T](x: ref T): bool {.noSideEffect, magic: "IsNil".} proc isNil*[T](x: ref T): bool {.noSideEffect, magic: "IsNil".}
proc isNil*(x: string): bool {.noSideEffect, magic: "IsNil".} proc isNil*(x: string): bool {.noSideEffect, magic: "IsNil", deprecated.}
proc isNil*[T](x: ptr T): bool {.noSideEffect, magic: "IsNil".} proc isNil*[T](x: ptr T): bool {.noSideEffect, magic: "IsNil".}
proc isNil*(x: pointer): bool {.noSideEffect, magic: "IsNil".} proc isNil*(x: pointer): bool {.noSideEffect, magic: "IsNil".}
proc isNil*(x: cstring): bool {.noSideEffect, magic: "IsNil".} proc isNil*(x: cstring): bool {.noSideEffect, magic: "IsNil".}
@ -2382,8 +2344,12 @@ proc `==`*[T](x, y: seq[T]): bool {.noSideEffect.} =
## Generic equals operator for sequences: relies on a equals operator for ## Generic equals operator for sequences: relies on a equals operator for
## the element type `T`. ## the element type `T`.
when nimvm: when nimvm:
if x.isNil and y.isNil: when not defined(nimNoNil):
return true if x.isNil and y.isNil:
return true
else:
if x.len == 0 and y.len == 0:
return true
else: else:
when not defined(JS): when not defined(JS):
proc seqToPtr[T](x: seq[T]): pointer {.inline, nosideeffect.} = proc seqToPtr[T](x: seq[T]): pointer {.inline, nosideeffect.} =
@ -2520,7 +2486,7 @@ proc `$`*[T: tuple|object](x: T): string =
result.add(name) result.add(name)
result.add(": ") result.add(": ")
when compiles($value): when compiles($value):
when compiles(value.isNil): when value isnot string and value isnot seq and compiles(value.isNil):
if value.isNil: result.add "nil" if value.isNil: result.add "nil"
else: result.addQuoted(value) else: result.addQuoted(value)
else: else:
@ -2539,7 +2505,7 @@ proc collectionToString[T](x: T, prefix, separator, suffix: string): string =
else: else:
result.add(separator) result.add(separator)
when compiles(value.isNil): when value isnot string and value isnot seq and compiles(value.isNil):
# this branch should not be necessary # this branch should not be necessary
if value.isNil: if value.isNil:
result.add "nil" result.add "nil"
@ -4001,19 +3967,28 @@ when hasAlloc:
proc safeAdd*[T](x: var seq[T], y: T) {.noSideEffect, deprecated.} = proc safeAdd*[T](x: var seq[T], y: T) {.noSideEffect, deprecated.} =
## Adds ``y`` to ``x`` unless ``x`` is not yet initialized; in that case, ## Adds ``y`` to ``x`` unless ``x`` is not yet initialized; in that case,
## ``x`` becomes ``@[y]`` ## ``x`` becomes ``@[y]``
if x == nil: x = @[y] when defined(nimNoNilSeqs):
else: x.add(y) x.add(y)
else:
if x == nil: x = @[y]
else: x.add(y)
proc safeAdd*(x: var string, y: char) {.noSideEffect, deprecated.} = proc safeAdd*(x: var string, y: char) {.noSideEffect, deprecated.} =
## Adds ``y`` to ``x``. If ``x`` is ``nil`` it is initialized to ``""`` ## Adds ``y`` to ``x``. If ``x`` is ``nil`` it is initialized to ``""``
if x == nil: x = "" when defined(nimNoNilSeqs):
x.add(y) x.add(y)
else:
if x == nil: x = ""
x.add(y)
proc safeAdd*(x: var string, y: string) {.noSideEffect, deprecated.} = proc safeAdd*(x: var string, y: string) {.noSideEffect, deprecated.} =
## Adds ``y`` to ``x`` unless ``x`` is not yet initalized; in that ## Adds ``y`` to ``x`` unless ``x`` is not yet initalized; in that
## case, ``x`` becomes ``y`` ## case, ``x`` becomes ``y``
if x == nil: x = y when defined(nimNoNilSeqs):
else: x.add(y) x.add(y)
else:
if x == nil: x = y
else: x.add(y)
proc locals*(): RootObj {.magic: "Plugin", noSideEffect.} = proc locals*(): RootObj {.magic: "Plugin", noSideEffect.} =
## generates a tuple constructor expression listing all the local variables ## generates a tuple constructor expression listing all the local variables
@ -4073,6 +4048,11 @@ proc `==`*(x, y: cstring): bool {.magic: "EqCString", noSideEffect,
elif x.isNil or y.isNil: result = false elif x.isNil or y.isNil: result = false
else: result = strcmp(x, y) == 0 else: result = strcmp(x, y) == 0
when defined(nimNoNilSeqs2):
when not compileOption("nilseqs"):
proc `==`*(x: string; y: type(nil)): bool {.error.} = discard
proc `==`*(x: type(nil); y: string): bool {.error.} = discard
template closureScope*(body: untyped): untyped = template closureScope*(body: untyped): untyped =
## Useful when creating a closure in a loop to capture local loop variables by ## Useful when creating a closure in a loop to capture local loop variables by
## their current iteration values. Example: ## their current iteration values. Example:

View file

@ -110,7 +110,7 @@ proc getStackTrace*(e: ref Exception): string = e.trace
proc unhandledException(e: ref Exception) {. proc unhandledException(e: ref Exception) {.
compilerproc, asmNoStackFrame.} = compilerproc, asmNoStackFrame.} =
var buf = "" var buf = ""
if e.msg != nil and e.msg[0] != '\0': if e.msg.len != 0:
add(buf, "Error: unhandled exception: ") add(buf, "Error: unhandled exception: ")
add(buf, e.msg) add(buf, e.msg)
else: else:

View file

@ -95,6 +95,7 @@ proc mnewString(len: int): NimString {.compilerProc.} =
proc copyStrLast(s: NimString, start, last: int): NimString {.compilerProc.} = proc copyStrLast(s: NimString, start, last: int): NimString {.compilerProc.} =
let start = max(start, 0) let start = max(start, 0)
if s == nil: return nil
let len = min(last, s.len-1) - start + 1 let len = min(last, s.len-1) - start + 1
if len > 0: if len > 0:
result = rawNewStringNoInit(len) result = rawNewStringNoInit(len)
@ -109,6 +110,7 @@ proc nimToCStringConv(s: NimString): cstring {.compilerProc, inline.} =
else: result = cstring(addr s.data) else: result = cstring(addr s.data)
proc copyStr(s: NimString, start: int): NimString {.compilerProc.} = proc copyStr(s: NimString, start: int): NimString {.compilerProc.} =
if s == nil: return nil
result = copyStrLast(s, start, s.len-1) result = copyStrLast(s, start, s.len-1)
proc toNimStr(str: cstring, len: int): NimString {.compilerProc.} = proc toNimStr(str: cstring, len: int): NimString {.compilerProc.} =

View file

@ -109,7 +109,7 @@ proc sexp(s: IdeCmd|TSymKind|PrefixMatch): SexpNode = sexp($s)
proc sexp(s: Suggest): SexpNode = proc sexp(s: Suggest): SexpNode =
# If you change the order here, make sure to change it over in # If you change the order here, make sure to change it over in
# nim-mode.el too. # nim-mode.el too.
let qp = if s.qualifiedPath.isNil: @[] else: s.qualifiedPath let qp = if s.qualifiedPath.len == 0: @[] else: s.qualifiedPath
result = convertSexp([ result = convertSexp([
s.section, s.section,
TSymKind s.symkind, TSymKind s.symkind,
@ -176,7 +176,7 @@ proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int;
let dirtyIdx = fileInfoIdx(conf, file, isKnownFile) let dirtyIdx = fileInfoIdx(conf, file, isKnownFile)
if dirtyfile.len != 0: msgs.setDirtyFile(conf, dirtyIdx, dirtyfile) if dirtyfile.len != 0: msgs.setDirtyFile(conf, dirtyIdx, dirtyfile)
else: msgs.setDirtyFile(conf, dirtyIdx, nil) else: msgs.setDirtyFile(conf, dirtyIdx, "")
conf.m.trackPos = newLineInfo(dirtyIdx, line, col) conf.m.trackPos = newLineInfo(dirtyIdx, line, col)
conf.m.trackPosAttached = false conf.m.trackPosAttached = false
@ -209,7 +209,7 @@ proc executeEpc(cmd: IdeCmd, args: SexpNode;
column = args[2].getNum column = args[2].getNum
var dirtyfile = "" var dirtyfile = ""
if len(args) > 3: if len(args) > 3:
dirtyfile = args[3].getStr(nil) dirtyfile = args[3].getStr("")
execute(cmd, file, dirtyfile, int(line), int(column), graph) execute(cmd, file, dirtyfile, int(line), int(column), graph)
proc returnEpc(socket: Socket, uid: BiggestInt, s: SexpNode|string, proc returnEpc(socket: Socket, uid: BiggestInt, s: SexpNode|string,

View file

@ -70,22 +70,22 @@ proc parseCmd(c: string): seq[string] =
result = @[] result = @[]
var i = 0 var i = 0
var a = "" var a = ""
while true: while i < c.len:
setLen(a, 0) setLen(a, 0)
# eat all delimiting whitespace # eat all delimiting whitespace
while c[i] in {' ', '\t', '\l', '\r'}: inc(i) while i < c.len and c[i] in {' ', '\t', '\l', '\r'}: inc(i)
if i >= c.len: break
case c[i] case c[i]
of '"': raise newException(ValueError, "double quotes not yet supported: " & c) of '"': raise newException(ValueError, "double quotes not yet supported: " & c)
of '\'': of '\'':
var delim = c[i] var delim = c[i]
inc(i) # skip ' or " inc(i) # skip ' or "
while c[i] != '\0' and c[i] != delim: while i < c.len and c[i] != delim:
add a, c[i] add a, c[i]
inc(i) inc(i)
if c[i] != '\0': inc(i) if i < c.len: inc(i)
of '\0': break
else: else:
while c[i] > ' ': while i < c.len and c[i] > ' ':
add(a, c[i]) add(a, c[i])
inc(i) inc(i)
add(result, a) add(result, a)

View file

@ -2,7 +2,7 @@ discard """
$nimsuggest --tester --maxresults:2 $file $nimsuggest --tester --maxresults:2 $file
>sug $1 >sug $1
sug;;skProc;;tdot4.main;;proc (inp: string): string;;$file;;10;;5;;"";;100;;None sug;;skProc;;tdot4.main;;proc (inp: string): string;;$file;;10;;5;;"";;100;;None
sug;;skProc;;strutils.replace;;proc (s: string, sub: string, by: string): string{.noSideEffect, gcsafe, locks: 0.};;$lib/pure/strutils.nim;;1575;;5;;"Replaces `sub` in `s` by the string `by`.";;100;;None sug;;skProc;;strutils.replace;;proc (s: string, sub: string, by: string): string{.noSideEffect, gcsafe, locks: 0.};;$lib/pure/strutils.nim;;1506;;5;;"Replaces `sub` in `s` by the string `by`.";;100;;None
""" """
import strutils import strutils

View file

@ -1,7 +1,7 @@
discard """ discard """
$nimsuggest --tester compiler/nim.nim $nimsuggest --tester compiler/nim.nim
>def compiler/semexprs.nim:13:50 >def compiler/semexprs.nim:25:50
def;;skType;;ast.PSym;;PSym;;*ast.nim;;691;;2;;"";;100 def;;skType;;ast.PSym;;PSym;;*ast.nim;;707;;2;;"";;100
>def compiler/semexprs.nim:13:50 >def compiler/semexprs.nim:25:50
def;;skType;;ast.PSym;;PSym;;*ast.nim;;691;;2;;"";;100 def;;skType;;ast.PSym;;PSym;;*ast.nim;;707;;2;;"";;100
""" """

View file

@ -37,8 +37,7 @@ block: #8399
case line[0] case line[0]
of '+', '-': @[] of '+', '-': @[]
of '$': (let x = await bar(); @[""]) of '$': (let x = await bar(); @[""])
else: else: @[]
nil
doAssert(res == @[""]) doAssert(res == @[""])

View file

@ -15,11 +15,11 @@ when defined(ssl):
var clientCount = 0 var clientCount = 0
proc sendMessages(client: AsyncSocket) {.async.} = proc sendMessages(client: AsyncSocket) {.async.} =
for i in 0 .. <messagesToSend: for i in 0 ..< messagesToSend:
await send(client, "Message " & $i & "\c\L") await send(client, "Message " & $i & "\c\L")
proc launchSwarm(port: Port) {.async.} = proc launchSwarm(port: Port) {.async.} =
for i in 0 .. <swarmSize: for i in 0 ..< swarmSize:
var sock = newAsyncSocket() var sock = newAsyncSocket()
var clientContext = newContext(verifyMode = CVerifyNone) var clientContext = newContext(verifyMode = CVerifyNone)
clientContext.wrapSocket(sock) clientContext.wrapSocket(sock)

View file

@ -41,16 +41,16 @@ let str2 = "NN"
let a = case str1: let a = case str1:
of "Y": true of "Y": true
of "N": false of "N": false
else: else:
echo "no good" echo "no good"
quit("quiting") quit("quiting")
proc toBool(s: string): bool = proc toBool(s: string): bool =
case s: case s:
of nil, "": raise newException(ValueError, "Invalid boolean") of "": raise newException(ValueError, "Invalid boolean")
elif s[0] == 'Y': true elif s[0] == 'Y': true
elif s[0] == 'N': false elif s[0] == 'N': false
else: "error".quit(2) else: "error".quit(2)
let b = "NN".toBool() let b = "NN".toBool()
@ -66,7 +66,7 @@ static:
var bb: bool var bb: bool
doassert(not compiles( doassert(not compiles(
bb = case str2: bb = case str2:
of nil, "": raise newException(ValueError, "Invalid boolean") of "": raise newException(ValueError, "Invalid boolean")
elif str.startsWith("Y"): true elif str.startsWith("Y"): true
elif str.startsWith("N"): false elif str.startsWith("N"): false
)) ))
@ -94,7 +94,7 @@ doassert(not compiles(
bb = case str2: bb = case str2:
of "Y": of "Y":
raise newException(ValueError, "Invalid Y") raise newException(ValueError, "Invalid Y")
true true
else: raise newException(ValueError, "Invalid") else: raise newException(ValueError, "Invalid")
)) ))
@ -103,6 +103,6 @@ doassert(not compiles(
bb = case str2: bb = case str2:
of "Y": of "Y":
"invalid Y".quit(3) "invalid Y".quit(3)
true true
else: raise newException(ValueError, "Invalid") else: raise newException(ValueError, "Invalid")
)) ))

View file

@ -1,6 +1,7 @@
discard """ discard """
targets: "cpp" targets: "cpp"
output: "hello" output: "hello"
cmd: "nim cpp --nilseqs:on $file"
""" """
# bug #3299 # bug #3299

View file

@ -9,7 +9,6 @@ discard """
type type
MyPointer = distinct pointer MyPointer = distinct pointer
MyString = distinct string MyString = distinct string
MyStringNotNil = distinct (string not nil)
MyInt = distinct int MyInt = distinct int
proc foo(a: MyPointer) = proc foo(a: MyPointer) =
@ -26,20 +25,6 @@ p = cast[MyPointer](nil)
p = nil.MyPointer p = nil.MyPointer
p = nil p = nil
var c: MyString
c = "Test".MyString
c = nil.MyString
c = nil
p = nil
doAssert(compiles(c = p) == false)
var n: MyStringNotNil = "Test".MyStringNotNil # Cannot prove warning ...
n = "Test".MyStringNotNil
doAssert(compiles(n = nil.MyStringNotNil) == false)
doAssert(compiles(n = nil.MyStringNotNil) == false)
doAssert(compiles(n = nil) == false)
var i: MyInt var i: MyInt
i = 1.MyInt i = 1.MyInt
doAssert(compiles(i = nil) == false) doAssert(compiles(i = nil) == false)

View file

@ -7,10 +7,10 @@ Check passed'''
# bug #5628 # bug #5628
proc checkException(ex: ref Exception) = proc checkException(ex: ref Exception) =
doAssert(ex.name == "ValueError") doAssert(ex.name == cstring"ValueError")
doAssert(ex.msg == "SecondException") doAssert(ex.msg == "SecondException")
doAssert(ex.parent != nil) doAssert(ex.parent != nil)
doAssert(ex.parent.name == "KeyError") doAssert(ex.parent.name == cstring"KeyError")
doAssert(ex.parent.msg == "FirstException") doAssert(ex.parent.msg == "FirstException")
echo "Check passed" echo "Check passed"

View file

@ -87,7 +87,7 @@ proc parseResponse(): JsonNode =
var excMsg = key & "(" var excMsg = key & "("
if (var n=result["key2"]; n != nil): if (var n=result["key2"]; n != nil):
excMsg &= n.str excMsg &= n.str
raise newException(SystemError, excMsg) raise newException(CatchableError, excMsg)

View file

@ -103,7 +103,7 @@ proc DeleteItem[T,D] (n: PNode[T,D], x: int): PNode[T,D] {.inline.} =
else : else :
result = n.left result = n.left
n.slots = nil n.slots = @[]
n.left = nil n.left = nil
proc internalDelete[T,D] (ANode: PNode[T,D], key: T, Avalue: var D): PNode[T,D] = proc internalDelete[T,D] (ANode: PNode[T,D], key: T, Avalue: var D): PNode[T,D] =
@ -200,7 +200,7 @@ proc traceTree[T,D](root: PNode[T,D]) =
traceln(space) traceln(space)
write stdout, "left: " write stdout, "left: "
doTrace(n.left, level+1) doTrace(n.left, level+1)
for i, el in n.slots : for i, el in n.slots:
if el != nil and not isClean(el): if el != nil and not isClean(el):
traceln(space) traceln(space)
traceX(i) traceX(i)

View file

@ -2,8 +2,8 @@ discard """
output: '''(peel: 0, color: 15) output: '''(peel: 0, color: 15)
(color: 15) (color: 15)
17 17
(width: 0.0, taste: nil, color: 13) (width: 0.0, taste: "", color: 13)
(width: 0.0, taste: nil, color: 15) (width: 0.0, taste: "", color: 15)
cool''' cool'''
""" """
@ -11,16 +11,16 @@ cool'''
type type
BaseFruit[T] = object of RootObj BaseFruit[T] = object of RootObj
color: T color: T
MidLevel[T] = object of BaseFruit[T] MidLevel[T] = object of BaseFruit[T]
Mango = object of MidLevel[int] Mango = object of MidLevel[int]
peel: int peel: int
Peach[X, T, Y] = object of T Peach[X, T, Y] = object of T
width: X width: X
taste: Y taste: Y
proc setColor[T](self: var BaseFruit[T]) = proc setColor[T](self: var BaseFruit[T]) =
self.color = 15 self.color = 15

View file

@ -48,4 +48,4 @@ for i in 1 .. 10:
let results = runCallbacks() let results = runCallbacks()
doAssert(expected == results) doAssert(expected == $results)

View file

@ -34,7 +34,7 @@ var
recurse1 = Recurse[int](data: 1, next: recurse2) recurse1 = Recurse[int](data: 1, next: recurse2)
doAssert test.name == "Jorden" doAssert test.name == cstring"Jorden"
doAssert knight.age == 19 doAssert knight.age == 19
doAssert knight.item.price == 50 doAssert knight.item.price == 50
doAssert recurse1.next.next.data == 3 doAssert recurse1.next.next.data == 3

View file

@ -64,7 +64,7 @@ block:
proc test(): bool = proc test(): bool =
let obj = newJsObject() let obj = newJsObject()
obj.`?!$` = proc(x, y, z: int, t: cstring): cstring = t & $(x + y + z) obj.`?!$` = proc(x, y, z: int, t: cstring): cstring = t & $(x + y + z)
obj.`?!$`(1, 2, 3, "Result is: ").to(cstring) == "Result is: 6" obj.`?!$`(1, 2, 3, "Result is: ").to(cstring) == cstring"Result is: 6"
echo test() echo test()
# Test JsObject []() # Test JsObject []()

View file

@ -12,7 +12,7 @@ discard """
""" """
import macros import macros
{.experimental: "forLoopMacros".}
macro mymacro(): untyped = macro mymacro(): untyped =
result = newLit([1, 2, 3]) result = newLit([1, 2, 3])

View file

@ -74,14 +74,14 @@ type
## nothing prevents you from accessing directly the type of field you want ## nothing prevents you from accessing directly the type of field you want
## if you expect only one kind. ## if you expect only one kind.
case kind*: Tparam_kind case kind*: Tparam_kind
of PK_EMPTY: nil of PK_EMPTY: discard
of PK_INT: int_val*: int of PK_INT: int_val*: int
of PK_BIGGEST_INT: big_int_val*: BiggestInt of PK_BIGGEST_INT: big_int_val*: BiggestInt
of PK_FLOAT: float_val*: float of PK_FLOAT: float_val*: float
of PK_BIGGEST_FLOAT: big_float_val*: BiggestFloat of PK_BIGGEST_FLOAT: big_float_val*: BiggestFloat
of PK_STRING: str_val*: string of PK_STRING: str_val*: string
of PK_BOOL: bool_val*: bool of PK_BOOL: bool_val*: bool
of PK_HELP: nil of PK_HELP: discard
Tcommandline_results* = object of RootObj ## \ Tcommandline_results* = object of RootObj ## \
## Contains the results of the parsing. ## Contains the results of the parsing.
@ -319,7 +319,7 @@ proc echo_help*(expected: seq[Tparameter_specification] = @[],
proc parse*(expected: seq[Tparameter_specification] = @[], proc parse*(expected: seq[Tparameter_specification] = @[],
type_of_positional_parameters = PK_STRING, args: seq[TaintedString] = nil, type_of_positional_parameters = PK_STRING, args: seq[TaintedString] = @[],
bad_prefixes = @["-", "--"], end_of_options = "--", bad_prefixes = @["-", "--"], end_of_options = "--",
quit_on_failure = true): Tcommandline_results = quit_on_failure = true): Tcommandline_results =
## Parses parameters and returns results. ## Parses parameters and returns results.
@ -339,7 +339,7 @@ proc parse*(expected: seq[Tparameter_specification] = @[],
## ##
## The args sequence should be the list of parameters passed to your program ## The args sequence should be the list of parameters passed to your program
## without the program binary (usually OSes provide the path to the binary as ## without the program binary (usually OSes provide the path to the binary as
## the zeroth parameter). If args is nil, the list will be retrieved from the ## the zeroth parameter). If args is empty, the list will be retrieved from the
## OS. ## OS.
## ##
## If there is any kind of error and quit_on_failure is true, the quit proc ## If there is any kind of error and quit_on_failure is true, the quit proc
@ -358,7 +358,7 @@ proc parse*(expected: seq[Tparameter_specification] = @[],
# Prepare the input parameter list, maybe get it from the OS if not available. # Prepare the input parameter list, maybe get it from the OS if not available.
var args = args var args = args
if args == nil: if args.len == 0:
let total_params = paramCount() let total_params = paramCount()
#echo "Got no explicit args, retrieving from OS. Count: ", total_params #echo "Got no explicit args, retrieving from OS. Count: ", total_params
newSeq(args, total_params) newSeq(args, total_params)

View file

@ -145,7 +145,7 @@ proc importHandling(data: JsonNode): THandlingRecord
proc importBullet(data: JsonNode; errors: var seq[string]): PBulletRecord proc importBullet(data: JsonNode; errors: var seq[string]): PBulletRecord
proc importSoul(data: JsonNode): TSoulRecord proc importSoul(data: JsonNode): TSoulRecord
proc importExplosion(data: JsonNode; errors: var seq[string]): TExplosionRecord proc importExplosion(data: JsonNode; errors: var seq[string]): TExplosionRecord
proc importSound*(data: JsonNode; errors: var seq[string]; fieldName: string = nil): PSoundRecord proc importSound*(data: JsonNode; errors: var seq[string]; fieldName: string = ""): PSoundRecord
## this is the only pipe between lobby and main.nim ## this is the only pipe between lobby and main.nim
proc getActiveState*(): TGameState = proc getActiveState*(): TGameState =
@ -514,7 +514,7 @@ proc importExplosion(data: JsonNode; errors: var seq[string]): TExplosionRecord
let expl = data["explode"] let expl = data["explode"]
result.anim = importAnim(expl, errors) result.anim = importAnim(expl, errors)
result.sound = importSound(expl, errors, "sound") result.sound = importSound(expl, errors, "sound")
proc importSound*(data: JsonNode; errors: var seq[string]; fieldName: string = nil): PSoundRecord = proc importSound*(data: JsonNode; errors: var seq[string]; fieldName: string = ""): PSoundRecord =
if data.kind == JObject: if data.kind == JObject:
checkKey(data, fieldName) checkKey(data, fieldName)
result = newSound(data[fieldName].str, errors) result = newSound(data[fieldName].str, errors)

View file

@ -92,8 +92,8 @@ proc newGuiContainer*(pos: TVector2f): PGuiContainer =
result = newGuiContainer() result = newGuiContainer()
result.setPosition pos result.setPosition pos
proc free*(container: PGuiContainer) = proc free*(container: PGuiContainer) =
container.widgets = nil container.widgets = @[]
container.buttons = nil container.buttons = @[]
proc add*(container: PGuiContainer; widget: PGuiObject) = proc add*(container: PGuiContainer; widget: PGuiObject) =
container.widgets.add(widget) container.widgets.add(widget)
proc add*(container: PGuiContainer; button: PButton) = proc add*(container: PGuiContainer; button: PButton) =

View file

@ -44,16 +44,16 @@ import json
type type
foo = object foo = object
thing: string not nil thing: ptr int not nil
CTS = ref object CTS = ref object
subs_by_sid: Table[int, foo] subs_by_sid: Table[int, foo]
proc parse(cts: CTS, jn: JsonNode) = proc parse(cts: CTS, jn: JsonNode) =
var y = jn.getInt(4523)
let ces = foo( let ces = foo(
thing: jn.getStr("thing") thing: addr y
) )
cts.subs_by_sid[0] = ces cts.subs_by_sid[0] = ces
@ -63,17 +63,3 @@ proc parse(cts: CTS, jn: JsonNode) =
proc p(x: proc(){.closure.} not nil) = discard proc p(x: proc(){.closure.} not nil) = discard
p(proc(){.closure.} = discard) p(proc(){.closure.} = discard)
# bug #3993
type
List[T] = seq[T] not nil
proc `^^`[T](v: T, lst: List[T]): List[T] =
result = @[v]
result.add(lst)
proc Nil[T](): List[T] = @[]
when isMainModule:
let lst = 1 ^^ 2 ^^ Nil[int]()

View file

@ -1,5 +1,5 @@
discard """ discard """
line: 22 line: 13
errormsg: "type mismatch" errormsg: "type mismatch"
""" """
{.experimental: "notnil".} {.experimental: "notnil".}
@ -8,16 +8,6 @@ type
TObj = object TObj = object
x: int x: int
MyString = string not nil
#var x: PObj = nil
proc p(x: string not nil): int =
result = 45
proc q(x: MyString) = discard
proc q2(x: string) = discard proc q2(x: string) = discard
q2(nil) q2(nil)
q(nil)

View file

@ -1,6 +1,6 @@
discard """ discard """
errormsg: "'y' is provably nil" errormsg: "'y' is provably nil"
line:38 line:25
""" """
import strutils import strutils
@ -10,19 +10,6 @@ type
TObj = object TObj = object
x, y: int x, y: int
type
superstring = string not nil
proc q(s: superstring) =
echo s
proc p2() =
var a: string = "I am not nil"
q(a) # but this should and does not
p2()
proc q(x: pointer not nil) = proc q(x: pointer not nil) =
discard discard

View file

@ -6,9 +6,9 @@ discard """
# bug #2355 # bug #2355
type type
Foo = object Foo = object
foo: string not nil foo: ref int
bar: string not nil bar: ref int not nil
var x: ref int = new(int)
# Create instance without initializaing the `bar` field # Create instance without initializaing the `bar` field
var f = Foo(foo: "foo") var f = Foo(foo: x)
echo f.bar.isNil # true echo f.bar.isNil # true

View file

@ -11,7 +11,7 @@ type
Killer* = object Killer* = object
lock: Lock lock: Lock
bailed {.guard: lock.}: bool bailed {.guard: lock.}: bool
processes {.guard: lock.}: array[0..MAX_WORKERS-1, foreign ptr Process] processes {.guard: lock.}: array[0..MAX_WORKERS-1, ptr Process]
# Hold a lock for a statement. # Hold a lock for a statement.
template hold(lock: Lock, body: untyped) = template hold(lock: Lock, body: untyped) =
@ -32,7 +32,7 @@ proc initKiller*(): Killer =
var killer = initKiller() var killer = initKiller()
# remember that a process has been launched, killing it if we have bailed. # remember that a process has been launched, killing it if we have bailed.
proc launched*(process: foreign ptr Process): int {.gcsafe.} = proc launched*(process: ptr Process): int {.gcsafe.} =
result = killer.processes.high + 1 result = killer.processes.high + 1
killer.lock.hold: killer.lock.hold:
if killer.bailed: if killer.bailed:

View file

@ -18,12 +18,12 @@ var results: seq[int] = @[]
for i in 0 .. durations.high: for i in 0 .. durations.high:
tasks.add spawn timer(durations[i]) tasks.add spawn timer(durations[i])
var index = awaitAny(tasks) var index = blockUntilAny(tasks)
while index != -1: while index != -1:
results.add ^cast[FlowVar[int]](tasks[index]) results.add ^cast[FlowVar[int]](tasks[index])
tasks.del(index) tasks.del(index)
#echo repr results #echo repr results
index = awaitAny(tasks) index = blockUntilAny(tasks)
doAssert results.len == 5 doAssert results.len == 5
doAssert 1000 in results doAssert 1000 in results

View file

@ -2,4 +2,4 @@
template serializationKey*(s: string) {.pragma.} template serializationKey*(s: string) {.pragma.}
template defaultValue*(V: typed) {.pragma.} template defaultValue*(V: typed) {.pragma.}
template alternativeKey*(s: string = nil, V: typed) {.pragma.} template alternativeKey*(s: string = "", V: typed) {.pragma.}

View file

@ -27,7 +27,7 @@ suite "captures":
let ex2 = "foo".find(re("(?<foo>foo)(?<bar>bar)?")) let ex2 = "foo".find(re("(?<foo>foo)(?<bar>bar)?"))
check(ex2.captures["foo"] == "foo") check(ex2.captures["foo"] == "foo")
check(ex2.captures["bar"] == nil) check(ex2.captures["bar"] == "")
test "named capture bounds": test "named capture bounds":
let ex1 = "foo".find(re("(?<foo>foo)(?<bar>bar)?")) let ex1 = "foo".find(re("(?<foo>foo)(?<bar>bar)?"))
@ -41,7 +41,7 @@ suite "captures":
test "named capture table": test "named capture table":
let ex1 = "foo".find(re("(?<foo>foo)(?<bar>bar)?")) let ex1 = "foo".find(re("(?<foo>foo)(?<bar>bar)?"))
check(ex1.captures.toTable == {"foo" : "foo", "bar" : nil}.toTable()) check(ex1.captures.toTable == {"foo" : "foo", "bar" : ""}.toTable())
check(ex1.captureBounds.toTable == {"foo" : some(0..2), "bar" : none(Slice[int])}.toTable()) check(ex1.captureBounds.toTable == {"foo" : some(0..2), "bar" : none(Slice[int])}.toTable())
check(ex1.captures.toTable("") == {"foo" : "foo", "bar" : ""}.toTable()) check(ex1.captures.toTable("") == {"foo" : "foo", "bar" : ""}.toTable())
@ -50,7 +50,7 @@ suite "captures":
test "capture sequence": test "capture sequence":
let ex1 = "foo".find(re("(?<foo>foo)(?<bar>bar)?")) let ex1 = "foo".find(re("(?<foo>foo)(?<bar>bar)?"))
check(ex1.captures.toSeq == @["foo", nil]) check(ex1.captures.toSeq == @["foo", ""])
check(ex1.captureBounds.toSeq == @[some(0..2), none(Slice[int])]) check(ex1.captureBounds.toSeq == @[some(0..2), none(Slice[int])])
check(ex1.captures.toSeq("") == @["foo", ""]) check(ex1.captures.toSeq("") == @["foo", ""])

Some files were not shown because too many files have changed in this diff Show more