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 csource
- ./koch nimsuggest - ./koch nimsuggest
# - nim c -r nimsuggest/tester # - nim c -r nimsuggest/tester
- ( ! grep -F '.. code-block' -l -r --include '*.html' --exclude contributing.html --exclude docgen.html --exclude tut2.html )
- ( ! grep -F '..code-block' -l -r --include '*.html' --exclude contributing.html --exclude docgen.html --exclude tut2.html )

View file

@ -55,6 +55,7 @@ build_script:
- nimble install jester@#head -y - nimble install jester@#head -y
- nimble install niminst - nimble install niminst
- nim c --taintMode:on -d:nimCoroutines tests/testament/tester - 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: test_script:
- tests\testament\tester --pedantic all -d:nimCoroutines - 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 - The parser now warns about inconsistent spacing around binary operators as
these can easily be confused with unary operators. This warning will likely these can easily be confused with unary operators. This warning will likely
become an error in the future. 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``). 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 - The dot style for import paths (e.g ``import path.to.module`` instead of
``import path/to/module``) has been deprecated. ``import path/to/module``) has been deprecated.
@ -37,9 +37,18 @@
strings anymore for its ``unit`` parameter. Instead the space is controlled strings anymore for its ``unit`` parameter. Instead the space is controlled
by a new parameter ``useUnitSpace``. 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 - ``proc `-`*(a, b: Time): int64`` in the ``times`` module has changed return type
to ``times.Duration`` in order to support higher time resolutions. to ``times.Duration`` in order to support higher time resolutions.
The proc is no longer deprecated. 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``. - ``posix.Timeval.tv_sec`` has changed type to ``posix.Time``.
- ``math.`mod` `` for floats now behaves the same as ``mod`` for integers - ``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. - ``lineInfoObj`` now returns absolute path instead of project path.
It's used by ``lineInfo``, ``check``, ``expect``, ``require``, etc. It's used by ``lineInfo``, ``check``, ``expect``, ``require``, etc.
- `threadpool`'s `await` and derivatives have been renamed to `blockUntil`
to avoid confusions with `await` from the `async` macro.
#### Breaking changes in the compiler #### Breaking changes in the compiler
- The undocumented ``#? braces`` parsing mode was removed. - The undocumented ``#? braces`` parsing mode was removed.
- The undocumented PHP backend 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 ### Library additions
@ -88,9 +104,11 @@
- Added the procs ``rationals.`div```, ``rationals.`mod```, ``rationals.floorDiv`` and ``rationals.floorMod`` for rationals. - Added the procs ``rationals.`div```, ``rationals.`mod```, ``rationals.floorDiv`` and ``rationals.floorMod`` for rationals.
- Added the proc ``math.prod`` for product of elements in openArray. - Added the proc ``math.prod`` for product of elements in openArray.
- Added the proc ``parseBinInt`` to parse a binary integer from a string, which returns the value. - Added the proc ``parseBinInt`` to parse a binary integer from a string, which returns the value.
- ``parseOct`` and ``parseBin`` in parseutils now also support the ``maxLen`` argument similar to ``parseHexInt`` - ``parseOct`` and ``parseBin`` in parseutils now also support the ``maxLen`` argument similar to ``parseHexInt``.
- Added the proc ``flush`` for memory mapped files. - Added the proc ``flush`` for memory mapped files.
- Added the ``MemMapFileStream``. - Added the ``MemMapFileStream``.
- Added ``macros.copyLineInfo`` to copy lineInfo from other node.
- Added ``system.ashr`` an arithmetic right shift for integers.
### Library changes ### Library changes
@ -129,7 +147,13 @@
- ``func`` is now an alias for ``proc {.noSideEffect.}``. - ``func`` is now an alias for ``proc {.noSideEffect.}``.
- In order to make ``for`` loops and iterators more flexible to use Nim now - In order to make ``for`` loops and iterators more flexible to use Nim now
supports so called "for-loop macros". See supports so called "for-loop macros". See
the `manual <manual.html#macros-for-loop-macros>`_ for more details. the [manual](manual.html#macros-for-loop-macros) for more details.
This feature enables a Python-like generic ``enumerate`` implementation.
- Case statements can now be rewritten via macros. See the [manual](manual.html#macros-case-statement-macros) for more information.
This feature enables custom pattern matchers.
- the `typedesc` special type has been renamed to just `type`. - the `typedesc` special type has been renamed to just `type`.
- `static` and `type` are now also modifiers similar to `ref` and `ptr`. - `static` and `type` are now also modifiers similar to `ref` and `ptr`.
They denote the special types `static[T]` and `type[T]`. They denote the special types `static[T]` and `type[T]`.
@ -150,7 +174,7 @@
More details in language manual. More details in language manual.
- ``nil`` for strings/seqs is finally gone. Instead the default value for - ``nil`` for strings/seqs is finally gone. Instead the default value for
these is ``"" / @[]``. these is ``"" / @[]``. Use ``--nilseqs:on`` for a transition period.
- Accessing the binary zero terminator in Nim's native strings - Accessing the binary zero terminator in Nim's native strings
is now invalid. Internally a Nim string still has the trailing zero for is now invalid. Internally a Nim string still has the trailing zero for
@ -160,11 +184,22 @@
- The command syntax now supports keyword arguments after the first comma. - The command syntax now supports keyword arguments after the first comma.
- Thread-local variables can now be declared inside procs. This implies all - Thread-local variables can now be declared inside procs. This implies all
the effects of the `global` pragma. the effects of the ``global`` pragma.
- Nim now supports `except` clause in the export statement. - Nim now supports the ``except`` clause in the export statement.
- Range float types, example ``range[0.0 .. Inf]``. More details in language manual.
- 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 ### Tool changes
@ -173,7 +208,7 @@
### Compiler changes ### Compiler changes
- The VM's instruction count limit was raised to 1 billion instructions in - The VM's instruction count limit was raised to 3 million instructions in
order to support more complex computations at compile-time. order to support more complex computations at compile-time.
- Support for hot code reloading has been implemented for the JavaScript - Support for hot code reloading has been implemented for the JavaScript
@ -199,6 +234,16 @@
- macros.bindSym now capable to accepts not only literal string or string constant expression. - macros.bindSym now capable to accepts not only literal string or string constant expression.
bindSym enhancement make it also can accepts computed string or ident node inside macros / bindSym enhancement make it also can accepts computed string or ident node inside macros /
compile time functions / static blocks. Only in templates / regular code it retains it's old behavior. compile time functions / static blocks. Only in templates / regular code it retains it's old behavior.
This new feature can be accessed via {.experimental: "dynamicBindSym".} pragma/switch This new feature can be accessed via {.experimental: "dynamicBindSym".} pragma/switch.
- 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 ### Bugfixes

View file

@ -591,7 +591,7 @@ type
mAddI, mSubI, mMulI, mDivI, mModI, mAddI, mSubI, mMulI, mDivI, mModI,
mSucc, mPred, mSucc, mPred,
mAddF64, mSubF64, mMulF64, mDivF64, mAddF64, mSubF64, mMulF64, mDivF64,
mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mShrI, mShlI, mAshrI, mBitandI, mBitorI, mBitxorI,
mMinI, mMaxI, mMinI, mMaxI,
mMinF64, mMaxF64, mMinF64, mMaxF64,
mAddU, mSubU, mMulU, mDivU, mModU, mAddU, mSubU, mMulU, mDivU, mModU,
@ -972,8 +972,8 @@ const
tyFloat..tyFloat128, tyUInt..tyUInt64} tyFloat..tyFloat128, tyUInt..tyUInt64}
ConstantDataTypes*: TTypeKinds = {tyArray, tySet, ConstantDataTypes*: TTypeKinds = {tyArray, tySet,
tyTuple, tySequence} tyTuple, tySequence}
NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tySequence, NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr,
tyProc, tyString, tyError} tyProc, tyError}
ExportableSymKinds* = {skVar, skConst, skProc, skFunc, skMethod, skType, ExportableSymKinds* = {skVar, skConst, skProc, skFunc, skMethod, skType,
skIterator, skIterator,
skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias} skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias}
@ -1151,6 +1151,9 @@ proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) =
for i in countup(0, high(src.data)): dest.data[i] = src.data[i] for i in countup(0, high(src.data)): dest.data[i] = src.data[i]
proc discardSons*(father: PNode) = proc discardSons*(father: PNode) =
when defined(nimNoNilSeqs):
father.sons = @[]
else:
father.sons = nil father.sons = nil
proc withInfo*(n: PNode, info: TLineInfo): PNode = proc withInfo*(n: PNode, info: TLineInfo): PNode =
@ -1368,7 +1371,7 @@ proc createModuleAlias*(s: PSym, newIdent: PIdent, info: TLineInfo;
result.loc = s.loc result.loc = s.loc
result.annex = s.annex result.annex = s.annex
# XXX once usedGenerics is used, ensure module aliases keep working! # XXX once usedGenerics is used, ensure module aliases keep working!
assert s.usedGenerics == nil assert s.usedGenerics.len == 0
proc initStrTable*(x: var TStrTable) = proc initStrTable*(x: var TStrTable) =
x.counter = 0 x.counter = 0
@ -1593,6 +1596,9 @@ proc getStr*(a: PNode): string =
of nkStrLit..nkTripleStrLit: result = a.strVal of nkStrLit..nkTripleStrLit: result = a.strVal
of nkNilLit: of nkNilLit:
# let's hope this fixes more problems than it creates: # let's hope this fixes more problems than it creates:
when defined(nimNoNilSeqs):
result = ""
else:
result = nil result = nil
else: else:
doAssert false, "getStr" doAssert false, "getStr"
@ -1675,6 +1681,14 @@ proc skipStmtList*(n: PNode): PNode =
else: else:
result = n 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 = proc toRef*(typ: PType): PType =
## If ``typ`` is a tyObject then it is converted into a `ref <typ>` and ## If ``typ`` is a tyObject then it is converted into a `ref <typ>` and
## returned. Otherwise ``typ`` is simply returned as-is. ## 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: else:
addf(result, ",$N$1\"ident\": null", [istr]) addf(result, ",$N$1\"ident\": null", [istr])
else: else:
if renderType and n.typ != nil:
addf(result, ",$N$1\"typ\": $2", [istr, debugType(conf, n.typ, 2)])
if sonsLen(n) > 0: if sonsLen(n) > 0:
addf(result, ",$N$1\"sons\": [", [istr]) addf(result, ",$N$1\"sons\": [", [istr])
for i in countup(0, sonsLen(n) - 1): 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)) assert(sonsLen(typ) == sonsLen(typ.n))
# don't call '$' here for efficiency: # don't call '$' here for efficiency:
let pat = ri.sons[0].sym.loc.r.data let pat = ri.sons[0].sym.loc.r.data
internalAssert p.config, pat != nil internalAssert p.config, pat.len > 0
if pat.contains({'#', '(', '@', '\''}): if pat.contains({'#', '(', '@', '\''}):
var pl = genPatternCall(p, ri, pat, typ) var pl = genPatternCall(p, ri, pat, typ)
# simpler version of 'fixupCall' that works with the pl+params combination: # simpler version of 'fixupCall' that works with the pl+params combination:
@ -484,7 +484,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
# don't call '$' here for efficiency: # don't call '$' here for efficiency:
let pat = ri.sons[0].sym.loc.r.data let pat = ri.sons[0].sym.loc.r.data
internalAssert p.config, pat != nil internalAssert p.config, pat.len > 0
var start = 3 var start = 3
if ' ' in pat: if ' ' in pat:
start = 1 start = 1

View file

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

View file

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

View file

@ -256,7 +256,15 @@ proc genSingleVar(p: BProc, a: PNode) =
# That's why we are doing the construction inside the preInitProc. # That's why we are doing the construction inside the preInitProc.
# genObjectInit relies on the C runtime's guarantees that # genObjectInit relies on the C runtime's guarantees that
# global variables will be initialized to zero. # 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): # Alternative construction using default constructor (which may zeromem):
# if sfImportc notin v.flags: constructLoc(p.module.preInitProc, v.loc) # if sfImportc notin v.flags: constructLoc(p.module.preInitProc, v.loc)
if sfExportc in v.flags and p.module.g.generatedHeader != nil: 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) patchAsgnStmtListExpr(patchedTree, e, ri)
genStmts(p, patchedTree) genStmts(p, patchedTree)
return return
var a: TLoc var a: TLoc
discard getTypeDesc(p.module, le.typ.skipTypes(skipPtrs))
if le.kind in {nkDerefExpr, nkHiddenDeref}: if le.kind in {nkDerefExpr, nkHiddenDeref}:
genDeref(p, le, a, enforceDeref=true) genDeref(p, le, a, enforceDeref=true)
else: else:

View file

@ -1147,12 +1147,29 @@ proc genInitCode(m: BModule) =
appcg(m, m.s[cfsTypeInit1], "static #TNimType $1[$2];$n", appcg(m, m.s[cfsTypeInit1], "static #TNimType $1[$2];$n",
[m.nimTypesName, rope(m.nimTypes)]) [m.nimTypesName, rope(m.nimTypes)])
add(prc, initGCFrame(m.initProc)) # 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, genSectionStart(cpsLocals, m.config))
add(prc, m.preInitProc.s(cpsLocals)) 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.initProc.s(cpsLocals)) add(prc, m.initProc.s(cpsLocals))
add(prc, m.postInitProc.s(cpsLocals))
add(prc, genSectionEnd(cpsLocals, m.config)) add(prc, genSectionEnd(cpsLocals, m.config))
if optStackTrace in m.initProc.options and frameDeclared notin m.flags: 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, ~"\tTFrame FR_; FR_.len = 0;$N")
add(prc, genSectionStart(cpsInit, m.config)) add(prc, genSectionStart(cpsInit, m.config))
add(prc, m.preInitProc.s(cpsInit))
add(prc, m.initProc.s(cpsInit)) add(prc, m.initProc.s(cpsInit))
add(prc, m.postInitProc.s(cpsInit))
add(prc, genSectionEnd(cpsInit, m.config)) add(prc, genSectionEnd(cpsInit, m.config))
add(prc, genSectionStart(cpsStmts, m.config)) add(prc, genSectionStart(cpsStmts, m.config))
add(prc, m.preInitProc.s(cpsStmts))
add(prc, m.initProc.s(cpsStmts)) add(prc, m.initProc.s(cpsStmts))
add(prc, m.postInitProc.s(cpsStmts))
add(prc, genSectionEnd(cpsStmts, m.config)) add(prc, genSectionEnd(cpsStmts, m.config))
if optStackTrace in m.initProc.options and preventStackTrace notin m.flags: if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
add(prc, deinitFrame(m.initProc)) add(prc, deinitFrame(m.initProc))
add(prc, deinitGCFrame(m.initProc)) add(prc, deinitGCFrame(m.initProc))
@ -1221,11 +1235,6 @@ proc newPreInitProc(m: BModule): BProc =
# little hack so that unique temporaries are generated: # little hack so that unique temporaries are generated:
result.labels = 100_000 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 = proc initProcOptions(m: BModule): TOptions =
let opts = m.config.options let opts = m.config.options
if sfSystemModule in m.module.flags: opts-{optStackTrace} else: opts 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 = newProc(nil, result)
result.initProc.options = initProcOptions(result) result.initProc.options = initProcOptions(result)
result.preInitProc = newPreInitProc(result) result.preInitProc = newPreInitProc(result)
result.postInitProc = newPostInitProc(result)
initNodeTable(result.dataCache) initNodeTable(result.dataCache)
result.typeStack = @[] result.typeStack = @[]
result.forwardedProcs = @[] result.forwardedProcs = @[]
@ -1258,7 +1266,6 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: string): BModule =
if sfSystemModule in module.flags: if sfSystemModule in module.flags:
incl result.flags, preventStackTrace incl result.flags, preventStackTrace
excl(result.preInitProc.options, optStackTrace) excl(result.preInitProc.options, optStackTrace)
excl(result.postInitProc.options, optStackTrace)
let ndiName = if optCDebug in g.config.globalOptions: changeFileExt(completeCFilePath(g.config, filename), "ndi") let ndiName = if optCDebug in g.config.globalOptions: changeFileExt(completeCFilePath(g.config, filename), "ndi")
else: "" else: ""
open(result.ndi, ndiName, g.config) open(result.ndi, ndiName, g.config)
@ -1276,7 +1283,6 @@ proc resetModule*(m: BModule) =
m.initProc = newProc(nil, m) m.initProc = newProc(nil, m)
m.initProc.options = initProcOptions(m) m.initProc.options = initProcOptions(m)
m.preInitProc = newPreInitProc(m) m.preInitProc = newPreInitProc(m)
m.postInitProc = newPostInitProc(m)
initNodeTable(m.dataCache) initNodeTable(m.dataCache)
m.typeStack = @[] m.typeStack = @[]
m.forwardedProcs = @[] m.forwardedProcs = @[]

View file

@ -147,7 +147,6 @@ type
headerFiles*: seq[string] # needed headers to include headerFiles*: seq[string] # needed headers to include
typeInfoMarker*: TypeCache # needed for generating type information typeInfoMarker*: TypeCache # needed for generating type information
initProc*: BProc # code for init procedure initProc*: BProc # code for init procedure
postInitProc*: BProc # code to be executed after the init proc
preInitProc*: BProc # code executed before the init proc preInitProc*: BProc # code executed before the init proc
typeStack*: TTypeSeq # used for type generation typeStack*: TTypeSeq # used for type generation
dataCache*: TNodeTable 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 "implicitstatic": result = contains(conf.options, optImplicitStatic)
of "patterns": result = contains(conf.options, optPatterns) of "patterns": result = contains(conf.options, optPatterns)
of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace) of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace)
of "nilseqs": result = contains(conf.options, optNilSeqs)
else: invalidCmdLineOption(conf, passCmd1, switch, info) else: invalidCmdLineOption(conf, passCmd1, switch, info)
proc processPath(conf: ConfigRef; path: string, info: TLineInfo, proc processPath(conf: ConfigRef; path: string, info: TLineInfo,
@ -497,6 +498,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
else: else:
localError(conf, info, errOnOrOffExpectedButXFound % arg) localError(conf, info, errOnOrOffExpectedButXFound % arg)
of "laxstrings": processOnOffSwitch(conf, {optLaxStrings}, arg, pass, info) of "laxstrings": processOnOffSwitch(conf, {optLaxStrings}, arg, pass, info)
of "nilseqs": processOnOffSwitch(conf, {optNilSeqs}, arg, pass, info)
of "checks", "x": processOnOffSwitch(conf, ChecksOptions, arg, pass, info) of "checks", "x": processOnOffSwitch(conf, ChecksOptions, arg, pass, info)
of "floatchecks": of "floatchecks":
processOnOffSwitch(conf, {optNaNCheck, optInfCheck}, arg, pass, info) processOnOffSwitch(conf, {optNaNCheck, optInfCheck}, arg, pass, info)

View file

@ -12,6 +12,8 @@
import import
strtabs, platform, strutils, idents strtabs, platform, strutils, idents
from options import Feature
const const
catNone = "false" catNone = "false"
@ -74,3 +76,10 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimVmExportFixed") defineSymbol("nimVmExportFixed")
defineSymbol("nimNewRuntime") defineSymbol("nimNewRuntime")
defineSymbol("nimIncrSeqV3") 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 = proc genComment(d: PDoc, n: PNode): string =
result = "" result = ""
var dummyHasToc: bool var dummyHasToc: bool
if n.comment != nil: if n.comment.len > 0:
renderRstToOut(d[], parseRst(n.comment, toFilename(d.conf, n.info), renderRstToOut(d[], parseRst(n.comment, toFilename(d.conf, n.info),
toLinenumber(n.info), toColumn(n.info), toLinenumber(n.info), toColumn(n.info),
dummyHasToc, d.options, d.conf), result) dummyHasToc, d.options, d.conf), result)
@ -205,7 +205,8 @@ proc genRecComment(d: PDoc, n: PNode): Rope =
result = genRecComment(d, n.sons[i]) result = genRecComment(d, n.sons[i])
if result != nil: return if result != nil: return
else: else:
n.comment = nil when defined(nimNoNilSeqs): n.comment = ""
else: n.comment = nil
proc getPlainDocstring(n: PNode): string = proc getPlainDocstring(n: PNode): string =
## Gets the plain text docstring of a node non destructively. ## Gets the plain text docstring of a node non destructively.
@ -215,7 +216,7 @@ proc getPlainDocstring(n: PNode): string =
## the concatenated ``##`` comments of the node. ## the concatenated ``##`` comments of the node.
result = "" result = ""
if n == nil: return if n == nil: return
if n.comment != nil and startsWith(n.comment, "##"): if startsWith(n.comment, "##"):
result = n.comment result = n.comment
if result.len < 1: if result.len < 1:
for i in countup(0, safeLen(n)-1): for i in countup(0, safeLen(n)-1):
@ -564,9 +565,9 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode =
result = %{ "name": %name, "type": %($k), "line": %n.info.line.int, result = %{ "name": %name, "type": %($k), "line": %n.info.line.int,
"col": %n.info.col} "col": %n.info.col}
if comm != nil and comm != "": if comm.len > 0:
result["description"] = %comm result["description"] = %comm
if r.buf != nil: if r.buf.len > 0:
result["code"] = %r.buf result["code"] = %r.buf
proc checkForFalse(n: PNode): bool = proc checkForFalse(n: PNode): bool =
@ -634,7 +635,7 @@ proc add(d: PDoc; j: JsonNode) =
proc generateJson*(d: PDoc, n: PNode) = proc generateJson*(d: PDoc, n: PNode) =
case n.kind case n.kind
of nkCommentStmt: of nkCommentStmt:
if n.comment != nil and startsWith(n.comment, "##"): if startsWith(n.comment, "##"):
let stripped = n.comment.substr(2).strip let stripped = n.comment.substr(2).strip
d.add %{ "comment": %stripped, "line": %n.info.line.int, d.add %{ "comment": %stripped, "line": %n.info.line.int,
"col": %n.info.col } "col": %n.info.col }
@ -678,7 +679,7 @@ proc genTagsItem(d: PDoc, n, nameNode: PNode, k: TSymKind): string =
proc generateTags*(d: PDoc, n: PNode, r: var Rope) = proc generateTags*(d: PDoc, n: PNode, r: var Rope) =
case n.kind case n.kind
of nkCommentStmt: of nkCommentStmt:
if n.comment != nil and startsWith(n.comment, "##"): if startsWith(n.comment, "##"):
let stripped = n.comment.substr(2).strip let stripped = n.comment.substr(2).strip
r.add stripped r.add stripped
of nkProcDef: of nkProcDef:

View file

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

View file

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

View file

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

View file

@ -11,7 +11,7 @@
import import
intsets, ast, astalgo, idents, semdata, types, msgs, options, 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) 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 "\nThis might be caused by a recursive module dependency: "
err.add c.recursiveDep err.add c.recursiveDep
# prevent excessive errors for 'nim check' # prevent excessive errors for 'nim check'
c.recursiveDep = nil c.recursiveDep = ""
localError(c.config, info, errGenerated, err) localError(c.config, info, errGenerated, err)
proc lookUp*(c: PContext, n: PNode): PSym = proc lookUp*(c: PContext, n: PNode): PSym =

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -281,7 +281,7 @@ const
proc shouldRenderComment(g: var TSrcGen, n: PNode): bool = proc shouldRenderComment(g: var TSrcGen, n: PNode): bool =
result = false result = false
if n.comment != nil: if n.comment.len > 0:
result = (renderNoComments notin g.flags) or result = (renderNoComments notin g.flags) or
(renderDocComments in g.flags) (renderDocComments in g.flags)
@ -402,7 +402,7 @@ proc lsons(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int =
proc lsub(g: TSrcGen; n: PNode): int = proc lsub(g: TSrcGen; n: PNode): int =
# computes the length of a tree # computes the length of a tree
if isNil(n): return 0 if isNil(n): return 0
if n.comment != nil: return MaxLineLen + 1 if n.comment.len > 0: return MaxLineLen + 1
case n.kind case n.kind
of nkEmpty: result = 0 of nkEmpty: result = 0
of nkTripleStrLit: of nkTripleStrLit:
@ -500,7 +500,7 @@ proc lsub(g: TSrcGen; n: PNode): int =
of nkBreakStmt: result = lsub(g, n.sons[0]) + len("break_") of nkBreakStmt: result = lsub(g, n.sons[0]) + len("break_")
of nkContinueStmt: result = lsub(g, n.sons[0]) + len("continue_") of nkContinueStmt: result = lsub(g, n.sons[0]) + len("continue_")
of nkPragma: result = lcomma(g, n) + 4 of nkPragma: result = lcomma(g, n) + 4
of nkCommentStmt: result = if n.comment.isNil: 0 else: len(n.comment) of nkCommentStmt: result = len(n.comment)
of nkOfBranch: result = lcomma(g, n, 0, - 2) + lsub(g, lastSon(n)) + len("of_:_") of nkOfBranch: result = lcomma(g, n, 0, - 2) + lsub(g, lastSon(n)) + len("of_:_")
of nkImportAs: result = lsub(g, n.sons[0]) + len("_as_") + lsub(g, n.sons[1]) of nkImportAs: result = lsub(g, n.sons[0]) + len("_as_") + lsub(g, n.sons[1])
of nkElifBranch: result = lsons(g, n) + len("elif_:_") of nkElifBranch: result = lsons(g, n) + len("elif_:_")
@ -539,7 +539,7 @@ proc gsub(g: var TSrcGen, n: PNode) =
proc hasCom(n: PNode): bool = proc hasCom(n: PNode): bool =
result = false result = false
if n.isNil: return false if n.isNil: return false
if n.comment != nil: return true if n.comment.len > 0: return true
case n.kind case n.kind
of nkEmpty..nkNilLit: discard of nkEmpty..nkNilLit: discard
else: else:
@ -602,7 +602,7 @@ proc gsection(g: var TSrcGen, n: PNode, c: TContext, kind: TTokType,
dedent(g) dedent(g)
proc longMode(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): bool = proc longMode(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): bool =
result = n.comment != nil result = n.comment.len > 0
if not result: if not result:
# check further # check further
for i in countup(start, sonsLen(n) + theEnd): for i in countup(start, sonsLen(n) + theEnd):
@ -864,7 +864,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
if isNil(n): return if isNil(n): return
var var
a: TContext a: TContext
if n.comment != nil: pushCom(g, n) if n.comment.len > 0: pushCom(g, n)
case n.kind # atoms: case n.kind # atoms:
of nkTripleStrLit: put(g, tkTripleStrLit, atom(g, n)) of nkTripleStrLit: put(g, tkTripleStrLit, atom(g, n))
of nkEmpty: discard of nkEmpty: discard

View file

@ -10,6 +10,27 @@
## Serialization utilities for the compiler. ## Serialization utilities for the compiler.
import strutils, math 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 c_snprintf(s: cstring; n:uint; frmt: cstring): cint {.importc: "snprintf", header: "<stdio.h>", nodecl, varargs.}
proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string = proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string =

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -268,6 +268,14 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
of tyInt64, tyInt, tyUInt..tyUInt64: of tyInt64, tyInt, tyUInt..tyUInt64:
result = newIntNodeT(`shr`(getInt(a), getInt(b)), n, g) result = newIntNodeT(`shr`(getInt(a), getInt(b)), n, g)
else: internalError(g.config, n.info, "constant folding for shr") 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 mDivI: result = foldDiv(getInt(a), getInt(b), n, g)
of mModI: result = foldMod(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) of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n, g)

View file

@ -97,7 +97,6 @@ proc sameInstantiation(a, b: TInstantiation): bool =
proc genericCacheGet(genericSym: PSym, entry: TInstantiation; proc genericCacheGet(genericSym: PSym, entry: TInstantiation;
id: CompilesId): PSym = id: CompilesId): PSym =
if genericSym.procInstCache != nil:
for inst in genericSym.procInstCache: for inst in genericSym.procInstCache:
if inst.compilesId == id and sameInstantiation(entry, inst[]): if inst.compilesId == id and sameInstantiation(entry, inst[]):
return inst.sym return inst.sym

View file

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

View file

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

View file

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

View file

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

View file

@ -143,7 +143,7 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
c.calleeScope = 1 c.calleeScope = 1
else: else:
c.calleeScope = calleeScope c.calleeScope = calleeScope
c.diagnostics = if diagnosticsEnabled: @[] else: nil c.diagnostics = @[] # if diagnosticsEnabled: @[] else: nil
c.diagnosticsEnabled = diagnosticsEnabled c.diagnosticsEnabled = diagnosticsEnabled
c.magic = c.calleeSym.magic c.magic = c.calleeSym.magic
initIdTable(c.bindings) initIdTable(c.bindings)
@ -535,6 +535,12 @@ proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
proc allowsNil(f: PType): TTypeRelation {.inline.} = proc allowsNil(f: PType): TTypeRelation {.inline.} =
result = if tfNotNil notin f.flags: isSubtype else: isNone result = if tfNotNil notin f.flags: isSubtype else: isNone
proc allowsNilDeprecated(c: TCandidate, f: PType): TTypeRelation =
if optNilSeqs in c.c.config.options:
result = allowsNil(f)
else:
result = isNone
proc inconsistentVarTypes(f, a: PType): bool {.inline.} = proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
result = f.kind != a.kind and (f.kind in {tyVar, tyLent} or a.kind in {tyVar, tyLent}) result = f.kind != a.kind and (f.kind in {tyVar, tyLent} or a.kind in {tyVar, tyLent})
@ -741,7 +747,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
diagnostics = @[] diagnostics = @[]
flags = {efExplain} flags = {efExplain}
m.c.config.writelnHook = proc (s: string) = m.c.config.writelnHook = proc (s: string) =
if errorPrefix == nil: errorPrefix = typeClass.sym.name.s & ":" if errorPrefix.len == 0: errorPrefix = typeClass.sym.name.s & ":"
let msg = s.replace("Error:", errorPrefix) let msg = s.replace("Error:", errorPrefix)
if oldWriteHook != nil: oldWriteHook msg if oldWriteHook != nil: oldWriteHook msg
diagnostics.add msg diagnostics.add msg
@ -858,6 +864,10 @@ proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool =
if lhs[2].kind == nkIntLit: if lhs[2].kind == nkIntLit:
return inferStaticParam(c, lhs[1], rhs shl lhs[2].intVal) 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: of mUnaryMinusI:
return inferStaticParam(c, lhs[1], -rhs) return inferStaticParam(c, lhs[1], -rhs)
@ -1249,7 +1259,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
result = isNone result = isNone
elif tfNotNil in f.flags and tfNotNil notin a.flags: elif tfNotNil in f.flags and tfNotNil notin a.flags:
result = isNilConversion result = isNilConversion
of tyNil: result = f.allowsNil of tyNil: result = allowsNilDeprecated(c, f)
else: discard else: discard
of tyOrdinal: of tyOrdinal:
if isOrdinalType(a): if isOrdinalType(a):
@ -1334,7 +1344,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
result = isNilConversion result = isNilConversion
else: else:
result = isEqual result = isEqual
of tyNil: result = f.allowsNil of tyNil: result = allowsNilDeprecated(c, f)
else: discard else: discard
of tyCString: of tyCString:
# conversion from string to cstring is automatic: # conversion from string to cstring is automatic:
@ -1608,7 +1618,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
if f.sonsLen == 0: if f.sonsLen == 0:
result = isGeneric result = isGeneric
else: else:
internalAssert c.c.graph.config, a.sons != nil and a.sons.len > 0 internalAssert c.c.graph.config, a.len > 0
c.typedescMatched = true c.typedescMatched = true
var aa = a var aa = a
while aa.kind in {tyTypeDesc, tyGenericParam} and aa.len > 0: while aa.kind in {tyTypeDesc, tyGenericParam} and aa.len > 0:
@ -1806,6 +1816,10 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
let srca = typeRel(m, src, a) let srca = typeRel(m, src, a)
if srca notin {isEqual, isGeneric, isSubtype}: continue if srca notin {isEqual, isGeneric, isSubtype}: continue
let constraint = c.converters[i].typ.n[1].sym.constraint
if not constraint.isNil and not matchNodeKinds(constraint, arg):
continue
let destIsGeneric = containsGenericType(dest) let destIsGeneric = containsGenericType(dest)
if destIsGeneric: if destIsGeneric:
dest = generateTypeInstance(c, m.bindings, arg, dest) 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]) result = PTransNode(n.sons[0])
if n.typ.skipTypes(abstractVar).kind != tyOpenArray: if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
PNode(result).typ = n.typ PNode(result).typ = n.typ
elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
PNode(result).typ = toVar(PNode(result).typ)
of nkHiddenStdConv, nkHiddenSubConv, nkConv: of nkHiddenStdConv, nkHiddenSubConv, nkConv:
var m = n.sons[0].sons[1] var m = n.sons[0].sons[1]
if m.kind == a or m.kind == b: 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]) result = PTransNode(n.sons[0])
if n.typ.skipTypes(abstractVar).kind != tyOpenArray: if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
PNode(result).typ = n.typ PNode(result).typ = n.typ
elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
PNode(result).typ = toVar(PNode(result).typ)
else: else:
if n.sons[0].kind == a or n.sons[0].kind == b: if n.sons[0].kind == a or n.sons[0].kind == b:
# addr ( deref ( x )) --> x # 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 if call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
call.sons[0].typ.callConv == ccClosure: call.sons[0].typ.callConv == ccClosure:
n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode 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 n.sons[length-2] = transform(c, n.sons[length-2]).PNode
result[1] = lambdalifting.liftForLoop(c.graph, n, getCurrOwner(c)).PTransNode result[1] = lambdalifting.liftForLoop(c.graph, n, getCurrOwner(c)).PTransNode
else:
result[1] = newNode(nkEmpty).PTransNode
discard c.breakSyms.pop discard c.breakSyms.pop
return result 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 & ")" result = t.sym.name.s & " literal(" & $t.n.intVal & ")"
elif prefer in {preferName, preferTypeName} or t.sym.owner.isNil: elif prefer in {preferName, preferTypeName} or t.sym.owner.isNil:
result = t.sym.name.s result = t.sym.name.s
if t.kind == tyGenericParam and t.sons != nil and t.sonsLen > 0: if t.kind == tyGenericParam and t.sonsLen > 0:
result.add ": " result.add ": "
var first = true var first = true
for son in t.sons: for son in t.sons:
@ -1202,7 +1202,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
tyNone, tyForward, tyFromExpr: tyNone, tyForward, tyFromExpr:
result = t result = t
of tyNil: of tyNil:
if kind != skConst: result = t if kind != skConst and kind != skParam: result = t
of tyString, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString, tyPointer: of tyString, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString, tyPointer:
result = nil result = nil
of tyOrdinal: of tyOrdinal:
@ -1539,7 +1539,6 @@ proc isCompileTimeOnly*(t: PType): bool {.inline.} =
proc containsCompileTimeOnly*(t: PType): bool = proc containsCompileTimeOnly*(t: PType): bool =
if isCompileTimeOnly(t): return true if isCompileTimeOnly(t): return true
if t.sons != nil:
for i in 0 ..< t.sonsLen: for i in 0 ..< t.sonsLen:
if t.sons[i] != nil and isCompileTimeOnly(t.sons[i]): if t.sons[i] != nil and isCompileTimeOnly(t.sons[i]):
return true return true

