Merge branch 'devel' of github.com:nim-lang/Nim into devel
This commit is contained in:
commit
b3732e2371
23 changed files with 419 additions and 59 deletions
|
|
@ -48,3 +48,5 @@ 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 )
|
||||||
|
|
|
||||||
27
changelog.md
27
changelog.md
|
|
@ -102,10 +102,12 @@ This now needs to be written as:
|
||||||
- Nim's ``rst2html`` command now supports the testing of code snippets via an RST
|
- Nim's ``rst2html`` command now supports the testing of code snippets via an RST
|
||||||
extension that we called ``:test:``::
|
extension that we called ``:test:``::
|
||||||
|
|
||||||
|
```rst
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
:test:
|
:test:
|
||||||
# shows how the 'if' statement works
|
# shows how the 'if' statement works
|
||||||
if true: echo "yes"
|
if true: echo "yes"
|
||||||
|
```
|
||||||
- The ``[]`` proc for strings now raises an ``IndexError`` exception when
|
- The ``[]`` proc for strings now raises an ``IndexError`` exception when
|
||||||
the specified slice is out of bounds. See issue
|
the specified slice is out of bounds. See issue
|
||||||
[#6223](https://github.com/nim-lang/Nim/issues/6223) for more details.
|
[#6223](https://github.com/nim-lang/Nim/issues/6223) for more details.
|
||||||
|
|
@ -130,7 +132,8 @@ This now needs to be written as:
|
||||||
- The ``random`` procs in ``random.nim`` have all been deprecated. Instead use
|
- The ``random`` procs in ``random.nim`` have all been deprecated. Instead use
|
||||||
the new ``rand`` procs. The module now exports the state of the random
|
the new ``rand`` procs. The module now exports the state of the random
|
||||||
number generator as type ``Rand`` so multiple threads can easily use their
|
number generator as type ``Rand`` so multiple threads can easily use their
|
||||||
own random number generators that do not require locking.
|
own random number generators that do not require locking. For more information
|
||||||
|
about this rename see issue [#6934](https://github.com/nim-lang/Nim/issues/6934)
|
||||||
- The compiler is now more consistent in its treatment of ambiguous symbols:
|
- The compiler is now more consistent in its treatment of ambiguous symbols:
|
||||||
Types that shadow procs and vice versa are marked as ambiguous (bug #6693).
|
Types that shadow procs and vice versa are marked as ambiguous (bug #6693).
|
||||||
- ``yield`` (or ``await`` which is mapped to ``yield``) never worked reliably
|
- ``yield`` (or ``await`` which is mapped to ``yield``) never worked reliably
|
||||||
|
|
@ -141,3 +144,25 @@ This now needs to be written as:
|
||||||
- codegenDecl pragma now works for the JavaScript backend. It returns an empty string for
|
- codegenDecl pragma now works for the JavaScript backend. It returns an empty string for
|
||||||
function return type placeholders.
|
function return type placeholders.
|
||||||
- Asynchronous programming for the JavaScript backend using the `asyncjs` module.
|
- Asynchronous programming for the JavaScript backend using the `asyncjs` module.
|
||||||
|
- Extra semantic checks for procs with noreturn pragma: return type is not allowed,
|
||||||
|
statements after call to noreturn procs are no longer allowed.
|
||||||
|
- Noreturn proc calls and raising exceptions branches are now skipped during common type
|
||||||
|
deduction in if and case expressions. The following code snippets now compile:
|
||||||
|
```nim
|
||||||
|
import strutils
|
||||||
|
let str = "Y"
|
||||||
|
let a = case str:
|
||||||
|
of "Y": true
|
||||||
|
of "N": false
|
||||||
|
else: raise newException(ValueError, "Invalid boolean")
|
||||||
|
let b = case str:
|
||||||
|
of nil, "": raise newException(ValueError, "Invalid boolean")
|
||||||
|
elif str.startsWith("Y"): true
|
||||||
|
elif str.startsWith("N"): false
|
||||||
|
else: false
|
||||||
|
let c = if str == "Y": true
|
||||||
|
elif str == "N": false
|
||||||
|
else:
|
||||||
|
echo "invalid bool"
|
||||||
|
quit("this is the end")
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -611,7 +611,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
of "skipparentcfg":
|
of "skipparentcfg":
|
||||||
expectNoArg(switch, arg, pass, info)
|
expectNoArg(switch, arg, pass, info)
|
||||||
incl(gGlobalOptions, optSkipParentConfigFiles)
|
incl(gGlobalOptions, optSkipParentConfigFiles)
|
||||||
of "genscript":
|
of "genscript", "gendeps":
|
||||||
expectNoArg(switch, arg, pass, info)
|
expectNoArg(switch, arg, pass, info)
|
||||||
incl(gGlobalOptions, optGenScript)
|
incl(gGlobalOptions, optGenScript)
|
||||||
of "colors": processOnOffSwitchG({optUseColors}, arg, pass, info)
|
of "colors": processOnOffSwitchG({optUseColors}, arg, pass, info)
|
||||||
|
|
|
||||||
|
|
@ -344,20 +344,20 @@ proc dfa(code: seq[Instr]) =
|
||||||
case code[i].kind
|
case code[i].kind
|
||||||
of use, useWithinCall: u[i].incl(code[i].sym.id)
|
of use, useWithinCall: u[i].incl(code[i].sym.id)
|
||||||
of def: d[i].incl(code[i].sym.id)
|
of def: d[i].incl(code[i].sym.id)
|
||||||
of fork:
|
of fork, goto:
|
||||||
let d = i+code[i].dest
|
let d = i+code[i].dest
|
||||||
backrefs.add(d, i)
|
backrefs.add(d, i)
|
||||||
of goto: discard
|
|
||||||
|
|
||||||
var w = @[0]
|
var w = @[0]
|
||||||
var maxIters = 50
|
var maxIters = 50
|
||||||
var someChange = true
|
var someChange = true
|
||||||
while w.len > 0 and maxIters > 0 and someChange:
|
var takenGotos = initIntSet()
|
||||||
|
while w.len > 0 and maxIters > 0: # and someChange:
|
||||||
dec maxIters
|
dec maxIters
|
||||||
var pc = w.pop() # w[^1]
|
var pc = w.pop() # w[^1]
|
||||||
var prevPc = -1
|
var prevPc = -1
|
||||||
# this simulates a single linear control flow execution:
|
# this simulates a single linear control flow execution:
|
||||||
while pc < code.len and someChange:
|
while pc < code.len:
|
||||||
# according to the paper, it is better to shrink the working set here
|
# according to the paper, it is better to shrink the working set here
|
||||||
# in this inner loop:
|
# in this inner loop:
|
||||||
#let widx = w.find(pc)
|
#let widx = w.find(pc)
|
||||||
|
|
@ -386,17 +386,21 @@ proc dfa(code: seq[Instr]) =
|
||||||
if def notin d[prevPc]:
|
if def notin d[prevPc]:
|
||||||
excl(intersect, def)
|
excl(intersect, def)
|
||||||
someChange = true
|
someChange = true
|
||||||
|
when defined(debugDfa):
|
||||||
|
echo "Excluding ", pc, " prev ", prevPc
|
||||||
assign d[pc], intersect
|
assign d[pc], intersect
|
||||||
|
|
||||||
# our interpretation ![I!]:
|
# our interpretation ![I!]:
|
||||||
prevPc = pc
|
prevPc = pc
|
||||||
|
when defined(debugDfa):
|
||||||
|
echo "looking at ", pc
|
||||||
case code[pc].kind
|
case code[pc].kind
|
||||||
of goto:
|
of goto:
|
||||||
# we must leave endless loops eventually:
|
# we must leave endless loops eventually:
|
||||||
#if someChange:
|
if not takenGotos.containsOrIncl(pc) or someChange:
|
||||||
pc = pc + code[pc].dest
|
pc = pc + code[pc].dest
|
||||||
#else:
|
else:
|
||||||
# inc pc
|
inc pc
|
||||||
of fork:
|
of fork:
|
||||||
# we follow the next instruction but push the dest onto our "work" stack:
|
# we follow the next instruction but push the dest onto our "work" stack:
|
||||||
#if someChange:
|
#if someChange:
|
||||||
|
|
@ -405,6 +409,10 @@ proc dfa(code: seq[Instr]) =
|
||||||
of use, useWithinCall, def:
|
of use, useWithinCall, def:
|
||||||
inc pc
|
inc pc
|
||||||
|
|
||||||
|
when defined(useDfa) and defined(debugDfa):
|
||||||
|
for i in 0..<code.len:
|
||||||
|
echo "PC ", i, ": defs: ", d[i], "; uses ", u[i]
|
||||||
|
|
||||||
# now check the condition we're interested in:
|
# now check the condition we're interested in:
|
||||||
for i in 0..<code.len:
|
for i in 0..<code.len:
|
||||||
case code[i].kind
|
case code[i].kind
|
||||||
|
|
@ -508,7 +516,7 @@ proc dfaUnused(code: seq[Instr]) =
|
||||||
proc dataflowAnalysis*(s: PSym; body: PNode) =
|
proc dataflowAnalysis*(s: PSym; body: PNode) =
|
||||||
var c = Con(code: @[], blocks: @[])
|
var c = Con(code: @[], blocks: @[])
|
||||||
gen(c, body)
|
gen(c, body)
|
||||||
#echoCfg(c.code)
|
when defined(useDfa) and defined(debugDfa): echoCfg(c.code)
|
||||||
dfa(c.code)
|
dfa(c.code)
|
||||||
|
|
||||||
proc constructCfg*(s: PSym; body: PNode): ControlFlowGraph =
|
proc constructCfg*(s: PSym; body: PNode): ControlFlowGraph =
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import
|
||||||
cgen, jsgen, json, nversion,
|
cgen, jsgen, json, nversion,
|
||||||
platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen,
|
platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen,
|
||||||
docgen2, service, parser, modules, ccgutils, sigmatch, ropes,
|
docgen2, service, parser, modules, ccgutils, sigmatch, ropes,
|
||||||
modulegraphs
|
modulegraphs, tables
|
||||||
|
|
||||||
from magicsys import systemModule, resetSysTypes
|
from magicsys import systemModule, resetSysTypes
|
||||||
|
|
||||||
|
|
@ -36,6 +36,9 @@ proc writeDepsFile(g: ModuleGraph; project: string) =
|
||||||
for m in g.modules:
|
for m in g.modules:
|
||||||
if m != nil:
|
if m != nil:
|
||||||
f.writeLine(toFullPath(m.position.int32))
|
f.writeLine(toFullPath(m.position.int32))
|
||||||
|
for k in g.inclToMod.keys:
|
||||||
|
if g.getModule(k).isNil: # don't repeat includes which are also modules
|
||||||
|
f.writeLine(k.toFullPath)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
proc commandGenDepend(graph: ModuleGraph; cache: IdentCache) =
|
proc commandGenDepend(graph: ModuleGraph; cache: IdentCache) =
|
||||||
|
|
@ -77,6 +80,8 @@ proc commandCompileToC(graph: ModuleGraph; cache: IdentCache) =
|
||||||
let proj = changeFileExt(gProjectFull, "")
|
let proj = changeFileExt(gProjectFull, "")
|
||||||
extccomp.callCCompiler(proj)
|
extccomp.callCCompiler(proj)
|
||||||
extccomp.writeJsonBuildInstructions(proj)
|
extccomp.writeJsonBuildInstructions(proj)
|
||||||
|
if optGenScript in gGlobalOptions:
|
||||||
|
writeDepsFile(graph, toGeneratedFile(proj, ""))
|
||||||
|
|
||||||
proc commandJsonScript(graph: ModuleGraph; cache: IdentCache) =
|
proc commandJsonScript(graph: ModuleGraph; cache: IdentCache) =
|
||||||
let proj = changeFileExt(gProjectFull, "")
|
let proj = changeFileExt(gProjectFull, "")
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,8 @@ type
|
||||||
errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation,
|
errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation,
|
||||||
errExceptionExpected, errExceptionAlreadyHandled,
|
errExceptionExpected, errExceptionAlreadyHandled,
|
||||||
errYieldNotAllowedHere, errYieldNotAllowedInTryStmt,
|
errYieldNotAllowedHere, errYieldNotAllowedInTryStmt,
|
||||||
errInvalidNumberOfYieldExpr, errCannotReturnExpr, errAttemptToRedefine,
|
errInvalidNumberOfYieldExpr, errCannotReturnExpr,
|
||||||
|
errNoReturnWithReturnTypeNotAllowed, errAttemptToRedefine,
|
||||||
errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel,
|
errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel,
|
||||||
errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected,
|
errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected,
|
||||||
errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler,
|
errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler,
|
||||||
|
|
@ -179,8 +180,9 @@ const
|
||||||
errYieldNotAllowedInTryStmt: "'yield' cannot be used within 'try' in a non-inlined iterator",
|
errYieldNotAllowedInTryStmt: "'yield' cannot be used within 'try' in a non-inlined iterator",
|
||||||
errInvalidNumberOfYieldExpr: "invalid number of \'yield\' expressions",
|
errInvalidNumberOfYieldExpr: "invalid number of \'yield\' expressions",
|
||||||
errCannotReturnExpr: "current routine cannot return an expression",
|
errCannotReturnExpr: "current routine cannot return an expression",
|
||||||
|
errNoReturnWithReturnTypeNotAllowed: "routines with NoReturn pragma are not allowed to have return type",
|
||||||
errAttemptToRedefine: "redefinition of \'$1\'",
|
errAttemptToRedefine: "redefinition of \'$1\'",
|
||||||
errStmtInvalidAfterReturn: "statement not allowed after \'return\', \'break\', \'raise\' or \'continue'",
|
errStmtInvalidAfterReturn: "statement not allowed after \'return\', \'break\', \'raise\', \'continue\' or proc call with noreturn pragma",
|
||||||
errStmtExpected: "statement expected",
|
errStmtExpected: "statement expected",
|
||||||
errInvalidLabel: "\'$1\' is no label",
|
errInvalidLabel: "\'$1\' is no label",
|
||||||
errInvalidCmdLineOption: "invalid command line option: \'$1\'",
|
errInvalidCmdLineOption: "invalid command line option: \'$1\'",
|
||||||
|
|
|
||||||
|
|
@ -771,6 +771,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||||
of wNoreturn:
|
of wNoreturn:
|
||||||
noVal(it)
|
noVal(it)
|
||||||
incl(sym.flags, sfNoReturn)
|
incl(sym.flags, sfNoReturn)
|
||||||
|
if sym.typ[0] != nil:
|
||||||
|
localError(sym.ast[paramsPos][0].info, errNoReturnWithReturnTypeNotAllowed)
|
||||||
of wDynlib:
|
of wDynlib:
|
||||||
processDynLib(c, it, sym)
|
processDynLib(c, it, sym)
|
||||||
of wCompilerproc:
|
of wCompilerproc:
|
||||||
|
|
|
||||||
|
|
@ -165,6 +165,19 @@ proc commonType*(x, y: PType): PType =
|
||||||
result = newType(k, r.owner)
|
result = newType(k, r.owner)
|
||||||
result.addSonSkipIntLit(r)
|
result.addSonSkipIntLit(r)
|
||||||
|
|
||||||
|
proc endsInNoReturn(n: PNode): bool =
|
||||||
|
# check if expr ends in raise exception or call of noreturn proc
|
||||||
|
var it = n
|
||||||
|
while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0:
|
||||||
|
it = it.lastSon
|
||||||
|
result = it.kind == nkRaiseStmt or
|
||||||
|
it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags
|
||||||
|
|
||||||
|
proc commonType*(x: PType, y: PNode): PType =
|
||||||
|
# ignore exception raising branches in case/if expressions
|
||||||
|
if endsInNoReturn(y): return x
|
||||||
|
commonType(x, y.typ)
|
||||||
|
|
||||||
proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
|
proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
|
||||||
result = newSym(kind, considerQuotedIdent(n), getCurrOwner(c), n.info)
|
result = newSym(kind, considerQuotedIdent(n), getCurrOwner(c), n.info)
|
||||||
when defined(nimsuggest):
|
when defined(nimsuggest):
|
||||||
|
|
|
||||||
|
|
@ -165,14 +165,14 @@ proc semIf(c: PContext, n: PNode): PNode =
|
||||||
it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0]))
|
it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0]))
|
||||||
when not newScopeForIf: openScope(c)
|
when not newScopeForIf: openScope(c)
|
||||||
it.sons[1] = semExprBranch(c, it.sons[1])
|
it.sons[1] = semExprBranch(c, it.sons[1])
|
||||||
typ = commonType(typ, it.sons[1].typ)
|
typ = commonType(typ, it.sons[1])
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
elif it.len == 1:
|
elif it.len == 1:
|
||||||
hasElse = true
|
hasElse = true
|
||||||
it.sons[0] = semExprBranchScope(c, it.sons[0])
|
it.sons[0] = semExprBranchScope(c, it.sons[0])
|
||||||
typ = commonType(typ, it.sons[0].typ)
|
typ = commonType(typ, it.sons[0])
|
||||||
else: illFormedAst(it)
|
else: illFormedAst(it)
|
||||||
if isEmptyType(typ) or typ.kind == tyNil or not hasElse:
|
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse:
|
||||||
for it in n: discardCheck(c, it.lastSon)
|
for it in n: discardCheck(c, it.lastSon)
|
||||||
result.kind = nkIfStmt
|
result.kind = nkIfStmt
|
||||||
# propagate any enforced VoidContext:
|
# propagate any enforced VoidContext:
|
||||||
|
|
@ -180,6 +180,7 @@ proc semIf(c: PContext, n: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
for it in n:
|
for it in n:
|
||||||
let j = it.len-1
|
let j = it.len-1
|
||||||
|
if not endsInNoReturn(it.sons[j]):
|
||||||
it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
|
it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
|
||||||
result.kind = nkIfExpr
|
result.kind = nkIfExpr
|
||||||
result.typ = typ
|
result.typ = typ
|
||||||
|
|
@ -213,7 +214,7 @@ proc semCase(c: PContext, n: PNode): PNode =
|
||||||
semCaseBranch(c, n, x, i, covered)
|
semCaseBranch(c, n, x, i, covered)
|
||||||
var last = sonsLen(x)-1
|
var last = sonsLen(x)-1
|
||||||
x.sons[last] = semExprBranchScope(c, x.sons[last])
|
x.sons[last] = semExprBranchScope(c, x.sons[last])
|
||||||
typ = commonType(typ, x.sons[last].typ)
|
typ = commonType(typ, x.sons[last])
|
||||||
of nkElifBranch:
|
of nkElifBranch:
|
||||||
chckCovered = false
|
chckCovered = false
|
||||||
checkSonsLen(x, 2)
|
checkSonsLen(x, 2)
|
||||||
|
|
@ -221,13 +222,13 @@ proc semCase(c: PContext, n: PNode): PNode =
|
||||||
x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0]))
|
x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0]))
|
||||||
when not newScopeForIf: openScope(c)
|
when not newScopeForIf: openScope(c)
|
||||||
x.sons[1] = semExprBranch(c, x.sons[1])
|
x.sons[1] = semExprBranch(c, x.sons[1])
|
||||||
typ = commonType(typ, x.sons[1].typ)
|
typ = commonType(typ, x.sons[1])
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
of nkElse:
|
of nkElse:
|
||||||
chckCovered = false
|
chckCovered = false
|
||||||
checkSonsLen(x, 1)
|
checkSonsLen(x, 1)
|
||||||
x.sons[0] = semExprBranchScope(c, x.sons[0])
|
x.sons[0] = semExprBranchScope(c, x.sons[0])
|
||||||
typ = commonType(typ, x.sons[0].typ)
|
typ = commonType(typ, x.sons[0])
|
||||||
hasElse = true
|
hasElse = true
|
||||||
else:
|
else:
|
||||||
illFormedAst(x)
|
illFormedAst(x)
|
||||||
|
|
@ -237,7 +238,7 @@ proc semCase(c: PContext, n: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
localError(n.info, errNotAllCasesCovered)
|
localError(n.info, errNotAllCasesCovered)
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
if isEmptyType(typ) or typ.kind == tyNil or not hasElse:
|
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)
|
for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon)
|
||||||
# propagate any enforced VoidContext:
|
# propagate any enforced VoidContext:
|
||||||
if typ == enforceVoidContext:
|
if typ == enforceVoidContext:
|
||||||
|
|
@ -246,6 +247,7 @@ proc semCase(c: PContext, n: PNode): PNode =
|
||||||
for i in 1..n.len-1:
|
for i in 1..n.len-1:
|
||||||
var it = n.sons[i]
|
var it = n.sons[i]
|
||||||
let j = it.len-1
|
let j = it.len-1
|
||||||
|
if not endsInNoReturn(it.sons[j]):
|
||||||
it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
|
it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
|
||||||
result.typ = typ
|
result.typ = typ
|
||||||
|
|
||||||
|
|
@ -1851,8 +1853,8 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
else:
|
else:
|
||||||
n.typ = n.sons[i].typ
|
n.typ = n.sons[i].typ
|
||||||
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
|
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
|
||||||
case n.sons[i].kind
|
if n.sons[i].kind in LastBlockStmts or
|
||||||
of LastBlockStmts:
|
n.sons[i].kind in nkCallKinds and n.sons[i][0].kind == nkSym and sfNoReturn in n.sons[i][0].sym.flags:
|
||||||
for j in countup(i + 1, length - 1):
|
for j in countup(i + 1, length - 1):
|
||||||
case n.sons[j].kind
|
case n.sons[j].kind
|
||||||
of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr,
|
of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr,
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ Advanced options:
|
||||||
--noMain do not generate a main procedure
|
--noMain do not generate a main procedure
|
||||||
--genScript generate a compile script (in the 'nimcache'
|
--genScript generate a compile script (in the 'nimcache'
|
||||||
subdirectory named 'compile_$project$scriptext')
|
subdirectory named 'compile_$project$scriptext')
|
||||||
|
--genDeps generate a '.deps' file containing the dependencies
|
||||||
--os:SYMBOL set the target operating system (cross-compilation)
|
--os:SYMBOL set the target operating system (cross-compilation)
|
||||||
--cpu:SYMBOL set the target processor (cross-compilation)
|
--cpu:SYMBOL set the target processor (cross-compilation)
|
||||||
--debuginfo enables debug information
|
--debuginfo enables debug information
|
||||||
|
|
|
||||||
|
|
@ -44,10 +44,10 @@
|
||||||
## resolve(game)
|
## resolve(game)
|
||||||
## return promise
|
## return promise
|
||||||
##
|
##
|
||||||
## Forward definitions work properly, you just don't need to add the ``{.async.}`` pragma:
|
## Forward definitions work properly, you just need to always add the ``{.async.}`` pragma:
|
||||||
##
|
##
|
||||||
## .. code-block:: nim
|
## .. code-block:: nim
|
||||||
## proc loadGame(name: string): Future[Game]
|
## proc loadGame(name: string): Future[Game] {.async.}
|
||||||
##
|
##
|
||||||
## JavaScript compatibility
|
## JavaScript compatibility
|
||||||
## ~~~~~~~~~~~~~~~~~~~~~~~~~
|
## ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
@ -83,23 +83,54 @@ proc replaceReturn(node: var NimNode) =
|
||||||
replaceReturn(son)
|
replaceReturn(son)
|
||||||
inc z
|
inc z
|
||||||
|
|
||||||
|
proc isFutureVoid(node: NimNode): bool =
|
||||||
|
result = node.kind == nnkBracketExpr and
|
||||||
|
node[0].kind == nnkIdent and $node[0] == "Future" and
|
||||||
|
node[1].kind == nnkIdent and $node[1] == "void"
|
||||||
|
|
||||||
proc generateJsasync(arg: NimNode): NimNode =
|
proc generateJsasync(arg: NimNode): NimNode =
|
||||||
assert arg.kind == nnkProcDef
|
assert arg.kind == nnkProcDef
|
||||||
result = arg
|
result = arg
|
||||||
|
var isVoid = false
|
||||||
|
var jsResolveNode = ident("jsResolve")
|
||||||
|
|
||||||
if arg.params[0].kind == nnkEmpty:
|
if arg.params[0].kind == nnkEmpty:
|
||||||
result.params[0] = nnkBracketExpr.newTree(ident("Future"), ident("void"))
|
result.params[0] = nnkBracketExpr.newTree(ident("Future"), ident("void"))
|
||||||
|
isVoid = true
|
||||||
|
elif isFutureVoid(arg.params[0]):
|
||||||
|
isVoid = true
|
||||||
|
|
||||||
var code = result.body
|
var code = result.body
|
||||||
replaceReturn(code)
|
replaceReturn(code)
|
||||||
result.body = nnkStmtList.newTree()
|
result.body = nnkStmtList.newTree()
|
||||||
var q = quote:
|
|
||||||
|
if len(code) > 0:
|
||||||
|
var awaitFunction = quote:
|
||||||
proc await[T](f: Future[T]): T {.importcpp: "(await #)".}
|
proc await[T](f: Future[T]): T {.importcpp: "(await #)".}
|
||||||
|
result.body.add(awaitFunction)
|
||||||
|
|
||||||
|
var resolve: NimNode
|
||||||
|
if isVoid:
|
||||||
|
resolve = quote:
|
||||||
|
var `jsResolveNode` {.importcpp: "undefined".}: Future[void]
|
||||||
|
else:
|
||||||
|
resolve = quote:
|
||||||
proc jsResolve[T](a: T): Future[T] {.importcpp: "#".}
|
proc jsResolve[T](a: T): Future[T] {.importcpp: "#".}
|
||||||
result.body.add(q)
|
result.body.add(resolve)
|
||||||
|
else:
|
||||||
|
result.body = newEmptyNode()
|
||||||
for child in code:
|
for child in code:
|
||||||
result.body.add(child)
|
result.body.add(child)
|
||||||
|
|
||||||
|
if len(code) > 0 and isVoid:
|
||||||
|
var voidFix = quote:
|
||||||
|
return `jsResolveNode`
|
||||||
|
result.body.add(voidFix)
|
||||||
|
|
||||||
result.pragma = quote:
|
result.pragma = quote:
|
||||||
{.codegenDecl: "async function $2($3)".}
|
{.codegenDecl: "async function $2($3)".}
|
||||||
|
|
||||||
|
|
||||||
macro async*(arg: untyped): untyped =
|
macro async*(arg: untyped): untyped =
|
||||||
## Macro which converts normal procedures into
|
## Macro which converts normal procedures into
|
||||||
## javascript-compatible async procedures
|
## javascript-compatible async procedures
|
||||||
|
|
|
||||||
|
|
@ -1234,7 +1234,7 @@ else:
|
||||||
processBasicCallbacks(fd, writeList)
|
processBasicCallbacks(fd, writeList)
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
if Event.User in events or events == {Event.Error}:
|
if Event.User in events:
|
||||||
processBasicCallbacks(fd, readList)
|
processBasicCallbacks(fd, readList)
|
||||||
custom = true
|
custom = true
|
||||||
if rLength == 0:
|
if rLength == 0:
|
||||||
|
|
|
||||||
|
|
@ -351,15 +351,19 @@ proc round*[T: float32|float64](x: T, places: int = 0): T =
|
||||||
result = round0(x*mult)/mult
|
result = round0(x*mult)/mult
|
||||||
|
|
||||||
when not defined(JS):
|
when not defined(JS):
|
||||||
proc frexp*(x: float32, exponent: var int): float32 {.
|
proc c_frexp*(x: float32, exponent: var int32): float32 {.
|
||||||
importc: "frexp", header: "<math.h>".}
|
importc: "frexp", header: "<math.h>".}
|
||||||
proc frexp*(x: float64, exponent: var int): float64 {.
|
proc c_frexp*(x: float64, exponent: var int32): float64 {.
|
||||||
importc: "frexp", header: "<math.h>".}
|
importc: "frexp", header: "<math.h>".}
|
||||||
|
proc frexp*[T, U](x: T, exponent: var U): T =
|
||||||
## Split a number into mantissa and exponent.
|
## Split a number into mantissa and exponent.
|
||||||
## `frexp` calculates the mantissa m (a float greater than or equal to 0.5
|
## `frexp` calculates the mantissa m (a float greater than or equal to 0.5
|
||||||
## and less than 1) and the integer value n such that `x` (the original
|
## and less than 1) and the integer value n such that `x` (the original
|
||||||
## float value) equals m * 2**n. frexp stores n in `exponent` and returns
|
## float value) equals m * 2**n. frexp stores n in `exponent` and returns
|
||||||
## m.
|
## m.
|
||||||
|
var exp: int32
|
||||||
|
result = c_frexp(x, exp)
|
||||||
|
exponent = exp
|
||||||
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:
|
||||||
|
|
@ -368,9 +372,14 @@ else:
|
||||||
elif x < 0.0:
|
elif x < 0.0:
|
||||||
result = -frexp(-x, exponent)
|
result = -frexp(-x, exponent)
|
||||||
else:
|
else:
|
||||||
var ex = floor(log2(x))
|
var ex = trunc(log2(x))
|
||||||
exponent = round(ex)
|
exponent = int(ex)
|
||||||
result = x / pow(2.0, ex)
|
result = x / pow(2.0, ex)
|
||||||
|
if abs(result) >= 1:
|
||||||
|
inc(exponent)
|
||||||
|
result = result / 2
|
||||||
|
if exponent == 1024 and result == 0.0:
|
||||||
|
result = 0.99999999999999988898
|
||||||
|
|
||||||
proc splitDecimal*[T: float32|float64](x: T): tuple[intpart: T, floatpart: T] =
|
proc splitDecimal*[T: float32|float64](x: T): tuple[intpart: T, floatpart: T] =
|
||||||
## Breaks `x` into an integral and a fractional part.
|
## Breaks `x` into an integral and a fractional part.
|
||||||
|
|
|
||||||
|
|
@ -21,13 +21,41 @@
|
||||||
## ``nim c -r <testfile.nim>`` exits with 0 or 1
|
## ``nim c -r <testfile.nim>`` exits with 0 or 1
|
||||||
##
|
##
|
||||||
## Running a single test
|
## Running a single test
|
||||||
## ---------------------
|
## =====================
|
||||||
##
|
##
|
||||||
## Simply specify the test name as a command line argument.
|
## Specify the test name as a command line argument.
|
||||||
##
|
##
|
||||||
## .. code::
|
## .. code::
|
||||||
##
|
##
|
||||||
## nim c -r test "my super awesome test name"
|
## nim c -r test "my test name" "another test"
|
||||||
|
##
|
||||||
|
## Multiple arguments can be used.
|
||||||
|
##
|
||||||
|
## Running a single test suite
|
||||||
|
## ===========================
|
||||||
|
##
|
||||||
|
## Specify the suite name delimited by ``"::"``.
|
||||||
|
##
|
||||||
|
## .. code::
|
||||||
|
##
|
||||||
|
## nim c -r test "my test name::"
|
||||||
|
##
|
||||||
|
## Selecting tests by pattern
|
||||||
|
## ==========================
|
||||||
|
##
|
||||||
|
## A single ``"*"`` can be used for globbing.
|
||||||
|
##
|
||||||
|
## Delimit the end of a suite name with ``"::"``.
|
||||||
|
##
|
||||||
|
## Tests matching **any** of the arguments are executed.
|
||||||
|
##
|
||||||
|
## .. code::
|
||||||
|
##
|
||||||
|
## nim c -r test fast_suite::mytest1 fast_suite::mytest2
|
||||||
|
## nim c -r test "fast_suite::mytest*"
|
||||||
|
## nim c -r test "auth*::" "crypto::hashing*"
|
||||||
|
## # Run suites starting with 'bug #' and standalone tests starting with '#'
|
||||||
|
## nim c -r test 'bug #*::' '::#*'
|
||||||
##
|
##
|
||||||
## Example
|
## Example
|
||||||
## -------
|
## -------
|
||||||
|
|
@ -121,7 +149,7 @@ var
|
||||||
|
|
||||||
checkpoints {.threadvar.}: seq[string]
|
checkpoints {.threadvar.}: seq[string]
|
||||||
formatters {.threadvar.}: seq[OutputFormatter]
|
formatters {.threadvar.}: seq[OutputFormatter]
|
||||||
testsToRun {.threadvar.}: HashSet[string]
|
testsFilters {.threadvar.}: HashSet[string]
|
||||||
|
|
||||||
when declared(stdout):
|
when declared(stdout):
|
||||||
abortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR")
|
abortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR")
|
||||||
|
|
@ -300,22 +328,63 @@ method testEnded*(formatter: JUnitOutputFormatter, testResult: TestResult) =
|
||||||
method suiteEnded*(formatter: JUnitOutputFormatter) =
|
method suiteEnded*(formatter: JUnitOutputFormatter) =
|
||||||
formatter.stream.writeLine("\t</testsuite>")
|
formatter.stream.writeLine("\t</testsuite>")
|
||||||
|
|
||||||
proc shouldRun(testName: string): bool =
|
proc glob(matcher, filter: string): bool =
|
||||||
if testsToRun.len == 0:
|
## Globbing using a single `*`. Empty `filter` matches everything.
|
||||||
|
if filter.len == 0:
|
||||||
return true
|
return true
|
||||||
|
|
||||||
result = testName in testsToRun
|
if not filter.contains('*'):
|
||||||
|
return matcher == filter
|
||||||
|
|
||||||
|
let beforeAndAfter = filter.split('*', maxsplit=1)
|
||||||
|
if beforeAndAfter.len == 1:
|
||||||
|
# "foo*"
|
||||||
|
return matcher.startswith(beforeAndAfter[0])
|
||||||
|
|
||||||
|
if matcher.len < filter.len - 1:
|
||||||
|
return false # "12345" should not match "123*345"
|
||||||
|
|
||||||
|
return matcher.startsWith(beforeAndAfter[0]) and matcher.endsWith(beforeAndAfter[1])
|
||||||
|
|
||||||
|
proc matchFilter(suiteName, testName, filter: string): bool =
|
||||||
|
if filter == "":
|
||||||
|
return true
|
||||||
|
if testName == filter:
|
||||||
|
# corner case for tests containing "::" in their name
|
||||||
|
return true
|
||||||
|
let suiteAndTestFilters = filter.split("::", maxsplit=1)
|
||||||
|
|
||||||
|
if suiteAndTestFilters.len == 1:
|
||||||
|
# no suite specified
|
||||||
|
let test_f = suiteAndTestFilters[0]
|
||||||
|
return glob(testName, test_f)
|
||||||
|
|
||||||
|
return glob(suiteName, suiteAndTestFilters[0]) and glob(testName, suiteAndTestFilters[1])
|
||||||
|
|
||||||
|
when defined(testing): export matchFilter
|
||||||
|
|
||||||
|
proc shouldRun(currentSuiteName, testName: string): bool =
|
||||||
|
## Check if a test should be run by matching suiteName and testName against
|
||||||
|
## test filters.
|
||||||
|
if testsFilters.len == 0:
|
||||||
|
return true
|
||||||
|
|
||||||
|
for f in testsFilters:
|
||||||
|
if matchFilter(currentSuiteName, testName, f):
|
||||||
|
return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
proc ensureInitialized() =
|
proc ensureInitialized() =
|
||||||
if formatters == nil:
|
if formatters == nil:
|
||||||
formatters = @[OutputFormatter(defaultConsoleFormatter())]
|
formatters = @[OutputFormatter(defaultConsoleFormatter())]
|
||||||
|
|
||||||
if not testsToRun.isValid:
|
if not testsFilters.isValid:
|
||||||
testsToRun.init()
|
testsFilters.init()
|
||||||
when declared(paramCount):
|
when declared(paramCount):
|
||||||
# Read tests to run from the command line.
|
# Read tests to run from the command line.
|
||||||
for i in 1 .. paramCount():
|
for i in 1 .. paramCount():
|
||||||
testsToRun.incl(paramStr(i))
|
testsFilters.incl(paramStr(i))
|
||||||
|
|
||||||
# These two procs are added as workarounds for
|
# These two procs are added as workarounds for
|
||||||
# https://github.com/nim-lang/Nim/issues/5549
|
# https://github.com/nim-lang/Nim/issues/5549
|
||||||
|
|
@ -395,7 +464,7 @@ template test*(name, body) {.dirty.} =
|
||||||
|
|
||||||
ensureInitialized()
|
ensureInitialized()
|
||||||
|
|
||||||
if shouldRun(name):
|
if shouldRun(when declared(testSuiteName): testSuiteName else: "", name):
|
||||||
checkpoints = @[]
|
checkpoints = @[]
|
||||||
var testStatusIMPL {.inject.} = OK
|
var testStatusIMPL {.inject.} = OK
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2916,7 +2916,10 @@ when not defined(JS): #and not defined(nimscript):
|
||||||
elif x > y: result = 1
|
elif x > y: result = 1
|
||||||
else: result = 0
|
else: result = 0
|
||||||
else:
|
else:
|
||||||
result = int(c_strcmp(x, y))
|
let minlen = min(x.len, y.len)
|
||||||
|
result = int(c_memcmp(x.cstring, y.cstring, minlen.csize))
|
||||||
|
if result == 0:
|
||||||
|
result = x.len - y.len
|
||||||
|
|
||||||
when defined(nimscript):
|
when defined(nimscript):
|
||||||
proc readFile*(filename: string): string {.tags: [ReadIOEffect], benign.}
|
proc readFile*(filename: string): string {.tags: [ReadIOEffect], benign.}
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,6 @@ proc chckObj(obj, subclass: PNimType) {.compilerproc.} =
|
||||||
while x != subclass:
|
while x != subclass:
|
||||||
if x == nil:
|
if x == nil:
|
||||||
sysFatal(ObjectConversionError, "invalid object conversion")
|
sysFatal(ObjectConversionError, "invalid object conversion")
|
||||||
break
|
|
||||||
x = x.base
|
x = x.base
|
||||||
|
|
||||||
proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} =
|
proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} =
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,10 @@ proc cmpStrings(a, b: NimString): int {.inline, compilerProc.} =
|
||||||
if a == b: return 0
|
if a == b: return 0
|
||||||
if a == nil: return -1
|
if a == nil: return -1
|
||||||
if b == nil: return 1
|
if b == nil: return 1
|
||||||
when defined(nimNoArrayToCstringConversion):
|
let minlen = min(a.len, b.len)
|
||||||
return c_strcmp(addr a.data, addr b.data)
|
result = c_memcmp(addr a.data, addr b.data, minlen.csize)
|
||||||
else:
|
if result == 0:
|
||||||
return c_strcmp(a.data, b.data)
|
result = a.len - b.len
|
||||||
|
|
||||||
proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} =
|
proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} =
|
||||||
if a == b: return true
|
if a == b: return true
|
||||||
|
|
|
||||||
|
|
@ -36,5 +36,64 @@ var z = case i
|
||||||
echo z
|
echo z
|
||||||
#OUT ayyy
|
#OUT ayyy
|
||||||
|
|
||||||
|
let str1 = "Y"
|
||||||
|
let str2 = "NN"
|
||||||
|
let a = case str1:
|
||||||
|
of "Y": true
|
||||||
|
of "N": false
|
||||||
|
else:
|
||||||
|
echo "no good"
|
||||||
|
quit("quiting")
|
||||||
|
|
||||||
|
let b = case str2:
|
||||||
|
of nil, "": raise newException(ValueError, "Invalid boolean")
|
||||||
|
elif str2[0] == 'Y': true
|
||||||
|
elif str2[0] == 'N': false
|
||||||
|
else: "error".quit(2)
|
||||||
|
|
||||||
|
doAssert(a == true)
|
||||||
|
doAssert(b == false)
|
||||||
|
|
||||||
|
var bb: bool
|
||||||
|
doassert(not compiles(
|
||||||
|
bb = case str2:
|
||||||
|
of nil, "": raise newException(ValueError, "Invalid boolean")
|
||||||
|
elif str.startsWith("Y"): true
|
||||||
|
elif str.startsWith("N"): false
|
||||||
|
))
|
||||||
|
|
||||||
|
doassert(not compiles(
|
||||||
|
bb = case str2:
|
||||||
|
of "Y": true
|
||||||
|
of "N": false
|
||||||
|
))
|
||||||
|
|
||||||
|
doassert(not compiles(
|
||||||
|
bb = case str2:
|
||||||
|
of "Y": true
|
||||||
|
of "N": raise newException(ValueError, "N not allowed")
|
||||||
|
))
|
||||||
|
|
||||||
|
doassert(not compiles(
|
||||||
|
bb = case str2:
|
||||||
|
of "Y": raise newException(ValueError, "Invalid Y")
|
||||||
|
else: raise newException(ValueError, "Invalid N")
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
doassert(not compiles(
|
||||||
|
bb = case str2:
|
||||||
|
of "Y":
|
||||||
|
raise newException(ValueError, "Invalid Y")
|
||||||
|
true
|
||||||
|
else: raise newException(ValueError, "Invalid")
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
doassert(not compiles(
|
||||||
|
bb = case str2:
|
||||||
|
of "Y":
|
||||||
|
"invalid Y".quit(3)
|
||||||
|
true
|
||||||
|
else: raise newException(ValueError, "Invalid")
|
||||||
|
))
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
discard """
|
discard """
|
||||||
disabled: true
|
disabled: true
|
||||||
output: '''
|
output: '''
|
||||||
0
|
|
||||||
x
|
x
|
||||||
|
e
|
||||||
'''
|
'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -10,17 +10,23 @@ import asyncjs
|
||||||
|
|
||||||
# demonstrate forward definition
|
# demonstrate forward definition
|
||||||
# for js
|
# for js
|
||||||
proc y(e: int): Future[string]
|
proc y(e: int): Future[string] {.async.}
|
||||||
|
|
||||||
proc x(e: int) {.async.} =
|
proc e: int {.discardable.} =
|
||||||
|
echo "e"
|
||||||
|
return 2
|
||||||
|
|
||||||
|
proc x(e: int): Future[void] {.async.} =
|
||||||
var s = await y(e)
|
var s = await y(e)
|
||||||
echo s
|
echo s
|
||||||
|
e()
|
||||||
|
|
||||||
proc y(e: int): Future[string] {.async.} =
|
proc y(e: int): Future[string] {.async.} =
|
||||||
echo 0
|
if e > 0:
|
||||||
|
return await y(0)
|
||||||
|
else:
|
||||||
return "x"
|
return "x"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
discard x(2)
|
discard x(2)
|
||||||
|
|
||||||
|
|
|
||||||
22
tests/pragmas/tnoreturn.nim
Normal file
22
tests/pragmas/tnoreturn.nim
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
discard """
|
||||||
|
ccodeCheck: "\\i @'__attribute__((noreturn))' .*"
|
||||||
|
"""
|
||||||
|
|
||||||
|
proc noret1*(i: int) {.noreturn.} =
|
||||||
|
echo i
|
||||||
|
|
||||||
|
|
||||||
|
proc noret2*(i: int): void {.noreturn.} =
|
||||||
|
echo i
|
||||||
|
|
||||||
|
var p {.used.}: proc(i: int): int
|
||||||
|
doAssert(not compiles(
|
||||||
|
p = proc(i: int): int {.noreturn.} = i # noreturn lambda returns int
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
doAssert(not compiles(
|
||||||
|
block:
|
||||||
|
noret1(5)
|
||||||
|
echo 1 # statement after noreturn
|
||||||
|
))
|
||||||
44
tests/stdlib/tfrexp1.nim
Normal file
44
tests/stdlib/tfrexp1.nim
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
discard """
|
||||||
|
targets: "js c c++"
|
||||||
|
output: '''ok'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
import math
|
||||||
|
import strformat
|
||||||
|
|
||||||
|
const manualTest = false
|
||||||
|
|
||||||
|
proc frexp_test(lo, hi, step: float64) =
|
||||||
|
var exp: int
|
||||||
|
var frac: float64
|
||||||
|
|
||||||
|
var eps = 1e-15.float64
|
||||||
|
|
||||||
|
var x:float64 = lo
|
||||||
|
while x <= hi:
|
||||||
|
frac = frexp(x.float, exp)
|
||||||
|
let rslt = pow(2.0, float(exp)) * frac
|
||||||
|
|
||||||
|
doAssert(abs(rslt - x) < eps)
|
||||||
|
|
||||||
|
when manualTest:
|
||||||
|
echo fmt("x: {x:10.3f} exp: {exp:4d} frac: {frac:24.20f} check: {$(abs(rslt - x) < eps):-5s} {rslt: 9.3f}")
|
||||||
|
x += step
|
||||||
|
|
||||||
|
when manualTest:
|
||||||
|
var exp: int
|
||||||
|
var frac: float64
|
||||||
|
|
||||||
|
for flval in [1.7976931348623157e+308, -1.7976931348623157e+308, # max, min float64
|
||||||
|
3.4028234663852886e+38, -3.4028234663852886e+38, # max, min float32
|
||||||
|
4.9406564584124654e-324, -4.9406564584124654e-324, # smallest/largest positive/negative float64
|
||||||
|
1.4012984643248171e-45, -1.4012984643248171e-45, # smallest/largest positive/negative float32
|
||||||
|
2.2250738585072014e-308, 1.1754943508222875e-38]: # smallest normal float64/float32
|
||||||
|
frac = frexp(flval, exp)
|
||||||
|
echo fmt("{flval:25.16e}, {exp: 6d}, {frac: .20f} {frac * pow(2.0, float(exp)): .20e}")
|
||||||
|
|
||||||
|
frexp_test(-1000.0, 1000.0, 0.0125)
|
||||||
|
else:
|
||||||
|
frexp_test(-1000000.0, 1000000.0, 0.125)
|
||||||
|
|
||||||
|
echo "ok"
|
||||||
|
|
@ -56,4 +56,24 @@ proc test_string_slice() =
|
||||||
|
|
||||||
echo("OK")
|
echo("OK")
|
||||||
|
|
||||||
|
proc test_string_cmp() =
|
||||||
|
let world = "hello\0world"
|
||||||
|
let earth = "hello\0earth"
|
||||||
|
let short = "hello\0"
|
||||||
|
let hello = "hello"
|
||||||
|
let goodbye = "goodbye"
|
||||||
|
|
||||||
|
doAssert world == world
|
||||||
|
doAssert world != earth
|
||||||
|
doAssert world != short
|
||||||
|
doAssert world != hello
|
||||||
|
doAssert world != goodbye
|
||||||
|
|
||||||
|
doAssert cmp(world, world) == 0
|
||||||
|
doAssert cmp(world, earth) > 0
|
||||||
|
doAssert cmp(world, short) > 0
|
||||||
|
doAssert cmp(world, hello) > 0
|
||||||
|
doAssert cmp(world, goodbye) > 0
|
||||||
|
|
||||||
test_string_slice()
|
test_string_slice()
|
||||||
|
test_string_cmp()
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ discard """
|
||||||
|
|
||||||
[Suite] bug #5784
|
[Suite] bug #5784
|
||||||
|
|
||||||
|
[Suite] test name filtering
|
||||||
|
|
||||||
'''
|
'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -120,3 +122,39 @@ suite "bug #5784":
|
||||||
field: int
|
field: int
|
||||||
var obj: Obj
|
var obj: Obj
|
||||||
check obj.isNil or obj.field == 0
|
check obj.isNil or obj.field == 0
|
||||||
|
|
||||||
|
when defined(testing):
|
||||||
|
suite "test name filtering":
|
||||||
|
test "test name":
|
||||||
|
check matchFilter("suite1", "foo", "")
|
||||||
|
check matchFilter("suite1", "foo", "foo")
|
||||||
|
check matchFilter("suite1", "foo", "::")
|
||||||
|
check matchFilter("suite1", "foo", "*")
|
||||||
|
check matchFilter("suite1", "foo", "::foo")
|
||||||
|
check matchFilter("suite1", "::foo", "::foo")
|
||||||
|
|
||||||
|
test "test name - glob":
|
||||||
|
check matchFilter("suite1", "foo", "f*")
|
||||||
|
check matchFilter("suite1", "foo", "*oo")
|
||||||
|
check matchFilter("suite1", "12345", "12*345")
|
||||||
|
check matchFilter("suite1", "q*wefoo", "q*wefoo")
|
||||||
|
check false == matchFilter("suite1", "foo", "::x")
|
||||||
|
check false == matchFilter("suite1", "foo", "::x*")
|
||||||
|
check false == matchFilter("suite1", "foo", "::*x")
|
||||||
|
# overlap
|
||||||
|
check false == matchFilter("suite1", "12345", "123*345")
|
||||||
|
check matchFilter("suite1", "ab*c::d*e::f", "ab*c::d*e::f")
|
||||||
|
|
||||||
|
test "suite name":
|
||||||
|
check matchFilter("suite1", "foo", "suite1::")
|
||||||
|
check false == matchFilter("suite1", "foo", "suite2::")
|
||||||
|
check matchFilter("suite1", "qwe::foo", "qwe::foo")
|
||||||
|
check matchFilter("suite1", "qwe::foo", "suite1::qwe::foo")
|
||||||
|
|
||||||
|
test "suite name - glob":
|
||||||
|
check matchFilter("suite1", "foo", "::*")
|
||||||
|
check matchFilter("suite1", "foo", "*::*")
|
||||||
|
check matchFilter("suite1", "foo", "*::foo")
|
||||||
|
check false == matchFilter("suite1", "foo", "*ite2::")
|
||||||
|
check matchFilter("suite1", "q**we::foo", "q**we::foo")
|
||||||
|
check matchFilter("suite1", "a::b*c::d*e", "a::b*c::d*e")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue