fixes merge conflict

This commit is contained in:
Andreas Rumpf 2018-08-19 15:14:03 +02:00
commit 696c0c48c7
195 changed files with 2049 additions and 1344 deletions

View file

@ -51,5 +51,3 @@ script:
- ./koch csource
- ./koch nimsuggest
# - 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

@ -55,6 +55,7 @@ build_script:
- nimble install jester@#head -y
- nimble install niminst
- nim c --taintMode:on -d:nimCoroutines tests/testament/tester
- nim c --taintMode:on -d:nimCoroutines --os:genode -d:posix --compileOnly tests/testament/tester
test_script:
- tests\testament\tester --pedantic all -d:nimCoroutines

31
build_all.sh Normal file
View file

@ -0,0 +1,31 @@
#! /bin/sh
# build development version of the compiler; can be rerun safely
set -u # error on undefined variables
set -e # exit on first error
echo_run(){
echo "\n$@"
"$@"
}
[ -d csources ] || echo_run git clone --depth 1 https://github.com/nim-lang/csources.git
nim_csources=bin/nim_csources
build_nim_csources(){
## avoid changing dir in case of failure
(
echo_run cd csources
echo_run sh build.sh
)
# keep $nim_csources in case needed to investigate bootstrap issues
# without having to rebuild from csources
echo_run cp bin/nim $nim_csources
}
[ -f $nim_csources ] || echo_run build_nim_csources
echo_run bin/nim c koch
echo_run ./koch boot -d:release
echo_run ./koch tools # Compile Nimble and other tools.

View file