View file

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

View file

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

View file

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

View file

@ -82,19 +82,21 @@ path="$lib/pure"
clang.cpp.options.linker = "-ldl" clang.cpp.options.linker = "-ldl"
tcc.options.linker = "-ldl" tcc.options.linker = "-ldl"
@end @end
@if bsd or haiku: @if bsd:
# BSD got posix_spawn only recently, so we deactivate it for osproc: # BSD got posix_spawn only recently, so we deactivate it for osproc:
define:useFork define:useFork
# at least NetBSD has problems with thread local storage: # at least NetBSD has problems with thread local storage:
tlsEmulation:on tlsEmulation:on
@end @end
@if haiku: @if haiku:
# -fopenmp # Haiku currently have problems with TLS
gcc.options.linker = "-lroot -lnetwork" # https://dev.haiku-os.org/ticket/14342
gcc.cpp.options.linker = "-lroot -lnetwork" tlsEmulation:on
clang.options.linker = "-lroot -lnetwork" gcc.options.linker = "-Wl,--as-needed -lnetwork"
clang.cpp.options.linker = "-lroot -lnetwork" gcc.cpp.options.linker = "-Wl,--as-needed -lnetwork"
tcc.options.linker = "-lroot -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
@end @end
@ -253,9 +255,14 @@ vcc.cpp.options.size = "/O1"
tcc.options.always = "-w" tcc.options.always = "-w"
# Configuration for the Genode toolchain # Configuration for the Genode toolchain
amd64.genode.gcc.cpp.exe = "genode-x86-g++" @if genode:
amd64.genode.gcc.exe = "genode-x86-gcc" gcc.path = "/usr/local/genode-gcc/bin"
amd64.genode.gcc.path = "/usr/local/genode-gcc/bin" gcc.cpp.options.always = "-D__GENODE__ -fno-stack-protector"
arm.genode.gcc.cpp.exe = "genode-arm-g++" @if i386 or amd64:
arm.genode.gcc.exe = "genode-arm-gcc" gcc.exe = "genode-x86-gcc"
arm.genode.gcc.path = "/usr/local/genode-gcc/bin" 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" --oldNewlines:on|off turn on|off the old behaviour of "\n"
--laxStrings:on|off when turned on, accessing the zero terminator in --laxStrings:on|off when turned on, accessing the zero terminator in
strings is allowed; only for backwards compatibility strings is allowed; only for backwards compatibility
--nilseqs:on|off allow 'nil' for strings/seqs for
backwards compatibility
--skipCfg do not read the general configuration file --skipCfg do not read the general configuration file
--skipUserCfg do not read the user's configuration file --skipUserCfg do not read the user's configuration file
--skipParentCfg do not read the parent dirs' configuration files --skipParentCfg do not read the parent dirs' configuration files

View file

@ -65,7 +65,6 @@ The JavaScript target
--------------------- ---------------------
Nim can also generate `JavaScript`:idx: code through the ``js`` command. 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. 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, Since JavaScript does not have a portable means to include another module,
@ -77,7 +76,7 @@ available. This includes:
* manual memory management (``alloc``, etc.) * manual memory management (``alloc``, etc.)
* casting and other unsafe operations (``cast`` operator, ``zeroMem``, etc.) * casting and other unsafe operations (``cast`` operator, ``zeroMem``, etc.)
* file management * file management
* most modules of the Standard library * most modules of the standard library
* proper 64 bit integer arithmetic * proper 64 bit integer arithmetic
* unsigned 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 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`` 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 file. However, you can also run the code with `nodejs`:idx:
platform for easily building fast, scalable network applications (`<http://nodejs.org>`_)::
<http://nodejs.org>`_::
nim js -d:nodejs -r examples/hallo.nim 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 The `nimcache`:idx: directory is generated during compilation and will hold
either temporary or final files depending on your backend target. The default either temporary or final files depending on your backend target. The default
name for the directory is ``nimcache`` but you can use the ``--nimcache`` name for the directory depends on the used backend and on your OS but you can
`compiler switch <nimc.html#command-line-switches>`_ to change it. use the ``--nimcache`` `compiler switch <nimc.html#command-line-switches>`_ to
change it.
Nimcache and C like targets Nimcache and C like targets
~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~