@ -20,7 +20,7 @@
- The parser now warns about inconsistent spacing around binary operators as
these can easily be confused with unary operators. This warning will likely
become an error in the future.
- The ``'c`` and ``'C'`` prefix for octal literals is now deprecated to
- The ``'c`` and ``'C'`` suffix for octal literals is now deprecated to
bring the language in line with the standard library (e.g. ``parseOct``).
- The dot style for import paths (e.g ``import path.to.module`` instead of
``import path/to/module``) has been deprecated.
@ -37,9 +37,18 @@
strings anymore for its ``unit`` parameter. Instead the space is controlled
by a new parameter ``useUnitSpace``.
- The ``times.parse`` and ``times.format`` procs have been rewritten.
The proc signatures are the same so it should generally not break anything.
However, the new implementation is a bit stricter, which is a breaking change.
For example ``parse("2017-01-01 foo", "yyyy-MM-dd")`` will now raise an error.
- ``proc `-`*(a, b: Time): int64`` in the ``times`` module has changed return type
to ``times.Duration`` in order to support higher time resolutions.
The proc is no longer deprecated.
- The ``times.Timezone`` is now an immutable ref-type that must be initialized
with an explicit constructor (``newTimezone``).
- ``posix.Timeval.tv_sec`` has changed type to ``posix.Time``.
- ``math.`mod` `` for floats now behaves the same as ``mod`` for integers
@ -63,10 +72,17 @@
- ``lineInfoObj`` now returns absolute path instead of project path.
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
- The undocumented ``#? braces`` parsing mode was removed.
- The undocumented PHP backend was removed.
- The default location of ``nimcache`` for the native code targets was
changed. Read [the compiler user guide](https://nim-lang.org/docs/nimc.html#generated-c-code-directory)
for more information.
### Library additions
@ -88,9 +104,11 @@
- 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 ``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 ``MemMapFileStream``.
- Added ``macros.copyLineInfo`` to copy lineInfo from other node.
- Added ``system.ashr`` an arithmetic right shift for integers.
### Library changes
@ -129,7 +147,13 @@
- ``func`` is now an alias for ``proc {.noSideEffect.}``.
- In order to make ``for`` loops and iterators more flexible to use Nim now
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`.
- `static` and `type` are now also modifiers similar to `ref` and `ptr`.
They denote the special types `static[T]` and `type[T]`.
@ -150,7 +174,7 @@
More details in language manual.
- ``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
is now invalid. Internally a Nim string still has the trailing zero for
@ -160,11 +184,22 @@
- The command syntax now supports keyword arguments after the first comma.
- 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.
- 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.
- Range float types, example `range[0.0 .. Inf]`. More details in language manual.
### Tool changes
@ -173,7 +208,7 @@
### 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.
- Support for hot code reloading has been implemented for the JavaScript
@ -199,6 +234,16 @@
- 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 /
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.
- On Posix systems the global system wide configuration is now put under ``/etc/nim/nim.cfg``,
it used to be ``/etc/nim.cfg``. Usually it does not exist, however.
- On Posix systems the user configuration is now looked under ``$XDG_CONFIG_HOME/nim/nim.cfg``
(if ``XDG_CONFIG_HOME`` is not defined, then under ``~/.config/nim/nim.cfg``). It used to be
``$XDG_CONFIG_DIR/nim.cfg`` (and ``~/.config/nim.cfg``).
Similarly, on Windows, the user configuration is now looked under ``%APPDATA%/nim/nim.cfg``.
This used to be ``%APPDATA%/nim.cfg``.
### Bugfixes

View file

@ -591,7 +591,7 @@ type
mAddI, mSubI, mMulI, mDivI, mModI,
mSucc, mPred,
mAddF64, mSubF64, mMulF64, mDivF64,
mShrI, mShlI, mBitandI, mBitorI, mBitxorI,
mShrI, mShlI, mAshrI, mBitandI, mBitorI, mBitxorI,
mMinI, mMaxI,
mMinF64, mMaxF64,
mAddU, mSubU, mMulU, mDivU, mModU,
@ -972,8 +972,8 @@ const
tyFloat..tyFloat128, tyUInt..tyUInt64}
ConstantDataTypes*: TTypeKinds = {tyArray, tySet,
tyTuple, tySequence}
NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tySequence,
tyProc, tyString, tyError}
NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr,
tyProc, tyError}
ExportableSymKinds* = {skVar, skConst, skProc, skFunc, skMethod, skType,
skIterator,
skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias}
@ -1151,7 +1151,10 @@ proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) =
for i in countup(0, high(src.data)): dest.data[i] = src.data[i]
proc discardSons*(father: PNode) =
father.sons = nil
when defined(nimNoNilSeqs):
father.sons = @[]
else:
father.sons = nil
proc withInfo*(n: PNode, info: TLineInfo): PNode =
n.info = info
@ -1368,7 +1371,7 @@ proc createModuleAlias*(s: PSym, newIdent: PIdent, info: TLineInfo;
result.loc = s.loc
result.annex = s.annex
# XXX once usedGenerics is used, ensure module aliases keep working!
assert s.usedGenerics == nil
assert s.usedGenerics.len == 0
proc initStrTable*(x: var TStrTable) =
x.counter = 0
@ -1593,7 +1596,10 @@ proc getStr*(a: PNode): string =
of nkStrLit..nkTripleStrLit: result = a.strVal
of nkNilLit:
# let's hope this fixes more problems than it creates:
result = nil
when defined(nimNoNilSeqs):
result = ""
else:
result = nil
else:
doAssert false, "getStr"
#internalError(a.info, "getStr")
@ -1675,6 +1681,14 @@ proc skipStmtList*(n: PNode): PNode =
else:
result = n
proc toVar*(typ: PType): PType =
## If ``typ`` is not a tyVar then it is converted into a `var <typ>` and
## returned. Otherwise ``typ`` is simply returned as-is.
result = typ
if typ.kind != tyVar:
result = newType(tyVar, typ.owner)
rawAddSon(result, typ)
proc toRef*(typ: PType): PType =
## If ``typ`` is a tyObject then it is converted into a `ref <typ>` and
## returned. Otherwise ``typ`` is simply returned as-is.

View file

@ -412,6 +412,8 @@ proc debugTree(conf: ConfigRef; n: PNode, indent: int, maxRecDepth: int;
else:
addf(result, ",$N$1\"ident\": null", [istr])
else:
if renderType and n.typ != nil:
addf(result, ",$N$1\"typ\": $2", [istr, debugType(conf, n.typ, 2)])
if sonsLen(n) > 0:
addf(result, ",$N$1\"sons\": [", [istr])
for i in countup(0, sonsLen(n) - 1):

View file

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

View file

@ -65,9 +65,10 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
of tyString:
# with the new semantics for 'nil' strings, we can map "" to nil and
# save tons of allocations:
#if n.strVal.len == 0: result = genNilStringLiteral(p.module, n.info)
#else:
result = genStringLiteral(p.module, n)
if n.strVal.len == 0 and optNilSeqs notin p.options:
result = genNilStringLiteral(p.module, n.info)
else:
result = genStringLiteral(p.module, n)
else:
if n.strVal.isNil: result = rope("NIM_NIL")
else: result = makeCString(n.strVal)
@ -567,9 +568,9 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
"(($4)($1) - ($4)($2))", # SubF64
"(($4)($1) * ($4)($2))", # MulF64
"(($4)($1) / ($4)($2))", # DivF64
"($4)((NU$5)($1) >> (NU$3)($2))", # ShrI
"($4)((NU$3)($1) << (NU$3)($2))", # ShlI
"($4)((NI$3)($1) >> (NU$3)($2))", # AshrI
"($4)($1 & $2)", # BitandI
"($4)($1 | $2)", # BitorI
"($4)($1 ^ $2)", # BitxorI
@ -996,7 +997,7 @@ proc genEcho(p: BProc, n: PNode) =
# is threadsafe.
internalAssert p.config, n.kind == nkBracket
if p.config.target.targetOS == osGenode:
# bypass libc and print directly to the Genode LOG session
# echo directly to the Genode LOG session
var args: Rope = nil
var a: TLoc
for it in n.sons:
@ -1004,8 +1005,9 @@ proc genEcho(p: BProc, n: PNode) =
add(args, ", \"\"")
else:
initLocExpr(p, it, a)
add(args, ropecg(p.module, ", #nimToCStringConv($1)", [rdLoc(a)]))
add(args, ropecg(p.module, ", Genode::Cstring($1->data, $1->len)", [rdLoc(a)]))
p.module.includeHeader("<base/log.h>")
p.module.includeHeader("<util/string.h>")
linefmt(p, cpsStmts, """Genode::log(""$1);$n""", args)
else:
if n.len == 0:
@ -1112,8 +1114,8 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
initLoc(call, locCall, e, OnHeap)
call.r = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, rope(L)])
genAssignment(p, dest, call, {})
gcUsage(p.config, e)
add(p.s(cpsStmts), appends)
gcUsage(p.config, e)
proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
# seq &= x -->
@ -1201,7 +1203,7 @@ proc genNew(p: BProc, e: PNode) =
rawGenNew(p, a, nil)
gcUsage(p.config, e)
proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope) =
proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) =
let seqtype = skipTypes(dest.t, abstractVarRange)
let args = [getTypeDesc(p.module, seqtype),
genTypeInfo(p.module, seqtype, dest.lode.info), length]
@ -1212,10 +1214,14 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope) =
linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", dest.rdLoc)
else:
linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", dest.rdLoc)
call.r = ropecg(p.module, "($1) #newSeqRC1($2, $3)", args)
linefmt(p, cpsStmts, "$1 = $2;$n", dest.rdLoc, call.rdLoc)
if not lenIsZero:
call.r = ropecg(p.module, "($1) #newSeqRC1($2, $3)", args)
linefmt(p, cpsStmts, "$1 = $2;$n", dest.rdLoc, call.rdLoc)
else:
call.r = ropecg(p.module, "($1) #newSeq($2, $3)", args)
if lenIsZero:
call.r = rope"NIM_NIL"
else:
call.r = ropecg(p.module, "($1) #newSeq($2, $3)", args)
genAssignment(p, dest, call, {})
proc genNewSeq(p: BProc, e: PNode) =
@ -1228,7 +1234,9 @@ proc genNewSeq(p: BProc, e: PNode) =
a.rdLoc, b.rdLoc, getTypeDesc(p.module, seqtype.lastSon),
getSeqPayloadType(p.module, seqtype))
else:
genNewSeqAux(p, a, b.rdLoc)
let lenIsZero = optNilSeqs notin p.options and
e[2].kind == nkIntLit and e[2].intVal == 0
genNewSeqAux(p, a, b.rdLoc, lenIsZero)
gcUsage(p.config, e)
proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) =
@ -1328,7 +1336,8 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) =
elif d.k == locNone:
getTemp(p, n.typ, d)
# generate call to newSeq before adding the elements per hand:
genNewSeqAux(p, dest[], intLiteral(sonsLen(n)))
genNewSeqAux(p, dest[], intLiteral(sonsLen(n)),
optNilSeqs notin p.options and n.len == 0)
for i in countup(0, sonsLen(n) - 1):
initLoc(arr, locExpr, n[i], OnHeap)
arr.r = ropecg(p.module, "$1$3[$2]", rdLoc(dest[]), intLiteral(i), dataField(p))
@ -1351,7 +1360,7 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) =
getTemp(p, n.typ, d)
# generate call to newSeq before adding the elements per hand:
let L = int(lengthOrd(p.config, n.sons[1].typ))
genNewSeqAux(p, d, intLiteral(L))
genNewSeqAux(p, d, intLiteral(L), optNilSeqs notin p.options and L == 0)
initLocExpr(p, n.sons[1], a)
# bug #5007; do not produce excessive C source code:
if L < 10:

View file

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

View file

@ -256,7 +256,15 @@ proc genSingleVar(p: BProc, a: PNode) =
# That's why we are doing the construction inside the preInitProc.
# genObjectInit relies on the C runtime's guarantees that
# global variables will be initialized to zero.
genObjectInit(p.module.preInitProc, cpsInit, v.typ, v.loc, true)
var loc = v.loc
# When the native TLS is unavailable, a global thread-local variable needs
# one more layer of indirection in order to access the TLS block.
# Only do this for complex types that may need a call to `objectInit`
if sfThread in v.flags and emulatedThreadVars(p.config) and
isComplexValueType(v.typ):
initLocExprSingleUse(p.module.preInitProc, vn, loc)
genObjectInit(p.module.preInitProc, cpsInit, v.typ, loc, true)
# Alternative construction using default constructor (which may zeromem):
# if sfImportc notin v.flags: constructLoc(p.module.preInitProc, v.loc)
if sfExportc in v.flags and p.module.g.generatedHeader != nil:
@ -1130,8 +1138,8 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
patchAsgnStmtListExpr(patchedTree, e, ri)
genStmts(p, patchedTree)
return
var a: TLoc
discard getTypeDesc(p.module, le.typ.skipTypes(skipPtrs))
if le.kind in {nkDerefExpr, nkHiddenDeref}:
genDeref(p, le, a, enforceDeref=true)
else:

View file

@ -1147,12 +1147,29 @@ proc genInitCode(m: BModule) =
appcg(m, m.s[cfsTypeInit1], "static #TNimType $1[$2];$n",
[m.nimTypesName, rope(m.nimTypes)])
# Give this small function its own scope
addf(prc, "{$N", [])
block:
# Keep a bogus frame in case the code needs one
add(prc, ~"\tTFrame FR_; FR_.len = 0;$N")
add(prc, genSectionStart(cpsLocals, m.config))
add(prc, m.preInitProc.s(cpsLocals))
add(prc, genSectionEnd(cpsLocals, m.config))
add(prc, genSectionStart(cpsInit, m.config))
add(prc, m.preInitProc.s(cpsInit))
add(prc, genSectionEnd(cpsInit, m.config))
add(prc, genSectionStart(cpsStmts, m.config))
add(prc, m.preInitProc.s(cpsStmts))
add(prc, genSectionEnd(cpsStmts, m.config))
addf(prc, "}$N", [])
add(prc, initGCFrame(m.initProc))
add(prc, genSectionStart(cpsLocals, m.config))
add(prc, m.preInitProc.s(cpsLocals))
add(prc, m.initProc.s(cpsLocals))
add(prc, m.postInitProc.s(cpsLocals))
add(prc, genSectionEnd(cpsLocals, m.config))
if optStackTrace in m.initProc.options and frameDeclared notin m.flags:
@ -1166,16 +1183,13 @@ proc genInitCode(m: BModule) =
add(prc, ~"\tTFrame FR_; FR_.len = 0;$N")
add(prc, genSectionStart(cpsInit, m.config))
add(prc, m.preInitProc.s(cpsInit))
add(prc, m.initProc.s(cpsInit))
add(prc, m.postInitProc.s(cpsInit))
add(prc, genSectionEnd(cpsInit, m.config))
add(prc, genSectionStart(cpsStmts, m.config))
add(prc, m.preInitProc.s(cpsStmts))
add(prc, m.initProc.s(cpsStmts))
add(prc, m.postInitProc.s(cpsStmts))
add(prc, genSectionEnd(cpsStmts, m.config))
if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
add(prc, deinitFrame(m.initProc))
add(prc, deinitGCFrame(m.initProc))
@ -1221,11 +1235,6 @@ proc newPreInitProc(m: BModule): BProc =
# little hack so that unique temporaries are generated:
result.labels = 100_000
proc newPostInitProc(m: BModule): BProc =
result = newProc(nil, m)
# little hack so that unique temporaries are generated:
result.labels = 200_000
proc initProcOptions(m: BModule): TOptions =
let opts = m.config.options
if sfSystemModule in m.module.flags: opts-{optStackTrace} else: opts
@ -1247,7 +1256,6 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: string): BModule =
result.initProc = newProc(nil, result)
result.initProc.options = initProcOptions(result)
result.preInitProc = newPreInitProc(result)
result.postInitProc = newPostInitProc(result)
initNodeTable(result.dataCache)
result.typeStack = @[]
result.forwardedProcs = @[]
@ -1258,7 +1266,6 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: string): BModule =
if sfSystemModule in module.flags:
incl result.flags, preventStackTrace
excl(result.preInitProc.options, optStackTrace)
excl(result.postInitProc.options, optStackTrace)
let ndiName = if optCDebug in g.config.globalOptions: changeFileExt(completeCFilePath(g.config, filename), "ndi")
else: ""
open(result.ndi, ndiName, g.config)
@ -1276,7 +1283,6 @@ proc resetModule*(m: BModule) =
m.initProc = newProc(nil, m)
m.initProc.options = initProcOptions(m)
m.preInitProc = newPreInitProc(m)
m.postInitProc = newPostInitProc(m)
initNodeTable(m.dataCache)
m.typeStack = @[]
m.forwardedProcs = @[]

View file

@ -147,7 +147,6 @@ type
headerFiles*: seq[string] # needed headers to include
typeInfoMarker*: TypeCache # needed for generating type information
initProc*: BProc # code for init procedure
postInitProc*: BProc # code to be executed after the init proc
preInitProc*: BProc # code executed before the init proc
typeStack*: TTypeSeq # used for type generation
dataCache*: TNodeTable

View file

@ -279,6 +279,7 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
of "implicitstatic": result = contains(conf.options, optImplicitStatic)
of "patterns": result = contains(conf.options, optPatterns)
of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace)
of "nilseqs": result = contains(conf.options, optNilSeqs)
else: invalidCmdLineOption(conf, passCmd1, switch, info)
proc processPath(conf: ConfigRef; path: string, info: TLineInfo,
@ -497,6 +498,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
else:
localError(conf, info, errOnOrOffExpectedButXFound % arg)
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 "floatchecks":
processOnOffSwitch(conf, {optNaNCheck, optInfCheck}, arg, pass, info)

View file

@ -12,6 +12,8 @@
import
strtabs, platform, strutils, idents
from options import Feature
const
catNone = "false"
@ -74,3 +76,10 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimVmExportFixed")
defineSymbol("nimNewRuntime")
defineSymbol("nimIncrSeqV3")
defineSymbol("nimAshr")
defineSymbol("nimNoNilSeqs")
defineSymbol("nimNoNilSeqs2")
defineSymbol("nimHasNilSeqs")
for f in low(Feature)..high(Feature):
defineSymbol("nimHas" & $f)

View file

@ -191,7 +191,7 @@ proc ropeFormatNamedVars(conf: ConfigRef; frmt: FormatStr,
proc genComment(d: PDoc, n: PNode): string =
result = ""
var dummyHasToc: bool
if n.comment != nil:
if n.comment.len > 0:
renderRstToOut(d[], parseRst(n.comment, toFilename(d.conf, n.info),
toLinenumber(n.info), toColumn(n.info),
dummyHasToc, d.options, d.conf), result)
@ -205,7 +205,8 @@ proc genRecComment(d: PDoc, n: PNode): Rope =
result = genRecComment(d, n.sons[i])
if result != nil: return
else:
n.comment = nil
when defined(nimNoNilSeqs): n.comment = ""
else: n.comment = nil
proc getPlainDocstring(n: PNode): string =
## 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.
result = ""
if n == nil: return
if n.comment != nil and startsWith(n.comment, "##"):
if startsWith(n.comment, "##"):
result = n.comment
if result.len < 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,
"col": %n.info.col}
if comm != nil and comm != "":
if comm.len > 0:
result["description"] = %comm
if r.buf != nil:
if r.buf.len > 0:
result["code"] = %r.buf
proc checkForFalse(n: PNode): bool =
@ -634,7 +635,7 @@ proc add(d: PDoc; j: JsonNode) =
proc generateJson*(d: PDoc, n: PNode) =
case n.kind
of nkCommentStmt:
if n.comment != nil and startsWith(n.comment, "##"):
if startsWith(n.comment, "##"):
let stripped = n.comment.substr(2).strip
d.add %{ "comment": %stripped, "line": %n.info.line.int,
"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) =
case n.kind
of nkCommentStmt:
if n.comment != nil and startsWith(n.comment, "##"):
if startsWith(n.comment, "##"):
let stripped = n.comment.substr(2).strip
r.add stripped
of nkProcDef:

View file

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

View file

@ -379,6 +379,7 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
["", "", "($1 / $2)", "($1 / $2)"], # DivF64
["", "", "", ""], # ShrI
["", "", "($1 << $2)", "($1 << $2)"], # ShlI
["", "", "($1 >> $2)", "($1 >> $2)"], # AshrI
["", "", "($1 & $2)", "($1 & $2)"], # BitandI
["", "", "($1 | $2)", "($1 | $2)"], # BitorI
["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI
@ -1266,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 sfInfixCall in f.flags:
let pat = n.sons[0].sym.loc.r.data
internalAssert p.config, pat != nil
internalAssert p.config, pat.len > 0
if pat.contains({'#', '(', '@'}):
var typ = skipTypes(n.sons[0].typ, abstractInst)
assert(typ.kind == tyProc)
@ -1349,7 +1350,7 @@ proc arrayTypeForElemType(typ: PType): string =
of tyUint8: "Uint8Array"
of tyFloat32: "Float32Array"
of tyFloat64, tyFloat: "Float64Array"
else: nil
else: ""
proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
var t = skipTypes(typ, abstractInst)

View file

@ -92,7 +92,7 @@ const
warnResultShadowed: "Special variable 'result' is shadowed.",
warnInconsistentSpacing: "Number of spaces around '$#' is not consistent",
warnUser: "$1",
hintSuccess: "operation successful",
hintSuccess: "operation successful: $#",
hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)",
hintCC: "CC: \'$1\'", # unused
hintLineTooLong: "line too long",
@ -164,13 +164,13 @@ type
TNoteKinds* = set[TNoteKind]
proc computeNotesVerbosity(): array[0..3, TNoteKinds] =
result[3] = {low(TNoteKind)..high(TNoteKind)} - {}
result[2] = result[3] - {hintStackTrace, warnUninit, hintExtendedContext}
result[1] = result[2] - {warnShadowIdent, warnProveField, warnProveIndex,
warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
hintSource, hintGlobalVar, hintGCStats}
result[0] = result[1] - {hintSuccessX, hintConf, hintProcessing,
hintPattern, hintExecuting, hintLinking}
result[3] = {low(TNoteKind)..high(TNoteKind)} - {}
result[2] = result[3] - {hintStackTrace, warnUninit, hintExtendedContext}
result[1] = result[2] - {warnShadowIdent, warnProveField, warnProveIndex,
warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
hintSource, hintGlobalVar, hintGCStats}
result[0] = result[1] - {hintSuccessX, hintSuccess, hintConf,
hintProcessing, hintPattern, hintExecuting, hintLinking}
const
NotesVerbosity* = computeNotesVerbosity()

View file

@ -11,7 +11,7 @@
import
intsets, ast, astalgo, idents, semdata, types, msgs, options,
renderer, wordrecg, idgen, nimfix.prettybase, lineinfos, strutils
renderer, wordrecg, idgen, nimfix/prettybase, lineinfos, strutils
proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope)
@ -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 c.recursiveDep
# prevent excessive errors for 'nim check'
c.recursiveDep = nil
c.recursiveDep = ""
localError(c.config, info, errGenerated, err)
proc lookUp*(c: PContext, n: PNode): PSym =

View file

@ -65,10 +65,8 @@ type
proc hash*(x: FileIndex): Hash {.borrow.}
{.this: g.}
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 =
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]]()
proc resetAllModules*(g: ModuleGraph) =
initStrTable(packageSyms)
deps = initIntSet()
modules = @[]
importStack = @[]
inclToMod = initTable[FileIndex, FileIndex]()
usageSym = nil
owners = @[]
methods = @[]
initStrTable(compilerprocs)
initStrTable(exposed)
initStrTable(g.packageSyms)
g.deps = initIntSet()
g.modules = @[]
g.importStack = @[]
g.inclToMod = initTable[FileIndex, FileIndex]()
g.usageSym = nil
g.owners = @[]
g.methods = @[]
initStrTable(g.compilerprocs)
initStrTable(g.exposed)
proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym =
if fileIdx.int32 >= 0 and fileIdx.int32 < modules.len:
result = modules[fileIdx.int32]
if fileIdx.int32 >= 0 and fileIdx.int32 < g.modules.len:
result = g.modules[fileIdx.int32]
proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b
proc addDep*(g: ModuleGraph; m: PSym, dep: FileIndex) =
assert m.position == m.info.fileIndex.int32
addModuleDep(g.incr, g.config, m.info.fileIndex, dep, isIncludeFile = false)
if suggestMode:
deps.incl m.position.dependsOn(dep.int)
if g.suggestMode:
g.deps.incl m.position.dependsOn(dep.int)
# we compute the transitive closure later when quering the graph lazily.
# this improves efficiency quite a lot:
#invalidTransitiveClosure = true
proc addIncludeDep*(g: ModuleGraph; module, includeFile: FileIndex) =
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 =
## returns 'fileIdx' if the file belonging to this index is
## directly used as a module or else the module that first
## 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
else:
result = inclToMod.getOrDefault(fileIdx)
result = g.inclToMod.getOrDefault(fileIdx)
proc transitiveClosure(g: var IntSet; n: int) =
# warshall's algorithm
@ -147,22 +145,22 @@ proc transitiveClosure(g: var IntSet; n: int) =
g.incl i.dependsOn(j)
proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) =
let m = getModule fileIdx
let m = g.getModule fileIdx
if m != nil: incl m.flags, sfDirty
proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) =
# 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
# cleared but D still needs to be remembered as 'dirty'.
if invalidTransitiveClosure:
invalidTransitiveClosure = false
transitiveClosure(deps, modules.len)
if g.invalidTransitiveClosure:
g.invalidTransitiveClosure = false
transitiveClosure(g.deps, g.modules.len)
# every module that *depends* on this file is also dirty:
for i in 0i32..<modules.len.int32:
let m = modules[i]
if m != nil and deps.contains(i.dependsOn(fileIdx.int)):
for i in 0i32..<g.modules.len.int32:
let m = g.modules[i]
if m != nil and g.deps.contains(i.dependsOn(fileIdx.int)):
incl m.flags, sfDirty
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:
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 kind != nil:
if kind.len > 0:
styledMsgWriteln(color, title, resetStyle, s,
KindColor, `%`(KindFormat, kind))
else:
@ -483,9 +483,9 @@ proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
if not ignoreMsg:
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 kind != nil:
if kind.len > 0:
styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s,
KindColor, `%`(KindFormat, kind))
else:

View file

@ -220,7 +220,7 @@ proc readConfigFile(
return true
proc getUserConfigPath(filename: string): string =
result = joinPath(getConfigDir(), filename)
result = joinPath([getConfigDir(), "nim", filename])
proc getSystemConfigPath(conf: ConfigRef; filename: string): string =
# try standard configuration file (installation did not distribute files
@ -228,8 +228,8 @@ proc getSystemConfigPath(conf: ConfigRef; filename: string): string =
let p = getPrefixDir(conf)
result = joinPath([p, "config", filename])
when defined(unix):
if not existsFile(result): result = joinPath([p, "etc", filename])
if not existsFile(result): result = "/etc/" & filename
if not existsFile(result): result = joinPath([p, "etc/nim", filename])
if not existsFile(result): result = "/etc/nim/" & filename
proc loadConfigs*(cfg: string; cache: IdentCache; conf: ConfigRef) =
setDefaultLibpath(conf)

View file

@ -38,7 +38,8 @@ type # please make sure we have under 32 options
optPatterns, # en/disable pattern matching
optMemTracker,
optHotCodeReloading,
optLaxStrings
optLaxStrings,
optNilSeqs
TOptions* = set[TOption]
TGlobalOption* = enum # **keep binary compatible**
@ -110,14 +111,16 @@ type
ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod,
ideHighlight, ideOutline, ideKnown, ideMsg
Feature* = enum ## experimental features
Feature* = enum ## experimental features; DO NOT RENAME THESE!
implicitDeref,
dotOperators,
callOperator,
parallel,
destructor,
notnil,
dynamicBindSym
dynamicBindSym,
forLoopMacros,
caseStmtMacros
SymbolFilesOption* = enum
disabledSf, writeOnlySf, readOnlySf, v2Sf
@ -354,6 +357,7 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool =
of "msdos": result = conf.target.targetOS == osDos
of "mswindows", "win32": result = conf.target.targetOS == osWindows
of "macintosh": result = conf.target.targetOS in {osMacos, osMacosx}
of "osx": result = conf.target.targetOS == osMacosx
of "sunos": result = conf.target.targetOS == osSolaris
of "nintendoswitch":
result = conf.target.targetOS == osNintendoSwitch
@ -479,9 +483,20 @@ proc disableNimblePath*(conf: ConfigRef) =
include packagehandling
proc getOsCacheDir(): string =
when defined(posix):
result = getEnv("XDG_CACHE_HOME", getHomeDir() / ".cache") / "nim"
else:
result = getHomeDir() / genSubDir
proc getNimcacheDir*(conf: ConfigRef): string =
result = if conf.nimcacheDir.len > 0: conf.nimcacheDir
else: shortenDir(conf, conf.projectPath) / genSubDir
# XXX projectName should always be without a file extension!
result = if conf.nimcacheDir.len > 0:
conf.nimcacheDir
elif conf.cmd == cmdCompileToJS:
shortenDir(conf, conf.projectPath) / genSubDir
else: getOsCacheDir() / splitFile(conf.projectName).name &
(if isDefined(conf, "release"): "_r" else: "_d")
proc pathSubs*(conf: ConfigRef; p, config: string): string =
let home = removeTrailingDirSep(os.getHomeDir())
@ -588,18 +603,22 @@ proc findModule*(conf: ConfigRef; modulename, currentModule: string): string =
proc findProjectNimFile*(conf: ConfigRef; pkg: string): string =
const extensions = [".nims", ".cfg", ".nimcfg", ".nimble"]
var candidates: seq[string] = @[]
for k, f in os.walkDir(pkg, relative=true):
if k == pcFile and f != "config.nims":
let (_, name, ext) = splitFile(f)
if ext in extensions:
let x = changeFileExt(pkg / name, ".nim")
if fileExists(x):
candidates.add x
for c in candidates:
# nim-foo foo or foo nfoo
if (pkg in c) or (c in pkg): return c
if candidates.len >= 1:
return candidates[0]
var dir = pkg
while true:
for k, f in os.walkDir(dir, relative=true):
if k == pcFile and f != "config.nims":
let (_, name, ext) = splitFile(f)
if ext in extensions:
let x = changeFileExt(dir / name, ".nim")
if fileExists(x):
candidates.add x
for c in candidates:
# nim-foo foo or foo nfoo
if (pkg in c) or (c in pkg): return c
if candidates.len >= 1:
return candidates[0]
dir = parentDir(dir)
if dir == "": break
return ""
proc canonDynlibName(s: string): string =

View file

@ -217,7 +217,7 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult
if n.typ != nil and n.typ.kind == tyVar:
result = arLValue
of nkSym:
let kinds = if isUnsafeAddr: {skVar, skResult, skTemp, skParam, skLet}
let kinds = if isUnsafeAddr: {skVar, skResult, skTemp, skParam, skLet, skForVar}
else: {skVar, skResult, skTemp}
if n.sym.kind in kinds:
if owner != nil and owner == n.sym.owner and

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) =
if p.tok.tokType == tkComment:
if node != nil:
if node.comment == nil: node.comment = ""
when not defined(nimNoNilSeqs):
if node.comment == nil: node.comment = ""
when defined(nimpretty):
if p.tok.commentOffsetB > p.tok.commentOffsetA:
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
c: PContext
subMatch: bool # subnode matches are special
mappingIsFull: bool
PPatternContext = var TPatternContext
proc getLazy(c: PPatternContext, sym: PSym): PNode =
if not isNil(c.mapping):
if c.mappingIsFull:
result = c.mapping[sym.position]
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
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:
if not matches(c, p.sons[j], n.sons[i+j]):
# 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
result = true

View file

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

View file

@ -281,7 +281,7 @@ const
proc shouldRenderComment(g: var TSrcGen, n: PNode): bool =
result = false
if n.comment != nil:
if n.comment.len > 0:
result = (renderNoComments notin g.flags) or
(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 =
# computes the length of a tree
if isNil(n): return 0
if n.comment != nil: return MaxLineLen + 1
if n.comment.len > 0: return MaxLineLen + 1
case n.kind
of nkEmpty: result = 0
of nkTripleStrLit:
@ -500,7 +500,7 @@ proc lsub(g: TSrcGen; n: PNode): int =
of nkBreakStmt: result = lsub(g, n.sons[0]) + len("break_")
of nkContinueStmt: result = lsub(g, n.sons[0]) + len("continue_")
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 nkImportAs: result = lsub(g, n.sons[0]) + len("_as_") + lsub(g, n.sons[1])
of nkElifBranch: result = lsons(g, n) + len("elif_:_")
@ -539,7 +539,7 @@ proc gsub(g: var TSrcGen, n: PNode) =
proc hasCom(n: PNode): bool =
result = false
if n.isNil: return false
if n.comment != nil: return true
if n.comment.len > 0: return true
case n.kind
of nkEmpty..nkNilLit: discard
else:
@ -602,7 +602,7 @@ proc gsection(g: var TSrcGen, n: PNode, c: TContext, kind: TTokType,
dedent(g)
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:
# check further
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) =
if n.kind == nkStmtListExpr:
put(g, tkParLe, "(")
gsub(g, n)
gsub(g, n)
if n.kind == nkStmtListExpr:
put(g, tkParRi, ")")
@ -864,7 +864,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
if isNil(n): return
var
a: TContext
if n.comment != nil: pushCom(g, n)
if n.comment.len > 0: pushCom(g, n)
case n.kind # atoms:
of nkTripleStrLit: put(g, tkTripleStrLit, atom(g, n))
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
else: nil
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:
n_next = n_next[0]
if n_next.kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):

View file

@ -10,6 +10,27 @@
## Serialization utilities for the compiler.
import strutils, math
# MSVC prior to 2013 doesn't have C99 functions
when defined(windows) and (defined(vcc) or defined(bcc)):
{.emit: """#if defined(_MSC_VER) && _MSC_VER < 1900
#include <stdarg.h>
static int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) {
int count = -1;
if (size != 0) count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
if (count == -1) count = _vscprintf(format, ap);
return count;
}
int snprintf(char *outBuf, size_t size, const char *format, ...) {
int count;
va_list ap;
va_start(ap, format);
count = c99_vsnprintf(outBuf, size, format, ap);
va_end(ap);
return count;
}
#endif
""".}
proc c_snprintf(s: cstring; n:uint; frmt: cstring): cint {.importc: "snprintf", header: "<stdio.h>", nodecl, varargs.}
proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string =

View file

@ -66,29 +66,19 @@ type
Rope* = ref RopeObj
RopeObj*{.acyclic.} = object of RootObj # the empty rope is represented
# by nil to safe space
left*, right*: Rope
length*: int
data*: string # != nil if a leaf
left, right: Rope
L: int # <= 0 if a leaf
data*: string
proc len*(a: Rope): int =
## the rope's length
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)
if data != nil:
result.length = len(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
result.L = -len(data)
result.data = data
var
cache: array[0..2048*2 - 1, Rope] # XXX Global here!
@ -147,7 +137,7 @@ proc `&`*(a, b: Rope): Rope =
result = a
else:
result = newRope()
result.length = a.length + b.length
result.L = abs(a.L) + abs(b.L)
result.left = a
result.right = b

View file

@ -45,7 +45,7 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
template cbos(name, body) {.dirty.} =
result.registerCallback "stdlib.system." & astToStr(name),
proc (a: VmArgs) =
errorMsg = nil
errorMsg = ""
try:
body
except OSError:
@ -159,8 +159,11 @@ proc runNimScript*(cache: IdentCache; scriptName: string;
defineSymbol(conf.symbols, "nimscript")
defineSymbol(conf.symbols, "nimconfig")
registerPass(graph, semPass)
registerPass(graph, evalPass)
var registeredPasses {.global.} = false
if not registeredPasses:
registerPass(graph, semPass)
registerPass(graph, evalPass)
registeredPasses = true
conf.searchPaths.add(conf.libpath)

View file

@ -16,12 +16,12 @@ import
procfind, lookups, pragmas, passes, semdata, semtypinst, sigmatch,
intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity,
semparallel, lowerings, pluginsupport, plugins.active, rod, lineinfos
semparallel, lowerings, pluginsupport, plugins/active, rod, lineinfos
from modulegraphs import ModuleGraph
when defined(nimfix):
import nimfix.prettybase
import nimfix/prettybase
# implementation
@ -119,7 +119,7 @@ proc commonType*(x, y: PType): PType =
elif b.kind == tyStmt: result = b
elif a.kind == tyTypeDesc:
# turn any concrete typedesc into the abstract typedesc type
if a.sons == nil: result = a
if a.len == 0: result = a
else:
result = newType(tyTypeDesc, a.owner)
rawAddSon(result, newType(tyNone, a.owner))
@ -621,7 +621,8 @@ proc testExamples(c: PContext) =
if os.execShellCmd(os.getAppFilename() & " " & backend & " --nimcache:" & nimcache & " -r " & outp) != 0:
quit "[Examples] failed: see " & outp
else:
removeFile(outp)
# keep generated source file `outp` to allow inspection.
rawMessage(c.config, hintSuccess, ["runnableExamples: " & outp])
removeFile(outp.changeFileExt(ExeExt))
try:
removeDir(nimcache)

View file

@ -89,7 +89,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
continue
determineType(c, sym)
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)
if z.state == csMatch:
#if sym.name.s == "==" and (n.info ?? "temp3"):
@ -237,7 +237,7 @@ proc bracketNotFoundError(c: PContext; n: PNode) =
if symx.kind in routineKinds:
errors.add(CandidateError(sym: symx,
unmatchedVarParam: 0, firstMismatch: 0,
diagnostics: nil,
diagnostics: @[],
enabled: false))
symx = nextOverloadIter(o, c, headSymbol)
if errors.len == 0:
@ -455,7 +455,7 @@ proc tryDeref(n: PNode): PNode =
proc semOverloadedCall(c: PContext, n, nOrig: 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)
if r.state == csMatch:
# this may be triggered, when the explain pragma is used

View file

@ -37,6 +37,7 @@ type
# in standalone ``except`` and ``finally``
next*: PProcCon # used for stacking procedure contexts
wasForwarded*: bool # whether the current proc has a separate header
mappingExists*: bool
mapping*: TIdTable
TMatchedConcept* = object
@ -176,12 +177,14 @@ proc lastOptionEntry*(c: PContext): POptionEntry =
proc popProcCon*(c: PContext) {.inline.} = c.p = c.p.next
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
p.mapping.idTablePut(key, val)
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))
proc getGenSym*(c: PContext; s: PSym): PSym =

View file

@ -268,6 +268,14 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
of tyInt64, tyInt, tyUInt..tyUInt64:
result = newIntNodeT(`shr`(getInt(a), getInt(b)), n, g)
else: internalError(g.config, n.info, "constant folding for shr")
of mAshrI:
case skipTypes(n.typ, abstractRange).kind
of tyInt8: result = newIntNodeT(ashr(int8(getInt(a)), int8(getInt(b))), n, g)
of tyInt16: result = newIntNodeT(ashr(int16(getInt(a)), int16(getInt(b))), n, g)
of tyInt32: result = newIntNodeT(ashr(int32(getInt(a)), int32(getInt(b))), n, g)
of tyInt64, tyInt:
result = newIntNodeT(ashr(getInt(a), getInt(b)), n, g)
else: internalError(g.config, n.info, "constant folding for ashr")
of mDivI: result = foldDiv(getInt(a), getInt(b), n, g)
of mModI: result = foldMod(getInt(a), getInt(b), n, g)
of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n, g)

View file

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

View file

@ -88,7 +88,7 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
else:
globalError(conf, n.info, "string literal must be of some string type")
of nkNilLit:
if x.kind in NilableTypes:
if x.kind in NilableTypes+{tyString, tySequence}:
n.typ = t
else:
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 != {}:
let assignment = locateFieldInInitExpr(c, r.sym, initExpr)
if assignment == nil:
if result == nil:
if result.len == 0:
result = r.sym.name.s
else:
result.add ", "
@ -129,7 +129,7 @@ proc missingMandatoryFields(c: PContext, fieldsRecList, initExpr: PNode): string
proc checkForMissingFields(c: PContext, recList, initExpr: PNode) =
let missing = missingMandatoryFields(c, recList, initExpr)
if missing != nil:
if missing.len > 0:
localError(c.config, initExpr.info, "fields not initialized: $1.", [missing])
proc semConstructFields(c: PContext, recNode: PNode,

View file

@ -178,71 +178,6 @@ proc semIf(c: PContext, n: PNode): PNode =
result.kind = nkIfExpr
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 =
var check = initIntSet()
@ -683,29 +618,28 @@ proc isTrivalStmtExpr(n: PNode): bool =
return false
result = true
proc handleForLoopMacro(c: PContext; n: PNode): PNode =
let iterExpr = n[^2]
if iterExpr.kind in nkCallKinds:
proc handleStmtMacro(c: PContext; n, selector: PNode; magicType: string): PNode =
if selector.kind in nkCallKinds:
# we transform
# n := for a, b, c in m(x, y, z): Y
# to
# m(n)
let forLoopStmt = magicsys.getCompilerProc(c.graph, "ForLoopStmt")
if forLoopStmt == nil: return
let maType = magicsys.getCompilerProc(c.graph, magicType)
if maType == nil: return
let headSymbol = iterExpr[0]
let headSymbol = selector[0]
var o: TOverloadIter
var match: PSym = nil
var symx = initOverloadIter(o, c, headSymbol)
while symx != nil:
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:
match = symx
else:
localError(c.config, n.info, errAmbiguousCallXYZ % [
getProcHeader(c.config, match),
getProcHeader(c.config, symx), $iterExpr])
getProcHeader(c.config, symx), $selector])
symx = nextOverloadIter(o, c, headSymbol)
if match == nil: return
@ -717,11 +651,44 @@ proc handleForLoopMacro(c: PContext; n: PNode): PNode =
of skTemplate: result = semTemplateExpr(c, callExpr, match, {})
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 =
checkMinSonsLen(n, 3, c.config)
var length = sonsLen(n)
result = handleForLoopMacro(c, n)
if result != nil: return result
if forLoopMacros in c.features:
result = handleForLoopMacro(c, n)
if result != nil: return result
openScope(c)
result = n
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
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 =
result = n
checkSonsLen(n, 1, c.config)
@ -970,7 +1006,10 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
var body = s.typ.lastSon
if body.kind == tyObject:
# erases all declared fields
body.n.sons = nil
when defined(nimNoNilSeqs):
body.n.sons = @[]
else:
body.n.sons = nil
popOwner(c)
closeScope(c)

View file

@ -196,6 +196,8 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
if region.skipTypes({tyGenericInst, tyAlias, tySink}).kind notin {
tyError, tyObject}:
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, t)
if tfPartial in result.flags:

View file

@ -40,8 +40,8 @@ proc searchInstTypes*(key: PType): PType =
if not (genericTyp.kind == tyGenericBody and
key.sons[0] == genericTyp and genericTyp.sym != nil): return
if genericTyp.sym.typeInstCache == nil:
return
when not defined(nimNoNilSeqs):
if genericTyp.sym.typeInstCache == nil: return
for inst in genericTyp.sym.typeInstCache:
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
else:
c.calleeScope = calleeScope
c.diagnostics = if diagnosticsEnabled: @[] else: nil
c.diagnostics = @[] # if diagnosticsEnabled: @[] else: nil
c.diagnosticsEnabled = diagnosticsEnabled
c.magic = c.calleeSym.magic
initIdTable(c.bindings)
@ -535,6 +535,12 @@ proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
proc allowsNil(f: PType): TTypeRelation {.inline.} =
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.} =
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 = @[]
flags = {efExplain}
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)
if oldWriteHook != nil: oldWriteHook msg
diagnostics.add msg
@ -858,6 +864,10 @@ proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool =
if lhs[2].kind == nkIntLit:
return inferStaticParam(c, lhs[1], rhs shl lhs[2].intVal)
of mAshrI:
if lhs[2].kind == nkIntLit:
return inferStaticParam(c, lhs[1], ashr(rhs, lhs[2].intVal))
of mUnaryMinusI:
return inferStaticParam(c, lhs[1], -rhs)
@ -1249,7 +1259,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
result = isNone
elif tfNotNil in f.flags and tfNotNil notin a.flags:
result = isNilConversion
of tyNil: result = f.allowsNil
of tyNil: result = allowsNilDeprecated(c, f)
else: discard
of tyOrdinal:
if isOrdinalType(a):
@ -1334,7 +1344,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
result = isNilConversion
else:
result = isEqual
of tyNil: result = f.allowsNil
of tyNil: result = allowsNilDeprecated(c, f)
else: discard
of tyCString:
# conversion from string to cstring is automatic:
@ -1608,7 +1618,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
if f.sonsLen == 0:
result = isGeneric
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
var aa = a
while aa.kind in {tyTypeDesc, tyGenericParam} and aa.len > 0:
@ -1806,6 +1816,10 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
let srca = typeRel(m, src, a)
if srca notin {isEqual, isGeneric, isSubtype}: continue
let constraint = c.converters[i].typ.n[1].sym.constraint
if not constraint.isNil and not matchNodeKinds(constraint, arg):
continue
let destIsGeneric = containsGenericType(dest)
if destIsGeneric:
dest = generateTypeInstance(c, m.bindings, arg, dest)

View file

@ -369,6 +369,8 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
result = PTransNode(n.sons[0])
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
PNode(result).typ = n.typ
elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
PNode(result).typ = toVar(PNode(result).typ)
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
var m = n.sons[0].sons[1]
if m.kind == a or m.kind == b:
@ -377,6 +379,8 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
result = PTransNode(n.sons[0])
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
PNode(result).typ = n.typ
elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
PNode(result).typ = toVar(PNode(result).typ)
else:
if n.sons[0].kind == a or n.sons[0].kind == b:
# addr ( deref ( x )) --> x
@ -539,11 +543,8 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
if call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
call.sons[0].typ.callConv == ccClosure:
n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode
if not c.tooEarly:
n.sons[length-2] = transform(c, n.sons[length-2]).PNode
result[1] = lambdalifting.liftForLoop(c.graph, n, getCurrOwner(c)).PTransNode
else:
result[1] = newNode(nkEmpty).PTransNode
n.sons[length-2] = transform(c, n.sons[length-2]).PNode
result[1] = lambdalifting.liftForLoop(c.graph, n, getCurrOwner(c)).PTransNode
discard c.breakSyms.pop
return result

View file

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

View file

@ -752,6 +752,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of opcShlInt:
decodeBC(rkInt)
regs[ra].intVal = regs[rb].intVal shl regs[rc].intVal
of opcAshrInt:
decodeBC(rkInt)
regs[ra].intVal = ashr(regs[rb].intVal, regs[rc].intVal)
of opcBitandInt:
decodeBC(rkInt)
regs[ra].intVal = regs[rb].intVal and regs[rc].intVal
@ -1422,24 +1425,23 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
ensureKind(rkNode)
if c.callsite != nil: regs[ra].node = c.callsite
else: stackTrace(c, tos, pc, errFieldXNotFound & "callsite")
of opcNGetFile:
decodeB(rkNode)
of opcNGetLineInfo:
decodeBImm(rkNode)
let n = regs[rb].node
regs[ra].node = newStrNode(nkStrLit, toFullPath(c.config, n.info))
case imm
of 0: # getFile
regs[ra].node = newStrNode(nkStrLit, toFullPath(c.config, n.info))
of 1: # getLine
regs[ra].node = newIntNode(nkIntLit, n.info.line.int)
of 2: # getColumn
regs[ra].node = newIntNode(nkIntLit, n.info.col)
else:
internalAssert c.config, false
regs[ra].node.info = n.info
regs[ra].node.typ = n.typ
of opcNGetLine:
of opcNSetLineInfo:
decodeB(rkNode)
let n = regs[rb].node
regs[ra].node = newIntNode(nkIntLit, n.info.line.int)
regs[ra].node.info = n.info
regs[ra].node.typ = n.typ
of opcNGetColumn:
decodeB(rkNode)
let n = regs[rb].node
regs[ra].node = newIntNode(nkIntLit, n.info.col)
regs[ra].node.info = n.info
regs[ra].node.typ = n.typ
regs[ra].node.info = regs[rb].node.info
of opcEqIdent:
decodeBC(rkInt)
# aliases for shorter and easier to understand code below

View file

@ -17,7 +17,7 @@ const
byteExcess* = 128 # we use excess-K for immediates
wordExcess* = 32768
MaxLoopIterations* = 1_000_000_000 # max iterations of all loops
MaxLoopIterations* = 3_000_000 # max iterations of all loops
type
@ -57,7 +57,8 @@ type
opcLenStr,
opcIncl, opcInclRange, opcExcl, opcCard, opcMulInt, opcDivInt, opcModInt,
opcAddFloat, opcSubFloat, opcMulFloat, opcDivFloat, opcShrInt, opcShlInt,
opcAddFloat, opcSubFloat, opcMulFloat, opcDivFloat,
opcShrInt, opcShlInt, opcAshrInt,
opcBitandInt, opcBitorInt, opcBitxorInt, opcAddu, opcSubu, opcMulu,
opcDivu, opcModu, opcEqInt, opcLeInt, opcLtInt, opcEqFloat,
opcLeFloat, opcLtFloat, opcLeu, opcLtu,
@ -102,7 +103,7 @@ type
opcNError,
opcNWarning,
opcNHint,
opcNGetLine, opcNGetColumn, opcNGetFile,
opcNGetLineInfo, opcNSetLineInfo,
opcEqIdent,
opcStrToIdent,
opcGetImpl,

View file

@ -939,6 +939,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
c.freeTemp(tmp2)
of mShlI: genBinaryABCnarrowU(c, n, dest, opcShlInt)
of mAshrI: genBinaryABCnarrow(c, n, dest, opcAshrInt)
of mBitandI: genBinaryABCnarrowU(c, n, dest, opcBitandInt)
of mBitorI: genBinaryABCnarrowU(c, n, dest, opcBitorInt)
of mBitxorI: genBinaryABCnarrowU(c, n, dest, opcBitxorInt)
@ -1181,14 +1182,14 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
of mSameNodeType: genBinaryABC(c, n, dest, opcSameNodeType)
of mNLineInfo:
case n[0].sym.name.s
of "getFile":
genUnaryABC(c, n, dest, opcNGetFile)
of "getLine":
genUnaryABC(c, n, dest, opcNGetLine)
of "getColumn":
genUnaryABC(c, n, dest, opcNGetColumn)
else:
internalAssert c.config, false
of "getFile": genUnaryABI(c, n, dest, opcNGetLineInfo, 0)
of "getLine": genUnaryABI(c, n, dest, opcNGetLineInfo, 1)
of "getColumn": genUnaryABI(c, n, dest, opcNGetLineInfo, 2)
of "copyLineInfo":
internalAssert c.config, n.len == 3
unused(c, n, dest)
genBinaryStmt(c, n, opcNSetLineInfo)
else: internalAssert c.config, false
of mNHint:
unused(c, n, dest)
genUnaryStmt(c, n, opcNHint)

View file

@ -82,19 +82,21 @@ path="$lib/pure"
clang.cpp.options.linker = "-ldl"
tcc.options.linker = "-ldl"
@end
@if bsd or haiku:
@if bsd:
# BSD got posix_spawn only recently, so we deactivate it for osproc:
define:useFork
# at least NetBSD has problems with thread local storage:
tlsEmulation:on
@end
@if haiku:
# -fopenmp
gcc.options.linker = "-lroot -lnetwork"
gcc.cpp.options.linker = "-lroot -lnetwork"
clang.options.linker = "-lroot -lnetwork"
clang.cpp.options.linker = "-lroot -lnetwork"
tcc.options.linker = "-lroot -lnetwork"
# Haiku currently have problems with TLS
# https://dev.haiku-os.org/ticket/14342
tlsEmulation:on
gcc.options.linker = "-Wl,--as-needed -lnetwork"
gcc.cpp.options.linker = "-Wl,--as-needed -lnetwork"
clang.options.linker = "-Wl,--as-needed -lnetwork"
clang.cpp.options.linker = "-Wl,--as-needed -lnetwork"
tcc.options.linker = "-Wl,--as-needed -lnetwork"
@end
@end
@ -253,9 +255,14 @@ vcc.cpp.options.size = "/O1"
tcc.options.always = "-w"
# Configuration for the Genode toolchain
amd64.genode.gcc.cpp.exe = "genode-x86-g++"
amd64.genode.gcc.exe = "genode-x86-gcc"
amd64.genode.gcc.path = "/usr/local/genode-gcc/bin"
arm.genode.gcc.cpp.exe = "genode-arm-g++"
arm.genode.gcc.exe = "genode-arm-gcc"
arm.genode.gcc.path = "/usr/local/genode-gcc/bin"
@if genode:
gcc.path = "/usr/local/genode-gcc/bin"
gcc.cpp.options.always = "-D__GENODE__ -fno-stack-protector"
@if i386 or amd64:
gcc.exe = "genode-x86-gcc"
gcc.cpp.exe = "genode-x86-g++"
@elif arm:
gcc.exe = "genode-arm-gcc"
gcc.cpp.exe = "genode-arm-g++"
@end
@end

View file

@ -68,6 +68,8 @@ Advanced options:
--oldNewlines:on|off turn on|off the old behaviour of "\n"
--laxStrings:on|off when turned on, accessing the zero terminator in
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
--skipUserCfg do not read the user's configuration file
--skipParentCfg do not read the parent dirs' configuration files

View file

@ -65,7 +65,6 @@ The JavaScript target
---------------------
Nim can also generate `JavaScript`:idx: code through the ``js`` command.
However, the JavaScript code generator is experimental!
Nim targets JavaScript 1.5 which is supported by any widely used browser.
Since JavaScript does not have a portable means to include another module,
@ -77,7 +76,7 @@ available. This includes:
* manual memory management (``alloc``, etc.)
* casting and other unsafe operations (``cast`` operator, ``zeroMem``, etc.)
* file management
* most modules of the Standard library
* most modules of the standard library
* proper 64 bit integer arithmetic
* unsigned integer arithmetic
@ -87,9 +86,8 @@ However, the modules `strutils <strutils.html>`_, `math <math.html>`_, and
To compile a Nim module into a ``.js`` file use the ``js`` command; the
default is a ``.js`` file that is supposed to be referenced in an ``.html``
file. However, you can also run the code with `nodejs`:idx:, a `software
platform for easily building fast, scalable network applications
<http://nodejs.org>`_::
file. However, you can also run the code with `nodejs`:idx:
(`<http://nodejs.org>`_)::
nim js -d:nodejs -r examples/hallo.nim
@ -330,8 +328,9 @@ Nimcache naming logic
The `nimcache`:idx: directory is generated during compilation and will hold
either temporary or final files depending on your backend target. The default
name for the directory is ``nimcache`` but you can use the ``--nimcache``
`compiler switch <nimc.html#command-line-switches>`_ to change it.
name for the directory depends on the used backend and on your OS but you can
use the ``--nimcache`` `compiler switch <nimc.html#command-line-switches>`_ to
change it.
Nimcache and C like targets
~~~~~~~~~~~~~~~~~~~~~~~~~~~

View file

@ -97,24 +97,9 @@ Partial Output::
proc helloWorld(times: int) {.raises: [], tags: [].}
...
The full output can be seen here: `docgen_sample2.html <docgen_sample2.html>`_.
The older version of the ``doc`` command, now renamed ``doc0`` runs before
semantic checking which means it lacks some of the things ``doc`` will output.
The ``doc0`` command::
nim doc0 sample
Partial Output::
...
proc helloWorld*(times: int)
...
Output can be viewed in full here: `docgen_sample.html <docgen_sample.html>`_.
As you can see, the tool has extracted less information than what the ``doc``
command provides, such as pragmas attached implicitly by the compiler. This type
of information is not available from looking at the AST (Abstract Syntax Tree)
prior to semantic checking, which is why ``doc0`` doesn't show it.
The full output can be seen here: `docgen_sample.html <docgen_sample.html>`_.
It runs after semantic checking, and includes pragmas attached implicitly by the
compiler.
JSON

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:
``float``
the generic floating point type; its size is platform dependent
(the compiler chooses the processor's fastest floating point type).
the generic floating point type; its size used to be platform dependent,
but now it is always mapped to ``float64``.
This type should be used in general.
floatXX
@ -1507,68 +1507,6 @@ non nilable pointers. The details of this analysis are still to be specified
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
---------------
A procedural type is internally a pointer to a procedure. ``nil`` is
@ -1673,7 +1611,8 @@ A ``distinct`` type is new type derived from a `base type`:idx: that is
incompatible with its base type. In particular, it is an essential property
of a distinct type that it **does not** imply a subtype relation between it
and its base type. Explicit type conversions from a distinct type to its
base type and vice versa are allowed.
base type and vice versa are allowed. See also ``distinctBase`` to get the
reverse operation.
Modelling currencies
@ -2336,6 +2275,8 @@ pointer type and overloading resolution is tried with ``a[]`` instead.
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
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.}``
@ -4028,9 +3969,13 @@ exception.
Exception hierarchy
-------------------
The exception tree is defined in the `system <system.html>`_ module:
.. include:: exception_hierarchy_fragment.txt
The exception tree is defined in the `system <system.html>`_ module.
Every exception inherits from ``system.Exception``. Exceptions that indicate
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
@ -5435,6 +5380,7 @@ type ``system.ForLoopStmt`` can rewrite the entirety of a ``for`` loop:
:test: "nim c $1"
import macros
{.experimental: "forLoopMacros".}
macro enumerate(x: ForLoopStmt): untyped =
expectKind x, nnkForStmt
@ -5461,6 +5407,62 @@ type ``system.ForLoopStmt`` can rewrite the entirety of a ``for`` loop:
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
=============
@ -7932,7 +7934,7 @@ that ``spawn`` takes is restricted:
``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
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:
.. code-block:: nim
@ -7943,10 +7945,10 @@ wait on multiple flow variables at the same time:
var responses = newSeq[FlowVarBase](3)
for i in 0..2:
responses[i] = spawn tellServer(Update, "key", "value")
var index = awaitAny(responses)
var index = blockUntilAny(responses)
assert index >= 0
responses.del(index)
discard awaitAny(responses)
discard blockUntilAny(responses)
Data flow variables ensure that no data races
are possible. Due to technical limitations not every type ``T`` is possible in

View file

@ -68,6 +68,46 @@ User Some user defined warning.
========================== ============================================
List of hints
-------------
Each hint can be activated individually with ``--hint[NAME]:on|off`` or in a
``push`` pragma.
========================== ============================================
Name Description
========================== ============================================
CC Shows when the C compiler is called.
CodeBegin
CodeEnd
CondTrue
Conf A config file was loaded.
ConvToBaseNotNeeded
ConvFromXtoItselfNotNeeded
Dependency
Exec Program is executed.
ExprAlwaysX
ExtendedContext
GCStats Dumps statistics about the Garbage Collector.
GlobalVar Shows global variables declarations.
LineTooLong Line exceeds the maximum length.
Link Linking phase.
Name
Path Search paths modifications.
Pattern
Performance
Processing Artifact being compiled.
QuitCalled
Source The source line that triggered a diagnostic
message.
StackTrace
Success, SuccessX Successful compilation of a library or a binary.
User
UserRaw
XDeclaredButNotUsed Unused symbols in the code.
========================== ============================================
Verbosity levels
----------------
@ -114,7 +154,7 @@ passed as a command line argument to the compiler.
The ``nim`` executable processes configuration files in the following
directories (in this order; later files overwrite previous settings):
1) ``$nim/config/nim.cfg``, ``/etc/nim.cfg`` (UNIX) or ``%NIMROD%/config/nim.cfg`` (Windows). This file can be skipped with the ``--skipCfg`` command line option.
1) ``$nim/config/nim.cfg``, ``/etc/nim/nim.cfg`` (UNIX) or ``%NIM%/config/nim.cfg`` (Windows). This file can be skipped with the ``--skipCfg`` command line option.
2) ``$HOME/.config/nim.cfg`` (POSIX) or ``%APPDATA%/nim.cfg`` (Windows). This file can be skipped with the ``--skipUserCfg`` command line option.
3) ``$parentDir/nim.cfg`` where ``$parentDir`` stands for any parent directory of the project file's path. These files can be skipped with the ``--skipParentCfg`` command line option.
4) ``$projectDir/nim.cfg`` where ``$projectDir`` stands for the project file's path. This file can be skipped with the ``--skipProjCfg`` command line option.
@ -157,38 +197,28 @@ the first matching file is used.
Generated C code directory
--------------------------
The generated files that Nim produces all go into a subdirectory called
``nimcache`` in your project directory. This makes it easy to delete all
``nimcache``. Its full path is
- ``$XDG_CACHE_HOME/nim/$projectname(_r|_d)`` or ``~/.cache/nim/$projectname(_r|_d)``
on Posix
- ``$HOME/nimcache/$projectname(_r|_d)`` on Windows.
The ``_r`` suffix is used for release builds, ``_d`` is for debug builds.
This makes it easy to delete all
generated files. Files generated in this directory follow a naming logic which
you can read about in the `Nim Backend Integration document
<backends.html#nimcache-naming-logic>`_.
The ``--nimcache``
`compiler switch <nimc.html#command-line-switches>`_ can be used to
to change the ``nimcache`` directory.
However, the generated C code is not platform independent. C code generated for
Linux does not compile on Windows, for instance. The comment on top of the
C file lists the OS, CPU and CC the file has been compiled for.
Compilation cache
=================
**Warning**: The compilation cache is still highly experimental!
The ``nimcache`` directory may also contain so called `rod`:idx:
or `symbol files`:idx:. These files are pre-compiled modules that are used by
the compiler to perform `incremental compilation`:idx:. This means that only
modules that have changed since the last compilation (or the modules depending
on them etc.) are re-compiled. However, per default no symbol files are
generated; use the ``--symbolFiles:on`` command line switch to activate them.
Unfortunately due to technical reasons the ``--symbolFiles:on`` needs
to *aggregate* some generated C code. This means that the resulting executable
might contain some cruft even with dead code elimination. So
the final release build should be done with ``--symbolFiles:off``.
Due to the aggregation of C code it is also recommended that each project
resides in its own directory so that the generated ``nimcache`` directory
is not shared between different projects.
Compiler Selection
==================

View file

@ -25,7 +25,7 @@ Spawn statement
A standalone ``spawn`` statement is a simple construct. It executes
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
**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:
.. code-block:: nim
@ -36,10 +36,10 @@ variables at the same time:
var responses = newSeq[FlowVarBase](3)
for i in 0..2:
responses[i] = spawn tellServer(Update, "key", "value")
var index = awaitAny(responses)
var index = blockUntilAny(responses)
assert index >= 0
responses.del(index)
discard awaitAny(responses)
discard blockUntilAny(responses)
Data flow variables ensure that no data races
are possible. Due to technical limitations not every type ``T`` is possible in

View file

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

View file

@ -57,7 +57,8 @@ Commands for core developers:
zip builds the installation zip package
xz builds the installation tar.xz package
testinstall test tar.xz package; Unix only!
tests [options] run the testsuite
tests [options] run the testsuite (run a subset of tests by
specifying a category, e.g. `tests cat async`)
temp options creates a temporary compiler for testing
winrelease creates a Windows release
pushcsource push generated C sources to its repo
@ -182,7 +183,7 @@ proc bundleNimbleExe() =
bundleNimbleSrc()
# now compile Nimble and copy it to $nim/bin for the installer.ini
# 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)
proc buildNimble(latest: bool) =
@ -209,7 +210,7 @@ proc buildNimble(latest: bool) =
else:
exec("git checkout -f stable")
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)
proc bundleNimsuggest(buildExe: bool) =