View file

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

View file

@ -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 Verbosity levels
---------------- ----------------
@ -114,7 +154,7 @@ passed as a command line argument to the compiler.
The ``nim`` executable processes configuration files in the following The ``nim`` executable processes configuration files in the following
directories (in this order; later files overwrite previous settings): 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. 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. 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. 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 Generated C code directory
-------------------------- --------------------------
The generated files that Nim produces all go into a subdirectory called The generated files that Nim produces all go into a subdirectory called
``nimcache`` 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 generated files. Files generated in this directory follow a naming logic which
you can read about in the `Nim Backend Integration document you can read about in the `Nim Backend Integration document
<backends.html#nimcache-naming-logic>`_. <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 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 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. 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 Compiler Selection
================== ==================

View file

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

View file

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

View file

@ -57,7 +57,8 @@ Commands for core developers:
zip builds the installation zip package zip builds the installation zip package
xz builds the installation tar.xz package xz builds the installation tar.xz package
testinstall test tar.xz package; Unix only! 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 temp options creates a temporary compiler for testing
winrelease creates a Windows release winrelease creates a Windows release
pushcsource push generated C sources to its repo pushcsource push generated C sources to its repo
@ -182,7 +183,7 @@ proc bundleNimbleExe() =
bundleNimbleSrc() bundleNimbleSrc()
# now compile Nimble and copy it to $nim/bin for the installer.ini # now compile Nimble and copy it to $nim/bin for the installer.ini
# to pick it up: # to pick it up:
nimexec("c -d:release dist/nimble/src/nimble.nim") nimexec("c -d:release --nilseqs:on dist/nimble/src/nimble.nim")
copyExe("dist/nimble/src/nimble".exe, "bin/nimble".exe) copyExe("dist/nimble/src/nimble".exe, "bin/nimble".exe)
proc buildNimble(latest: bool) = proc buildNimble(latest: bool) =
@ -209,7 +210,7 @@ proc buildNimble(latest: bool) =
else: else:
exec("git checkout -f stable") exec("git checkout -f stable")
exec("git pull") exec("git pull")
nimexec("c --noNimblePath -p:compiler -d:release " & installDir / "src/nimble.nim") nimexec("c --noNimblePath -p:compiler --nilseqs:on -d:release " & installDir / "src/nimble.nim")
copyExe(installDir / "src/nimble".exe, "bin/nimble".exe) copyExe(installDir / "src/nimble".exe, "bin/nimble".exe)
proc bundleNimsuggest(buildExe: bool) = proc bundleNimsuggest(buildExe: bool) =

View file