View file

@ -432,6 +432,9 @@ proc getLine(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.}
proc getColumn(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.}
proc getFile(arg: NimNode): string {.magic: "NLineInfo", noSideEffect.}
proc copyLineInfo*(arg: NimNode, info: NimNode) {.magic: "NLineInfo", noSideEffect.}
## copy lineinfo from info node
proc lineInfoObj*(n: NimNode): LineInfo {.compileTime.} =
## returns ``LineInfo`` of ``n``, using absolute path for ``filename``
result.filename = n.getFile

View file

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

View file

@ -36,6 +36,8 @@ include "system/inclrtl"
when hostOS == "solaris":
{.passl: "-lsocket -lnsl".}
elif hostOS == "haiku":
{.passl: "-lnetwork".}
import os, parseutils
from times import epochTime

View file

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

View file

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

View file

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

View file

@ -267,7 +267,7 @@ proc `[]`*(pattern: Captures, i: int): string =
let bounds = bounds.get
return pattern.str.substr(bounds.a, bounds.b)
else:
return nil
return ""
proc match*(pattern: RegexMatch): string =
return pattern.captures[-1]
@ -291,9 +291,9 @@ template toTableImpl(cond: untyped) {.dirty.} =
else:
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]()
toTableImpl(nextVal == nil)
toTableImpl(nextVal.len == 0)
proc toTable*(pattern: CaptureBounds, default = none(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]] =
itemsImpl(nextVal.isNone)
iterator items*(pattern: Captures, default: string = nil): string =
itemsImpl(nextVal == nil)
iterator items*(pattern: Captures, default: string = ""): string =
itemsImpl(nextVal.len == 0)
proc toSeq*(pattern: CaptureBounds, default = none(HSlice[int, int])): seq[Option[HSlice[int, int]]] =
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))
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 StartIdent = Ident - {'0'..'9'}
proc checkNil(arg: string): string =
if arg == nil:
raise newException(ValueError, "Cannot use nil capture")
else:
return arg
template checkNil(arg: string): string = arg
template formatStr*(howExpr, namegetter, idgetter): untyped =
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]
if a >= 0'i32:
matches[i-1] = bufSubstr(buf, int(a), int(b))
else: matches[i-1] = nil
else: matches[i-1] = ""
return rawMatches[1] - rawMatches[0]
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 b = rawMatches[i * 2 + 1]
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)
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 b = rawMatches[i * 2 + 1]
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]
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:
var match = findBounds(s, sub, caps, prev)
if match.first < 0: break
assert result != nil
assert s != nil
add(result, substr(s, prev, match.first-1))
addf(result, by, caps)
prev = match.last + 1
@ -615,7 +613,7 @@ when isMainModule:
doAssert false
if "abc" =~ re"(cba)?.*":
doAssert matches[0] == nil
doAssert matches[0] == ""
else: doAssert false
if "abc" =~ re"().*":

View file

@ -1,3 +1,12 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2018 Nim contributors
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module wraps core JavaScript functions.
##
## Unless your application has very
@ -19,49 +28,47 @@ var
Date* {.importc, nodecl.}: DateLib
JSON* {.importc, nodecl.}: JsonLib
{.push importcpp.}
# Math library
proc abs*(m: MathLib, a: SomeNumber): SomeNumber
proc acos*(m: MathLib, a: SomeNumber): float
proc acosh*(m: MathLib, a: SomeNumber): float
proc asin*(m: MathLib, a: SomeNumber): float
proc asinh*(m: MathLib, a: SomeNumber): float
proc atan*(m: MathLib, a: SomeNumber): float
proc atan2*(m: MathLib, a: SomeNumber): float
proc atanh*(m: MathLib, a: SomeNumber): float
proc cbrt*(m: MathLib, f: SomeFloat): SomeFloat
proc ceil*(m: MathLib, f: SomeFloat): SomeFloat
proc clz32*(m: MathLib, f: SomeInteger): int
proc cos*(m: MathLib, a: SomeNumber): float
proc cosh*(m: MathLib, a: SomeNumber): float
proc exp*(m: MathLib, a: SomeNumber): float
proc expm1*(m: MathLib, a: SomeNumber): float
proc floor*(m: MathLib, f: SomeFloat): int
proc fround*(m: MathLib, f: SomeFloat): float32
proc hypot*(m: MathLib, args: varargs[distinct SomeNumber]): float
proc imul*(m: MathLib, a, b: int32): int32
proc log*(m: MathLib, a: SomeNumber): float
proc log10*(m: MathLib, a: SomeNumber): float
proc log1p*(m: MathLib, a: SomeNumber): float
proc log2*(m: MathLib, a: SomeNumber): float
proc max*(m: MathLib, a, b: SomeNumber): SomeNumber
proc min*[T: SomeNumber | JsRoot](m: MathLib, a, b: T): T
proc pow*(m: MathLib, a, b: distinct SomeNumber): float
proc random*(m: MathLib): float
proc round*(m: MathLib, f: SomeFloat): int
proc sign*(m: MathLib, f: SomeNumber): int
proc sin*(m: MathLib, a: SomeNumber): float
proc sinh*(m: MathLib, a: SomeNumber): float
proc sqrt*(m: MathLib, f: SomeFloat): SomeFloat
proc tan*(m: MathLib, a: SomeNumber): float
proc tanh*(m: MathLib, a: SomeNumber): float
proc trunc*(m: MathLib, f: SomeFloat): int
proc abs*(m: MathLib, a: SomeNumber): SomeNumber {.importcpp.}
proc acos*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc acosh*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc asin*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc asinh*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc atan*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc atan2*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc atanh*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc cbrt*(m: MathLib, f: SomeFloat): SomeFloat {.importcpp.}
proc ceil*(m: MathLib, f: SomeFloat): SomeFloat {.importcpp.}
proc clz32*(m: MathLib, f: SomeInteger): int {.importcpp.}
proc cos*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc cosh*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc exp*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc expm1*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc floor*(m: MathLib, f: SomeFloat): int {.importcpp.}
proc fround*(m: MathLib, f: SomeFloat): float32 {.importcpp.}
proc hypot*(m: MathLib, args: varargs[distinct SomeNumber]): float {.importcpp.}
proc imul*(m: MathLib, a, b: int32): int32 {.importcpp.}
proc log*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc log10*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc log1p*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc log2*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc max*(m: MathLib, a, b: SomeNumber): SomeNumber {.importcpp.}
proc min*[T: SomeNumber | JsRoot](m: MathLib, a, b: T): T {.importcpp.}
proc pow*(m: MathLib, a, b: distinct SomeNumber): float {.importcpp.}
proc random*(m: MathLib): float {.importcpp.}
proc round*(m: MathLib, f: SomeFloat): int {.importcpp.}
proc sign*(m: MathLib, f: SomeNumber): int {.importcpp.}
proc sin*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc sinh*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc sqrt*(m: MathLib, f: SomeFloat): SomeFloat {.importcpp.}
proc tan*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc tanh*(m: MathLib, a: SomeNumber): float {.importcpp.}
proc trunc*(m: MathLib, f: SomeFloat): int {.importcpp.}
# Date library
proc now*(d: DateLib): int
proc UTC*(d: DateLib): int
proc parse*(d: DateLib, s: cstring): int
proc now*(d: DateLib): int {.importcpp.}
proc UTC*(d: DateLib): int {.importcpp.}
proc parse*(d: DateLib, s: cstring): int {.importcpp.}
proc newDate*(): DateTime {.
importcpp: "new Date()".}
@ -73,19 +80,17 @@ proc newDate*(year, month, day, hours, minutes,
seconds, milliseconds: int): DateTime {.
importcpp: "new Date(#,#,#,#,#,#,#)".}
proc getDay*(d: DateTime): int
proc getFullYear*(d: DateTime): int
proc getHours*(d: DateTime): int
proc getMilliseconds*(d: DateTime): int
proc getMinutes*(d: DateTime): int
proc getMonth*(d: DateTime): int
proc getSeconds*(d: DateTime): int
proc getYear*(d: DateTime): int
proc getTime*(d: DateTime): int
proc toString*(d: DateTime): cstring
proc getDay*(d: DateTime): int {.importcpp.}
proc getFullYear*(d: DateTime): int {.importcpp.}
proc getHours*(d: DateTime): int {.importcpp.}
proc getMilliseconds*(d: DateTime): int {.importcpp.}
proc getMinutes*(d: DateTime): int {.importcpp.}
proc getMonth*(d: DateTime): int {.importcpp.}
proc getSeconds*(d: DateTime): int {.importcpp.}
proc getYear*(d: DateTime): int {.importcpp.}
proc getTime*(d: DateTime): int {.importcpp.}
proc toString*(d: DateTime): cstring {.importcpp.}
#JSON library
proc stringify*(l: JsonLib, s: JsRoot): cstring
proc parse*(l: JsonLib, s: cstring): JsRoot
{.pop.}
proc stringify*(l: JsonLib, s: JsRoot): cstring {.importcpp.}
proc parse*(l: JsonLib, s: cstring): JsRoot {.importcpp.}