@ -432,6 +432,9 @@ proc getLine(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.}
proc getColumn(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.} proc getColumn(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.}
proc getFile(arg: NimNode): string {.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.} = proc lineInfoObj*(n: NimNode): LineInfo {.compileTime.} =
## returns ``LineInfo`` of ``n``, using absolute path for ``filename`` ## returns ``LineInfo`` of ``n``, using absolute path for ``filename``
result.filename = n.getFile result.filename = n.getFile

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -10,7 +10,7 @@
{.deadCodeElim: on.} # dce option deprecated {.deadCodeElim: on.} # dce option deprecated
const const
hasSpawnH = not defined(haiku) # should exist for every Posix system nowadays hasSpawnH = true # should exist for every Posix system nowadays
hasAioH = defined(linux) hasAioH = defined(linux)
when defined(linux) and not defined(android): when defined(linux) and not defined(android):
@ -43,6 +43,9 @@ type
Dirent* {.importc: "struct dirent", Dirent* {.importc: "struct dirent",
header: "<dirent.h>", final, pure.} = object ## dirent_t struct 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. d_ino*: Ino ## File serial number.
when defined(dragonfly): when defined(dragonfly):
# DragonflyBSD doesn't have `d_reclen` field. # DragonflyBSD doesn't have `d_reclen` field.
@ -54,6 +57,9 @@ type
## (not POSIX) ## (not POSIX)
when defined(linux) or defined(openbsd): when defined(linux) or defined(openbsd):
d_off*: Off ## Not an offset. Value that ``telldir()`` would return. 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. d_name*: array[0..255, char] ## Name of entry.
@ -599,6 +605,10 @@ else:
MSG_NOSIGNAL* {.importc, header: "<sys/socket.h>".}: cint 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. ## 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 hasSpawnH:
when defined(linux): when defined(linux):
# better be safe than sorry; Linux has this flag, macosx doesn't, don't # 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 ## We try to provide some hints about stack trace entries that the user
## may not be familiar with, in particular calls inside the stdlib. ## may not be familiar with, in particular calls inside the stdlib.
result = "" result = ""
if entry.procname == "processPendingCallbacks": if entry.procname == cstring"processPendingCallbacks":
if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0: if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0:
return "Executes pending callbacks" return "Executes pending callbacks"
elif entry.procname == "poll": elif entry.procname == cstring"poll":
if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0: if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0:
return "Processes asynchronous completion events" return "Processes asynchronous completion events"

View file

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

View file

@ -212,6 +212,9 @@ proc initIntSet*: IntSet =
#newSeq(result.data, InitIntSetSize) #newSeq(result.data, InitIntSetSize)
#result.max = InitIntSetSize-1 #result.max = InitIntSetSize-1
when defined(nimNoNilSeqs):
result.data = @[]
else:
result.data = nil result.data = nil
result.max = 0 result.max = 0
result.counter = 0 result.counter = 0
@ -222,6 +225,9 @@ proc clear*(result: var IntSet) =
#setLen(result.data, InitIntSetSize) #setLen(result.data, InitIntSetSize)
#for i in 0..InitIntSetSize-1: result.data[i] = nil #for i in 0..InitIntSetSize-1: result.data[i] = nil
#result.max = InitIntSetSize-1 #result.max = InitIntSetSize-1
when defined(nimNoNilSeqs):
result.data = @[]
else:
result.data = nil result.data = nil
result.max = 0 result.max = 0
result.counter = 0 result.counter = 0
@ -234,6 +240,9 @@ proc assign*(dest: var IntSet, src: IntSet) =
## copies `src` to `dest`. `dest` does not need to be initialized by ## copies `src` to `dest`. `dest` does not need to be initialized by
## `initIntSet`. ## `initIntSet`.
if src.elems <= src.a.len: if src.elems <= src.a.len:
when defined(nimNoNilSeqs):
dest.data = @[]
else:
dest.data = nil dest.data = nil
dest.max = 0 dest.max = 0
dest.counter = src.counter dest.counter = src.counter
@ -247,11 +256,9 @@ proc assign*(dest: var IntSet, src: IntSet) =
var it = src.head var it = src.head
while it != nil: while it != nil:
var h = it.key and dest.max var h = it.key and dest.max
while dest.data[h] != nil: h = nextTry(h, dest.max) while dest.data[h] != nil: h = nextTry(h, dest.max)
assert(dest.data[h] == nil) assert(dest.data[h] == nil)
var n: PTrunk var n: PTrunk
new(n) new(n)
n.next = dest.head n.next = dest.head
@ -259,7 +266,6 @@ proc assign*(dest: var IntSet, src: IntSet) =
n.bits = it.bits n.bits = it.bits
dest.head = n dest.head = n
dest.data[h] = n dest.data[h] = n
it = it.next it = it.next
proc union*(s1, s2: IntSet): IntSet = proc union*(s1, s2: IntSet): IntSet =

View file

@ -25,6 +25,28 @@ import macros
when not defined(nimhygiene): when not defined(nimhygiene):
{.pragma: dirty.} {.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] = proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
## Takes several sequences' items and returns them inside a new sequence. ## Takes several sequences' items and returns them inside a new sequence.
## ##
@ -496,10 +518,14 @@ template toSeq*(iter: untyped): untyped =
## result = true) ## result = true)
## assert odd_numbers == @[1, 3, 5, 7, 9] ## assert odd_numbers == @[1, 3, 5, 7, 9]
# Note: see also `mapIt` for explanation of some of the implementation
# subtleties.
when compiles(iter.len): when compiles(iter.len):
block:
evalOnceAs(iter2, iter, true)
var result = newSeq[type(iter)](iter2.len)
var i = 0 var i = 0
var result = newSeq[type(iter)](iter.len) for x in iter2:
for x in iter:
result[i] = x result[i] = x
inc i inc i
result result
@ -635,8 +661,7 @@ template mapIt*(s, typ, op: untyped): untyped =
result.add(op) result.add(op)
result result
template mapIt*(s: typed, op: untyped): untyped =
template mapIt*(s, op: untyped): untyped =
## Convenience template around the ``map`` proc to reduce typing. ## Convenience template around the ``map`` proc to reduce typing.
## ##
## The template injects the ``it`` variable which you can use directly in an ## The template injects the ``it`` variable which you can use directly in an
@ -653,16 +678,21 @@ template mapIt*(s, op: untyped): untyped =
block: block:
var it{.inject.}: type(items(s)); var it{.inject.}: type(items(s));
op)) op))
var result: seq[outType]
when compiles(s.len): when compiles(s.len):
let t = s 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 i = 0
result = newSeq[outType](t.len) var result = newSeq[outType](s2.len)
for it {.inject.} in t: for it {.inject.} in s2:
result[i] = op result[i] = op
i += 1 i += 1
result
else: else:
result = @[] var result: seq[outType] = @[]
for it {.inject.} in s: for it {.inject.} in s:
result.add(op) result.add(op)
result result
@ -752,6 +782,14 @@ macro mapLiterals*(constructor, op: untyped;
when isMainModule: when isMainModule:
import strutils 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 block: # concat test
let let
s1 = @[1, 2, 3] s1 = @[1, 2, 3]
@ -997,6 +1035,12 @@ when isMainModule:
result = true) result = true)
assert odd_numbers == @[1, 3, 5, 7, 9] 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 block: # foldl tests
let let
numbers = @[5, 9, 11] numbers = @[5, 9, 11]
@ -1023,10 +1067,12 @@ when isMainModule:
assert multiplication == 495, "Multiplication is (5*(9*(11)))" assert multiplication == 495, "Multiplication is (5*(9*(11)))"
assert concatenation == "nimiscool" assert concatenation == "nimiscool"
block: # mapIt tests block: # mapIt + applyIt test
counter = 0
var var
nums = @[1, 2, 3, 4] nums = @[1, 2, 3, 4]
strings = nums.mapIt($(4 * it)) strings = nums.identity.mapIt($(4 * it))
doAssert counter == 1
nums.applyIt(it * 3) nums.applyIt(it * 3)
assert nums[0] + nums[3] == 15 assert nums[0] + nums[3] == 15
assert strings[2] == "12" 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), float, nested=false) == (float(1), "abc", float(2))
doAssert mapLiterals(([1], ("abc"), 2), `$`, nested=true) == (["1"], "abc", "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): when not defined(testing):
echo "Finished doc tests" echo "Finished doc tests"

View file

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

View file

@ -43,6 +43,14 @@ when defined(genode):
proc affinitySpaceTotal(env: GenodeEnvPtr): cuint {. proc affinitySpaceTotal(env: GenodeEnvPtr): cuint {.
importcpp: "@->cpu().affinity_space().total()".} 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".} = proc countProcessors*(): int {.rtl, extern: "ncpi$1".} =
## returns the numer of the processors/cores the machine has. ## returns the numer of the processors/cores the machine has.
## Returns 0 if it cannot be detected. ## Returns 0 if it cannot be detected.
@ -86,6 +94,10 @@ proc countProcessors*(): int {.rtl, extern: "ncpi$1".} =
result = sysconf(SC_NPROC_ONLN) result = sysconf(SC_NPROC_ONLN)
elif defined(genode): elif defined(genode):
result = runtimeEnv.affinitySpaceTotal().int result = runtimeEnv.affinitySpaceTotal().int
elif defined(haiku):
var sysinfo: SystemInfo
if getSystemInfo(addr sysinfo) == 0:
result = sysinfo.cpuCount.int
else: else:
result = sysconf(SC_NPROCESSORS_ONLN) result = sysconf(SC_NPROCESSORS_ONLN)
if result <= 0: result = 0 if result <= 0: result = 0

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -378,23 +378,23 @@ proc newMultipartData*: MultipartData =
## Constructs a new ``MultipartData`` object. ## Constructs a new ``MultipartData`` object.
MultipartData(content: @[]) MultipartData(content: @[])
proc add*(p: var MultipartData, name, content: string, filename: string = nil, proc add*(p: var MultipartData, name, content: string, filename: string = "",
contentType: string = nil) = contentType: string = "") =
## Add a value to the multipart data. Raises a `ValueError` exception if ## Add a value to the multipart data. Raises a `ValueError` exception if
## `name`, `filename` or `contentType` contain newline characters. ## `name`, `filename` or `contentType` contain newline characters.
if {'\c','\L'} in name: if {'\c','\L'} in name:
raise newException(ValueError, "name contains a newline character") raise newException(ValueError, "name contains a newline character")
if filename != nil and {'\c','\L'} in filename: if {'\c','\L'} in filename:
raise newException(ValueError, "filename contains a newline character") raise newException(ValueError, "filename contains a newline character")
if contentType != nil and {'\c','\L'} in contentType: if {'\c','\L'} in contentType:
raise newException(ValueError, "contentType contains a newline character") raise newException(ValueError, "contentType contains a newline character")
var str = "Content-Disposition: form-data; name=\"" & name & "\"" var str = "Content-Disposition: form-data; name=\"" & name & "\""
if filename != nil: if filename.len > 0:
str.add("; filename=\"" & filename & "\"") str.add("; filename=\"" & filename & "\"")
str.add("\c\L") str.add("\c\L")
if contentType != nil: if contentType.len > 0:
str.add("Content-Type: " & contentType & "\c\L") str.add("Content-Type: " & contentType & "\c\L")
str.add("\c\L" & content & "\c\L") str.add("\c\L" & content & "\c\L")
@ -434,7 +434,7 @@ proc addFiles*(p: var MultipartData, xs: openarray[tuple[name, file: string]]):
var contentType: string var contentType: string
let (_, fName, ext) = splitFile(file) let (_, fName, ext) = splitFile(file)
if ext.len > 0: if ext.len > 0:
contentType = m.getMimetype(ext[1..ext.high], nil) contentType = m.getMimetype(ext[1..ext.high], "")
p.add(name, readFile(file), fName & ext, contentType) p.add(name, readFile(file), fName & ext, contentType)
result = p result = p
@ -457,7 +457,7 @@ proc `[]=`*(p: var MultipartData, name: string,
p.add(name, file.content, file.name, file.contentType) p.add(name, file.content, file.name, file.contentType)
proc format(p: MultipartData): tuple[contentType, body: string] = proc format(p: MultipartData): tuple[contentType, body: string] =
if p == nil or p.content == nil or p.content.len == 0: if p == nil or p.content.len == 0:
return ("", "") return ("", "")
# Create boundary that is not in the data to be formatted # Create boundary that is not in the data to be formatted
@ -807,6 +807,7 @@ type
lastProgressReport: float lastProgressReport: float
when SocketType is AsyncSocket: when SocketType is AsyncSocket:
bodyStream: FutureStream[string] bodyStream: FutureStream[string]
parseBodyFut: Future[void]
else: else:
bodyStream: Stream bodyStream: Stream
getBody: bool ## When `false`, the body is never read in requestAux. getBody: bool ## When `false`, the body is never read in requestAux.
@ -1066,10 +1067,14 @@ proc parseResponse(client: HttpClient | AsyncHttpClient,
if getBody: if getBody:
when client is HttpClient: when client is HttpClient:
client.bodyStream = newStringStream() client.bodyStream = newStringStream()
result.bodyStream = client.bodyStream
parseBody(client, result.headers, result.version)
else: else:
client.bodyStream = newFutureStream[string]("parseResponse") 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, proc newConnection(client: HttpClient | AsyncHttpClient,
url: Uri) {.multisync.} = url: Uri) {.multisync.} =
@ -1159,6 +1164,12 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url: string,
# Helper that actually makes the request. Does not handle redirects. # Helper that actually makes the request. Does not handle redirects.
let requestUrl = parseUri(url) 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) await newConnection(client, requestUrl)
let effectiveHeaders = client.headers.override(headers) 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!") doAssert(true, "Unsupported kqueue filter in the queue!")
if (kevent.flags and EV_EOF) != 0: 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: if kevent.fflags != 0:
rkey.errorCode = kevent.fflags.OSErrorCode rkey.errorCode = cast[OSErrorCode](kevent.fflags)
else: else:
# This assumes we are dealing with sockets. # This assumes we are dealing with sockets.
# TODO: For future-proofing it might be a good idea to give the # TODO: For future-proofing it might be a good idea to give the

View file

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

View file

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

View file

@ -49,7 +49,7 @@ proc fac*(n: int): int =
{.push checks:off, line_dir:off, stack_trace:off.} {.push checks:off, line_dir:off, stack_trace:off.}
when defined(Posix) and not defined(haiku): when defined(Posix):
{.passl: "-lm".} {.passl: "-lm".}
const const
@ -162,9 +162,6 @@ when not defined(JS): # C
proc log10*(x: float32): float32 {.importc: "log10f", header: "<math.h>".} proc log10*(x: float32): float32 {.importc: "log10f", header: "<math.h>".}
proc log10*(x: float64): float64 {.importc: "log10", header: "<math.h>".} proc log10*(x: float64): float64 {.importc: "log10", header: "<math.h>".}
## Computes the common logarithm (base 10) of `x` ## 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: float32): float32 {.importc: "expf", header: "<math.h>".}
proc exp*(x: float64): float64 {.importc: "exp", header: "<math.h>".} proc exp*(x: float64): float64 {.importc: "exp", header: "<math.h>".}
## Computes the exponential function of `x` (pow(E, x)) ## 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) proc arccsch*[T: float32|float64](x: T): T = arcsinh(1.0 / x)
## Computes the inverse hyperbolic cosecant of `x` ## Computes the inverse hyperbolic cosecant of `x`
const windowsCC89 = defined(windows) and (defined(vcc) or defined(bcc))
when not defined(JS): # C when not defined(JS): # C
proc hypot*(x, y: float32): float32 {.importc: "hypotf", header: "<math.h>".} proc hypot*(x, y: float32): float32 {.importc: "hypotf", header: "<math.h>".}
proc hypot*(x, y: float64): float64 {.importc: "hypot", header: "<math.h>".} proc hypot*(x, y: float64): float64 {.importc: "hypot", header: "<math.h>".}
@ -280,6 +279,8 @@ when not defined(JS): # C
## ##
## To compute power between integers, use `^` e.g. 2 ^ 6 ## To compute power between integers, use `^` e.g. 2 ^ 6
# TODO: add C89 version on windows
when not windowsCC89:
proc erf*(x: float32): float32 {.importc: "erff", header: "<math.h>".} proc erf*(x: float32): float32 {.importc: "erff", header: "<math.h>".}
proc erf*(x: float64): float64 {.importc: "erf", header: "<math.h>".} proc erf*(x: float64): float64 {.importc: "erf", header: "<math.h>".}
## The error function ## The error function
@ -314,7 +315,7 @@ when not defined(JS): # C
## .. code-block:: nim ## .. code-block:: nim
## echo ceil(-2.1) ## -2.0 ## echo ceil(-2.1) ## -2.0
when defined(windows) and (defined(vcc) or defined(bcc)): when windowsCC89:
# MSVC 2010 don't have trunc/truncf # MSVC 2010 don't have trunc/truncf
# this implementation was inspired by Go-lang Math.Trunc # this implementation was inspired by Go-lang Math.Trunc
proc truncImpl(f: float64): float64 = proc truncImpl(f: float64): float64 =
@ -452,6 +453,28 @@ when not defined(JS):
var exp: int32 var exp: int32
result = c_frexp(x, exp) result = c_frexp(x, exp)
exponent = 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: else:
proc frexp*[T: float32|float64](x: T, exponent: var int): T = proc frexp*[T: float32|float64](x: T, exponent: var int): T =
if x == 0.0: if x == 0.0:
@ -506,8 +529,8 @@ proc sgn*[T: SomeNumber](x: T): int {.inline.} =
{.pop.} {.pop.}
proc `^`*[T](x: T, y: Natural): T = proc `^`*[T](x: T, y: Natural): T =
## Computes ``x`` to the power ``y`. ``x`` must be non-negative, use ## Computes ``x`` to the power ``y``. ``x`` must be non-negative, use
## `pow <#pow,float,float>` for negative exponents. ## `pow <#pow,float,float>`_ for negative exponents.
when compiles(y >= T(0)): when compiles(y >= T(0)):
assert y >= T(0) assert y >= T(0)
else: else:
@ -564,7 +587,7 @@ proc lcm*[T](x, y: T): T =
## Computes the least common multiple of ``x`` and ``y``. ## Computes the least common multiple of ``x`` and ``y``.
x div gcd(x, y) * 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 # Check for no side effect annotation
proc mySqrt(num: float): float {.noSideEffect.} = proc mySqrt(num: float): float {.noSideEffect.} =
return sqrt(num) return sqrt(num)
@ -692,3 +715,14 @@ when isMainModule:
block: # log block: # log
doAssert log(4.0, 3.0) == ln(4.0) / ln(3.0) 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_socktype = toInt(sockType)
hints.ai_protocol = toInt(protocol) hints.ai_protocol = toInt(protocol)
# OpenBSD doesn't support AI_V4MAPPED and doesn't define the macro AI_V4MAPPED. # 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 # 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: if domain == AF_INET6:
hints.ai_flags = AI_V4MAPPED hints.ai_flags = AI_V4MAPPED
var gaiResult = getaddrinfo(address, $port, addr(hints), result) 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()) raiseOSError(osLastError())
result = newSocket(fd, domain, sockType, protocol, buffered) result = newSocket(fd, domain, sockType, protocol, buffered)
proc parseIPv4Address(address_str: string): IpAddress = proc parseIPv4Address(addressStr: string): IpAddress =
## Parses IPv4 adresses ## Parses IPv4 adresses
## Raises EInvalidValue on errors ## Raises EInvalidValue on errors
var var
@ -241,15 +241,15 @@ proc parseIPv4Address(address_str: string): IpAddress =
result.family = IpAddressFamily.IPv4 result.family = IpAddressFamily.IPv4
for i in 0 .. high(address_str): for i in 0 .. high(addressStr):
if address_str[i] in strutils.Digits: # Character is a number if addressStr[i] in strutils.Digits: # Character is a number
currentByte = currentByte * 10 + currentByte = currentByte * 10 +
cast[uint16](ord(address_str[i]) - ord('0')) cast[uint16](ord(addressStr[i]) - ord('0'))
if currentByte > 255'u16: if currentByte > 255'u16:
raise newException(ValueError, raise newException(ValueError,
"Invalid IP Address. Value is out of range") "Invalid IP Address. Value is out of range")
seperatorValid = true seperatorValid = true
elif address_str[i] == '.': # IPv4 address separator elif addressStr[i] == '.': # IPv4 address separator
if not seperatorValid or byteCount >= 3: if not seperatorValid or byteCount >= 3:
raise newException(ValueError, raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups") "Invalid IP Address. The address consists of too many groups")
@ -265,11 +265,11 @@ proc parseIPv4Address(address_str: string): IpAddress =
raise newException(ValueError, "Invalid IP Address") raise newException(ValueError, "Invalid IP Address")
result.address_v4[byteCount] = cast[uint8](currentByte) result.address_v4[byteCount] = cast[uint8](currentByte)
proc parseIPv6Address(address_str: string): IpAddress = proc parseIPv6Address(addressStr: string): IpAddress =
## Parses IPv6 adresses ## Parses IPv6 adresses
## Raises EInvalidValue on errors ## Raises EInvalidValue on errors
result.family = IpAddressFamily.IPv6 result.family = IpAddressFamily.IPv6
if address_str.len < 2: if addressStr.len < 2:
raise newException(ValueError, "Invalid IP Address") raise newException(ValueError, "Invalid IP Address")
var var
@ -282,7 +282,7 @@ proc parseIPv6Address(address_str: string): IpAddress =
v4StartPos = -1 v4StartPos = -1
byteCount = 0 byteCount = 0
for i,c in address_str: for i,c in addressStr:
if c == ':': if c == ':':
if not seperatorValid: if not seperatorValid:
raise newException(ValueError, raise newException(ValueError,
@ -293,7 +293,7 @@ proc parseIPv6Address(address_str: string): IpAddress =
"Invalid IP Address. Address contains more than one \"::\" seperator") "Invalid IP Address. Address contains more than one \"::\" seperator")
dualColonGroup = groupCount dualColonGroup = groupCount
seperatorValid = false seperatorValid = false
elif i != 0 and i != high(address_str): elif i != 0 and i != high(addressStr):
if groupCount >= 8: if groupCount >= 8:
raise newException(ValueError, raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups") "Invalid IP Address. The address consists of too many groups")
@ -303,11 +303,11 @@ proc parseIPv6Address(address_str: string): IpAddress =
groupCount.inc() groupCount.inc()
if dualColonGroup != -1: seperatorValid = false if dualColonGroup != -1: seperatorValid = false
elif i == 0: # only valid if address starts with :: elif i == 0: # only valid if address starts with ::
if address_str[1] != ':': if addressStr[1] != ':':
raise newException(ValueError, raise newException(ValueError,
"Invalid IP Address. Address may not start with \":\"") "Invalid IP Address. Address may not start with \":\"")
else: # i == high(address_str) - only valid if address ends with :: else: # i == high(addressStr) - only valid if address ends with ::
if address_str[high(address_str)-1] != ':': if addressStr[high(addressStr)-1] != ':':
raise newException(ValueError, raise newException(ValueError,
"Invalid IP Address. Address may not end with \":\"") "Invalid IP Address. Address may not end with \":\"")
lastWasColon = true lastWasColon = true
@ -345,7 +345,7 @@ proc parseIPv6Address(address_str: string): IpAddress =
result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF) result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
groupCount.inc() groupCount.inc()
else: # Must parse IPv4 address else: # Must parse IPv4 address
for i,c in address_str[v4StartPos..high(address_str)]: for i,c in addressStr[v4StartPos..high(addressStr)]:
if c in strutils.Digits: # Character is a number if c in strutils.Digits: # Character is a number
currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0')) currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0'))
if currentShort > 255'u32: if currentShort > 255'u32:
@ -386,21 +386,21 @@ proc parseIPv6Address(address_str: string): IpAddress =
raise newException(ValueError, raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups") "Invalid IP Address. The address consists of too many groups")
proc parseIpAddress*(address_str: string): IpAddress = proc parseIpAddress*(addressStr: string): IpAddress =
## Parses an IP address ## Parses an IP address
## Raises EInvalidValue on error ## Raises EInvalidValue on error
if address_str == nil: if addressStr.len == 0:
raise newException(ValueError, "IP Address string is nil") raise newException(ValueError, "IP Address string is empty")
if address_str.contains(':'): if addressStr.contains(':'):
return parseIPv6Address(address_str) return parseIPv6Address(addressStr)
else: else:
return parseIPv4Address(address_str) return parseIPv4Address(addressStr)
proc isIpAddress*(address_str: string): bool {.tags: [].} = proc isIpAddress*(addressStr: string): bool {.tags: [].} =
## Checks if a string is an IP address ## Checks if a string is an IP address
## Returns true if it is, false otherwise ## Returns true if it is, false otherwise
try: try:
discard parseIpAddress(address_str) discard parseIpAddress(addressStr)
except ValueError: except ValueError:
return false return false
return true return true
@ -587,7 +587,7 @@ when defineSsl:
proc pskClientCallback(ssl: SslPtr; hint: cstring; identity: cstring; max_identity_len: cuint; psk: ptr cuchar; proc pskClientCallback(ssl: SslPtr; hint: cstring; identity: cstring; max_identity_len: cuint; psk: ptr cuchar;
max_psk_len: cuint): cuint {.cdecl.} = max_psk_len: cuint): cuint {.cdecl.} =
let ctx = SSLContext(context: ssl.SSL_get_SSL_CTX) let ctx = SSLContext(context: ssl.SSL_get_SSL_CTX)
let hintString = if hint == nil: nil else: $hint let hintString = if hint == nil: "" else: $hint
let (identityString, pskString) = (ctx.clientGetPskFunc)(hintString) let (identityString, pskString) = (ctx.clientGetPskFunc)(hintString)
if psk.len.cuint > max_psk_len: if psk.len.cuint > max_psk_len:
return 0 return 0
@ -657,7 +657,7 @@ when defineSsl:
proc wrapConnectedSocket*(ctx: SSLContext, socket: Socket, proc wrapConnectedSocket*(ctx: SSLContext, socket: Socket,
handshake: SslHandshakeType, handshake: SslHandshakeType,
hostname: string = nil) = hostname: string = "") =
## Wraps a connected socket in an SSL context. This function effectively ## Wraps a connected socket in an SSL context. This function effectively
## turns ``socket`` into an SSL socket. ## turns ``socket`` into an SSL socket.
## ``hostname`` should be specified so that the client knows which hostname ## ``hostname`` should be specified so that the client knows which hostname
@ -671,7 +671,7 @@ when defineSsl:
wrapSocket(ctx, socket) wrapSocket(ctx, socket)
case handshake case handshake
of handshakeAsClient: of handshakeAsClient:
if not hostname.isNil and not isIpAddress(hostname): if hostname.len > 0 and not isIpAddress(hostname):
# Discard result in case OpenSSL version doesn't support SNI, or we're # Discard result in case OpenSSL version doesn't support SNI, or we're
# not using TLSv1+ # not using TLSv1+
discard SSL_set_tlsext_host_name(socket.sslHandle, hostname) discard SSL_set_tlsext_host_name(socket.sslHandle, hostname)

View file

@ -129,7 +129,7 @@ proc get*[T](self: Option[T]): T =
## Returns contents of the Option. If it is none, then an exception is ## Returns contents of the Option. If it is none, then an exception is
## thrown. ## thrown.
if self.isNone: 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 self.val
proc get*[T](self: Option[T], otherwise: T): T = proc get*[T](self: Option[T], otherwise: T): T =
@ -139,6 +139,13 @@ proc get*[T](self: Option[T], otherwise: T): T =
else: else:
otherwise 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)) = proc map*[T](self: Option[T], callback: proc (input: T)) =
## Applies a callback to the value in this Option ## Applies a callback to the value in this Option
if self.isSome: 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 ## 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. ## the type contained in the option.
if self.isSome: if self.isSome:
"Some(" & $self.val & ")" result = "Some("
result.addQuoted self.val
result.add ")"
else: else:
"None[" & name(T) & "]" result = "None[" & name(T) & "]"
when isMainModule: when isMainModule:
import unittest, sequtils import unittest, sequtils
@ -240,7 +249,7 @@ when isMainModule:
check(stringNone.get("Correct") == "Correct") check(stringNone.get("Correct") == "Correct")
test "$": test "$":
check($(some("Correct")) == "Some(Correct)") check($(some("Correct")) == "Some(\"Correct\")")
check($(stringNone) == "None[string]") check($(stringNone) == "None[string]")
test "map with a void result": 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 not declared(ENOENT) and not defined(Windows):
when NoFakeVars: when NoFakeVars:
when not defined(haiku):
const ENOENT = cint(2) # 2 on most systems including Solaris const ENOENT = cint(2) # 2 on most systems including Solaris
else:
const ENOENT = cint(-2147459069)
else: else:
var ENOENT {.importc, header: "<errno.h>".}: cint var ENOENT {.importc, header: "<errno.h>".}: cint
@ -972,6 +975,14 @@ proc rawCreateDir(dir: string): bool =
result = false result = false
else: else:
raiseOSError(osLastError(), dir) 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): elif defined(posix):
let res = mkdir(dir, 0o777) let res = mkdir(dir, 0o777)
if res == 0'i32: if res == 0'i32:
@ -1352,9 +1363,15 @@ elif defined(nintendoswitch):
proc paramCount*(): int {.tags: [ReadIOEffect].} = proc paramCount*(): int {.tags: [ReadIOEffect].} =
raise newException(OSError, "paramCount is not implemented on Nintendo Switch") 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 elif not defined(createNimRtl) and
not(defined(posix) and appType == "lib") and not(defined(posix) and appType == "lib"):
not defined(genode):
# On Posix, there is no portable way to get the command line from a DLL. # On Posix, there is no portable way to get the command line from a DLL.
var var
cmdCount {.importc: "cmdCount".}: cint 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. ## Returns the config directory of the current user for applications.
## ##
## On non-Windows OSs, this proc conforms to the XDG Base Directory ## 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, ## variable if it is set, and returns the default configuration directory,
## "~/.config/", otherwise. ## "~/.config/", otherwise.
## ##
## An OS-dependent trailing slash is always present at the end of the ## An OS-dependent trailing slash is always present at the end of the
## returned string; `\\` on Windows and `/` on all other OSs. ## returned string; `\\` on Windows and `/` on all other OSs.
when defined(windows): return string(getEnv("APPDATA")) & "\\" 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/" else: return string(getEnv("HOME")) & "/.config/"
proc getTempDir*(): string {.rtl, extern: "nos$1", proc getTempDir*(): string {.rtl, extern: "nos$1",
@ -553,25 +553,25 @@ proc getTempDir*(): string {.rtl, extern: "nos$1",
proc expandTilde*(path: string): string {. proc expandTilde*(path: string): string {.
tags: [ReadEnvEffect, ReadIOEffect].} = 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 `\\` ## Windows: this is still supported despite Windows platform not having this
## this proc will return the reminder of the path appended to the result of ## convention; also, both ``~/`` and ``~\`` are handled.
## the getHomeDir() proc, otherwise the input path will be returned without runnableExamples:
## modification. doAssert expandTilde("~" / "appname.cfg") == getHomeDir() / "appname.cfg"
## if len(path) == 0 or path[0] != '~':
## The behaviour of this proc is the same on the Windows platform despite result = path
## not having this convention. Example: elif len(path) == 1:
## result = getHomeDir()
## .. code-block:: nim elif (path[1] in {DirSep, AltSep}):
## 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] == '\\'):
result = getHomeDir() / path.substr(2) result = getHomeDir() / path.substr(2)
else: else:
# TODO: handle `~bob` and `~bob/` which means home of bob
result = path 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".} = proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
## Quote s, so it can be safely passed to Windows API. ## Quote s, so it can be safely passed to Windows API.
## Based on Python's subprocess.list2cmdline ## Based on Python's subprocess.list2cmdline
@ -623,6 +623,18 @@ when defined(windows) or defined(posix) or defined(nintendoswitch):
else: else:
return quoteShellPosix(s) 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: when isMainModule:
assert quoteShellWindows("aaa") == "aaa" assert quoteShellWindows("aaa") == "aaa"
assert quoteShellWindows("aaa\"") == "aaa\\\"" assert quoteShellWindows("aaa\"") == "aaa\\\""

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -377,6 +377,9 @@ when not defined(js):
proc ssClose(s: Stream) = proc ssClose(s: Stream) =
var s = StringStream(s) var s = StringStream(s)
when defined(nimNoNilSeqs):
s.data = ""
else:
s.data = nil s.data = nil
proc newStringStream*(s: string = ""): StringStream = proc newStringStream*(s: string = ""): StringStream =

View file

@ -358,9 +358,6 @@ proc isNilOrEmpty*(s: string): bool {.noSideEffect, procvar, rtl,
proc isNilOrWhitespace*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsNilOrWhitespace".} = proc isNilOrWhitespace*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsNilOrWhitespace".} =
## Checks if `s` is nil or consists entirely of whitespace characters. ## Checks if `s` is nil or consists entirely of whitespace characters.
if len(s) == 0:
return true
result = true result = true
for c in s: for c in s:
if not c.isSpaceAscii(): if not c.isSpaceAscii():
@ -624,12 +621,13 @@ iterator rsplit*(s: string, sep: string, maxsplit: int = -1,
## Substrings are separated from the right by the string `sep` ## Substrings are separated from the right by the string `sep`
rsplitCommon(s, sep, maxsplit, sep.len) 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. ## Splits the string `s` into its containing lines.
## ##
## Every `character literal <manual.html#character-literals>`_ newline ## Every `character literal <manual.html#character-literals>`_ newline
## combination (CR, LF, CR-LF) is supported. The result strings contain no ## 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: ## Example:
## ##
@ -649,22 +647,30 @@ iterator splitLines*(s: string): string =
## "" ## ""
var first = 0 var first = 0
var last = 0 var last = 0
var eolpos = 0
while true: while true:
while last < s.len and s[last] notin {'\c', '\l'}: inc(last) while last < s.len and s[last] notin {'\c', '\l'}: inc(last)
yield substr(s, first, last-1)
# skip newlines: eolpos = last
if last >= s.len: break if last < s.len:
if s[last] == '\l': inc(last) if s[last] == '\l': inc(last)
elif s[last] == '\c': elif s[last] == '\c':
inc(last) inc(last)
if last < s.len and s[last] == '\l': 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 first = last
proc splitLines*(s: string): seq[string] {.noSideEffect, proc splitLines*(s: string, keepEol = false): seq[string] {.noSideEffect,
rtl, extern: "nsuSplitLines".} = rtl, extern: "nsuSplitLines".} =
## The same as the `splitLines <#splitLines.i,string>`_ iterator, but is a ## The same as the `splitLines <#splitLines.i,string>`_ iterator, but is a
## proc that returns a sequence of substrings. ## proc that returns a sequence of substrings.
accumulateResult(splitLines(s)) accumulateResult(splitLines(s, keepEol=keepEol))
proc countLines*(s: string): int {.noSideEffect, proc countLines*(s: string): int {.noSideEffect,
rtl, extern: "nsuCountLines".} = rtl, extern: "nsuCountLines".} =
@ -1369,7 +1375,9 @@ proc find*(s: string, sub: char, start: Natural = 0, last: Natural = 0): int {.n
if sub == s[i]: return i if sub == s[i]: return i
else: else:
when hasCStringBuiltin: when hasCStringBuiltin:
let found = c_memchr(s[start].unsafeAddr, sub, last-start+1) let L = last-start+1
if L > 0:
let found = c_memchr(s[start].unsafeAddr, sub, L)
if not found.isNil: if not found.isNil:
return cast[ByteAddress](found) -% cast[ByteAddress](s.cstring) return cast[ByteAddress](found) -% cast[ByteAddress](s.cstring)
else: else:
@ -1518,7 +1526,7 @@ proc replace*(s, sub: string, by = ""): string {.noSideEffect,
elif subLen == 1: elif subLen == 1:
# when the pattern is a single char, we use a faster # when the pattern is a single char, we use a faster
# char-based search that doesn't need a skip table: # char-based search that doesn't need a skip table:
var c = sub[0] let c = sub[0]
let last = s.high let last = s.high
var i = 0 var i = 0
while true: while true:

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