View file

@ -129,13 +129,13 @@ __clang__
defined __DMC__ || \
defined __BORLANDC__ )
# define NIM_THREADVAR __declspec(thread)
#elif defined(__TINYC__) || defined(__GENODE__)
# define NIM_THREADVAR
/* note that ICC (linux) and Clang are covered by __GNUC__ */
#elif defined __GNUC__ || \
defined __SUNPRO_C || \
defined __xlC__
# define NIM_THREADVAR __thread
#elif defined __TINYC__
# define NIM_THREADVAR
#else
# error "Cannot define NIM_THREADVAR"
#endif

View file

@ -293,9 +293,9 @@ proc renderRstToJsonNode(node: PRstNode): JsonNode =
(key: "kind", val: %($node.kind)),
(key: "level", val: %BiggestInt(node.level))
]
if node.text != nil:
if node.text.len > 0:
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))
for i, son in node.sons:
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()
## <#writeIndexFile>`_. The purpose of the index is documented in the `docgen
## tools guide <docgen.html#index-switch>`_.
assert(not d.theIndex.isNil)
var
entry = term
isTitle = false
@ -337,7 +336,7 @@ proc hash(n: PRstNode): int =
result = hash(n.text)
elif n.len > 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
@ -398,9 +397,9 @@ proc hash(x: IndexEntry): Hash =
proc `<-`(a: var IndexEntry, b: IndexEntry) =
shallowCopy a.keyword, b.keyword
shallowCopy a.link, b.link
if b.linkTitle.isNil: a.linkTitle = nil
if b.linkTitle.isNil: a.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
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>
""", [url, text, desc])
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>
""", [url, text])
inc j
@ -524,7 +523,7 @@ proc generateDocumentationTOC(entries: seq[IndexEntry]): string =
titleTag = levels[L].text
else:
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>
""", [titleTag & " : " & levels[L].text, link, levels[L].text])
inc L
@ -608,8 +607,8 @@ proc readIndexDir(dir: string):
fileEntries[F].linkTitle = extraCols[1].unquoteIndexColumn
fileEntries[F].linkDesc = extraCols[2].unquoteIndexColumn
else:
fileEntries[F].linkTitle = nil
fileEntries[F].linkDesc = nil
fileEntries[F].linkTitle = ""
fileEntries[F].linkDesc = ""
inc F
# Depending on type add this to the list of symbols or table of APIs.
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
## be further embedded into nimdoc templates.
var (modules, symbols, docs) = readIndexDir(dir)
assert(not symbols.isNil)
result = ""
# Generate a quick jump list of documents.

View file

@ -10,7 +10,7 @@
{.deadCodeElim: on.} # dce option deprecated
const
hasSpawnH = not defined(haiku) # should exist for every Posix system nowadays
hasSpawnH = true # should exist for every Posix system nowadays
hasAioH = defined(linux)
when defined(linux) and not defined(android):
@ -43,6 +43,9 @@ type
Dirent* {.importc: "struct dirent",
header: "<dirent.h>", final, pure.} = object ## dirent_t struct
when defined(haiku):
d_dev*: Dev ## Device (not POSIX)
d_pdev*: Dev ## Parent device (only for queries) (not POSIX)
d_ino*: Ino ## File serial number.
when defined(dragonfly):
# DragonflyBSD doesn't have `d_reclen` field.
@ -54,6 +57,9 @@ type
## (not POSIX)
when defined(linux) or defined(openbsd):
d_off*: Off ## Not an offset. Value that ``telldir()`` would return.
elif defined(haiku):
d_pino*: Ino ## Parent inode (only for queries) (not POSIX)
d_reclen*: cushort ## Length of this record. (not POSIX)
d_name*: array[0..255, char] ## Name of entry.
@ -599,6 +605,10 @@ else:
MSG_NOSIGNAL* {.importc, header: "<sys/socket.h>".}: cint
## No SIGPIPE generated when an attempt to send is made on a stream-oriented socket that is no longer connected.
when defined(haiku):
const
SIGKILLTHR* = 21 ## BeOS specific: Kill just the thread, not team
when hasSpawnH:
when defined(linux):
# better be safe than sorry; Linux has this flag, macosx doesn't, don't

View file

@ -219,10 +219,10 @@ proc getHint(entry: StackTraceEntry): string =
## We try to provide some hints about stack trace entries that the user
## may not be familiar with, in particular calls inside the stdlib.
result = ""
if entry.procname == "processPendingCallbacks":
if entry.procname == cstring"processPendingCallbacks":
if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0:
return "Executes pending callbacks"
elif entry.procname == "poll":
elif entry.procname == cstring"poll":
if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0:
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
## protocol uses ``\r\L`` to delimit a new line.
assert SocketFlag.Peek notin flags ## TODO:
assert(not resString.mget.isNil(),
"String inside resString future needs to be initialised")
result = newFuture[void]("asyncnet.recvLineInto")
# TODO: Make the async transformation check for FutureVar params and complete
@ -657,7 +655,7 @@ when defineSsl:
proc wrapConnectedSocket*(ctx: SslContext, socket: AsyncSocket,
handshake: SslHandshakeType,
hostname: string = nil) =
hostname: string = "") =
## Wraps a connected socket in an SSL context. This function effectively
## turns ``socket`` into an SSL socket.
## ``hostname`` should be specified so that the client knows which hostname
@ -672,7 +670,7 @@ when defineSsl:
case handshake
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
# we're not using TLSv1+.
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.
var count = s.elems
exclImpl(s, key)
result = count == s.elems
result = count == s.elems
proc containsOrIncl*(s: var IntSet, key: int): bool =
## returns true if `s` contains `key`, otherwise `key` is included in `s`
@ -212,7 +212,10 @@ proc initIntSet*: IntSet =
#newSeq(result.data, InitIntSetSize)
#result.max = InitIntSetSize-1
result.data = nil
when defined(nimNoNilSeqs):
result.data = @[]
else:
result.data = nil
result.max = 0
result.counter = 0
result.head = nil
@ -222,7 +225,10 @@ proc clear*(result: var IntSet) =
#setLen(result.data, InitIntSetSize)
#for i in 0..InitIntSetSize-1: result.data[i] = nil
#result.max = InitIntSetSize-1
result.data = nil
when defined(nimNoNilSeqs):
result.data = @[]
else:
result.data = nil
result.max = 0
result.counter = 0
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
## `initIntSet`.
if src.elems <= src.a.len:
dest.data = nil
when defined(nimNoNilSeqs):
dest.data = @[]
else:
dest.data = nil
dest.max = 0
dest.counter = src.counter
dest.head = nil
@ -247,11 +256,9 @@ proc assign*(dest: var IntSet, src: IntSet) =
var it = src.head
while it != nil:
var h = it.key and dest.max
while dest.data[h] != nil: h = nextTry(h, dest.max)
assert(dest.data[h] == nil)
var n: PTrunk
new(n)
n.next = dest.head
@ -259,7 +266,6 @@ proc assign*(dest: var IntSet, src: IntSet) =
n.bits = it.bits
dest.head = n
dest.data[h] = n
it = it.next
proc union*(s1, s2: IntSet): IntSet =
@ -315,7 +321,7 @@ proc len*(s: IntSet): int {.inline.} =
for _ in s:
inc(result)
proc card*(s: IntSet): int {.inline.} =
proc card*(s: IntSet): int {.inline.} =
## alias for `len() <#len>` _.
result = s.len()
@ -361,7 +367,7 @@ when isMainModule:
x.incl(1056)
x.incl(1044)
x.excl(1044)
x.excl(1044)
assert x.containsOrIncl(888) == false
assert 888 in x

View file

@ -25,6 +25,28 @@ import macros
when not defined(nimhygiene):
{.pragma: dirty.}
macro evalOnceAs(expAlias, exp: untyped, letAssigneable: static[bool]): untyped =
## Injects ``expAlias`` in caller scope, to avoid bugs involving multiple
## substitution in macro arguments such as
## https://github.com/nim-lang/Nim/issues/7187
## ``evalOnceAs(myAlias, myExp)`` will behave as ``let myAlias = myExp``
## except when ``letAssigneable`` is false (eg to handle openArray) where
## it just forwards ``exp`` unchanged
expectKind(expAlias, nnkIdent)
var val = exp
result = newStmtList()
# If `exp` is not a symbol we evaluate it once here and then use the temporary
# symbol as alias
if exp.kind != nnkSym and letAssigneable:
val = genSym()
result.add(newLetStmt(val, exp))
result.add(
newProc(name = genSym(nskTemplate, $expAlias), params = [getType(untyped)],
body = val, procType = nnkTemplateDef))
proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
## Takes several sequences' items and returns them inside a new sequence.
##
@ -496,13 +518,17 @@ template toSeq*(iter: untyped): untyped =
## result = true)
## assert odd_numbers == @[1, 3, 5, 7, 9]
# Note: see also `mapIt` for explanation of some of the implementation
# subtleties.
when compiles(iter.len):
var i = 0
var result = newSeq[type(iter)](iter.len)
for x in iter:
result[i] = x
inc i
result
block:
evalOnceAs(iter2, iter, true)
var result = newSeq[type(iter)](iter2.len)
var i = 0
for x in iter2:
result[i] = x
inc i
result
else:
var result: seq[type(iter)] = @[]
for x in iter:
@ -635,8 +661,7 @@ template mapIt*(s, typ, op: untyped): untyped =
result.add(op)
result
template mapIt*(s, op: untyped): untyped =
template mapIt*(s: typed, op: untyped): untyped =
## Convenience template around the ``map`` proc to reduce typing.
##
## The template injects the ``it`` variable which you can use directly in an
@ -653,19 +678,24 @@ template mapIt*(s, op: untyped): untyped =
block:
var it{.inject.}: type(items(s));
op))
var result: seq[outType]
when compiles(s.len):
let t = s
var i = 0
result = newSeq[outType](t.len)
for it {.inject.} in t:
result[i] = op
i += 1
block: # using a block avoids https://github.com/nim-lang/Nim/issues/8580
# BUG: `evalOnceAs(s2, s, false)` would lead to C compile errors
# (`error: use of undeclared identifier`) instead of Nim compile errors
evalOnceAs(s2, s, compiles((let _ = s)))
var i = 0
var result = newSeq[outType](s2.len)
for it {.inject.} in s2:
result[i] = op
i += 1
result
else:
result = @[]
var result: seq[outType] = @[]
for it {.inject.} in s:
result.add(op)
result
result
template applyIt*(varSeq, op: untyped) =
## Convenience template around the mutable ``apply`` proc to reduce typing.
@ -752,6 +782,14 @@ macro mapLiterals*(constructor, op: untyped;
when isMainModule:
import strutils
# helper for testing double substitution side effects which are handled
# by `evalOnceAs`
var counter = 0
proc identity[T](a:T):auto=
counter.inc
a
block: # concat test
let
s1 = @[1, 2, 3]
@ -997,6 +1035,12 @@ when isMainModule:
result = true)
assert odd_numbers == @[1, 3, 5, 7, 9]
block:
# tests https://github.com/nim-lang/Nim/issues/7187
counter = 0
let ret = toSeq(@[1, 2, 3].identity().filter(proc (x: int): bool = x < 3))
doAssert ret == @[1, 2]
doAssert counter == 1
block: # foldl tests
let
numbers = @[5, 9, 11]
@ -1023,10 +1067,12 @@ when isMainModule:
assert multiplication == 495, "Multiplication is (5*(9*(11)))"
assert concatenation == "nimiscool"
block: # mapIt tests
block: # mapIt + applyIt test
counter = 0
var
nums = @[1, 2, 3, 4]
strings = nums.mapIt($(4 * it))
strings = nums.identity.mapIt($(4 * it))
doAssert counter == 1
nums.applyIt(it * 3)
assert nums[0] + nums[3] == 15
assert strings[2] == "12"
@ -1044,5 +1090,51 @@ when isMainModule:
doAssert mapLiterals((1, ("abc"), 2), float, nested=false) == (float(1), "abc", float(2))
doAssert mapLiterals(([1], ("abc"), 2), `$`, nested=true) == (["1"], "abc", "2")
block: # mapIt with openArray
counter = 0
proc foo(x: openArray[int]): seq[int] = x.mapIt(it * 10)
doAssert foo([identity(1),identity(2)]) == @[10, 20]
doAssert counter == 2
block: # mapIt with direct openArray
proc foo1(x: openArray[int]): seq[int] = x.mapIt(it * 10)
counter = 0
doAssert foo1(openArray[int]([identity(1),identity(2)])) == @[10,20]
doAssert counter == 2
# Corner cases (openArray litterals should not be common)
template foo2(x: openArray[int]): seq[int] = x.mapIt(it * 10)
counter = 0
doAssert foo2(openArray[int]([identity(1),identity(2)])) == @[10,20]
# TODO: this fails; not sure how to fix this case
# doAssert counter == 2
counter = 0
doAssert openArray[int]([identity(1), identity(2)]).mapIt(it) == @[1,2]
# ditto
# doAssert counter == 2
block: # mapIt empty test, see https://github.com/nim-lang/Nim/pull/8584#pullrequestreview-144723468
# NOTE: `[].mapIt(it)` is illegal, just as `let a = @[]` is (lacks type
# of elements)
doAssert: not compiles(mapIt(@[], it))
doAssert: not compiles(mapIt([], it))
doAssert newSeq[int](0).mapIt(it) == @[]
block: # mapIt redifinition check, see https://github.com/nim-lang/Nim/issues/8580
let s2 = [1,2].mapIt(it)
doAssert s2 == @[1,2]
block:
counter = 0
doAssert [1,2].identity().mapIt(it*2).mapIt(it*10) == @[20, 40]
# https://github.com/nim-lang/Nim/issues/7187 test case
doAssert counter == 1
block: # mapIt with invalid RHS for `let` (#8566)
type X = enum
A, B
doAssert mapIt(X, $it) == @["A", "B"]
when not defined(testing):
echo "Finished doc tests"

View file

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

View file

@ -43,6 +43,14 @@ when defined(genode):
proc affinitySpaceTotal(env: GenodeEnvPtr): cuint {.
importcpp: "@->cpu().affinity_space().total()".}
when defined(haiku):
{.emit: "#include <OS.h>".}
type
SystemInfo {.importc: "system_info", bycopy.} = object
cpuCount {.importc: "cpu_count".}: uint32
proc getSystemInfo(info: ptr SystemInfo): int32 {.importc: "get_system_info".}
proc countProcessors*(): int {.rtl, extern: "ncpi$1".} =
## returns the numer of the processors/cores the machine has.
## Returns 0 if it cannot be detected.
@ -86,6 +94,10 @@ proc countProcessors*(): int {.rtl, extern: "ncpi$1".} =
result = sysconf(SC_NPROC_ONLN)
elif defined(genode):
result = runtimeEnv.affinitySpaceTotal().int
elif defined(haiku):
var sysinfo: SystemInfo
if getSystemInfo(addr sysinfo) == 0:
result = sysinfo.cpuCount.int
else:
result = sysconf(SC_NPROCESSORS_ONLN)
if result <= 0: result = 0

View file

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

View file

@ -43,6 +43,10 @@ when defined(windows):
{.warning: "ucontext coroutine backend is not available on windows, defaulting to fibers.".}
when defined(nimCoroutinesSetjmp):
{.warning: "setjmp coroutine backend is not available on windows, defaulting to fibers.".}
elif defined(haiku):
const coroBackend = CORO_BACKEND_SETJMP
when defined(nimCoroutinesUcontext):
{.warning: "ucontext coroutine backend is not available on haiku, defaulting to setjmp".}
elif defined(nimCoroutinesSetjmp) or defined(nimCoroutinesSetjmpBundled):
const coroBackend = CORO_BACKEND_SETJMP
else:
@ -55,21 +59,21 @@ when coroBackend == CORO_BACKEND_FIBERS:
elif coroBackend == CORO_BACKEND_UCONTEXT:
type
stack_t {.importc, header: "<sys/ucontext.h>".} = object
stack_t {.importc, header: "<ucontext.h>".} = object
ss_sp: pointer
ss_flags: int
ss_size: int
ucontext_t {.importc, header: "<sys/ucontext.h>".} = object
ucontext_t {.importc, header: "<ucontext.h>".} = object
uc_link: ptr ucontext_t
uc_stack: stack_t
Context = ucontext_t
proc getcontext(context: var ucontext_t): int32 {.importc, header: "<sys/ucontext.h>".}
proc setcontext(context: var ucontext_t): int32 {.importc, header: "<sys/ucontext.h>".}
proc swapcontext(fromCtx, toCtx: var ucontext_t): int32 {.importc, header: "<sys/ucontext.h>".}
proc makecontext(context: var ucontext_t, fn: pointer, argc: int32) {.importc, header: "<sys/ucontext.h>", varargs.}
proc getcontext(context: var ucontext_t): int32 {.importc, header: "<ucontext.h>".}
proc setcontext(context: var ucontext_t): int32 {.importc, header: "<ucontext.h>".}
proc swapcontext(fromCtx, toCtx: var ucontext_t): int32 {.importc, header: "<ucontext.h>".}
proc makecontext(context: var ucontext_t, fn: pointer, argc: int32) {.importc, header: "<ucontext.h>", varargs.}
elif coroBackend == CORO_BACKEND_SETJMP:
proc coroExecWithStack*(fn: pointer, stack: pointer) {.noreturn, importc: "narch_$1", fastcall.}
@ -190,7 +194,7 @@ proc switchTo(current, to: CoroutinePtr) =
elif to.state == CORO_CREATED:
# Coroutine is started.
coroExecWithStack(runCurrentTask, to.stack.bottom)
doAssert false
#doAssert false
else:
{.error: "Invalid coroutine backend set.".}
# Execution was just resumed. Restore frame information and set active stack.

View file

@ -126,6 +126,8 @@ type
OpenBSD
DragonFlyBSD
Haiku
const
LacksDevPackages* = {Distribution.Gentoo, Distribution.Slackware,
@ -166,6 +168,8 @@ proc detectOsImpl(d: Distribution): bool =
of Distribution.Solaris:
let uname = toLowerAscii(uname())
result = ("sun" in uname) or ("solaris" in uname)
of Distribution.Haiku:
result = defined(haiku)
else:
let dd = toLowerAscii($d)
result = dd in toLowerAscii(uname()) or dd in toLowerAscii(release())
@ -224,6 +228,8 @@ proc foreignDepInstallCmd*(foreignPackageName: string): (string, bool) =
result = ("pacman -S " & p, true)
else:
result = ("<your package manager here> install " & p, true)
elif defined(haiku):
result = ("pkgman install " & p, true)
else:
result = ("brew install " & p, false)

View file

@ -255,7 +255,7 @@ when defined(windows):
else:
when defined(haiku):
const iconvDll = "(libc.so.6|libiconv.so|libtextencoding.so)"
const iconvDll = "libiconv.so"
elif defined(macosx):
const iconvDll = "libiconv.dylib"
else:
@ -272,6 +272,8 @@ else:
const EILSEQ = 86.cint
elif defined(solaris):
const EILSEQ = 88.cint
elif defined(haiku):
const EILSEQ = -2147454938.cint
var errno {.importc, header: "<errno.h>".}: cint

View file

@ -12,7 +12,7 @@
{.deadCodeElim: on.} # dce option deprecated
when defined(Posix) and not defined(haiku):
when defined(Posix):
{.passl: "-lm".}
var

View file

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

View file

@ -378,23 +378,23 @@ proc newMultipartData*: MultipartData =
## Constructs a new ``MultipartData`` object.
MultipartData(content: @[])
proc add*(p: var MultipartData, name, content: string, filename: string = nil,
contentType: string = nil) =
proc add*(p: var MultipartData, name, content: string, filename: string = "",
contentType: string = "") =
## Add a value to the multipart data. Raises a `ValueError` exception if
## `name`, `filename` or `contentType` contain newline characters.
if {'\c','\L'} in name:
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")
if contentType != nil and {'\c','\L'} in contentType:
if {'\c','\L'} in contentType:
raise newException(ValueError, "contentType contains a newline character")
var str = "Content-Disposition: form-data; name=\"" & name & "\""
if filename != nil:
if filename.len > 0:
str.add("; filename=\"" & filename & "\"")
str.add("\c\L")
if contentType != nil:
if contentType.len > 0:
str.add("Content-Type: " & contentType & "\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
let (_, fName, ext) = splitFile(file)
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)
result = p
@ -457,7 +457,7 @@ proc `[]=`*(p: var MultipartData, name: string,
p.add(name, file.content, file.name, file.contentType)
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 ("", "")
# Create boundary that is not in the data to be formatted
@ -807,6 +807,7 @@ type
lastProgressReport: float
when SocketType is AsyncSocket:
bodyStream: FutureStream[string]
parseBodyFut: Future[void]
else:
bodyStream: Stream
getBody: bool ## When `false`, the body is never read in requestAux.
@ -1066,10 +1067,14 @@ proc parseResponse(client: HttpClient | AsyncHttpClient,
if getBody:
when client is HttpClient:
client.bodyStream = newStringStream()
result.bodyStream = client.bodyStream
parseBody(client, result.headers, result.version)
else:
client.bodyStream = newFutureStream[string]("parseResponse")
await parseBody(client, result.headers, result.version)
result.bodyStream = client.bodyStream
result.bodyStream = client.bodyStream
assert(client.parseBodyFut.isNil or client.parseBodyFut.finished)
client.parseBodyFut = parseBody(client, result.headers, result.version)
# do not wait here for the body request to complete
proc newConnection(client: HttpClient | AsyncHttpClient,
url: Uri) {.multisync.} =
@ -1159,6 +1164,12 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url: string,
# Helper that actually makes the request. Does not handle redirects.
let requestUrl = parseUri(url)
when client is AsyncHttpClient:
if not client.parseBodyFut.isNil:
# let the current operation finish before making another request
await client.parseBodyFut
client.parseBodyFut = nil
await newConnection(client, requestUrl)
let effectiveHeaders = client.headers.override(headers)

View file

@ -567,8 +567,11 @@ proc selectInto*[T](s: Selector[T], timeout: int,
doAssert(true, "Unsupported kqueue filter in the queue!")
if (kevent.flags and EV_EOF) != 0:
# TODO this error handling needs to be rethought.
# `fflags` can sometimes be `0x80000000` and thus we use 'cast'
# here:
if kevent.fflags != 0:
rkey.errorCode = kevent.fflags.OSErrorCode
rkey.errorCode = cast[OSErrorCode](kevent.fflags)
else:
# This assumes we are dealing with sockets.
# TODO: For future-proofing it might be a good idea to give the

View file

@ -310,7 +310,10 @@ proc selectInto*[T](s: Selector[T], timeout: int,
var rset, wset, eset: FdSet
if timeout != -1:
tv.tv_sec = timeout.int32 div 1_000
when defined(genode):
tv.tv_sec = Time(timeout div 1_000)
else:
tv.tv_sec = timeout.int32 div 1_000
tv.tv_usec = (timeout.int32 %% 1_000) * 1_000
else:
ptv = nil
@ -391,7 +394,6 @@ proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} =
for i in 0..<FD_SETSIZE:
if s.fds[i].ident == fdi:
return true
inc(i)
when hasThreadSupport:
template withSelectLock[T](s: Selector[T], body: untyped) =

View file

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

View file

@ -49,7 +49,7 @@ proc fac*(n: int): int =
{.push checks:off, line_dir:off, stack_trace:off.}
when defined(Posix) and not defined(haiku):
when defined(Posix):
{.passl: "-lm".}
const
@ -162,9 +162,6 @@ when not defined(JS): # C
proc log10*(x: float32): float32 {.importc: "log10f", header: "<math.h>".}
proc log10*(x: float64): float64 {.importc: "log10", header: "<math.h>".}
## Computes the common logarithm (base 10) of `x`
proc log2*(x: float32): float32 {.importc: "log2f", header: "<math.h>".}
proc log2*(x: float64): float64 {.importc: "log2", header: "<math.h>".}
## Computes the binary logarithm (base 2) of `x`
proc exp*(x: float32): float32 {.importc: "expf", header: "<math.h>".}
proc exp*(x: float64): float64 {.importc: "exp", header: "<math.h>".}
## Computes the exponential function of `x` (pow(E, x))
@ -268,6 +265,8 @@ proc arcsech*[T: float32|float64](x: T): T = arccosh(1.0 / x)
proc arccsch*[T: float32|float64](x: T): T = arcsinh(1.0 / x)
## Computes the inverse hyperbolic cosecant of `x`
const windowsCC89 = defined(windows) and (defined(vcc) or defined(bcc))
when not defined(JS): # C
proc hypot*(x, y: float32): float32 {.importc: "hypotf", header: "<math.h>".}
proc hypot*(x, y: float64): float64 {.importc: "hypot", header: "<math.h>".}
@ -280,25 +279,27 @@ when not defined(JS): # C
##
## To compute power between integers, use `^` e.g. 2 ^ 6
proc erf*(x: float32): float32 {.importc: "erff", header: "<math.h>".}
proc erf*(x: float64): float64 {.importc: "erf", header: "<math.h>".}
## The error function
proc erfc*(x: float32): float32 {.importc: "erfcf", header: "<math.h>".}
proc erfc*(x: float64): float64 {.importc: "erfc", header: "<math.h>".}
## The complementary error function
# TODO: add C89 version on windows
when not windowsCC89:
proc erf*(x: float32): float32 {.importc: "erff", header: "<math.h>".}
proc erf*(x: float64): float64 {.importc: "erf", header: "<math.h>".}
## The error function
proc erfc*(x: float32): float32 {.importc: "erfcf", header: "<math.h>".}
proc erfc*(x: float64): float64 {.importc: "erfc", header: "<math.h>".}
## The complementary error function
proc gamma*(x: float32): float32 {.importc: "tgammaf", header: "<math.h>".}
proc gamma*(x: float64): float64 {.importc: "tgamma", header: "<math.h>".}
## The gamma function
proc tgamma*(x: float32): float32
{.deprecated: "use gamma instead", importc: "tgammaf", header: "<math.h>".}
proc tgamma*(x: float64): float64
{.deprecated: "use gamma instead", importc: "tgamma", header: "<math.h>".}
## The gamma function
## **Deprecated since version 0.19.0**: Use ``gamma`` instead.
proc lgamma*(x: float32): float32 {.importc: "lgammaf", header: "<math.h>".}
proc lgamma*(x: float64): float64 {.importc: "lgamma", header: "<math.h>".}
## Natural log of the gamma function
proc gamma*(x: float32): float32 {.importc: "tgammaf", header: "<math.h>".}
proc gamma*(x: float64): float64 {.importc: "tgamma", header: "<math.h>".}
## The gamma function
proc tgamma*(x: float32): float32
{.deprecated: "use gamma instead", importc: "tgammaf", header: "<math.h>".}
proc tgamma*(x: float64): float64
{.deprecated: "use gamma instead", importc: "tgamma", header: "<math.h>".}
## The gamma function
## **Deprecated since version 0.19.0**: Use ``gamma`` instead.
proc lgamma*(x: float32): float32 {.importc: "lgammaf", header: "<math.h>".}
proc lgamma*(x: float64): float64 {.importc: "lgamma", header: "<math.h>".}
## Natural log of the gamma function
proc floor*(x: float32): float32 {.importc: "floorf", header: "<math.h>".}
proc floor*(x: float64): float64 {.importc: "floor", header: "<math.h>".}
@ -314,7 +315,7 @@ when not defined(JS): # C
## .. code-block:: nim
## echo ceil(-2.1) ## -2.0
when defined(windows) and (defined(vcc) or defined(bcc)):
when windowsCC89:
# MSVC 2010 don't have trunc/truncf
# this implementation was inspired by Go-lang Math.Trunc
proc truncImpl(f: float64): float64 =
@ -452,6 +453,28 @@ when not defined(JS):
var exp: int32
result = c_frexp(x, exp)
exponent = exp
when windowsCC89:
# taken from Go-lang Math.Log2
const ln2 = 0.693147180559945309417232121458176568075500134360255254120680009
template log2Impl[T](x: T): T =
var exp: int32
var frac = frexp(x, exp)
# Make sure exact powers of two give an exact answer.
# Don't depend on Log(0.5)*(1/Ln2)+exp being exactly exp-1.
if frac == 0.5: return T(exp - 1)
log10(frac)*(1/ln2) + T(exp)
proc log2*(x: float32): float32 = log2Impl(x)
proc log2*(x: float64): float64 = log2Impl(x)
## Log2 returns the binary logarithm of x.
## The special cases are the same as for Log.
else:
proc log2*(x: float32): float32 {.importc: "log2f", header: "<math.h>".}
proc log2*(x: float64): float64 {.importc: "log2", header: "<math.h>".}
## Computes the binary logarithm (base 2) of `x`
else:
proc frexp*[T: float32|float64](x: T, exponent: var int): T =
if x == 0.0:
@ -506,8 +529,8 @@ proc sgn*[T: SomeNumber](x: T): int {.inline.} =
{.pop.}
proc `^`*[T](x: T, y: Natural): T =
## Computes ``x`` to the power ``y`. ``x`` must be non-negative, use
## `pow <#pow,float,float>` for negative exponents.
## Computes ``x`` to the power ``y``. ``x`` must be non-negative, use
## `pow <#pow,float,float>`_ for negative exponents.
when compiles(y >= T(0)):
assert y >= T(0)
else:
@ -564,7 +587,7 @@ proc lcm*[T](x, y: T): T =
## Computes the least common multiple of ``x`` and ``y``.
x div gcd(x, y) * y
when isMainModule and not defined(JS):
when isMainModule and not defined(JS) and not windowsCC89:
# Check for no side effect annotation
proc mySqrt(num: float): float {.noSideEffect.} =
return sqrt(num)
@ -692,3 +715,14 @@ when isMainModule:
block: # log
doAssert log(4.0, 3.0) == ln(4.0) / ln(3.0)
doAssert log2(8.0'f64) == 3.0'f64
doAssert log2(4.0'f64) == 2.0'f64
doAssert log2(2.0'f64) == 1.0'f64
doAssert log2(1.0'f64) == 0.0'f64
doAssert classify(log2(0.0'f64)) == fcNegInf
doAssert log2(8.0'f32) == 3.0'f32
doAssert log2(4.0'f32) == 2.0'f32
doAssert log2(2.0'f32) == 1.0'f32
doAssert log2(1.0'f32) == 0.0'f32
doAssert classify(log2(0.0'f32)) == fcNegInf

View file

@ -248,9 +248,10 @@ proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET,
hints.ai_socktype = toInt(sockType)
hints.ai_protocol = toInt(protocol)
# OpenBSD doesn't support AI_V4MAPPED and doesn't define the macro AI_V4MAPPED.
# FreeBSD doesn't support AI_V4MAPPED but defines the macro.
# FreeBSD, Haiku don't support AI_V4MAPPED but defines the macro.
# https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=198092
when not defined(freebsd) and not defined(openbsd) and not defined(netbsd) and not defined(android):
# https://dev.haiku-os.org/ticket/14323
when not defined(freebsd) and not defined(openbsd) and not defined(netbsd) and not defined(android) and not defined(haiku):
if domain == AF_INET6:
hints.ai_flags = AI_V4MAPPED
var gaiResult = getaddrinfo(address, $port, addr(hints), result)

View file

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

View file

@ -129,7 +129,7 @@ proc get*[T](self: Option[T]): T =
## Returns contents of the Option. If it is none, then an exception is
## thrown.
if self.isNone:
raise UnpackError(msg : "Can't obtain a value from a `none`")
raise UnpackError(msg: "Can't obtain a value from a `none`")
self.val
proc get*[T](self: Option[T], otherwise: T): T =
@ -139,6 +139,13 @@ proc get*[T](self: Option[T], otherwise: T): T =
else:
otherwise
proc get*[T](self: var Option[T]): var T =
## Returns contents of the Option. If it is none, then an exception is
## thrown.
if self.isNone:
raise UnpackError(msg: "Can't obtain a value from a `none`")
return self.val
proc map*[T](self: Option[T], callback: proc (input: T)) =
## Applies a callback to the value in this Option
if self.isSome:
@ -187,9 +194,11 @@ proc `$`*[T](self: Option[T]): string =
## If the option does not have a value, the result will be `None[T]` where `T` is the name of
## the type contained in the option.
if self.isSome:
"Some(" & $self.val & ")"
result = "Some("
result.addQuoted self.val
result.add ")"
else:
"None[" & name(T) & "]"
result = "None[" & name(T) & "]"
when isMainModule:
import unittest, sequtils
@ -240,7 +249,7 @@ when isMainModule:
check(stringNone.get("Correct") == "Correct")
test "$":
check($(some("Correct")) == "Some(Correct)")
check($(some("Correct")) == "Some(\"Correct\")")
check($(stringNone) == "None[string]")
test "map with a void result":

View file

@ -615,7 +615,10 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
when not declared(ENOENT) and not defined(Windows):
when NoFakeVars:
const ENOENT = cint(2) # 2 on most systems including Solaris
when not defined(haiku):
const ENOENT = cint(2) # 2 on most systems including Solaris
else:
const ENOENT = cint(-2147459069)
else:
var ENOENT {.importc, header: "<errno.h>".}: cint
@ -972,6 +975,14 @@ proc rawCreateDir(dir: string): bool =
result = false
else:
raiseOSError(osLastError(), dir)
elif defined(haiku):
let res = mkdir(dir, 0o777)
if res == 0'i32:
result = true
elif errno == EEXIST or errno == EROFS:
result = false
else:
raiseOSError(osLastError(), dir)
elif defined(posix):
let res = mkdir(dir, 0o777)
if res == 0'i32:
@ -1352,9 +1363,15 @@ elif defined(nintendoswitch):
proc paramCount*(): int {.tags: [ReadIOEffect].} =
raise newException(OSError, "paramCount is not implemented on Nintendo Switch")
elif defined(genode):
proc paramStr*(i: int): TaintedString =
raise newException(OSError, "paramStr is not implemented on Genode")
proc paramCount*(): int =
raise newException(OSError, "paramCount is not implemented on Genode")
elif not defined(createNimRtl) and
not(defined(posix) and appType == "lib") and
not defined(genode):
not(defined(posix) and appType == "lib"):
# On Posix, there is no portable way to get the command line from a DLL.
var
cmdCount {.importc: "cmdCount".}: cint

View file

@ -525,14 +525,14 @@ proc getConfigDir*(): string {.rtl, extern: "nos$1",
## Returns the config directory of the current user for applications.
##
## On non-Windows OSs, this proc conforms to the XDG Base Directory
## spec. Thus, this proc returns the value of the XDG_CONFIG_DIR environment
## spec. Thus, this proc returns the value of the XDG_CONFIG_HOME environment
## variable if it is set, and returns the default configuration directory,
## "~/.config/", otherwise.
##
## An OS-dependent trailing slash is always present at the end of the
## returned string; `\\` on Windows and `/` on all other OSs.
when defined(windows): return string(getEnv("APPDATA")) & "\\"
elif getEnv("XDG_CONFIG_DIR"): return string(getEnv("XDG_CONFIG_DIR")) & "/"
elif getEnv("XDG_CONFIG_HOME"): return string(getEnv("XDG_CONFIG_HOME")) & "/"
else: return string(getEnv("HOME")) & "/.config/"
proc getTempDir*(): string {.rtl, extern: "nos$1",
@ -553,25 +553,25 @@ proc getTempDir*(): string {.rtl, extern: "nos$1",
proc expandTilde*(path: string): string {.
tags: [ReadEnvEffect, ReadIOEffect].} =
## Expands a path starting with ``~/`` to a full path.
## Expands ``~`` or a path starting with ``~/`` to a full path, replacing
## ``~`` with ``getHomeDir()`` (otherwise returns ``path`` unmodified).
##
## If `path` starts with the tilde character and is followed by `/` or `\\`
## this proc will return the reminder of the path appended to the result of
## the getHomeDir() proc, otherwise the input path will be returned without
## modification.
##
## The behaviour of this proc is the same on the Windows platform despite
## not having this convention. Example:
##
## .. code-block:: nim
## let configFile = expandTilde("~" / "appname.cfg")
## echo configFile
## # --> C:\Users\amber\appname.cfg
if len(path) > 1 and path[0] == '~' and (path[1] == '/' or path[1] == '\\'):
## Windows: this is still supported despite Windows platform not having this
## convention; also, both ``~/`` and ``~\`` are handled.
runnableExamples:
doAssert expandTilde("~" / "appname.cfg") == getHomeDir() / "appname.cfg"
if len(path) == 0 or path[0] != '~':
result = path
elif len(path) == 1:
result = getHomeDir()
elif (path[1] in {DirSep, AltSep}):
result = getHomeDir() / path.substr(2)
else:
# TODO: handle `~bob` and `~bob/` which means home of bob
result = path
# TODO: consider whether quoteShellPosix, quoteShellWindows, quoteShell, quoteShellCommand
# belong in `strutils` instead; they are not specific to paths
proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
## Quote s, so it can be safely passed to Windows API.
## Based on Python's subprocess.list2cmdline
@ -623,6 +623,18 @@ when defined(windows) or defined(posix) or defined(nintendoswitch):
else:
return quoteShellPosix(s)
proc quoteShellCommand*(args: openArray[string]): string =
## Concatenates and quotes shell arguments `args`
runnableExamples:
when defined(posix):
assert quoteShellCommand(["aaa", "", "c d"]) == "aaa '' 'c d'"
when defined(windows):
assert quoteShellCommand(["aaa", "", "c d"]) == "aaa \"\" \"c d\""
# can't use `map` pending https://github.com/nim-lang/Nim/issues/8303
for i in 0..<args.len:
if i > 0: result.add " "
result.add quoteShell(args[i])
when isMainModule:
assert quoteShellWindows("aaa") == "aaa"
assert quoteShellWindows("aaa\"") == "aaa\\\""

View file

@ -44,10 +44,10 @@ type
proc initOptParser*(cmdline: seq[string]): OptParser {.rtl.} =
## Initalizes option parses with cmdline. cmdline should not contain
## 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 = ""
when not defined(createNimRtl):
if cmdline == nil:
if cmdline.len == 0:
result.cmd = commandLineParams()
return
else:
@ -60,7 +60,7 @@ proc initOptParser*(cmdline: string): OptParser {.rtl, deprecated.} =
## and calls initOptParser(openarray[string])
## Do not use.
if cmdline == "": # backward compatibility
return initOptParser(seq[string](nil))
return initOptParser(@[])
else:
return initOptParser(cmdline.split)

View file

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

View file

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

View file

@ -37,7 +37,7 @@ type
length: int
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.
# 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".} =
## the rope's length
if a == nil: result = 0
else: result = a.length
else: result = abs a.length
proc newRope(): Rope = new(result)
proc newRope(data: string): Rope =
new(result)
result.length = len(data)
result.length = -len(data)
result.data = data
var
@ -129,7 +129,7 @@ proc insertInCache(s: string, tree: Rope): Rope =
result.left = t
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.
if s.len == 0:
result = nil
@ -170,17 +170,7 @@ proc `&`*(a, b: Rope): Rope {.rtl, extern: "nroConcRopeRope".} =
result = a
else:
result = newRope()
result.length = a.length + 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
result.length = abs(a.length) + abs(b.length)
proc `&`*(a: Rope, b: string): Rope {.rtl, extern: "nroConcRopeStr".} =
## the concatenation operator for ropes.
@ -229,7 +219,6 @@ iterator leaves*(r: Rope): string =
stack.add(it.right)
it = it.left
assert(it != nil)
assert(it.data != nil)
yield it.data
iterator items*(r: Rope): char =
@ -250,54 +239,6 @@ proc `$`*(r: Rope): string {.rtl, extern: "nroToString".}=
result = newStringOfCap(r.len)
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 {.
rtl, extern: "nroFormat".} =
## `%` substitution operator for ropes. Does not support the ``$identifier``

View file

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

View file

@ -377,7 +377,10 @@ when not defined(js):
proc ssClose(s: Stream) =
var s = StringStream(s)
s.data = nil
when defined(nimNoNilSeqs):
s.data = ""
else:
s.data = nil
proc newStringStream*(s: string = ""): StringStream =
## 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".} =
## Checks if `s` is nil or consists entirely of whitespace characters.
if len(s) == 0:
return true
result = true
for c in s:
if not c.isSpaceAscii():
@ -624,12 +621,13 @@ iterator rsplit*(s: string, sep: string, maxsplit: int = -1,
## Substrings are separated from the right by the string `sep`
rsplitCommon(s, sep, maxsplit, sep.len)
iterator splitLines*(s: string): string =
iterator splitLines*(s: string, keepEol = false): string =
## Splits the string `s` into its containing lines.
##
## Every `character literal <manual.html#character-literals>`_ newline
## combination (CR, LF, CR-LF) is supported. The result strings contain no
## trailing ``\n``.
## trailing end of line characters unless parameter ``keepEol`` is set to
## ``true``.
##
## Example:
##
@ -649,22 +647,30 @@ iterator splitLines*(s: string): string =
## ""
var first = 0
var last = 0
var eolpos = 0
while true:
while last < s.len and s[last] notin {'\c', '\l'}: inc(last)
yield substr(s, first, last-1)
# skip newlines:
if last >= s.len: break
if s[last] == '\l': inc(last)
elif s[last] == '\c':
inc(last)
if last < s.len and s[last] == '\l': inc(last)
eolpos = last
if last < s.len:
if s[last] == '\l': inc(last)
elif s[last] == '\c':
inc(last)
if last < s.len and s[last] == '\l': inc(last)
yield substr(s, first, if keepEol: last-1 else: eolpos-1)
# no eol characters consumed means that the string is over
if eolpos == last:
break
first = last
proc splitLines*(s: string): seq[string] {.noSideEffect,
proc splitLines*(s: string, keepEol = false): seq[string] {.noSideEffect,
rtl, extern: "nsuSplitLines".} =
## The same as the `splitLines <#splitLines.i,string>`_ iterator, but is a
## proc that returns a sequence of substrings.
accumulateResult(splitLines(s))
accumulateResult(splitLines(s, keepEol=keepEol))
proc countLines*(s: string): int {.noSideEffect,
rtl, extern: "nsuCountLines".} =
@ -908,7 +914,7 @@ proc parseOctInt*(s: string): int {.noSideEffect,
## `s` are ignored.
let L = parseutils.parseOct(s, result, 0)
if L != s.len or L == 0:
raise newException(ValueError, "invalid oct integer: " & s)
raise newException(ValueError, "invalid oct integer: " & s)
proc parseHexInt*(s: string): int {.noSideEffect, procvar,
rtl, extern: "nsuParseHexInt".} =
@ -1369,9 +1375,11 @@ proc find*(s: string, sub: char, start: Natural = 0, last: Natural = 0): int {.n
if sub == s[i]: return i
else:
when hasCStringBuiltin:
let found = c_memchr(s[start].unsafeAddr, sub, last-start+1)
if not found.isNil:
return cast[ByteAddress](found) -% cast[ByteAddress](s.cstring)
let L = last-start+1
if L > 0:
let found = c_memchr(s[start].unsafeAddr, sub, L)
if not found.isNil:
return cast[ByteAddress](found) -% cast[ByteAddress](s.cstring)
else:
for i in start..last:
if sub == s[i]: return i
@ -1518,7 +1526,7 @@ proc replace*(s, sub: string, by = ""): string {.noSideEffect,
elif subLen == 1:
# when the pattern is a single char, we use a faster
# char-based search that doesn't need a skip table:
var c = sub[0]
let c = sub[0]
let last = s.high
var i = 0
while true:

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