Merge branch 'devel' into sorted_deduplicate

This commit is contained in:
Andreas Rumpf 2018-12-12 16:29:46 +01:00 • committed by GitHub
commit a1bf9fd2b6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
494 changed files with 2495 additions and 2643 deletions

3
.gitignore vendored
View file

@ -66,3 +66,6 @@ testresults/
test.txt test.txt
/test.ini /test.ini
tweeter.db
tweeter_test.db
megatest.nim

View file

@ -53,9 +53,8 @@ test-windows:
<<: *win_set_path_def <<: *win_set_path_def
script: script:
- call ci\deps.bat - call ci\deps.bat
- nim c --taintMode:on testament\tester - nim c testament\tester
- testament\tester.exe --pedantic all - testament\tester.exe --pedantic all
tags: tags:
- windows - windows
- fast - fast

View file

@ -51,7 +51,7 @@ script:
#- nimble install sdl1 #- nimble install sdl1
#- nimble install jester@#head -y #- nimble install jester@#head -y
#- nimble install niminst #- nimble install niminst
- nim c --taintMode:on -d:nimCoroutines testament/tester - nim c -d:nimCoroutines testament/tester
- testament/tester --pedantic all -d:nimCoroutines - testament/tester --pedantic all -d:nimCoroutines
- nim c -o:bin/nimpretty nimpretty/nimpretty.nim - nim c -o:bin/nimpretty nimpretty/nimpretty.nim
- nim c -r nimpretty/tester.nim - nim c -r nimpretty/tester.nim

View file

@ -53,8 +53,8 @@ build_script:
# - nimble install opengl -y # - nimble install opengl -y
# - nimble install sdl1 -y # - nimble install sdl1 -y
# - nimble install jester@#head -y # - nimble install jester@#head -y
- nim c --taintMode:on -d:nimCoroutines --os:genode -d:posix --compileOnly testament/tester - nim c -d:nimCoroutines --os:genode -d:posix --compileOnly testament/tester
- nim c --taintMode:on -d:nimCoroutines testament/tester - nim c -d:nimCoroutines testament/tester
test_script: test_script:
- testament\tester --pedantic all -d:nimCoroutines - testament\tester --pedantic all -d:nimCoroutines

View file

@ -29,6 +29,27 @@
- `osproc.execProcess` now also takes a `workingDir` parameter. - `osproc.execProcess` now also takes a `workingDir` parameter.
- `options.UnpackError` is no longer a ref type and inherits from `System.Defect` instead of `System.ValueError`.
- nre's `RegexMatch.{captureBounds,captures}[]` no longer return `Option` or
`nil`/`""`, respectivly. Use the newly added `n in p.captures` method to
check if a group is captured, otherwise you'll recieve an exception.
- nre's `RegexMatch.{captureBounds,captures}.toTable` no longer accept a
default parameter. Instead uncaptured entries are left empty. Use
`Table.getOrDefault()` if you need defaults.
- nre's `RegexMatch.captures.{items,toSeq}` now returns an `Option[string]`
instead of a `string`. With the removal of `nil` strings, this is the only
way to indicate a missing match. Inside your loops, instead of `capture ==
""` or `capture == nil`, use `capture.isSome` to check if a capture is
present, and `capture.get` to get its value.
- nre's `replace()` no longer throws `ValueError` when the replacement string
has missing captures. It instead throws `KeyError` for named captures, and
`IndexError` for un-named captures. This is consistant with
`RegexMatch.{captureBounds,captures}[]`.
#### Breaking changes in the compiler #### Breaking changes in the compiler
- The compiler now implements the "generic symbol prepass" for `when` statements - The compiler now implements the "generic symbol prepass" for `when` statements
@ -66,6 +87,8 @@ proc enumToString*(enums: openArray[enum]): string =
is instantiation of generic proc symbol. is instantiation of generic proc symbol.
- Added the parameter ``isSorted`` for the ``sequtils.deduplicate`` proc. - Added the parameter ``isSorted`` for the ``sequtils.deduplicate`` proc.
- There is a new stdlib mdoule `std/diff` to compute the famous "diff"
of two texts by line.
### Library changes ### Library changes
@ -93,6 +116,9 @@ proc enumToString*(enums: openArray[enum]): string =
- There is a new pragma block `noSideEffect` that works like - There is a new pragma block `noSideEffect` that works like
the `gcsafe` pragma block. the `gcsafe` pragma block.
- added os.getCurrentProcessId() - added os.getCurrentProcessId()
- User defined pragmas are now allowed in the pragma blocks
- Pragma blocks are now longer eliminated from the typed AST tree to preserve
pragmas for further analysis by macros
### Language changes ### Language changes

View file

@ -723,10 +723,9 @@ type
sons*: TNodeSeq sons*: TNodeSeq
comment*: string comment*: string
TSymSeq* = seq[PSym]
TStrTable* = object # a table[PIdent] of PSym TStrTable* = object # a table[PIdent] of PSym
counter*: int counter*: int
data*: TSymSeq data*: seq[PSym]
# -------------- backend information ------------------------------- # -------------- backend information -------------------------------
TLocKind* = enum TLocKind* = enum
@ -1087,9 +1086,6 @@ proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym,
result.id = getID() result.id = getID()
when debugIds: when debugIds:
registerId(result) registerId(result)
#if result.id == 77131:
# writeStacktrace()
# echo name.s
proc isMetaType*(t: PType): bool = proc isMetaType*(t: PType): bool =
return t.kind in tyMetaTypes or return t.kind in tyMetaTypes or
@ -1261,6 +1257,9 @@ proc `$`*(x: TLockLevel): string =
elif x.ord == UnknownLockLevel.ord: result = "<unknown>" elif x.ord == UnknownLockLevel.ord: result = "<unknown>"
else: result = $int16(x) else: result = $int16(x)
proc `$`*(s: PSym): string =
result = s.name.s & "@" & $s.id
proc newType*(kind: TTypeKind, owner: PSym): PType = proc newType*(kind: TTypeKind, owner: PSym): PType =
new(result) new(result)
result.kind = kind result.kind = kind

View file

@ -507,7 +507,7 @@ proc strTableContains*(t: TStrTable, n: PSym): bool =
h = nextTry(h, high(t.data)) h = nextTry(h, high(t.data))
result = false result = false
proc strTableRawInsert(data: var TSymSeq, n: PSym) = proc strTableRawInsert(data: var seq[PSym], n: PSym) =
var h: Hash = n.name.h and high(data) var h: Hash = n.name.h and high(data)
if sfImmediate notin n.flags: if sfImmediate notin n.flags:
# fast path: # fast path:
@ -535,7 +535,7 @@ proc strTableRawInsert(data: var TSymSeq, n: PSym) =
data[h] = n data[h] = n
if favPos >= 0: swap data[h], data[favPos] if favPos >= 0: swap data[h], data[favPos]
proc symTabReplaceRaw(data: var TSymSeq, prevSym: PSym, newSym: PSym) = proc symTabReplaceRaw(data: var seq[PSym], prevSym: PSym, newSym: PSym) =
assert prevSym.name.h == newSym.name.h assert prevSym.name.h == newSym.name.h
var h: Hash = prevSym.name.h and high(data) var h: Hash = prevSym.name.h and high(data)
while data[h] != nil: while data[h] != nil:
@ -549,7 +549,7 @@ proc symTabReplace*(t: var TStrTable, prevSym: PSym, newSym: PSym) =
symTabReplaceRaw(t.data, prevSym, newSym) symTabReplaceRaw(t.data, prevSym, newSym)
proc strTableEnlarge(t: var TStrTable) = proc strTableEnlarge(t: var TStrTable) =
var n: TSymSeq var n: seq[PSym]
newSeq(n, len(t.data) * GrowthFactor) newSeq(n, len(t.data) * GrowthFactor)
for i in countup(0, high(t.data)): for i in countup(0, high(t.data)):
if t.data[i] != nil: strTableRawInsert(n, t.data[i]) if t.data[i] != nil: strTableRawInsert(n, t.data[i])

View file

@ -63,26 +63,6 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
add(pl, ~");$n") add(pl, ~");$n")
line(p, cpsStmts, pl) line(p, cpsStmts, pl)
proc isInCurrentFrame(p: BProc, n: PNode): bool =
# checks if `n` is an expression that refers to the current frame;
# this does not work reliably because of forwarding + inlining can break it
case n.kind
of nkSym:
if n.sym.kind in {skVar, skResult, skTemp, skLet} and p.prc != nil:
result = p.prc.id == n.sym.owner.id
of nkDotExpr, nkBracketExpr:
if skipTypes(n.sons[0].typ, abstractInst).kind notin {tyVar,tyLent,tyPtr,tyRef}:
result = isInCurrentFrame(p, n.sons[0])
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
result = isInCurrentFrame(p, n.sons[1])
of nkHiddenDeref, nkDerefExpr:
# what about: var x = addr(y); callAsOpenArray(x[])?
# *shrug* ``addr`` is unsafe anyway.
result = false
of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
result = isInCurrentFrame(p, n.sons[0])
else: discard
proc genBoundsCheck(p: BProc; arr, a, b: TLoc) proc genBoundsCheck(p: BProc; arr, a, b: TLoc)
proc openArrayLoc(p: BProc, n: PNode): Rope = proc openArrayLoc(p: BProc, n: PNode): Rope =

View file

@ -850,7 +850,6 @@ proc genUncheckedArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
var a, b: TLoc var a, b: TLoc
initLocExpr(p, x, a) initLocExpr(p, x, a)
initLocExpr(p, y, b) initLocExpr(p, y, b)
var ty = skipTypes(a.t, abstractVarRange + abstractPtrs + tyUserTypeClasses)
d.inheritLocation(a) d.inheritLocation(a)
putIntoDest(p, d, n, ropecg(p.module, "$1[$2]", rdLoc(a), rdCharLoc(b)), putIntoDest(p, d, n, ropecg(p.module, "$1[$2]", rdLoc(a), rdCharLoc(b)),
a.storage) a.storage)
@ -867,15 +866,15 @@ proc genArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
# semantic pass has already checked for const index expressions # semantic pass has already checked for const index expressions
if firstOrd(p.config, ty) == 0: if firstOrd(p.config, ty) == 0:
if (firstOrd(p.config, b.t) < firstOrd(p.config, ty)) or (lastOrd(p.config, b.t) > lastOrd(p.config, ty)): if (firstOrd(p.config, b.t) < firstOrd(p.config, ty)) or (lastOrd(p.config, b.t) > lastOrd(p.config, ty)):
linefmt(p, cpsStmts, "if ((NU)($1) > (NU)($2)) #raiseIndexError();$n", linefmt(p, cpsStmts, "if ((NU)($1) > (NU)($2)) #raiseIndexError2($1, $2);$n",
rdCharLoc(b), intLiteral(lastOrd(p.config, ty))) rdCharLoc(b), intLiteral(lastOrd(p.config, ty)))
else: else:
linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3) #raiseIndexError();$n", linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3) #raiseIndexError3($1, $2, $3);$n",
rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty))) rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty)))
else: else:
let idx = getOrdValue(y) let idx = getOrdValue(y)
if idx < firstOrd(p.config, ty) or idx > lastOrd(p.config, ty): if idx < firstOrd(p.config, ty) or idx > lastOrd(p.config, ty):
localError(p.config, x.info, "index out of bounds") localError(p.config, x.info, formatErrorIndexBound(idx, firstOrd(p.config, ty), lastOrd(p.config, ty)))
d.inheritLocation(a) d.inheritLocation(a)
putIntoDest(p, d, n, putIntoDest(p, d, n,
ropecg(p.module, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first), a.storage) ropecg(p.module, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first), a.storage)
@ -884,7 +883,6 @@ proc genCStringElem(p: BProc, n, x, y: PNode, d: var TLoc) =
var a, b: TLoc var a, b: TLoc
initLocExpr(p, x, a) initLocExpr(p, x, a)
initLocExpr(p, y, b) initLocExpr(p, y, b)
var ty = skipTypes(a.t, abstractVarRange)
inheritLocation(d, a) inheritLocation(d, a)
putIntoDest(p, d, n, putIntoDest(p, d, n,
ropecg(p.module, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.storage) ropecg(p.module, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.storage)
@ -915,7 +913,7 @@ proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
initLocExpr(p, x, a) initLocExpr(p, x, a)
initLocExpr(p, y, b) # emit range check: initLocExpr(p, y, b) # emit range check:
if optBoundsCheck in p.options: if optBoundsCheck in p.options:
linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)) #raiseIndexError();$n", linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)) #raiseIndexError2($1,$2Len_0-1);$n",
rdLoc(b), rdLoc(a)) # BUGFIX: ``>=`` and not ``>``! rdLoc(b), rdLoc(a)) # BUGFIX: ``>=`` and not ``>``!
inheritLocation(d, a) inheritLocation(d, a)
putIntoDest(p, d, n, putIntoDest(p, d, n,
@ -931,11 +929,11 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
if optBoundsCheck in p.options: if optBoundsCheck in p.options:
if ty.kind == tyString and (not defined(nimNoZeroTerminator) or optLaxStrings in p.options): if ty.kind == tyString and (not defined(nimNoZeroTerminator) or optLaxStrings in p.options):
linefmt(p, cpsStmts, linefmt(p, cpsStmts,
"if ((NU)($1) > (NU)$2) #raiseIndexError();$n", "if ((NU)($1) > (NU)$2) #raiseIndexError2($1,$2);$n",
rdLoc(b), lenExpr(p, a)) rdLoc(b), lenExpr(p, a))
else: else:
linefmt(p, cpsStmts, linefmt(p, cpsStmts,
"if ((NU)($1) >= (NU)$2) #raiseIndexError();$n", "if ((NU)($1) >= (NU)$2) #raiseIndexError2($1,$2-1);$n",
rdLoc(b), lenExpr(p, a)) rdLoc(b), lenExpr(p, a))
if d.k == locNone: d.storage = OnHeap if d.k == locNone: d.storage = OnHeap
if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}: if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
@ -1402,7 +1400,6 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) =
else: else:
var i: TLoc var i: TLoc
getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), i) getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), i)
let oldCode = p.s(cpsStmts)
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", i.r, L.rope) linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", i.r, L.rope)
initLoc(elem, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap) initLoc(elem, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap)
elem.r = ropecg(p.module, "$1$3[$2]", rdLoc(d), rdLoc(i), dataField(p)) elem.r = ropecg(p.module, "$1$3[$2]", rdLoc(d), rdLoc(i), dataField(p))
@ -2576,7 +2573,6 @@ proc genConstObjConstr(p: BProc; n: PNode): Rope =
proc genConstSimpleList(p: BProc, n: PNode): Rope = proc genConstSimpleList(p: BProc, n: PNode): Rope =
var length = sonsLen(n) var length = sonsLen(n)
result = rope("{") result = rope("{")
let t = n.typ.skipTypes(abstractInst)
for i in countup(0, length - 2): for i in countup(0, length - 2):
addf(result, "$1,$n", [genNamedConstExpr(p, n.sons[i])]) addf(result, "$1,$n", [genNamedConstExpr(p, n.sons[i])])
if length > 0: if length > 0:

View file

@ -559,7 +559,8 @@ proc genParForStmt(p: BProc, t: PNode) =
initLocExpr(p, call.sons[1], rangeA) initLocExpr(p, call.sons[1], rangeA)
initLocExpr(p, call.sons[2], rangeB) initLocExpr(p, call.sons[2], rangeB)
lineF(p, cpsStmts, "#pragma omp $4$n" & # $n at the beginning because of #9710
lineF(p, cpsStmts, "$n#pragma omp $4$n" &
"for ($1 = $2; $1 <= $3; ++$1)", "for ($1 = $2; $1 <= $3; ++$1)",
[forLoopVar.loc.rdLoc, [forLoopVar.loc.rdLoc,
rangeA.rdLoc, rangeB.rdLoc, rangeA.rdLoc, rangeB.rdLoc,

View file

@ -16,6 +16,8 @@ import
condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases,
lowerings, tables, sets, ndi, lineinfos, pathutils, transf lowerings, tables, sets, ndi, lineinfos, pathutils, transf
import system/helpers2
when not defined(leanCompiler): when not defined(leanCompiler):
import semparallel import semparallel

View file

@ -188,7 +188,7 @@ proc methodDef*(g: ModuleGraph; s: PSym, fromCache: bool) =
elif sfBase notin s.flags: elif sfBase notin s.flags:
message(g.config, s.info, warnUseBase) message(g.config, s.info, warnUseBase)
proc relevantCol(methods: TSymSeq, col: int): bool = proc relevantCol(methods: seq[PSym], col: int): bool =
# returns true iff the position is relevant # returns true iff the position is relevant
var t = methods[0].typ.sons[col].skipTypes(skipPtrs) var t = methods[0].typ.sons[col].skipTypes(skipPtrs)
if t.kind == tyObject: if t.kind == tyObject:
@ -206,7 +206,7 @@ proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int =
if (d != high(int)) and d != 0: if (d != high(int)) and d != 0:
return d return d
proc sortBucket(a: var TSymSeq, relevantCols: IntSet) = proc sortBucket(a: var seq[PSym], relevantCols: IntSet) =
# we use shellsort here; fast and simple # we use shellsort here; fast and simple
var n = len(a) var n = len(a)
var h = 1 var h = 1
@ -225,7 +225,7 @@ proc sortBucket(a: var TSymSeq, relevantCols: IntSet) =
a[j] = v a[j] = v
if h == 1: break if h == 1: break
proc genDispatcher(g: ModuleGraph; methods: TSymSeq, relevantCols: IntSet): PSym = proc genDispatcher(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet): PSym =
var base = lastSon(methods[0].ast).sym var base = lastSon(methods[0].ast).sym
result = base result = base
var paramLen = sonsLen(base.typ) var paramLen = sonsLen(base.typ)

View file

@ -480,7 +480,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
of "native", "gdb": of "native", "gdb":
incl(conf.globalOptions, optCDebug) incl(conf.globalOptions, optCDebug)
conf.options = conf.options + {optLineDir} - {optEndb} conf.options = conf.options + {optLineDir} - {optEndb}
defineSymbol(conf.symbols, "nimTypeNames") # type names are used in gdb pretty printing #defineSymbol(conf.symbols, "nimTypeNames") # type names are used in gdb pretty printing
undefSymbol(conf.symbols, "endb") undefSymbol(conf.symbols, "endb")
else: else:
localError(conf, info, "expected endb|gdb but found " & arg) localError(conf, info, "expected endb|gdb but found " & arg)
@ -617,6 +617,14 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
of "run", "r": of "run", "r":
expectNoArg(conf, switch, arg, pass, info) expectNoArg(conf, switch, arg, pass, info)
incl(conf.globalOptions, optRun) incl(conf.globalOptions, optRun)
of "errormax":
expectArg(conf, switch, arg, pass, info)
# Note: `nim check` (etc) can overwrite this.
# `0` is meaningless, give it a useful meaning as in clang's -ferror-limit
# If user doesn't set this flag and the code doesn't either, it'd
# have the same effect as errorMax = 1
let ret = parseInt(arg)
conf.errorMax = if ret == 0: high(int) else: ret
of "verbosity": of "verbosity":
expectArg(conf, switch, arg, pass, info) expectArg(conf, switch, arg, pass, info)
let verbosity = parseInt(arg) let verbosity = parseInt(arg)

View file

@ -116,7 +116,7 @@ Remarks: Rule 1.2 is not yet implemented because ``sink`` is currently
import import
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
strutils, options, dfa, lowerings, tables, modulegraphs, strutils, options, dfa, lowerings, tables, modulegraphs, msgs,
lineinfos, parampatterns lineinfos, parampatterns
const const
@ -127,20 +127,11 @@ type
owner: PSym owner: PSym
g: ControlFlowGraph g: ControlFlowGraph
jumpTargets: IntSet jumpTargets: IntSet
tmpObj: PType
tmp: PSym
destroys, topLevelVars: PNode destroys, topLevelVars: PNode
toDropBit: Table[int, PSym]
graph: ModuleGraph graph: ModuleGraph
emptyNode: PNode emptyNode: PNode
otherRead: PNode otherRead: PNode
proc getTemp(c: var Con; typ: PType; info: TLineInfo): PNode =
# XXX why are temps fields in an object here?
let f = newSym(skField, getIdent(c.graph.cache, ":d" & $c.tmpObj.n.len), c.owner, info)
f.typ = typ
rawAddField c.tmpObj, f
result = rawDirectAccess(c.tmp, f)
proc isHarmlessVar*(s: PSym; c: Con): bool = proc isHarmlessVar*(s: PSym; c: Con): bool =
# 's' is harmless if it used only once and its # 's' is harmless if it used only once and its
@ -329,22 +320,11 @@ proc genDestroy(c: Con; t: PType; dest: PNode): PNode =
proc addTopVar(c: var Con; v: PNode) = proc addTopVar(c: var Con; v: PNode) =
c.topLevelVars.add newTree(nkIdentDefs, v, c.emptyNode, c.emptyNode) c.topLevelVars.add newTree(nkIdentDefs, v, c.emptyNode, c.emptyNode)
proc dropBit(c: var Con; s: PSym): PSym = proc getTemp(c: var Con; typ: PType; info: TLineInfo): PNode =
result = c.toDropBit.getOrDefault(s.id) let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), c.owner, info)
assert result != nil sym.typ = typ
result = newSymNode(sym)
proc registerDropBit(c: var Con; s: PSym) = c.addTopVar(result)
let result = newSym(skTemp, getIdent(c.graph.cache, s.name.s & "_AliveBit"), c.owner, s.info)
result.typ = getSysType(c.graph, s.info, tyBool)
let trueVal = newIntTypeNode(nkIntLit, 1, result.typ)
c.topLevelVars.add newTree(nkIdentDefs, newSymNode result, c.emptyNode, trueVal)
c.toDropBit[s.id] = result
# generate:
# if not sinkParam_AliveBit: `=destroy`(sinkParam)
let t = s.typ.skipTypes({tyGenericInst, tyAlias, tySink})
if t.destructor != nil:
c.destroys.add newTree(nkIfStmt,
newTree(nkElifBranch, newSymNode result, genDestroy(c, t, newSymNode s)))
proc p(n: PNode; c: var Con): PNode proc p(n: PNode; c: var Con): PNode
@ -355,16 +335,6 @@ template recurse(n, dest) =
proc isSinkParam(s: PSym): bool {.inline.} = proc isSinkParam(s: PSym): bool {.inline.} =
result = s.kind == skParam and s.typ.kind == tySink result = s.kind == skParam and s.typ.kind == tySink
proc destructiveMoveSink(n: PNode; c: var Con): PNode =
# generate: (chckMove(sinkParam_AliveBit); sinkParam_AliveBit = false; sinkParam)
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
let bit = newSymNode dropBit(c, n.sym)
if optMoveCheck in c.owner.options:
result.add callCodegenProc(c.graph, "chckMove", bit.info, bit)
result.add newTree(nkAsgn, bit,
newIntTypeNode(nkIntLit, 0, getSysType(c.graph, n.info, tyBool)))
result.add n
proc genMagicCall(n: PNode; c: var Con; magicname: string; m: TMagic): PNode = proc genMagicCall(n: PNode; c: var Con; magicname: string; m: TMagic): PNode =
result = newNodeI(nkCall, n.info) result = newNodeI(nkCall, n.info)
result.add(newSymNode(createMagic(c.graph, magicname, m))) result.add(newSymNode(createMagic(c.graph, magicname, m)))
@ -395,6 +365,12 @@ proc destructiveMoveVar(n: PNode; c: var Con): PNode =
result.add genWasMoved(n, c) result.add genWasMoved(n, c)
result.add tempAsNode result.add tempAsNode
proc sinkParamIsLastReadCheck(c: var Con, s: PNode) =
assert s.kind == nkSym and s.sym.kind == skParam
if not isLastRead(s, c):
localError(c.graph.config, c.otherRead.info, "sink parameter `" & $s.sym.name.s &
"` is already consumed at " & toFileLineCol(c. graph.config, s.info))
proc passCopyToSink(n: PNode; c: var Con): PNode = proc passCopyToSink(n: PNode; c: var Con): PNode =
result = newNodeIT(nkStmtListExpr, n.info, n.typ) result = newNodeIT(nkStmtListExpr, n.info, n.typ)
let tmp = getTemp(c, n.typ, n.info) let tmp = getTemp(c, n.typ, n.info)
@ -411,6 +387,11 @@ proc passCopyToSink(n: PNode; c: var Con): PNode =
result.add tmp result.add tmp
proc pArg(arg: PNode; c: var Con; isSink: bool): PNode = proc pArg(arg: PNode; c: var Con; isSink: bool): PNode =
template pArgIfTyped(arg_part: PNode): PNode =
# typ is nil if we are in if/case expr branch with noreturn
if arg_part.typ == nil: p(arg_part, c)
else: pArg(arg_part, c, isSink)
if isSink: if isSink:
if arg.kind in nkCallKinds: if arg.kind in nkCallKinds:
# recurse but skip the call expression in order to prevent # recurse but skip the call expression in order to prevent
@ -421,17 +402,53 @@ proc pArg(arg: PNode; c: var Con; isSink: bool): PNode =
result.add arg[0] result.add arg[0]
for i in 1..<arg.len: for i in 1..<arg.len:
result.add pArg(arg[i], c, i < L and parameters[i].kind == tySink) result.add pArg(arg[i], c, i < L and parameters[i].kind == tySink)
elif arg.kind in {nkObjConstr, nkCharLit..nkFloat128Lit}: elif arg.kind in {nkBracket, nkObjConstr, nkTupleConstr, nkBracket, nkCharLit..nkFloat128Lit}:
discard "object construction to sink parameter: nothing to do" discard "object construction to sink parameter: nothing to do"
result = arg result = arg
elif arg.kind == nkSym and arg.sym.kind in InterestingSyms and isLastRead(arg, c):
# if x is a variable and it its last read we eliminate its
# destructor invokation, but don't. We need to reset its memory
# to disable its destructor which we have not elided:
result = destructiveMoveVar(arg, c)
elif arg.kind == nkSym and isSinkParam(arg.sym): elif arg.kind == nkSym and isSinkParam(arg.sym):
# mark the sink parameter as used: # Sinked params can be consumed only once. We need to reset the memory
result = destructiveMoveSink(arg, c) # to disable the destructor which we have not elided
sinkParamIsLastReadCheck(c, arg)
result = destructiveMoveVar(arg, c)
elif arg.kind == nkSym and arg.sym.kind in InterestingSyms and isLastRead(arg, c):
# it is the last read, can be sinked. We need to reset the memory
# to disable the destructor which we have not elided
result = destructiveMoveVar(arg, c)
elif arg.kind in {nkBlockExpr, nkBlockStmt}:
result = copyNode(arg)
result.add arg[0]
result.add pArg(arg[1], c, isSink)
elif arg.kind == nkStmtListExpr:
result = copyNode(arg)
for i in 0..arg.len-2:
result.add p(arg[i], c)
result.add pArg(arg[^1], c, isSink)
elif arg.kind in {nkIfExpr, nkIfStmt}:
result = copyNode(arg)
for i in 0..<arg.len:
var branch = copyNode(arg[i])
if arg[i].kind in {nkElifBranch, nkElifExpr}:
branch.add p(arg[i][0], c)
branch.add pArgIfTyped(arg[i][1])
else:
branch.add pArgIfTyped(arg[i][0])
result.add branch
elif arg.kind == nkCaseStmt:
result = copyNode(arg)
result.add p(arg[0], c)
for i in 1..<arg.len:
var branch: PNode
if arg[i].kind == nkOfbranch:
branch = arg[i] # of branch conditions are constants
branch[^1] = pArgIfTyped(arg[i][^1])
elif arg[i].kind in {nkElifBranch, nkElifExpr}:
branch = copyNode(arg[i])
branch.add p(arg[i][0], c)
branch.add pArgIfTyped(arg[i][1])
else:
branch = copyNode(arg[i])
branch.add pArgIfTyped(arg[i][0])
result.add branch
else: else:
# an object that is not temporary but passed to a 'sink' parameter # an object that is not temporary but passed to a 'sink' parameter
# results in a copy. # results in a copy.
@ -440,6 +457,11 @@ proc pArg(arg: PNode; c: var Con; isSink: bool): PNode =
result = p(arg, c) result = p(arg, c)
proc moveOrCopy(dest, ri: PNode; c: var Con): PNode = proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
template moveOrCopyIfTyped(ri_part: PNode): PNode =
# typ is nil if we are in if/case expr branch with noreturn
if ri_part.typ == nil: p(ri_part, c)
else: moveOrCopy(dest, ri_part, c)
case ri.kind case ri.kind
of nkCallKinds: of nkCallKinds:
result = genSink(c, dest.typ, dest, ri) result = genSink(c, dest.typ, dest, ri)
@ -464,6 +486,45 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
for i in 0..ri.len-2: for i in 0..ri.len-2:
result.add p(ri[i], c) result.add p(ri[i], c)
result.add moveOrCopy(dest, ri[^1], c) result.add moveOrCopy(dest, ri[^1], c)
of nkBlockExpr, nkBlockStmt:
result = newNodeI(nkBlockStmt, ri.info)
result.add ri[0] ## add label
result.add moveOrCopy(dest, ri[1], c)
of nkIfExpr, nkIfStmt:
result = newNodeI(nkIfStmt, ri.info)
for i in 0..<ri.len:
var branch = copyNode(ri[i])
if ri[i].kind in {nkElifBranch, nkElifExpr}:
branch.add p(ri[i][0], c)
branch.add moveOrCopyIfTyped(ri[i][1])
else:
branch.add moveOrCopyIfTyped(ri[i][0])
result.add branch
of nkCaseStmt:
result = newNodeI(nkCaseStmt, ri.info)
result.add p(ri[0], c)
for i in 1..<ri.len:
var branch: PNode
if ri[i].kind == nkOfbranch:
branch = ri[i] # of branch conditions are constants
branch[^1] = moveOrCopyIfTyped(ri[i][^1])
elif ri[i].kind in {nkElifBranch, nkElifExpr}:
branch = copyNode(ri[i])
branch.add p(ri[i][0], c)
branch.add moveOrCopyIfTyped(ri[i][1])
else:
branch = copyNode(ri[i])
branch.add moveOrCopyIfTyped(ri[i][0])
result.add branch
of nkBracket:
# array constructor
result = genSink(c, dest.typ, dest, ri)
let ri2 = copyTree(ri)
for i in 0..<ri.len:
# everything that is passed to an array constructor is consumed,
# so these all act like 'sink' parameters:
ri2[i] = pArg(ri[i], c, isSink = true)
result.add ri2
of nkObjConstr: of nkObjConstr:
result = genSink(c, dest.typ, dest, ri) result = genSink(c, dest.typ, dest, ri)
let ri2 = copyTree(ri) let ri2 = copyTree(ri)
@ -484,14 +545,17 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
ri2[i] = pArg(ri[i], c, isSink = true) ri2[i] = pArg(ri[i], c, isSink = true)
result.add ri2 result.add ri2
of nkSym: of nkSym:
if ri.sym.kind != skParam and isLastRead(ri, c): if isSinkParam(ri.sym):
# Rule 3: `=sink`(x, z); wasMoved(z)
sinkParamIsLastReadCheck(c, ri)
var snk = genSink(c, dest.typ, dest, ri)
snk.add ri
result = newTree(nkStmtList, snk, genMagicCall(ri, c, "wasMoved", mWasMoved))
elif ri.sym.kind != skParam and isLastRead(ri, c):
# Rule 3: `=sink`(x, z); wasMoved(z) # Rule 3: `=sink`(x, z); wasMoved(z)
var snk = genSink(c, dest.typ, dest, ri) var snk = genSink(c, dest.typ, dest, ri)
snk.add p(ri, c) snk.add ri
result = newTree(nkStmtList, snk, genMagicCall(ri, c, "wasMoved", mWasMoved)) result = newTree(nkStmtList, snk, genMagicCall(ri, c, "wasMoved", mWasMoved))
elif isSinkParam(ri.sym):
result = genSink(c, dest.typ, dest, ri)
result.add destructiveMoveSink(ri, c)
else: else:
result = genCopy(c, dest.typ, dest, ri) result = genCopy(c, dest.typ, dest, ri)
result.add p(ri, c) result.add p(ri, c)
@ -512,13 +576,14 @@ proc p(n: PNode; c: var Con): PNode =
if it.kind == nkVarTuple and hasDestructor(ri.typ): if it.kind == nkVarTuple and hasDestructor(ri.typ):
let x = lowerTupleUnpacking(c.graph, it, c.owner) let x = lowerTupleUnpacking(c.graph, it, c.owner)
result.add p(x, c) result.add p(x, c)
elif it.kind == nkIdentDefs and hasDestructor(it[0].typ) and not isUnpackedTuple(it[0].sym): elif it.kind == nkIdentDefs and hasDestructor(it[0].typ):
for j in 0..L-2: for j in 0..L-2:
let v = it[j] let v = it[j]
doAssert v.kind == nkSym doAssert v.kind == nkSym
# move the variable declaration to the top of the frame: # move the variable declaration to the top of the frame:
c.addTopVar v c.addTopVar v
# make sure it's destroyed at the end of the proc: # make sure it's destroyed at the end of the proc:
if not isUnpackedTuple(it[0].sym):
c.destroys.add genDestroy(c, v.typ, v) c.destroys.add genDestroy(c, v.typ, v)
if ri.kind != nkEmpty: if ri.kind != nkEmpty:
let r = moveOrCopy(v, ri, c) let r = moveOrCopy(v, ri, c)
@ -566,12 +631,8 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
echo "injecting into ", n echo "injecting into ", n
var c: Con var c: Con
c.owner = owner c.owner = owner
c.tmp = newSym(skTemp, getIdent(g.cache, ":d"), owner, n.info)
c.tmpObj = createObj(g, owner, n.info)
c.tmp.typ = c.tmpObj
c.destroys = newNodeI(nkStmtList, n.info) c.destroys = newNodeI(nkStmtList, n.info)
c.topLevelVars = newNodeI(nkVarSection, n.info) c.topLevelVars = newNodeI(nkVarSection, n.info)
c.toDropBit = initTable[int, PSym]()
c.graph = g c.graph = g
c.emptyNode = newNodeI(nkEmpty, n.info) c.emptyNode = newNodeI(nkEmpty, n.info)
let cfg = constructCfg(owner, n) let cfg = constructCfg(owner, n)
@ -586,10 +647,10 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
let params = owner.typ.n let params = owner.typ.n
for i in 1 ..< params.len: for i in 1 ..< params.len:
let param = params[i].sym let param = params[i].sym
if param.typ.kind == tySink: registerDropBit(c, param) if param.typ.kind == tySink and hasDestructor(param.typ):
c.destroys.add genDestroy(c, param.typ.skipTypes({tyGenericInst, tyAlias, tySink}), params[i])
let body = p(n, c) let body = p(n, c)
if c.tmp.typ.n.len > 0:
c.addTopVar(newSymNode c.tmp)
result = newNodeI(nkStmtList, n.info) result = newNodeI(nkStmtList, n.info)
if c.topLevelVars.len > 0: if c.topLevelVars.len > 0:
result.add c.topLevelVars result.add c.topLevelVars

View file

@ -272,7 +272,7 @@ proc genReturn(c: var Con; n: PNode) =
c.code.add Instr(n: n, kind: goto, dest: high(int) - c.code.len) c.code.add Instr(n: n, kind: goto, dest: high(int) - c.code.len)
const const
InterestingSyms = {skVar, skResult, skLet} InterestingSyms = {skVar, skResult, skLet, skParam}
proc genUse(c: var Con; n: PNode) = proc genUse(c: var Con; n: PNode) =
var n = n var n = n

View file

@ -37,18 +37,21 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
case templ.kind case templ.kind
of nkSym: of nkSym:
var s = templ.sym var s = templ.sym
if s.owner.id == c.owner.id: if s.owner == nil or s.owner.id == c.owner.id:
if s.kind == skParam and sfGenSym notin s.flags: if s.kind == skParam and sfGenSym notin s.flags:
handleParam actual.sons[s.position] handleParam actual.sons[s.position]
elif s.kind == skGenericParam or elif (s.owner != nil) and (s.kind == skGenericParam or
s.kind == skType and s.typ != nil and s.typ.kind == tyGenericParam: s.kind == skType and s.typ != nil and s.typ.kind == tyGenericParam):
handleParam actual.sons[s.owner.typ.len + s.position - 1] handleParam actual.sons[s.owner.typ.len + s.position - 1]
else: else:
internalAssert c.config, sfGenSym in s.flags or s.kind == skType internalAssert c.config, sfGenSym in s.flags or s.kind == skType
var x = PSym(idTableGet(c.mapping, s)) var x = PSym(idTableGet(c.mapping, s))
if x == nil: if x == nil:
x = copySym(s) x = copySym(s)
x.owner = c.genSymOwner # sem'check needs to set the owner properly later, see bug #9476
x.owner = nil # c.genSymOwner
#if x.kind == skParam and x.owner.kind == skModule:
# internalAssert c.config, false
idTablePut(c.mapping, s, x) idTablePut(c.mapping, s, x)
result.add newSymNode(x, if c.instLines: actual.info else: templ.info) result.add newSymNode(x, if c.instLines: actual.info else: templ.info)
else: else:
@ -173,6 +176,7 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
initIdTable(ctx.mapping) initIdTable(ctx.mapping)
let body = tmpl.getBody let body = tmpl.getBody
#echo "instantion of ", renderTree(body, {renderIds})
if isAtom(body): if isAtom(body):
result = newNodeI(nkPar, body.info) result = newNodeI(nkPar, body.info)
evalTemplateAux(body, args, ctx, result) evalTemplateAux(body, args, ctx, result)
@ -189,5 +193,7 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
evalTemplateAux(body.sons[i], args, ctx, result) evalTemplateAux(body.sons[i], args, ctx, result)
result.flags.incl nfFromTemplate result.flags.incl nfFromTemplate
result = wrapInComesFrom(n.info, tmpl, result) result = wrapInComesFrom(n.info, tmpl, result)
#if ctx.debugActive:
# echo "instantion of ", renderTree(result, {renderIds})
dec(conf.evalTemplateCounter) dec(conf.evalTemplateCounter)

View file

@ -763,6 +763,32 @@ proc execCmdsInParallel(conf: ConfigRef; cmds: seq[string]; prettyCb: proc (idx:
rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" % rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" %
cmds.join()) cmds.join())
proc linkViaResponseFile(conf: ConfigRef; cmd: string) =
# Extracting the linker.exe here is a bit hacky but the best solution
# given ``buildLib``'s design.
var i = 0
var last = 0
if cmd.len > 0 and cmd[0] == '"':
inc i
while i < cmd.len and cmd[i] != '"': inc i
last = i
inc i
else:
while i < cmd.len and cmd[i] != ' ': inc i
last = i
while i < cmd.len and cmd[i] == ' ': inc i
let linkerArgs = conf.projectName & "_" & "linkerArgs.txt"
let args = cmd.substr(i)
# GCC's response files don't support backslashes. Junk.
if conf.cCompiler == ccGcc:
writeFile(linkerArgs, args.replace('\\', '/'))
else:
writeFile(linkerArgs, args)
try:
execLinkCmd(conf, cmd.substr(0, last) & " @" & linkerArgs)
finally:
removeFile(linkerArgs)
proc callCCompiler*(conf: ConfigRef; projectfile: AbsoluteFile) = proc callCCompiler*(conf: ConfigRef; projectfile: AbsoluteFile) =
var var
linkCmd: string linkCmd: string
@ -795,6 +821,12 @@ proc callCCompiler*(conf: ConfigRef; projectfile: AbsoluteFile) =
linkCmd = getLinkCmd(conf, projectfile, objfiles) linkCmd = getLinkCmd(conf, projectfile, objfiles)
if optCompileOnly notin conf.globalOptions: if optCompileOnly notin conf.globalOptions:
if defined(windows) and linkCmd.len > 8_000:
# Windows's command line limit is about 8K (don't laugh...) so C compilers on
# Windows support a feature where the command line can be passed via ``@linkcmd``
# to them.
linkViaResponseFile(conf, linkCmd)
else:
execLinkCmd(conf, linkCmd) execLinkCmd(conf, linkCmd)
else: else:
linkCmd = "" linkCmd = ""

View file

@ -945,7 +945,7 @@ proc needsNoCopy(p: PProc; y: PNode): bool =
else: else:
return (mapType(y.typ) != etyBaseIndex and return (mapType(y.typ) != etyBaseIndex and
(skipTypes(y.typ, abstractInst).kind in (skipTypes(y.typ, abstractInst).kind in
{tyRef, tyPtr, tyLent, tyVar, tyCString} + IntegralTypes)) {tyRef, tyPtr, tyLent, tyVar, tyCString, tyProc} + IntegralTypes))
return true return true
proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =

View file

@ -407,11 +407,8 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
obj.n[0].sym.id = -s.id obj.n[0].sym.id = -s.id
else: else:
addField(obj, s, c.graph.cache) addField(obj, s, c.graph.cache)
# but always return because the rest of the proc is only relevant when
# ow != owner:
return
# direct or indirect dependency: # direct or indirect dependency:
if (innerProc and s.typ.callConv == ccClosure) or interestingVar(s): elif (innerProc and s.typ.callConv == ccClosure) or interestingVar(s):
discard """ discard """
proc outer() = proc outer() =
var x: int var x: int

View file

@ -169,7 +169,7 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) =
getSymRepr(c.config, s)) getSymRepr(c.config, s))
inc missingImpls inc missingImpls
elif {sfUsed, sfExported} * s.flags == {}: elif {sfUsed, sfExported} * s.flags == {}:
if s.kind notin {skForVar, skParam, skMethod, skUnknown, skGenericParam}: if s.kind notin {skForVar, skParam, skMethod, skUnknown, skGenericParam, skEnumField}:
# XXX: implicit type params are currently skTypes # XXX: implicit type params are currently skTypes
# maybe they can be made skGenericParam as well. # maybe they can be made skGenericParam as well.
if s.typ != nil and tfImplicitTypeParam notin s.typ.flags and if s.typ != nil and tfImplicitTypeParam notin s.typ.flags and

View file

@ -266,11 +266,23 @@ proc mainCommand*(graph: ModuleGraph) =
var libpaths = newJArray() var libpaths = newJArray()
for dir in conf.searchPaths: libpaths.elems.add(%dir.string) for dir in conf.searchPaths: libpaths.elems.add(%dir.string)
var hints = newJObject() # consider factoring with `listHints`
for a in hintMin..hintMax:
let key = lineinfos.HintsToStr[ord(a) - ord(hintMin)]
hints[key] = %(a in conf.notes)
var warnings = newJObject()
for a in warnMin..warnMax:
let key = lineinfos.WarningsToStr[ord(a) - ord(warnMin)]
warnings[key] = %(a in conf.notes)
var dumpdata = %[ var dumpdata = %[
(key: "version", val: %VersionAsString), (key: "version", val: %VersionAsString),
(key: "project_path", val: %conf.projectFull.string), (key: "project_path", val: %conf.projectFull.string),
(key: "defined_symbols", val: definedSymbols), (key: "defined_symbols", val: definedSymbols),
(key: "lib_paths", val: libpaths) (key: "lib_paths", val: %libpaths),
(key: "out", val: %conf.outFile.string),
(key: "hints", val: hints),
(key: "warnings", val: warnings),
] ]
msgWriteln(conf, $dumpdata, {msgStdout, msgSkipHook}) msgWriteln(conf, $dumpdata, {msgStdout, msgSkipHook})

View file

@ -47,7 +47,7 @@ type
doStopCompile*: proc(): bool {.closure.} doStopCompile*: proc(): bool {.closure.}
usageSym*: PSym # for nimsuggest usageSym*: PSym # for nimsuggest
owners*: seq[PSym] owners*: seq[PSym]
methods*: seq[tuple[methods: TSymSeq, dispatcher: PSym]] # needs serialization! methods*: seq[tuple[methods: seq[PSym], dispatcher: PSym]] # needs serialization!
systemModule*: PSym systemModule*: PSym
sysTypes*: array[TTypeKind, PType] sysTypes*: array[TTypeKind, PType]
compilerprocs*: TStrTable compilerprocs*: TStrTable

View file

@ -9,14 +9,14 @@
when defined(gcc) and defined(windows): when defined(gcc) and defined(windows):
when defined(x86): when defined(x86):
{.link: "icons/nim.res".} {.link: "../icons/nim.res".}
else: else:
{.link: "icons/nim_icon.o".} {.link: "../icons/nim_icon.o".}
when defined(amd64) and defined(windows) and defined(vcc): when defined(amd64) and defined(windows) and defined(vcc):
{.link: "icons/nim-amd64-windows-vcc.res".} {.link: "../icons/nim-amd64-windows-vcc.res".}
when defined(i386) and defined(windows) and defined(vcc): when defined(i386) and defined(windows) and defined(vcc):
{.link: "icons/nim-i386-windows-vcc.res".} {.link: "../icons/nim-i386-windows-vcc.res".}
import import
commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes, commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes,

View file

@ -1111,8 +1111,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
else: sym.flags.incl sfUsed else: sym.flags.incl sfUsed
of wLiftLocals: discard of wLiftLocals: discard
else: invalidPragma(c, it) else: invalidPragma(c, it)
elif sym != nil and sym.kind in {skVar, skLet, skParam, skField, skProc, elif sym == nil or (sym != nil and sym.kind in {skVar, skLet, skParam,
skFunc, skConverter, skMethod, skType}: skField, skProc, skFunc, skConverter, skMethod, skType}):
n.sons[i] = semCustomPragma(c, it) n.sons[i] = semCustomPragma(c, it)
elif sym != nil: elif sym != nil:
illegalCustomPragma(c, it, sym) illegalCustomPragma(c, it, sym)
@ -1138,7 +1138,7 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
internalError(c.config, n.info, "implicitPragmas") internalError(c.config, n.info, "implicitPragmas")
inc i inc i
popInfoContext(c.config) popInfoContext(c.config)
if sym.kind in routineKinds: mergePragmas(sym.ast, o) if sym.kind in routineKinds and sym.ast != nil: mergePragmas(sym.ast, o)
if lfExportLib in sym.loc.flags and sfExportc notin sym.flags: if lfExportLib in sym.loc.flags and sfExportc notin sym.flags:
localError(c.config, n.info, ".dynlib requires .exportc") localError(c.config, n.info, ".dynlib requires .exportc")

View file

@ -207,6 +207,7 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
if result.kind notin {kind, skTemp}: if result.kind notin {kind, skTemp}:
localError(c.config, n.info, "cannot use symbol of kind '" & localError(c.config, n.info, "cannot use symbol of kind '" &
$result.kind & "' as a '" & $kind & "'") $result.kind & "' as a '" & $kind & "'")
when false:
if sfGenSym in result.flags and result.kind notin {skTemplate, skMacro, skParam}: if sfGenSym in result.flags and result.kind notin {skTemplate, skMacro, skParam}:
# declarative context, so produce a fresh gensym: # declarative context, so produce a fresh gensym:
result = copySym(result) result = copySym(result)

View file

@ -309,6 +309,10 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
liftBodyAux(a, typ, body, newSymNode(dest).newDeref, newSymNode(src)) liftBodyAux(a, typ, body, newSymNode(dest).newDeref, newSymNode(src))
# recursion is handled explicitly, do not register the type based operation # recursion is handled explicitly, do not register the type based operation
# before 'liftBodyAux': # before 'liftBodyAux':
if c.config.selectedGC == gcDestructors and
typ.kind in {tySequence, tyString} and body.len == 0:
discard "do not cache it yet"
else:
case kind case kind
of attachedAsgn: typ.assignment = result of attachedAsgn: typ.assignment = result
of attachedSink: typ.sink = result of attachedSink: typ.sink = result

View file

@ -100,8 +100,8 @@ type
compilesContextId*: int # > 0 if we are in a ``compiles`` magic compilesContextId*: int # > 0 if we are in a ``compiles`` magic
compilesContextIdGenerator*: int compilesContextIdGenerator*: int
inGenericInst*: int # > 0 if we are instantiating a generic inGenericInst*: int # > 0 if we are instantiating a generic
converters*: TSymSeq # sequence of converters converters*: seq[PSym]
patterns*: TSymSeq # sequence of pattern matchers patterns*: seq[PSym] # sequence of pattern matchers
optionStack*: seq[POptionEntry] optionStack*: seq[POptionEntry]
symMapping*: TIdTable # every gensym'ed symbol needs to be mapped symMapping*: TIdTable # every gensym'ed symbol needs to be mapped
# to some new symbol in a generic instantiation # to some new symbol in a generic instantiation
@ -239,7 +239,7 @@ proc newContext*(graph: ModuleGraph; module: PSym): PContext =
result.typesWithOps = @[] result.typesWithOps = @[]
result.features = graph.config.features result.features = graph.config.features
proc inclSym(sq: var TSymSeq, s: PSym) = proc inclSym(sq: var seq[PSym], s: PSym) =
var L = len(sq) var L = len(sq)
for i in countup(0, L - 1): for i in countup(0, L - 1):
if sq[i].id == s.id: return if sq[i].id == s.id: return

View file

@ -1083,6 +1083,8 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
# XXX see the hack in sigmatch.nim ... # XXX see the hack in sigmatch.nim ...
return s.typ.n return s.typ.n
elif sfGenSym in s.flags: elif sfGenSym in s.flags:
# the owner should have been set by now by addParamOrResult
internalAssert c.config, s.owner != nil
if c.p.wasForwarded: if c.p.wasForwarded:
# gensym'ed parameters that nevertheless have been forward declared # gensym'ed parameters that nevertheless have been forward declared
# need a special fixup: # need a special fixup:
@ -2289,6 +2291,8 @@ proc semBlock(c: PContext, n: PNode; flags: TExprFlags): PNode =
var labl = newSymG(skLabel, n.sons[0], c) var labl = newSymG(skLabel, n.sons[0], c)
if sfGenSym notin labl.flags: if sfGenSym notin labl.flags:
addDecl(c, labl) addDecl(c, labl)
elif labl.owner == nil:
labl.owner = c.p.owner
n.sons[0] = newSymNode(labl, n.sons[0].info) n.sons[0] = newSymNode(labl, n.sons[0].info)
suggestSym(c.config, n.sons[0].info, labl, c.graph.usageSym) suggestSym(c.config, n.sons[0].info, labl, c.graph.usageSym)
styleCheckDef(c.config, labl) styleCheckDef(c.config, labl)

View file

@ -15,6 +15,8 @@ import
nversion, platform, math, msgs, os, condsyms, idents, renderer, types, nversion, platform, math, msgs, os, condsyms, idents, renderer, types,
commands, magicsys, modulegraphs, strtabs, lineinfos commands, magicsys, modulegraphs, strtabs, lineinfos
import system/helpers2
proc newIntNodeT*(intVal: BiggestInt, n: PNode; g: ModuleGraph): PNode = proc newIntNodeT*(intVal: BiggestInt, n: PNode; g: ModuleGraph): PNode =
case skipTypes(n.typ, abstractVarRange).kind case skipTypes(n.typ, abstractVarRange).kind
of tyInt: of tyInt:
@ -489,11 +491,11 @@ proc foldArrayAccess(m: PSym, n: PNode; g: ModuleGraph): PNode =
result = x.sons[int(idx)] result = x.sons[int(idx)]
if result.kind == nkExprColonExpr: result = result.sons[1] if result.kind == nkExprColonExpr: result = result.sons[1]
else: else:
localError(g.config, n.info, "index out of bounds: " & $n) localError(g.config, n.info, formatErrorIndexBound(idx, sonsLen(x)+1) & $n)
of nkBracket: of nkBracket:
idx = idx - firstOrd(g.config, x.typ) idx = idx - firstOrd(g.config, x.typ)
if idx >= 0 and idx < x.len: result = x.sons[int(idx)] if idx >= 0 and idx < x.len: result = x.sons[int(idx)]
else: localError(g.config, n.info, "index out of bounds: " & $n) else: localError(g.config, n.info, formatErrorIndexBound(idx, x.len+1) & $n)
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
result = newNodeIT(nkCharLit, x.info, n.typ) result = newNodeIT(nkCharLit, x.info, n.typ)
if idx >= 0 and idx < len(x.strVal): if idx >= 0 and idx < len(x.strVal):
@ -501,7 +503,7 @@ proc foldArrayAccess(m: PSym, n: PNode; g: ModuleGraph): PNode =
elif idx == len(x.strVal) and optLaxStrings in g.config.options: elif idx == len(x.strVal) and optLaxStrings in g.config.options:
discard discard
else: else:
localError(g.config, n.info, "index out of bounds: " & $n) localError(g.config, n.info, formatErrorIndexBound(idx, len(x.strVal)-1) & $n)
else: discard else: discard
proc foldFieldAccess(m: PSym, n: PNode; g: ModuleGraph): PNode = proc foldFieldAccess(m: PSym, n: PNode; g: ModuleGraph): PNode =
@ -626,6 +628,14 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode =
# It doesn't matter if the argument is const or not for mLengthArray. # It doesn't matter if the argument is const or not for mLengthArray.
# This fixes bug #544. # This fixes bug #544.
result = newIntNodeT(lengthOrd(g.config, n.sons[1].typ), n, g) result = newIntNodeT(lengthOrd(g.config, n.sons[1].typ), n, g)
of mSizeOf:
let size = getSize(g.config, n[1].typ)
if size >= 0:
result = newIntNode(nkIntLit, size)
result.info = n.info
result.typ = getSysType(g, n.info, tyInt)
else:
result = nil
of mAstToStr: of mAstToStr:
result = newStrNodeT(renderTree(n[1], {renderNoComments}), n, g) result = newStrNodeT(renderTree(n[1], {renderNoComments}), n, g)
of mConStrStr: of mConStrStr:

View file

@ -116,7 +116,7 @@ proc freshGenSyms(n: PNode, owner, orig: PSym, symMap: var TIdTable) =
var x = PSym(idTableGet(symMap, s)) var x = PSym(idTableGet(symMap, s))
if x != nil: if x != nil:
n.sym = x n.sym = x
elif s.owner.kind == skPackage: elif s.owner == nil or s.owner.kind == skPackage:
#echo "copied this ", s.name.s #echo "copied this ", s.name.s
x = copySym(s) x = copySym(s)
x.owner = owner x.owner = owner

View file

@ -321,8 +321,6 @@ proc semOf(c: PContext, n: PNode): PNode =
proc magicsAfterOverloadResolution(c: PContext, n: PNode, proc magicsAfterOverloadResolution(c: PContext, n: PNode,
flags: TExprFlags): PNode = flags: TExprFlags): PNode =
## This is the preferred code point to implement magics. ## This is the preferred code point to implement magics.
## This function basically works like a macro, with the difference
## that it is implemented in the compiler and not on the nimvm.
## ``c`` the current module, a symbol table to a very good approximation ## ``c`` the current module, a symbol table to a very good approximation
## ``n`` the ast like it would be passed to a real macro ## ``n`` the ast like it would be passed to a real macro
## ``flags`` Some flags for more contextual information on how the ## ``flags`` Some flags for more contextual information on how the

View file

@ -497,8 +497,10 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
var v = semIdentDef(c, a.sons[j], symkind) var v = semIdentDef(c, a.sons[j], symkind)
styleCheckDef(c.config, v) styleCheckDef(c.config, v)
onDef(a[j].info, v) onDef(a[j].info, v)
if sfGenSym notin v.flags and not isDiscardUnderscore(v): if sfGenSym notin v.flags:
addInterfaceDecl(c, v) if not isDiscardUnderscore(v): addInterfaceDecl(c, v)
else:
if v.owner == nil: v.owner = c.p.owner
when oKeepVariableNames: when oKeepVariableNames:
if c.inUnrolledContext > 0: v.flags.incl(sfShadowed) if c.inUnrolledContext > 0: v.flags.incl(sfShadowed)
else: else:
@ -573,6 +575,7 @@ proc semConst(c: PContext, n: PNode): PNode =
setVarType(c, v, typ) setVarType(c, v, typ)
v.ast = def # no need to copy v.ast = def # no need to copy
if sfGenSym notin v.flags: addInterfaceDecl(c, v) if sfGenSym notin v.flags: addInterfaceDecl(c, v)
elif v.owner == nil: v.owner = getCurrOwner(c)
var b = newNodeI(nkConstDef, a.info) var b = newNodeI(nkConstDef, a.info)
if importantComments(c.config): b.comment = a.comment if importantComments(c.config): b.comment = a.comment
addSon(b, newSymNode(v)) addSon(b, newSymNode(v))
@ -616,6 +619,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
v.typ = iterBase v.typ = iterBase
n.sons[0] = newSymNode(v) n.sons[0] = newSymNode(v)
if sfGenSym notin v.flags: addForVarDecl(c, v) if sfGenSym notin v.flags: addForVarDecl(c, v)
elif v.owner == nil: v.owner = getCurrOwner(c)
else: else:
localError(c.config, n.info, errWrongNumberOfVariables) localError(c.config, n.info, errWrongNumberOfVariables)
elif length-2 != sonsLen(iter): elif length-2 != sonsLen(iter):
@ -626,8 +630,9 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal) if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
v.typ = iter.sons[i] v.typ = iter.sons[i]
n.sons[i] = newSymNode(v) n.sons[i] = newSymNode(v)
if sfGenSym notin v.flags and not isDiscardUnderscore(v): if sfGenSym notin v.flags:
addForVarDecl(c, v) if not isDiscardUnderscore(v): addForVarDecl(c, v)
elif v.owner == nil: v.owner = getCurrOwner(c)
inc(c.p.nestedLoopCounter) inc(c.p.nestedLoopCounter)
openScope(c) openScope(c)
n.sons[length-1] = semExprBranch(c, n.sons[length-1], flags) n.sons[length-1] = semExprBranch(c, n.sons[length-1], flags)
@ -922,6 +927,7 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
s = typsym s = typsym
# add it here, so that recursive types are possible: # add it here, so that recursive types are possible:
if sfGenSym notin s.flags: addInterfaceDecl(c, s) if sfGenSym notin s.flags: addInterfaceDecl(c, s)
elif s.owner == nil: s.owner = getCurrOwner(c)
if name.kind == nkPragmaExpr: if name.kind == nkPragmaExpr:
a.sons[0].sons[0] = newSymNode(s) a.sons[0].sons[0] = newSymNode(s)
@ -1620,7 +1626,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
else: else:
s.typ.callConv = lastOptionEntry(c).defaultCC s.typ.callConv = lastOptionEntry(c).defaultCC
# add it here, so that recursive procs are possible: # add it here, so that recursive procs are possible:
if sfGenSym in s.flags: discard if sfGenSym in s.flags:
if s.owner == nil: s.owner = getCurrOwner(c)
elif kind in OverloadableSyms: elif kind in OverloadableSyms:
if not typeIsDetermined: if not typeIsDetermined:
addInterfaceOverloadableSymAt(c, oldScope, s) addInterfaceOverloadableSymAt(c, oldScope, s)
@ -1836,35 +1843,49 @@ proc semMacroDef(c: PContext, n: PNode): PNode =
if n.sons[bodyPos].kind == nkEmpty: if n.sons[bodyPos].kind == nkEmpty:
localError(c.config, n.info, errImplOfXexpected % s.name.s) localError(c.config, n.info, errImplOfXexpected % s.name.s)
proc evalInclude(c: PContext, n: PNode): PNode = proc incMod(c: PContext, n: PNode, it: PNode, includeStmtResult: PNode) =
result = newNodeI(nkStmtList, n.info) var f = checkModuleName(c.config, it)
addSon(result, n)
for i in countup(0, sonsLen(n) - 1):
var f = checkModuleName(c.config, n.sons[i])
if f != InvalidFileIDX: if f != InvalidFileIDX:
if containsOrIncl(c.includedFiles, f.int): if containsOrIncl(c.includedFiles, f.int):
localError(c.config, n.info, errRecursiveDependencyX % toFilename(c.config, f)) localError(c.config, n.info, errRecursiveDependencyX % toFilename(c.config, f))
else: else:
addSon(result, semStmt(c, c.graph.includeFileCallback(c.graph, c.module, f), {})) addSon(includeStmtResult, semStmt(c, c.graph.includeFileCallback(c.graph, c.module, f), {}))
excl(c.includedFiles, f.int) excl(c.includedFiles, f.int)
proc evalInclude(c: PContext, n: PNode): PNode =
result = newNodeI(nkStmtList, n.info)
addSon(result, n)
for i in countup(0, sonsLen(n) - 1):
var imp: PNode
let it = n.sons[i]
if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket:
let sep = it[0]
let dir = it[1]
imp = newNodeI(nkInfix, it.info)
imp.add sep
imp.add dir
imp.add sep # dummy entry, replaced in the loop
for x in it[2]:
imp.sons[2] = x
incMod(c, n, imp, result)
else:
incMod(c, n, it, result)
proc setLine(n: PNode, info: TLineInfo) = proc setLine(n: PNode, info: TLineInfo) =
for i in 0 ..< safeLen(n): setLine(n.sons[i], info) for i in 0 ..< safeLen(n): setLine(n.sons[i], info)
n.info = info n.info = info
proc semPragmaBlock(c: PContext, n: PNode): PNode = proc semPragmaBlock(c: PContext, n: PNode): PNode =
checkSonsLen(n, 2, c.config)
let pragmaList = n.sons[0] let pragmaList = n.sons[0]
pragma(c, nil, pragmaList, exprPragmas) pragma(c, nil, pragmaList, exprPragmas)
result = semExpr(c, n.sons[1]) n[1] = semExpr(c, n[1])
n.sons[1] = result result = n
result.typ = n[1].typ
for i in 0 ..< pragmaList.len: for i in 0 ..< pragmaList.len:
case whichPragma(pragmaList.sons[i]) case whichPragma(pragmaList.sons[i])
of wLine: setLine(result, pragmaList.sons[i].info) of wLine: setLine(result, pragmaList.sons[i].info)
of wLocks, wGcSafe, wNosideeffect: of wNoRewrite: incl(result.flags, nfNoRewrite)
result = n
result.typ = n.sons[1].typ
of wNoRewrite:
incl(result.flags, nfNoRewrite)
else: discard else: discard
proc semStaticStmt(c: PContext, n: PNode): PNode = proc semStaticStmt(c: PContext, n: PNode): PNode =

View file

@ -90,6 +90,8 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
if sonsLen(v) == 2: if sonsLen(v) == 2:
strVal = v.sons[1] # second tuple part is the string value strVal = v.sons[1] # second tuple part is the string value
if skipTypes(strVal.typ, abstractInst).kind in {tyString, tyCString}: if skipTypes(strVal.typ, abstractInst).kind in {tyString, tyCString}:
if not isOrdinalType(v.sons[0].typ):
localError(c.config, v.sons[0].info, errOrdinalTypeExpected)
x = getOrdValue(v.sons[0]) # first tuple part is the ordinal x = getOrdValue(v.sons[0]) # first tuple part is the ordinal
else: else:
localError(c.config, strVal.info, errStringLiteralExpected) localError(c.config, strVal.info, errStringLiteralExpected)
@ -99,6 +101,8 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
strVal = v strVal = v
x = counter x = counter
else: else:
if not isOrdinalType(v.typ):
localError(c.config, v.info, errOrdinalTypeExpected)
x = getOrdValue(v) x = getOrdValue(v)
if i != 1: if i != 1:
if x != counter: incl(result.flags, tfEnumHasHoles) if x != counter: incl(result.flags, tfEnumHasHoles)
@ -821,7 +825,11 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
a.typ = nn.typ a.typ = nn.typ
addDecl(c, a) addDecl(c, a)
else: else:
if sfGenSym notin param.flags: addDecl(c, param) if sfGenSym in param.flags:
# bug #XXX, fix the gensym'ed parameters owner:
if param.owner == nil:
param.owner = getCurrOwner(c)
else: addDecl(c, param)
template shouldHaveMeta(t) = template shouldHaveMeta(t) =
internalAssert c.config, tfHasMeta in t.flags internalAssert c.config, tfHasMeta in t.flags

View file

@ -413,26 +413,12 @@ proc recSetFlagIsRef(arg: PNode) =
arg.sons[i].recSetFlagIsRef arg.sons[i].recSetFlagIsRef
proc setLenSeq(c: PCtx; node: PNode; newLen: int; info: TLineInfo) = proc setLenSeq(c: PCtx; node: PNode; newLen: int; info: TLineInfo) =
# FIXME: this doesn't attempt to solve incomplete
# support of tyPtr, tyRef in VM.
let typ = node.typ.skipTypes(abstractInst+{tyRange}-{tyTypeDesc}) let typ = node.typ.skipTypes(abstractInst+{tyRange}-{tyTypeDesc})
let typeEntry = typ.sons[0].skipTypes(abstractInst+{tyRange}-{tyTypeDesc})
let typeKind = case typeEntry.kind
of tyUInt..tyUInt64: nkUIntLit
of tyRange, tyEnum, tyBool, tyChar, tyInt..tyInt64: nkIntLit
of tyFloat..tyFloat128: nkFloatLit
of tyString: nkStrLit
of tyObject: nkObjConstr
of tySequence: nkNilLit
of tyProc, tyTuple: nkTupleConstr
else: nkEmpty
let oldLen = node.len let oldLen = node.len
setLen(node.sons, newLen) setLen(node.sons, newLen)
if oldLen < newLen: if oldLen < newLen:
# TODO: This is still not correct for tyPtr, tyRef default value
for i in oldLen ..< newLen: for i in oldLen ..< newLen:
node.sons[i] = newNodeI(typeKind, info) node.sons[i] = getNullValue(typ.sons[0], info, c.config)
const const
errIndexOutOfBounds = "index out of bounds" errIndexOutOfBounds = "index out of bounds"

View file

@ -13,6 +13,7 @@ Advanced commands:
//genDepend generate a DOT file containing the //genDepend generate a DOT file containing the
module dependency graph module dependency graph
//dump dump all defined conditionals and search paths //dump dump all defined conditionals and search paths
see also: --dump.format:json (useful with: ` | jq`)
//check checks the project for syntax and semantic //check checks the project for syntax and semantic
Advanced options: Advanced options:
@ -103,6 +104,7 @@ Advanced options:
value = number of processors (0 for auto-detect) value = number of processors (0 for auto-detect)
--incremental:on|off only recompile the changed modules (experimental!) --incremental:on|off only recompile the changed modules (experimental!)
--verbosity:0|1|2|3 set Nim's verbosity level (1 is default) --verbosity:0|1|2|3 set Nim's verbosity level (1 is default)
--errorMax:N stop compilation after N errors; 0 means unlimited
--experimental:$1 --experimental:$1
enable experimental language feature enable experimental language feature
-v, --version show detailed version information -v, --version show detailed version information

View file

@ -263,17 +263,8 @@ Take advantage of no implicit bool conversion
doAssert isValid() == true doAssert isValid() == true
doAssert isValid() # preferred doAssert isValid() # preferred
.. _immediately_invoked_lambdas:
Immediately invoked lambdas (https://en.wikipedia.org/wiki/Immediately-invoked_function_expression)
.. code-block:: nim
let a = (proc (): auto = getFoo())()
let a = block: # preferred
getFoo()
.. _design_for_mcs: .. _design_for_mcs:
Design with method call syntax (UFCS in other languages) chaining in mind Design with method call syntax chaining in mind
.. code-block:: nim .. code-block:: nim

View file

@ -154,6 +154,10 @@ String handling
* `std/wordwrap <wordwrap.html>`_ * `std/wordwrap <wordwrap.html>`_
This module contains an algorithm to wordwrap a Unicode string. This module contains an algorithm to wordwrap a Unicode string.
* `std/diff <diff.html>`_
This module contains an algorithm to compute the famous "diff"
of two texts by line.
Generic Operating System Services Generic Operating System Services
--------------------------------- ---------------------------------

View file

@ -79,6 +79,7 @@ template withDir(dir, body) =
finally: finally:
setCurrentdir(old) setCurrentdir(old)
let origDir = getCurrentDir()
setCurrentDir(getAppDir()) setCurrentDir(getAppDir())
proc tryExec(cmd: string): bool = proc tryExec(cmd: string): bool =
@ -378,9 +379,7 @@ proc winRelease*() =
template `|`(a, b): string = (if a.len > 0: a else: b) template `|`(a, b): string = (if a.len > 0: a else: b)
proc tests(args: string) = proc tests(args: string) =
# we compile the tester with taintMode:on to have a basic nimexec "cc --opt:speed testament/tester"
# taint mode test :-)
nimexec "cc --taintMode:on --opt:speed testament/tester"
# Since tests take a long time (on my machine), and we want to defy Murhpys # Since tests take a long time (on my machine), and we want to defy Murhpys
# law - lets make sure the compiler really is freshly compiled! # law - lets make sure the compiler really is freshly compiled!
nimexec "c --lib:lib -d:release --opt:speed compiler/nim.nim" nimexec "c --lib:lib -d:release --opt:speed compiler/nim.nim"
@ -415,6 +414,7 @@ proc temp(args: string) =
let nimexec = findNim() let nimexec = findNim()
exec(nimexec & " c -d:debug --debugger:native " & bootArgs & " " & (d / "compiler" / "nim"), 125) exec(nimexec & " c -d:debug --debugger:native " & bootArgs & " " & (d / "compiler" / "nim"), 125)
copyExe(output, finalDest) copyExe(output, finalDest)
setCurrentDir(origDir)
if programArgs.len > 0: exec(finalDest & " " & programArgs) if programArgs.len > 0: exec(finalDest & " " & programArgs)
proc xtemp(cmd: string) = proc xtemp(cmd: string) =

View file

@ -65,7 +65,7 @@ runnableExamples:
let hasVowel = firstVowel.isSome() let hasVowel = firstVowel.isSome()
if hasVowel: if hasVowel:
let matchBounds = firstVowel.get().captureBounds[-1] let matchBounds = firstVowel.get().captureBounds[-1]
doAssert matchBounds.get().a == 1 doAssert matchBounds.a == 1
# Type definitions {{{ # Type definitions {{{
@ -167,14 +167,15 @@ type
## - ``"abc".match(re"(?<letter>\w)").get.captures["letter"] == "a"`` ## - ``"abc".match(re"(?<letter>\w)").get.captures["letter"] == "a"``
## - ``"abc".match(re"(\w)\w").get.captures[-1] == "ab"`` ## - ``"abc".match(re"(\w)\w").get.captures[-1] == "ab"``
## ##
## ``captureBounds[]: Option[HSlice[int, int]]`` ## ``captureBounds[]: HSlice[int, int]``
## gets the bounds of the given capture according to the same rules as ## gets the bounds of the given capture according to the same rules as
## the above. If the capture is not filled, then ``None`` is returned. ## the above. If the capture is not filled, then ``None`` is returned.
## The bounds are both inclusive. ## The bounds are both inclusive.
## ##
## - ``"abc".match(re"(\w)").get.captureBounds[0].get == 0 .. 0`` ## - ``"abc".match(re"(\w)").get.captureBounds[0] == 0 .. 0``
## - ``"abc".match(re"").get.captureBounds[-1].get == 0 .. -1`` ## - ``0 in "abc".match(re"(\w)").get.captureBounds == true``
## - ``"abc".match(re"abc").get.captureBounds[-1].get == 0 .. 2`` ## - ``"abc".match(re"").get.captureBounds[-1] == 0 .. -1``
## - ``"abc".match(re"abc").get.captureBounds[-1] == 0 .. 2``
## ##
## ``match: string`` ## ``match: string``
## the full text of the match. ## the full text of the match.
@ -227,9 +228,10 @@ runnableExamples:
doAssert "abc".match(re"(?<letter>\w)").get.captures["letter"] == "a" doAssert "abc".match(re"(?<letter>\w)").get.captures["letter"] == "a"
doAssert "abc".match(re"(\w)\w").get.captures[-1] == "ab" doAssert "abc".match(re"(\w)\w").get.captures[-1] == "ab"
doAssert "abc".match(re"(\w)").get.captureBounds[0].get == 0 .. 0 doAssert "abc".match(re"(\w)").get.captureBounds[0] == 0 .. 0
doAssert "abc".match(re"").get.captureBounds[-1].get == 0 .. -1 doAssert 0 in "abc".match(re"(\w)").get.captureBounds == true
doAssert "abc".match(re"abc").get.captureBounds[-1].get == 0 .. 2 doAssert "abc".match(re"").get.captureBounds[-1] == 0 .. -1
doAssert "abc".match(re"abc").get.captureBounds[-1] == 0 .. 2
# }}} # }}}
proc getinfo[T](pattern: Regex, opt: cint): T = proc getinfo[T](pattern: Regex, opt: cint): T =
@ -269,78 +271,99 @@ proc matchesCrLf(pattern: Regex): bool =
# }}} # }}}
# Capture accessors {{{ # Capture accessors {{{
proc captureBounds*(pattern: RegexMatch): CaptureBounds = return CaptureBounds(pattern) func captureBounds*(pattern: RegexMatch): CaptureBounds = return CaptureBounds(pattern)
proc captures*(pattern: RegexMatch): Captures = return Captures(pattern) func captures*(pattern: RegexMatch): Captures = return Captures(pattern)
proc `[]`*(pattern: CaptureBounds, i: int): Option[HSlice[int, int]] = func contains*(pattern: CaptureBounds, i: int): bool =
let pattern = RegexMatch(pattern) let pattern = RegexMatch(pattern)
if pattern.pcreMatchBounds[i + 1].a != -1: pattern.pcreMatchBounds[i + 1].a != -1
let bounds = pattern.pcreMatchBounds[i + 1]
return some(int(bounds.a) .. int(bounds.b-1))
else:
return none(HSlice[int, int])
proc `[]`*(pattern: Captures, i: int): string = func contains*(pattern: Captures, i: int): bool =
i in CaptureBounds(pattern)
func `[]`*(pattern: CaptureBounds, i: int): HSlice[int, int] =
let pattern = RegexMatch(pattern)
if not (i in pattern.captureBounds):
raise newException(IndexError, "Group '" & $i & "' was not captured")
let bounds = pattern.pcreMatchBounds[i + 1]
int(bounds.a)..int(bounds.b-1)
func `[]`*(pattern: Captures, i: int): string =
let pattern = RegexMatch(pattern) let pattern = RegexMatch(pattern)
let bounds = pattern.captureBounds[i] let bounds = pattern.captureBounds[i]
if bounds.isSome: pattern.str.substr(bounds.a, bounds.b)
let bounds = bounds.get
return pattern.str.substr(bounds.a, bounds.b)
else:
return ""
proc match*(pattern: RegexMatch): string = func match*(pattern: RegexMatch): string =
return pattern.captures[-1] return pattern.captures[-1]
proc matchBounds*(pattern: RegexMatch): HSlice[int, int] = func matchBounds*(pattern: RegexMatch): HSlice[int, int] =
return pattern.captureBounds[-1].get return pattern.captureBounds[-1]
proc `[]`*(pattern: CaptureBounds, name: string): Option[HSlice[int, int]] = func contains*(pattern: CaptureBounds, name: string): bool =
let pattern = RegexMatch(pattern) let pattern = RegexMatch(pattern)
return pattern.captureBounds[pattern.pattern.captureNameToId.fget(name)] let nameToId = pattern.pattern.captureNameToId
if not (name in nameToId):
return false
nameToId[name] in pattern.captureBounds
proc `[]`*(pattern: Captures, name: string): string = func contains*(pattern: Captures, name: string): bool =
name in CaptureBounds(pattern)
func checkNamedCaptured(pattern: RegexMatch, name: string): void =
if not (name in pattern.captureBounds):
raise newException(KeyError, "Group '" & name & "' was not captured")
func `[]`*(pattern: CaptureBounds, name: string): HSlice[int, int] =
let pattern = RegexMatch(pattern) let pattern = RegexMatch(pattern)
return pattern.captures[pattern.pattern.captureNameToId.fget(name)] checkNamedCaptured(pattern, name)
pattern.captureBounds[pattern.pattern.captureNameToId[name]]
template toTableImpl(cond: untyped) {.dirty.} = func `[]`*(pattern: Captures, name: string): string =
let pattern = RegexMatch(pattern)
checkNamedCaptured(pattern, name)
return pattern.captures[pattern.pattern.captureNameToId[name]]
template toTableImpl() {.dirty.} =
for key in RegexMatch(pattern).pattern.captureNameId.keys: for key in RegexMatch(pattern).pattern.captureNameId.keys:
let nextVal = pattern[key] if key in pattern:
if cond: result[key] = pattern[key]
result[key] = default
else:
result[key] = nextVal
proc toTable*(pattern: Captures, default: string = ""): Table[string, string] = func toTable*(pattern: Captures): Table[string, string] =
result = initTable[string, string]() result = initTable[string, string]()
toTableImpl(nextVal.len == 0) toTableImpl()
proc toTable*(pattern: CaptureBounds, default = none(HSlice[int, int])): func toTable*(pattern: CaptureBounds): Table[string, HSlice[int, int]] =
Table[string, Option[HSlice[int, int]]] = result = initTable[string, HSlice[int, int]]()
result = initTable[string, Option[HSlice[int, int]]]() toTableImpl()
toTableImpl(nextVal.isNone)
template itemsImpl(cond: untyped) {.dirty.} = template itemsImpl() {.dirty.} =
for i in 0 ..< RegexMatch(pattern).pattern.captureCount: for i in 0 ..< RegexMatch(pattern).pattern.captureCount:
let nextVal = pattern[i]
# done in this roundabout way to avoid multiple yields (potential code # done in this roundabout way to avoid multiple yields (potential code
# bloat) # bloat)
let nextYieldVal = if cond: default else: nextVal let nextYieldVal = if i in pattern:
some(pattern[i])
else:
default
yield nextYieldVal yield nextYieldVal
iterator items*(pattern: CaptureBounds,
default = none(HSlice[int, int])): Option[HSlice[int, int]] =
itemsImpl()
iterator items*(pattern: CaptureBounds, default = none(HSlice[int, int])): Option[HSlice[int, int]] = iterator items*(pattern: Captures,
itemsImpl(nextVal.isNone) default: Option[string] = none(string)): Option[string] =
itemsImpl()
iterator items*(pattern: Captures, default: string = ""): string = proc toSeq*(pattern: CaptureBounds,
itemsImpl(nextVal.len == 0) 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 = ""): seq[string] = proc toSeq*(pattern: Captures,
default: Option[string] = none(string)): seq[Option[string]] =
accumulateResult(pattern.items(default)) accumulateResult(pattern.items(default))
proc `$`*(pattern: RegexMatch): string = proc `$`*(pattern: RegexMatch): string =
@ -652,7 +675,8 @@ proc split*(str: string, pattern: Regex, maxSplit = -1, start = 0): seq[string]
for cap in match.captures: for cap in match.captures:
# if there are captures, include them in the result # if there are captures, include them in the result
result.add(cap) if cap.isSome:
result.add(cap.get)
if splits == maxSplit - 1: if splits == maxSplit - 1:
break break
@ -706,7 +730,8 @@ proc replace*(str: string, pattern: Regex,
## - ``$#`` - first capture ## - ``$#`` - first capture
## - ``$0`` - full match ## - ``$0`` - full match
## ##
## If a given capture is missing, a ``ValueError`` exception is thrown. ## If a given capture is missing, ``IndexError`` thrown for un-named captures
## and ``KeyError`` for named captures.
replaceImpl(str, pattern, subproc(match)) replaceImpl(str, pattern, subproc(match))
proc replace*(str: string, pattern: Regex, proc replace*(str: string, pattern: Regex,

View file

@ -1,17 +1,9 @@
## INTERNAL FILE FOR USE ONLY BY nre.nim. ## INTERNAL FILE FOR USE ONLY BY nre.nim.
import tables import tables
proc fget*[K, V](self: Table[K, V], key: K): V =
if self.hasKey(key):
return self[key]
else:
raise newException(KeyError, "Key does not exist in table: " & $key)
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'}
template checkNil(arg: string): string = arg
template formatStr*(howExpr, namegetter, idgetter): untyped = template formatStr*(howExpr, namegetter, idgetter): untyped =
let how = howExpr let how = howExpr
var val = newStringOfCap(how.len) var val = newStringOfCap(how.len)
@ -28,7 +20,7 @@ template formatStr*(howExpr, namegetter, idgetter): untyped =
i += 2 i += 2
elif how[i + 1] == '#': elif how[i + 1] == '#':
var id {.inject.} = lastNum var id {.inject.} = lastNum
val.add(checkNil(idgetter)) val.add(idgetter)
lastNum += 1 lastNum += 1
i += 2 i += 2
elif how[i + 1] in {'0'..'9'}: elif how[i + 1] in {'0'..'9'}:
@ -37,7 +29,7 @@ template formatStr*(howExpr, namegetter, idgetter): untyped =
while i < how.len and how[i] in {'0'..'9'}: while i < how.len and how[i] in {'0'..'9'}:
id += (id * 10) + (ord(how[i]) - ord('0')) id += (id * 10) + (ord(how[i]) - ord('0'))
i += 1 i += 1
val.add(checkNil(idgetter)) val.add(idgetter)
lastNum = id + 1 lastNum = id + 1
elif how[i + 1] in StartIdent: elif how[i + 1] in StartIdent:
i += 1 i += 1
@ -45,7 +37,7 @@ template formatStr*(howExpr, namegetter, idgetter): untyped =
while i < how.len and how[i] in Ident: while i < how.len and how[i] in Ident:
name.add(how[i]) name.add(how[i])
i += 1 i += 1
val.add(checkNil(namegetter)) val.add(namegetter)
elif how[i + 1] == '{': elif how[i + 1] == '{':
i += 2 i += 2
var name {.inject.} = "" var name {.inject.} = ""
@ -53,7 +45,7 @@ template formatStr*(howExpr, namegetter, idgetter): untyped =
name.add(how[i]) name.add(how[i])
i += 1 i += 1
i += 1 i += 1
val.add(checkNil(namegetter)) val.add(namegetter)
else: else:
raise newException(Exception, "Syntax error in format string at " & $i) raise newException(Exception, "Syntax error in format string at " & $i)
val val

View file

@ -1513,8 +1513,217 @@ proc poll*(timeout = 500) =
## `epoll`:idx: or `kqueue`:idx: primitive only once. ## `epoll`:idx: or `kqueue`:idx: primitive only once.
discard runOnce(timeout) discard runOnce(timeout)
# Common procedures between current and upcoming asyncdispatch template createAsyncNativeSocketImpl(domain, sockType, protocol) =
include includes/asynccommon let handle = newNativeSocket(domain, sockType, protocol)
if handle == osInvalidSocket:
return osInvalidSocket.AsyncFD
handle.setBlocking(false)
when defined(macosx) and not defined(nimdoc):
handle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1)
result = handle.AsyncFD
register(result)
proc createAsyncNativeSocket*(domain: cint, sockType: cint,
protocol: cint): AsyncFD =
createAsyncNativeSocketImpl(domain, sockType, protocol)
proc createAsyncNativeSocket*(domain: Domain = Domain.AF_INET,
sockType: SockType = SOCK_STREAM,
protocol: Protocol = IPPROTO_TCP): AsyncFD =
createAsyncNativeSocketImpl(domain, sockType, protocol)
proc newAsyncNativeSocket*(domain: cint, sockType: cint,
protocol: cint): AsyncFD {.deprecated: "use createAsyncNativeSocket instead".} =
createAsyncNativeSocketImpl(domain, sockType, protocol)
proc newAsyncNativeSocket*(domain: Domain = Domain.AF_INET,
sockType: SockType = SOCK_STREAM,
protocol: Protocol = IPPROTO_TCP): AsyncFD
{.deprecated: "use createAsyncNativeSocket instead".} =
createAsyncNativeSocketImpl(domain, sockType, protocol)
when defined(windows) or defined(nimdoc):
proc bindToDomain(handle: SocketHandle, domain: Domain) =
# Extracted into a separate proc, because connect() on Windows requires
# the socket to be initially bound.
template doBind(saddr) =
if bindAddr(handle, cast[ptr SockAddr](addr(saddr)),
sizeof(saddr).SockLen) < 0'i32:
raiseOSError(osLastError())
if domain == Domain.AF_INET6:
var saddr: Sockaddr_in6
saddr.sin6_family = uint16(toInt(domain))
doBind(saddr)
else:
var saddr: Sockaddr_in
saddr.sin_family = uint16(toInt(domain))
doBind(saddr)
proc doConnect(socket: AsyncFD, addrInfo: ptr AddrInfo): Future[void] =
let retFuture = newFuture[void]("doConnect")
result = retFuture
var ol = PCustomOverlapped()
GC_ref(ol)
ol.data = CompletionData(fd: socket, cb:
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
if not retFuture.finished:
if errcode == OSErrorCode(-1):
retFuture.complete()
else:
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
)
let ret = connectEx(socket.SocketHandle, addrInfo.ai_addr,
cint(addrInfo.ai_addrlen), nil, 0, nil,
cast[POVERLAPPED](ol))
if ret:
# Request to connect completed immediately.
retFuture.complete()
# We don't deallocate ``ol`` here because even though this completed
# immediately poll will still be notified about its completion and it
# will free ``ol``.
else:
let lastError = osLastError()
if lastError.int32 != ERROR_IO_PENDING:
# With ERROR_IO_PENDING ``ol`` will be deallocated in ``poll``,
# and the future will be completed/failed there, too.
GC_unref(ol)
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
else:
proc doConnect(socket: AsyncFD, addrInfo: ptr AddrInfo): Future[void] =
let retFuture = newFuture[void]("doConnect")
result = retFuture
proc cb(fd: AsyncFD): bool =
let ret = SocketHandle(fd).getSockOptInt(
cint(SOL_SOCKET), cint(SO_ERROR))
if ret == 0:
# We have connected.
retFuture.complete()
return true
elif ret == EINTR:
# interrupted, keep waiting
return false
else:
retFuture.fail(newException(OSError, osErrorMsg(OSErrorCode(ret))))
return true
let ret = connect(socket.SocketHandle,
addrInfo.ai_addr,
addrInfo.ai_addrlen.Socklen)
if ret == 0:
# Request to connect completed immediately.
retFuture.complete()
else:
let lastError = osLastError()
if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
addWrite(socket, cb)
else:
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
template asyncAddrInfoLoop(addrInfo: ptr AddrInfo, fd: untyped,
protocol: Protocol = IPPROTO_RAW) =
## Iterates through the AddrInfo linked list asynchronously
## until the connection can be established.
const shouldCreateFd = not declared(fd)
when shouldCreateFd:
let sockType = protocol.toSockType()
var fdPerDomain: array[low(Domain).ord..high(Domain).ord, AsyncFD]
for i in low(fdPerDomain)..high(fdPerDomain):
fdPerDomain[i] = osInvalidSocket.AsyncFD
template closeUnusedFds(domainToKeep = -1) {.dirty.} =
for i, fd in fdPerDomain:
if fd != osInvalidSocket.AsyncFD and i != domainToKeep:
fd.closeSocket()
var lastException: ref Exception
var curAddrInfo = addrInfo
var domain: Domain
when shouldCreateFd:
var curFd: AsyncFD
else:
var curFd = fd
proc tryNextAddrInfo(fut: Future[void]) {.gcsafe.} =
if fut == nil or fut.failed:
if fut != nil:
lastException = fut.readError()
while curAddrInfo != nil:
let domainOpt = curAddrInfo.ai_family.toKnownDomain()
if domainOpt.isSome:
domain = domainOpt.unsafeGet()
break
curAddrInfo = curAddrInfo.ai_next
if curAddrInfo == nil:
freeAddrInfo(addrInfo)
when shouldCreateFd:
closeUnusedFds()
if lastException != nil:
retFuture.fail(lastException)
else:
retFuture.fail(newException(
IOError, "Couldn't resolve address: " & address))
return
when shouldCreateFd:
curFd = fdPerDomain[ord(domain)]
if curFd == osInvalidSocket.AsyncFD:
try:
curFd = newAsyncNativeSocket(domain, sockType, protocol)
except:
freeAddrInfo(addrInfo)
closeUnusedFds()
raise getCurrentException()
when defined(windows):
curFd.SocketHandle.bindToDomain(domain)
fdPerDomain[ord(domain)] = curFd
doConnect(curFd, curAddrInfo).callback = tryNextAddrInfo
curAddrInfo = curAddrInfo.ai_next
else:
freeAddrInfo(addrInfo)
when shouldCreateFd:
closeUnusedFds(ord(domain))
retFuture.complete(curFd)
else:
retFuture.complete()
tryNextAddrInfo(nil)
proc dial*(address: string, port: Port,
protocol: Protocol = IPPROTO_TCP): Future[AsyncFD] =
## Establishes connection to the specified ``address``:``port`` pair via the
## specified protocol. The procedure iterates through possible
## resolutions of the ``address`` until it succeeds, meaning that it
## seamlessly works with both IPv4 and IPv6.
## Returns the async file descriptor, registered in the dispatcher of
## the current thread, ready to send or receive data.
let retFuture = newFuture[AsyncFD]("dial")
result = retFuture
let sockType = protocol.toSockType()
let aiList = getAddrInfo(address, port, Domain.AF_UNSPEC, sockType, protocol)
asyncAddrInfoLoop(aiList, noFD, protocol)
proc connect*(socket: AsyncFD, address: string, port: Port,
domain = Domain.AF_INET): Future[void] =
let retFuture = newFuture[void]("connect")
result = retFuture
when defined(windows):
verifyPresence(socket)
else:
assert getSockDomain(socket.SocketHandle) == domain
let aiList = getAddrInfo(address, port, domain)
when defined(windows):
socket.SocketHandle.bindToDomain(domain)
asyncAddrInfoLoop(aiList, socket)
proc sleepAsync*(ms: int | float): Future[void] = proc sleepAsync*(ms: int | float): Future[void] =
## Suspends the execution of the current async procedure for the next ## Suspends the execution of the current async procedure for the next

View file

@ -140,11 +140,26 @@ proc contains*[T](L: SomeLinkedCollection[T], value: T): bool {.inline.} =
## exist, true otherwise. ## exist, true otherwise.
result = find(L, value) != nil result = find(L, value) != nil
proc append*[T](L: var SinglyLinkedList[T],
n: SinglyLinkedNode[T]) {.inline.} =
## appends a node `n` to `L`. Efficiency: O(1).
n.next = nil
if L.tail != nil:
assert(L.tail.next == nil)
L.tail.next = n
L.tail = n
if L.head == nil: L.head = n
proc append*[T](L: var SinglyLinkedList[T], value: T) {.inline.} =
## appends a value to `L`. Efficiency: O(1).
append(L, newSinglyLinkedNode(value))
proc prepend*[T](L: var SinglyLinkedList[T], proc prepend*[T](L: var SinglyLinkedList[T],
n: SinglyLinkedNode[T]) {.inline.} = n: SinglyLinkedNode[T]) {.inline.} =
## prepends a node to `L`. Efficiency: O(1). ## prepends a node to `L`. Efficiency: O(1).
n.next = L.head n.next = L.head
L.head = n L.head = n
if L.tail == nil: L.tail = n
proc prepend*[T](L: var SinglyLinkedList[T], value: T) {.inline.} = proc prepend*[T](L: var SinglyLinkedList[T], value: T) {.inline.} =
## prepends a node to `L`. Efficiency: O(1). ## prepends a node to `L`. Efficiency: O(1).

View file

@ -12,6 +12,8 @@
type type
UncheckedCharArray = UncheckedArray[char] UncheckedCharArray = UncheckedArray[char]
import system/helpers2
type type
Buffer = ptr object Buffer = ptr object
refcount: int refcount: int
@ -49,11 +51,11 @@ proc len*(s: SharedString): int = s.len
proc `[]`*(s: SharedString; i: Natural): char = proc `[]`*(s: SharedString; i: Natural): char =
if i < s.len: result = s.buffer.data[i+s.first] if i < s.len: result = s.buffer.data[i+s.first]
else: raise newException(IndexError, "index out of bounds") else: raise newException(IndexError, formatErrorIndexBound(i, s.len-1))
proc `[]=`*(s: var SharedString; i: Natural; value: char) = proc `[]=`*(s: var SharedString; i: Natural; value: char) =
if i < s.len: s.buffer.data[i+s.first] = value if i < s.len: s.buffer.data[i+s.first] = value
else: raise newException(IndexError, "index out of bounds") else: raise newException(IndexError, formatErrorIndexBound(i, s.len-1))
proc `[]`*(s: SharedString; ab: HSlice[int, int]): SharedString = proc `[]`*(s: SharedString; ab: HSlice[int, int]): SharedString =
#incRef(src.buffer) #incRef(src.buffer)

View file

@ -1,211 +0,0 @@
template createAsyncNativeSocketImpl(domain, sockType, protocol) =
let handle = newNativeSocket(domain, sockType, protocol)
if handle == osInvalidSocket:
return osInvalidSocket.AsyncFD
handle.setBlocking(false)
when defined(macosx) and not defined(nimdoc):
handle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1)
result = handle.AsyncFD
register(result)
proc createAsyncNativeSocket*(domain: cint, sockType: cint,
protocol: cint): AsyncFD =
createAsyncNativeSocketImpl(domain, sockType, protocol)
proc createAsyncNativeSocket*(domain: Domain = Domain.AF_INET,
sockType: SockType = SOCK_STREAM,
protocol: Protocol = IPPROTO_TCP): AsyncFD =
createAsyncNativeSocketImpl(domain, sockType, protocol)
proc newAsyncNativeSocket*(domain: cint, sockType: cint,
protocol: cint): AsyncFD {.deprecated: "use createAsyncNativeSocket instead".} =
createAsyncNativeSocketImpl(domain, sockType, protocol)
proc newAsyncNativeSocket*(domain: Domain = Domain.AF_INET,
sockType: SockType = SOCK_STREAM,
protocol: Protocol = IPPROTO_TCP): AsyncFD
{.deprecated: "use createAsyncNativeSocket instead".} =
createAsyncNativeSocketImpl(domain, sockType, protocol)
when defined(windows) or defined(nimdoc):
proc bindToDomain(handle: SocketHandle, domain: Domain) =
# Extracted into a separate proc, because connect() on Windows requires
# the socket to be initially bound.
template doBind(saddr) =
if bindAddr(handle, cast[ptr SockAddr](addr(saddr)),
sizeof(saddr).SockLen) < 0'i32:
raiseOSError(osLastError())
if domain == Domain.AF_INET6:
var saddr: Sockaddr_in6
saddr.sin6_family = uint16(toInt(domain))
doBind(saddr)
else:
var saddr: Sockaddr_in
saddr.sin_family = uint16(toInt(domain))
doBind(saddr)
proc doConnect(socket: AsyncFD, addrInfo: ptr AddrInfo): Future[void] =
let retFuture = newFuture[void]("doConnect")
result = retFuture
var ol = PCustomOverlapped()
GC_ref(ol)
ol.data = CompletionData(fd: socket, cb:
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
if not retFuture.finished:
if errcode == OSErrorCode(-1):
retFuture.complete()
else:
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
)
let ret = connectEx(socket.SocketHandle, addrInfo.ai_addr,
cint(addrInfo.ai_addrlen), nil, 0, nil,
cast[POVERLAPPED](ol))
if ret:
# Request to connect completed immediately.
retFuture.complete()
# We don't deallocate ``ol`` here because even though this completed
# immediately poll will still be notified about its completion and it
# will free ``ol``.
else:
let lastError = osLastError()
if lastError.int32 != ERROR_IO_PENDING:
# With ERROR_IO_PENDING ``ol`` will be deallocated in ``poll``,
# and the future will be completed/failed there, too.
GC_unref(ol)
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
else:
proc doConnect(socket: AsyncFD, addrInfo: ptr AddrInfo): Future[void] =
let retFuture = newFuture[void]("doConnect")
result = retFuture
proc cb(fd: AsyncFD): bool =
let ret = SocketHandle(fd).getSockOptInt(
cint(SOL_SOCKET), cint(SO_ERROR))
if ret == 0:
# We have connected.
retFuture.complete()
return true
elif ret == EINTR:
# interrupted, keep waiting
return false
else:
retFuture.fail(newException(OSError, osErrorMsg(OSErrorCode(ret))))
return true
let ret = connect(socket.SocketHandle,
addrInfo.ai_addr,
addrInfo.ai_addrlen.Socklen)
if ret == 0:
# Request to connect completed immediately.
retFuture.complete()
else:
let lastError = osLastError()
if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
addWrite(socket, cb)
else:
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
template asyncAddrInfoLoop(addrInfo: ptr AddrInfo, fd: untyped,
protocol: Protocol = IPPROTO_RAW) =
## Iterates through the AddrInfo linked list asynchronously
## until the connection can be established.
const shouldCreateFd = not declared(fd)
when shouldCreateFd:
let sockType = protocol.toSockType()
var fdPerDomain: array[low(Domain).ord..high(Domain).ord, AsyncFD]
for i in low(fdPerDomain)..high(fdPerDomain):
fdPerDomain[i] = osInvalidSocket.AsyncFD
template closeUnusedFds(domainToKeep = -1) {.dirty.} =
for i, fd in fdPerDomain:
if fd != osInvalidSocket.AsyncFD and i != domainToKeep:
fd.closeSocket()
var lastException: ref Exception
var curAddrInfo = addrInfo
var domain: Domain
when shouldCreateFd:
var curFd: AsyncFD
else:
var curFd = fd
proc tryNextAddrInfo(fut: Future[void]) {.gcsafe.} =
if fut == nil or fut.failed:
if fut != nil:
lastException = fut.readError()
while curAddrInfo != nil:
let domainOpt = curAddrInfo.ai_family.toKnownDomain()
if domainOpt.isSome:
domain = domainOpt.unsafeGet()
break
curAddrInfo = curAddrInfo.ai_next
if curAddrInfo == nil:
freeAddrInfo(addrInfo)
when shouldCreateFd:
closeUnusedFds()
if lastException != nil:
retFuture.fail(lastException)
else:
retFuture.fail(newException(
IOError, "Couldn't resolve address: " & address))
return
when shouldCreateFd:
curFd = fdPerDomain[ord(domain)]
if curFd == osInvalidSocket.AsyncFD:
try:
curFd = newAsyncNativeSocket(domain, sockType, protocol)
except:
freeAddrInfo(addrInfo)
closeUnusedFds()
raise getCurrentException()
when defined(windows):
curFd.SocketHandle.bindToDomain(domain)
fdPerDomain[ord(domain)] = curFd
doConnect(curFd, curAddrInfo).callback = tryNextAddrInfo
curAddrInfo = curAddrInfo.ai_next
else:
freeAddrInfo(addrInfo)
when shouldCreateFd:
closeUnusedFds(ord(domain))
retFuture.complete(curFd)
else:
retFuture.complete()
tryNextAddrInfo(nil)
proc dial*(address: string, port: Port,
protocol: Protocol = IPPROTO_TCP): Future[AsyncFD] =
## Establishes connection to the specified ``address``:``port`` pair via the
## specified protocol. The procedure iterates through possible
## resolutions of the ``address`` until it succeeds, meaning that it
## seamlessly works with both IPv4 and IPv6.
## Returns the async file descriptor, registered in the dispatcher of
## the current thread, ready to send or receive data.
let retFuture = newFuture[AsyncFD]("dial")
result = retFuture
let sockType = protocol.toSockType()
let aiList = getAddrInfo(address, port, Domain.AF_UNSPEC, sockType, protocol)
asyncAddrInfoLoop(aiList, noFD, protocol)
proc connect*(socket: AsyncFD, address: string, port: Port,
domain = Domain.AF_INET): Future[void] =
let retFuture = newFuture[void]("connect")
result = retFuture
when defined(windows):
verifyPresence(socket)
else:
assert getSockDomain(socket.SocketHandle) == domain
let aiList = getAddrInfo(address, port, domain)
when defined(windows):
socket.SocketHandle.bindToDomain(domain)
asyncAddrInfoLoop(aiList, socket)

View file

@ -59,7 +59,8 @@ proc raiseOSError*(errorCode: OSErrorCode; additionalInfo = "") {.noinline.} =
e.errorCode = errorCode.int32 e.errorCode = errorCode.int32
e.msg = osErrorMsg(errorCode) e.msg = osErrorMsg(errorCode)
if additionalInfo.len > 0: if additionalInfo.len > 0:
e.msg.add "; Additional info: " if e.msg[^1] != '\n': e.msg.add '\n'
e.msg.add "Additional info: "
e.msg.addQuoted additionalInfo e.msg.addQuoted additionalInfo
if e.msg == "": if e.msg == "":
e.msg = "unknown OS error" e.msg = "unknown OS error"

View file

@ -36,11 +36,12 @@ type
size*: int ## size of the memory mapped file size*: int ## size of the memory mapped file
when defined(windows): when defined(windows):
fHandle: Handle fHandle*: Handle ## **Caution**: Windows specific public field to allow
mapHandle: Handle ## even more low level trickery.
wasOpened: bool ## only close if wasOpened mapHandle*: Handle ## **Caution**: Windows specific public field.
wasOpened*: bool ## **Caution**: Windows specific public field.
else: else:
handle: cint handle*: cint ## **Caution**: Posix specific public field.
proc mapMem*(m: var MemFile, mode: FileMode = fmRead, proc mapMem*(m: var MemFile, mode: FileMode = fmRead,
mappedSize = -1, offset = 0): pointer = mappedSize = -1, offset = 0): pointer =
@ -281,6 +282,35 @@ proc flush*(f: var MemFile; attempts: Natural = 3) =
if lastErr != EBUSY.OSErrorCode: if lastErr != EBUSY.OSErrorCode:
raiseOSError(lastErr, "error flushing mapping") raiseOSError(lastErr, "error flushing mapping")
when defined(posix) or defined(nimdoc):
proc resize*(f: var MemFile, newFileSize: int) {.raises: [IOError, OSError].} =
## resize and re-map the file underlying an ``allowRemap MemFile``.
## **Note**: this assumes the entire file is mapped read-write at offset zero.
## Also, the value of ``.mem`` will probably change.
## **Note**: This is not (yet) avaiable on Windows.
when defined(posix):
if f.handle == -1:
raise newException(IOError,
"Cannot resize MemFile opened with allowRemap=false")
if ftruncate(f.handle, newFileSize) == -1:
raiseOSError(osLastError())
when defined(linux): #Maybe NetBSD, too?
#On Linux this can be over 100 times faster than a munmap,mmap cycle.
proc mremap(old: pointer; oldSize,newSize: csize; flags: cint): pointer {.
importc: "mremap", header: "<sys/mman.h>" .}
let newAddr = mremap(f.mem, csize(f.size), csize(newFileSize), cint(1))
if newAddr == cast[pointer](MAP_FAILED):
raiseOSError(osLastError())
else:
if munmap(f.mem, f.size) != 0:
raiseOSError(osLastError())
let newAddr = mmap(nil, newFileSize, PROT_READ or PROT_WRITE,
MAP_SHARED or MAP_POPULATE, f.handle, 0)
if newAddr == cast[pointer](MAP_FAILED):
raiseOSError(osLastError())
f.mem = newAddr
f.size = newFileSize
proc close*(f: var MemFile) = proc close*(f: var MemFile) =
## closes the memory mapped file `f`. All changes are written back to the ## closes the memory mapped file `f`. All changes are written back to the
## file system, if `f` was opened with write access. ## file system, if `f` was opened with write access.

View file

@ -39,17 +39,18 @@
## ##
## .. code-block:: nim ## .. code-block:: nim
## ##
## try: ## let found = "abc".find('c')
## assert("abc".find('c').get() == 2) # Immediately extract the value ## assert found.isSome and found.get() == 2
## except UnpackError: # If there is no value
## assert false # This will not be reached, because the value is present
## ##
## The ``get`` operation demonstrated above returns the underlying value, or ## The ``get`` operation demonstrated above returns the underlying value, or
## raises ``UnpackError`` if there is no value. There is another option for ## raises ``UnpackError`` if there is no value. Note that ``UnpackError`` inherits
## obtaining the value: ``unsafeGet``, but you must only use it when you are ## from ``system.Defect``, and should therefore never be catched. Instead, rely on
## absolutely sure the value is present (e.g. after checking ``isSome``). If ## checking if the option contains a value with ``isSome`` and ``isNone``.
## you do not care about the tiny overhead that ``get`` causes, you should ##
## simply never use ``unsafeGet``. ## There is another option for obtaining the value: ``unsafeGet``, but you must
## only use it when you are absolutely sure the value is present (e.g. after
## checking ``isSome``). If you do not care about the tiny overhead that ``get``
## causes, you should simply never use ``unsafeGet``.
## ##
## How to deal with an absence of a value: ## How to deal with an absence of a value:
## ##
@ -61,12 +62,7 @@
## assert(result == none(int)) ## assert(result == none(int))
## # It has no value: ## # It has no value:
## assert(result.isNone) ## assert(result.isNone)
##
## try:
## echo result.get()
## assert(false) # This will not be reached
## except UnpackError: # Because an exception is raised
## discard
import typetraits import typetraits
type type
@ -81,7 +77,7 @@ type
val: T val: T
has: bool has: bool
UnpackError* = ref object of ValueError UnpackError* = object of Defect
proc some*[T](val: T): Option[T] = proc some*[T](val: T): Option[T] =
## Returns a ``Option`` that has this value. ## Returns a ``Option`` that has this value.
@ -129,7 +125,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 newException(UnpackError, "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 =
@ -143,7 +139,7 @@ proc get*[T](self: var Option[T]): var 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 newException(UnpackError, "Can't obtain a value from a `none`")
return self.val return self.val
proc map*[T](self: Option[T], callback: proc (input: T)) = proc map*[T](self: Option[T], callback: proc (input: T)) =

View file

@ -17,7 +17,7 @@
from strutils import parseInt, cmpIgnoreStyle, Digits from strutils import parseInt, cmpIgnoreStyle, Digits
include "system/inclrtl" include "system/inclrtl"
import system/helpers2
proc findNormalized(x: string, inArray: openarray[string]): int = proc findNormalized(x: string, inArray: openarray[string]): int =
var i = 0 var i = 0
@ -85,7 +85,7 @@ proc getFormatArg(p: var FormatParser, a: openArray[string]): int =
result = parseInt(a[result])-1 result = parseInt(a[result])-1
else: else:
raiseInvalidFormat("'#', '$', number or identifier expected") raiseInvalidFormat("'#', '$', number or identifier expected")
if result >=% a.len: raiseInvalidFormat("index out of bounds: " & $result) if result >=% a.len: raiseInvalidFormat(formatErrorIndexBound(result, a.len))
p.i = i p.i = i
proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) {. proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) {.

387
lib/std/diff.nim Normal file
View file

@ -0,0 +1,387 @@
#
#
# 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 implements an algorithm to compute the
## `diff`:idx: between two sequences of lines.
# code owner: Arne Döring
#
# This is based on C# code written by Matthias Hertel, http://www.mathertel.de
#
# This Class implements the Difference Algorithm published in
# "An O(ND) Difference Algorithm and its Variations" by Eugene Myers
# Algorithmica Vol. 1 No. 2, 1986, p 251.
import tables, strutils
type
Item* = object ## An Item in the list of differences.
startA*: int ## Start Line number in Data A.
startB*: int ## Start Line number in Data B.
deletedA*: int ## Number of changes in Data A.
insertedB*: int ## Number of changes in Data B.
DiffData = object ## Data on one input file being compared.
data: seq[int] ## Buffer of numbers that will be compared.
modified: seq[bool] ## Array of booleans that flag for modified
## data. This is the result of the diff.
## This means deletedA in the first Data or
## inserted in the second Data.
Smsrd = object
x, y: int
# template to avoid a seq copy. Required until ``sink`` parameters are ready.
template newDiffData(initData: seq[int]; L: int): DiffData =
DiffData(
data: initData,
modified: newSeq[bool](L + 2)
)
proc len(d: DiffData): int {.inline.} = d.data.len
proc diffCodes(aText: string; h: var Table[string, int]): DiffData =
## This function converts all textlines of the text into unique numbers for every unique textline
## so further work can work only with simple numbers.
## ``aText`` the input text
## ``h`` This extern initialized hashtable is used for storing all ever used textlines.
## ``trimSpace`` ignore leading and trailing space characters
## Returns a array of integers.
var lastUsedCode = h.len
result.data = newSeq[int]()
for s in aText.splitLines:
if h.contains s:
result.data.add h[s]
else:
inc lastUsedCode
h[s] = lastUsedCode
result.data.add lastUsedCode
result.modified = newSeq[bool](result.data.len + 2)
proc optimize(data: var DiffData) =
## If a sequence of modified lines starts with a line that contains the same content
## as the line that appends the changes, the difference sequence is modified so that the
## appended line and not the starting line is marked as modified.
## This leads to more readable diff sequences when comparing text files.
var startPos = 0
while startPos < data.len:
while startPos < data.len and not data.modified[startPos]:
inc startPos
var endPos = startPos
while endPos < data.len and data.modified[endPos]:
inc endPos
if endPos < data.len and data.data[startPos] == data.data[endPos]:
data.modified[startPos] = false
data.modified[endPos] = true
else:
startPos = endPos
proc sms(dataA: var DiffData; lowerA, upperA: int; dataB: DiffData; lowerB, upperB: int;
downVector, upVector: var openArray[int]): Smsrd =
## This is the algorithm to find the Shortest Middle Snake (sms).
## ``dataA`` sequence A
## ``lowerA`` lower bound of the actual range in dataA
## ``upperA`` upper bound of the actual range in dataA (exclusive)
## ``dataB`` sequence B
## ``lowerB`` lower bound of the actual range in dataB
## ``upperB`` upper bound of the actual range in dataB (exclusive)
## ``downVector`` a vector for the (0,0) to (x,y) search. Passed as a parameter for speed reasons.
## ``upVector`` a vector for the (u,v) to (N,M) search. Passed as a parameter for speed reasons.
## Returns a MiddleSnakeData record containing x,y and u,v.
let max = dataA.len + dataB.len + 1
let downK = lowerA - lowerB # the k-line to start the forward search
let upK = upperA - upperB # the k-line to start the reverse search
let delta = (upperA - lowerA) - (upperB - lowerB)
let oddDelta = (delta and 1) != 0
# The vectors in the publication accepts negative indexes. the vectors implemented here are 0-based
# and are access using a specific offset: upOffset upVector and downOffset for downVector
let downOffset = max - downK
let upOffset = max - upK
let maxD = ((upperA - lowerA + upperB - lowerB) div 2) + 1
downVector[downOffset + downK + 1] = lowerA
upVector[upOffset + upK - 1] = upperA
for D in 0 .. maxD:
# Extend the forward path.
for k in countUp(downK - D, downK + D, 2):
# find the only or better starting point
var x: int
if k == downK - D:
x = downVector[downOffset + k + 1] # down
else:
x = downVector[downOffset + k - 1] + 1 # a step to the right
if k < downK + D and downVector[downOffset + k + 1] >= x:
x = downVector[downOffset + k + 1] # down
var y = x - k
# find the end of the furthest reaching forward D-path in diagonal k.
while x < upperA and y < upperB and dataA.data[x] == dataB.data[y]:
inc x
inc y
downVector[downOffset + k] = x
# overlap ?
if oddDelta and upK - D < k and k < upK + D:
if upVector[upOffset + k] <= downVector[downOffset + k]:
return Smsrd(x: downVector[downOffset + k],
y: downVector[downOffset + k] - k)
# Extend the reverse path.
for k in countUp(upK - D, upK + D, 2):
# find the only or better starting point
var x: int
if k == upK + D:
x = upVector[upOffset + k - 1] # up
else:
x = upVector[upOffset + k + 1] - 1 # left
if k > upK - D and upVector[upOffset + k - 1] < x:
x = upVector[upOffset + k - 1] # up
var y = x - k
while x > lowerA and y > lowerB and dataA.data[x - 1] == dataB.data[y - 1]:
dec x
dec y
upVector[upOffset + k] = x
# overlap ?
if not oddDelta and downK-D <= k and k <= downK+D:
if upVector[upOffset + k] <= downVector[downOffset + k]:
return Smsrd(x: downVector[downOffset + k],
y: downVector[downOffset + k] - k)
assert false, "the algorithm should never come here."
proc lcs(dataA: var DiffData; lowerA, upperA: int; dataB: var DiffData; lowerB, upperB: int;
downVector, upVector: var openArray[int]) =
## This is the divide-and-conquer implementation of the longes common-subsequence (lcs)
## algorithm.
## The published algorithm passes recursively parts of the A and B sequences.
## To avoid copying these arrays the lower and upper bounds are passed while the sequences stay constant.
## ``dataA`` sequence A
## ``lowerA`` lower bound of the actual range in dataA
## ``upperA`` upper bound of the actual range in dataA (exclusive)
## ``dataB`` sequence B
## ``lowerB`` lower bound of the actual range in dataB
## ``upperB`` upper bound of the actual range in dataB (exclusive)
## ``downVector`` a vector for the (0,0) to (x,y) search. Passed as a parameter for speed reasons.
## ``upVector`` a vector for the (u,v) to (N,M) search. Passed as a parameter for speed reasons.
# make mutable copy:
var lowerA = lowerA
var lowerB = lowerB
var upperA = upperA
var upperB = upperB
# Fast walkthrough equal lines at the start
while lowerA < upperA and lowerB < upperB and dataA.data[lowerA] == dataB.data[lowerB]:
inc lowerA
inc lowerB
# Fast walkthrough equal lines at the end
while lowerA < upperA and lowerB < upperB and dataA.data[upperA - 1] == dataB.data[upperB - 1]:
dec upperA
dec upperB
if lowerA == upperA:
# mark as inserted lines.
while lowerB < upperB:
dataB.modified[lowerB] = true
inc lowerB
elif lowerB == upperB:
# mark as deleted lines.
while lowerA < upperA:
dataA.modified[lowerA] = true
inc lowerA
else:
# Find the middle snakea and length of an optimal path for A and B
let smsrd = sms(dataA, lowerA, upperA, dataB, lowerB, upperB, downVector, upVector)
# Debug.Write(2, "MiddleSnakeData", String.Format("{0},{1}", smsrd.x, smsrd.y))
# The path is from LowerX to (x,y) and (x,y) to UpperX
lcs(dataA, lowerA, smsrd.x, dataB, lowerB, smsrd.y, downVector, upVector)
lcs(dataA, smsrd.x, upperA, dataB, smsrd.y, upperB, downVector, upVector) # 2002.09.20: no need for 2 points
proc createDiffs(dataA, dataB: DiffData): seq[Item] =
## Scan the tables of which lines are inserted and deleted,
## producing an edit script in forward order.
var startA = 0
var startB = 0
var lineA = 0
var lineB = 0
while lineA < dataA.len or lineB < dataB.len:
if lineA < dataA.len and not dataA.modified[lineA] and
lineB < dataB.len and not dataB.modified[lineB]:
# equal lines
inc lineA
inc lineB
else:
# maybe deleted and/or inserted lines
startA = lineA
startB = lineB
while lineA < dataA.len and (lineB >= dataB.len or dataA.modified[lineA]):
inc lineA
while lineB < dataB.len and (lineA >= dataA.len or dataB.modified[lineB]):
inc lineB
if (startA < lineA) or (startB < lineB):
result.add Item(startA: startA,
startB: startB,
deletedA: lineA - startA,
insertedB: lineB - startB)
proc diffInt*(arrayA, arrayB: openArray[int]): seq[Item] =
## Find the difference in 2 arrays of integers.
## ``arrayA`` A-version of the numbers (usualy the old one)
## ``arrayB`` B-version of the numbers (usualy the new one)
## Returns a array of Items that describe the differences.
# The A-Version of the data (original data) to be compared.
var dataA = newDiffData(@arrayA, arrayA.len)
# The B-Version of the data (modified data) to be compared.
var dataB = newDiffData(@arrayB, arrayB.len)
let max = dataA.len + dataB.len + 1
## vector for the (0,0) to (x,y) search
var downVector = newSeq[int](2 * max + 2)
## vector for the (u,v) to (N,M) search
var upVector = newSeq[int](2 * max + 2)
lcs(dataA, 0, dataA.len, dataB, 0, dataB.len, downVector, upVector)
result = createDiffs(dataA, dataB)
proc diffText*(textA, textB: string): seq[Item] =
## Find the difference in 2 text documents, comparing by textlines.
## The algorithm itself is comparing 2 arrays of numbers so when comparing 2 text documents
## each line is converted into a (hash) number. This hash-value is computed by storing all
## textlines into a common hashtable so i can find dublicates in there, and generating a
## new number each time a new textline is inserted.
## ``TextA`` A-version of the text (usualy the old one)
## ``TextB`` B-version of the text (usualy the new one)
## ``trimSpace`` When set to true, all leading and trailing whitespace characters are stripped out before the comparation is done.
## ``ignoreSpace`` When set to true, all whitespace characters are converted to a single space character before the comparation is done.
## ``ignoreCase`` When set to true, all characters are converted to their lowercase equivivalence before the comparation is done.
## Returns a seq of Items that describe the differences.
# prepare the input-text and convert to comparable numbers.
var h = initTable[string, int]() # TextA.len + TextB.len <- probably wrong initial size
# The A-Version of the data (original data) to be compared.
var dataA = diffCodes(textA, h)
# The B-Version of the data (modified data) to be compared.
var dataB = diffCodes(textB, h)
h.clear # free up hashtable memory (maybe)
let max = dataA.len + dataB.len + 1
## vector for the (0,0) to (x,y) search
var downVector = newSeq[int](2 * max + 2)
## vector for the (u,v) to (N,M) search
var upVector = newSeq[int](2 * max + 2)
lcs(dataA, 0, dataA.len, dataB, 0, dataB.len, downVector, upVector)
optimize(dataA)
optimize(dataB)
result = createDiffs(dataA, dataB)
when isMainModule:
proc testHelper(f: seq[Item]): string =
for it in f:
result.add(
$it.deletedA & "." & $it.insertedB & "." & $it.startA & "." & $it.startB & "*"
)
proc main() =
var a, b: string
stdout.writeLine("Diff Self Test...")
# test all changes
a = "a,b,c,d,e,f,g,h,i,j,k,l".replace(',', '\n')
b = "0,1,2,3,4,5,6,7,8,9".replace(',', '\n')
assert(testHelper(diffText(a, b)) ==
"12.10.0.0*",
"all-changes test failed.")
stdout.writeLine("all-changes test passed.")
# test all same
a = "a,b,c,d,e,f,g,h,i,j,k,l".replace(',', '\n')
b = a
assert(testHelper(diffText(a, b)) ==
"",
"all-same test failed.")
stdout.writeLine("all-same test passed.")
# test snake
a = "a,b,c,d,e,f".replace(',', '\n')
b = "b,c,d,e,f,x".replace(',', '\n')
assert(testHelper(diffText(a, b)) ==
"1.0.0.0*0.1.6.5*",
"snake test failed.")
stdout.writeLine("snake test passed.")
# 2002.09.20 - repro
a = "c1,a,c2,b,c,d,e,g,h,i,j,c3,k,l".replace(',', '\n')
b = "C1,a,C2,b,c,d,e,I1,e,g,h,i,j,C3,k,I2,l".replace(',', '\n')
assert(testHelper(diffText(a, b)) ==
"1.1.0.0*1.1.2.2*0.2.7.7*1.1.11.13*0.1.13.15*",
"repro20020920 test failed.")
stdout.writeLine("repro20020920 test passed.")
# 2003.02.07 - repro
a = "F".replace(',', '\n')
b = "0,F,1,2,3,4,5,6,7".replace(',', '\n')
assert(testHelper(diffText(a, b)) ==
"0.1.0.0*0.7.1.2*",
"repro20030207 test failed.")
stdout.writeLine("repro20030207 test passed.")
# Muegel - repro
a = "HELLO\nWORLD"
b = "\n\nhello\n\n\n\nworld\n"
assert(testHelper(diffText(a, b)) ==
"2.8.0.0*",
"repro20030409 test failed.")
stdout.writeLine("repro20030409 test passed.")
# test some differences
a = "a,b,-,c,d,e,f,f".replace(',', '\n')
b = "a,b,x,c,e,f".replace(',', '\n')
assert(testHelper(diffText(a, b)) ==
"1.1.2.2*1.0.4.4*1.0.7.6*",
"some-changes test failed.")
stdout.writeLine("some-changes test passed.")
# test one change within long chain of repeats
a = "a,a,a,a,a,a,a,a,a,a".replace(',', '\n')
b = "a,a,a,a,-,a,a,a,a,a".replace(',', '\n')
assert(testHelper(diffText(a, b)) ==
"0.1.4.4*1.0.9.10*",
"long chain of repeats test failed.")
stdout.writeLine("End.")
stdout.flushFile
main()

View file

@ -8,6 +8,7 @@
# #
# Implementation of some runtime checks. # Implementation of some runtime checks.
import system/helpers2
proc raiseRangeError(val: BiggestInt) {.compilerproc, noinline.} = proc raiseRangeError(val: BiggestInt) {.compilerproc, noinline.} =
when hostOS == "standalone": when hostOS == "standalone":
@ -15,6 +16,12 @@ proc raiseRangeError(val: BiggestInt) {.compilerproc, noinline.} =
else: else:
sysFatal(RangeError, "value out of range: ", $val) sysFatal(RangeError, "value out of range: ", $val)
proc raiseIndexError3(i, a, b: int) {.compilerproc, noinline.} =
sysFatal(IndexError, formatErrorIndexBound(i, a, b))
proc raiseIndexError2(i, n: int) {.compilerproc, noinline.} =
sysFatal(IndexError, formatErrorIndexBound(i, n))
proc raiseIndexError() {.compilerproc, noinline.} = proc raiseIndexError() {.compilerproc, noinline.} =
sysFatal(IndexError, "index out of bounds") sysFatal(IndexError, "index out of bounds")
@ -25,7 +32,7 @@ proc chckIndx(i, a, b: int): int =
if i >= a and i <= b: if i >= a and i <= b:
return i return i
else: else:
raiseIndexError() raiseIndexError3(i, a, b)
proc chckRange(i, a, b: int): int = proc chckRange(i, a, b: int): int =
if i >= a and i <= b: if i >= a and i <= b:

View file

@ -454,16 +454,21 @@ when not defined(gcDestructors):
shallowCopy(result, e.trace) shallowCopy(result, e.trace)
when defined(nimRequiresNimFrame): when defined(nimRequiresNimFrame):
proc stackOverflow() {.noinline.} = const nimCallDepthLimit {.intdefine.} = 2000
proc callDepthLimitReached() {.noinline.} =
writeStackTrace() writeStackTrace()
showErrorMessage("Stack overflow\n") showErrorMessage("Error: call depth limit reached in a debug build (" &
$nimCallDepthLimit & " function calls). You can change it with " &
"-d:nimCallDepthLimit=<int> but really try to avoid deep " &
"recursions instead.\n")
quitOrDebug() quitOrDebug()
proc nimFrame(s: PFrame) {.compilerRtl, inl, exportc: "nimFrame".} = proc nimFrame(s: PFrame) {.compilerRtl, inl, exportc: "nimFrame".} =
s.calldepth = if framePtr == nil: 0 else: framePtr.calldepth+1 s.calldepth = if framePtr == nil: 0 else: framePtr.calldepth+1
s.prev = framePtr s.prev = framePtr
framePtr = s framePtr = s
if s.calldepth == 2000: stackOverflow() if s.calldepth == nimCallDepthLimit: callDepthLimitReached()
else: else:
proc pushFrame(s: PFrame) {.compilerRtl, inl, exportc: "nimFrame".} = proc pushFrame(s: PFrame) {.compilerRtl, inl, exportc: "nimFrame".} =
# XXX only for backwards compatibility # XXX only for backwards compatibility

5
lib/system/helpers2.nim Normal file
View file

@ -0,0 +1,5 @@
template formatErrorIndexBound*[T](i, a, b: T): string =
"index out of bounds: (a:" & $a & ") <= (i:" & $i & ") <= (b:" & $b & ") "
template formatErrorIndexBound*[T](i, n: T): string =
"index out of bounds: (i:" & $i & ") <= (n:" & $n & ") "

View file

@ -20,14 +20,11 @@ var
thisCommit: CommitId thisCommit: CommitId
thisBranch: string thisBranch: string
{.experimental.}
proc `()`(cmd: string{lit}): string = cmd.execProcess.string.strip
proc getMachine*(): MachineId = proc getMachine*(): MachineId =
var name = "hostname"() var name = execProcess("hostname").string.strip
if name.len == 0: if name.len == 0:
name = when defined(posix): getenv"HOSTNAME".string name = when defined(posix): getenv("HOSTNAME").string
else: getenv"COMPUTERNAME".string else: getenv("COMPUTERNAME").string
if name.len == 0: if name.len == 0:
quit "cannot determine the machine name" quit "cannot determine the machine name"
@ -35,8 +32,8 @@ proc getMachine*(): MachineId =
proc getCommit(): CommitId = proc getCommit(): CommitId =
const commLen = "commit ".len const commLen = "commit ".len
let hash = "git log -n 1"()[commLen..commLen+10] let hash = execProcess("git log -n 1").string.strip[commLen..commLen+10]
thisBranch = "git symbolic-ref --short HEAD"() thisBranch = execProcess("git symbolic-ref --short HEAD").string.strip
if hash.len == 0 or thisBranch.len == 0: quit "cannot determine git HEAD" if hash.len == 0 or thisBranch.len == 0: quit "cannot determine git HEAD"
result = CommitId(hash) result = CommitId(hash)
@ -45,8 +42,7 @@ var
currentCategory: string currentCategory: string
entries: int entries: int
proc writeTestResult*(name, category, target, proc writeTestResult*(name, category, target, action, result, expected, given: string) =
action, result, expected, given: string) =
createDir("testresults") createDir("testresults")
if currentCategory != category: if currentCategory != category:
if currentCategory.len > 0: if currentCategory.len > 0:

View file

@ -10,6 +10,36 @@
## Include for the tester that contains test suites that test special features ## Include for the tester that contains test suites that test special features
## of the compiler. ## of the compiler.
const
specialCategories = [
"assert",
"async",
"debugger",
"dll",
"examples",
"flags",
"gc",
"io",
"js",
"lib",
"longgc",
"manyloc",
"nimble-all",
"nimble-core",
"nimble-extra",
"niminaction",
"rodfiles",
"threads",
"untestable",
"stdlib",
"testdata",
"nimcache",
"coroutines",
"osproc",
"shouldfail",
"dir with space"
]
# included from tester.nim # included from tester.nim
# ---------------- ROD file tests --------------------------------------------- # ---------------- ROD file tests ---------------------------------------------
@ -27,7 +57,7 @@ proc delNimCache(filename, options: string) =
proc runRodFiles(r: var TResults, cat: Category, options: string) = proc runRodFiles(r: var TResults, cat: Category, options: string) =
template test(filename: string, clearCacheFirst=false) = template test(filename: string, clearCacheFirst=false) =
if clearCacheFirst: delNimCache(filename, options) if clearCacheFirst: delNimCache(filename, options)
testSpec r, makeTest(rodfilesDir / filename, options, cat, actionRun) testSpec r, makeTest(rodfilesDir / filename, options, cat)
# test basic recompilation scheme: # test basic recompilation scheme:
@ -97,10 +127,12 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) =
else: else:
"" ""
testNoSpec c, makeTest("lib/nimrtl.nim", var test1 = makeTest("lib/nimrtl.nim", options & " --app:lib -d:createNimRtl --threads:on", cat)
options & " --app:lib -d:createNimRtl --threads:on", cat, actionCompile) test1.spec.action = actionCompile
testNoSpec c, makeTest("tests/dll/server.nim", testSpec c, test1
options & " --app:lib -d:useNimRtl --threads:on" & rpath, cat, actionCompile) var test2 = makeTest("tests/dll/server.nim", options & " --app:lib -d:useNimRtl --threads:on" & rpath, cat)
test2.spec.action = actionCompile
testSpec c, test2
when defined(Windows): when defined(Windows):
# windows looks in the dir of the exe (yay!): # windows looks in the dir of the exe (yay!):
@ -120,7 +152,7 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) =
safeCopyFile("lib" / nimrtlDll, "tests/dll" / nimrtlDll) safeCopyFile("lib" / nimrtlDll, "tests/dll" / nimrtlDll)
testSpec r, makeTest("tests/dll/client.nim", options & " -d:useNimRtl --threads:on" & rpath, testSpec r, makeTest("tests/dll/client.nim", options & " -d:useNimRtl --threads:on" & rpath,
cat, actionRun) cat)
proc dllTests(r: var TResults, cat: Category, options: string) = proc dllTests(r: var TResults, cat: Category, options: string) =
# dummy compile result: # dummy compile result:
@ -138,32 +170,32 @@ proc dllTests(r: var TResults, cat: Category, options: string) =
proc gcTests(r: var TResults, cat: Category, options: string) = proc gcTests(r: var TResults, cat: Category, options: string) =
template testWithNone(filename: untyped) = template testWithNone(filename: untyped) =
testSpec r, makeTest("tests/gc" / filename, options & testSpec r, makeTest("tests/gc" / filename, options &
" --gc:none", cat, actionRun) " --gc:none", cat)
testSpec r, makeTest("tests/gc" / filename, options & testSpec r, makeTest("tests/gc" / filename, options &
" -d:release --gc:none", cat, actionRun) " -d:release --gc:none", cat)
template testWithoutMs(filename: untyped) = template testWithoutMs(filename: untyped) =
testSpec r, makeTest("tests/gc" / filename, options, cat, actionRun) testSpec r, makeTest("tests/gc" / filename, options, cat)
testSpec r, makeTest("tests/gc" / filename, options & testSpec r, makeTest("tests/gc" / filename, options &
" -d:release", cat, actionRun) " -d:release", cat)
testSpec r, makeTest("tests/gc" / filename, options & testSpec r, makeTest("tests/gc" / filename, options &
" -d:release -d:useRealtimeGC", cat, actionRun) " -d:release -d:useRealtimeGC", cat)
template testWithoutBoehm(filename: untyped) = template testWithoutBoehm(filename: untyped) =
testWithoutMs filename testWithoutMs filename
testSpec r, makeTest("tests/gc" / filename, options & testSpec r, makeTest("tests/gc" / filename, options &
" --gc:markAndSweep", cat, actionRun) " --gc:markAndSweep", cat)
testSpec r, makeTest("tests/gc" / filename, options & testSpec r, makeTest("tests/gc" / filename, options &
" -d:release --gc:markAndSweep", cat, actionRun) " -d:release --gc:markAndSweep", cat)
template test(filename: untyped) = template test(filename: untyped) =
testWithoutBoehm filename testWithoutBoehm filename
when not defined(windows) and not defined(android): when not defined(windows) and not defined(android):
# AR: cannot find any boehm.dll on the net, right now, so disabled # AR: cannot find any boehm.dll on the net, right now, so disabled
# for windows: # for windows:
testSpec r, makeTest("tests/gc" / filename, options & testSpec r, makeTest("tests/gc" / filename, options &
" --gc:boehm", cat, actionRun) " --gc:boehm", cat)
testSpec r, makeTest("tests/gc" / filename, options & testSpec r, makeTest("tests/gc" / filename, options &
" -d:release --gc:boehm", cat, actionRun) " -d:release --gc:boehm", cat)
testWithoutBoehm "foreign_thr" testWithoutBoehm "foreign_thr"
test "gcemscripten" test "gcemscripten"
@ -196,17 +228,18 @@ proc longGCTests(r: var TResults, cat: Category, options: string) =
var c = initResults() var c = initResults()
# According to ioTests, this should compile the file # According to ioTests, this should compile the file
testNoSpec c, makeTest("tests/realtimeGC/shared", options, cat, actionCompile) testSpec c, makeTest("tests/realtimeGC/shared", options, cat)
testC r, makeTest("tests/realtimeGC/cmain", cOptions, cat, actionRun) # ^- why is this not appended to r? Should this be discarded?
testSpec r, makeTest("tests/realtimeGC/nmain", options & "--threads: on", cat, actionRun) testC r, makeTest("tests/realtimeGC/cmain", cOptions, cat), actionRun
testSpec r, makeTest("tests/realtimeGC/nmain", options & "--threads: on", cat)
# ------------------------- threading tests ----------------------------------- # ------------------------- threading tests -----------------------------------
proc threadTests(r: var TResults, cat: Category, options: string) = proc threadTests(r: var TResults, cat: Category, options: string) =
template test(filename: untyped) = template test(filename: untyped) =
testSpec r, makeTest(filename, options, cat, actionRun) testSpec r, makeTest(filename, options, cat)
testSpec r, makeTest(filename, options & " -d:release", cat, actionRun) testSpec r, makeTest(filename, options & " -d:release", cat)
testSpec r, makeTest(filename, options & " --tlsEmulation:on", cat, actionRun) testSpec r, makeTest(filename, options & " --tlsEmulation:on", cat)
for t in os.walkFiles("tests/threads/t*.nim"): for t in os.walkFiles("tests/threads/t*.nim"):
test(t) test(t)
@ -229,16 +262,16 @@ proc asyncTests(r: var TResults, cat: Category, options: string) =
# ------------------------- debugger tests ------------------------------------ # ------------------------- debugger tests ------------------------------------
proc debuggerTests(r: var TResults, cat: Category, options: string) = proc debuggerTests(r: var TResults, cat: Category, options: string) =
testNoSpec r, makeTest("tools/nimgrep", options & " --debugger:on", cat) var t = makeTest("tools/nimgrep", options & " --debugger:on", cat)
t.spec.action = actionCompile
testSpec r, t
# ------------------------- JS tests ------------------------------------------ # ------------------------- JS tests ------------------------------------------
proc jsTests(r: var TResults, cat: Category, options: string) = proc jsTests(r: var TResults, cat: Category, options: string) =
template test(filename: untyped) = template test(filename: untyped) =
testSpec r, makeTest(filename, options & " -d:nodejs", cat, testSpec r, makeTest(filename, options & " -d:nodejs", cat), {targetJS}
actionRun), targetJS testSpec r, makeTest(filename, options & " -d:nodejs -d:release", cat), {targetJS}
testSpec r, makeTest(filename, options & " -d:nodejs -d:release", cat,
actionRun), targetJS
for t in os.walkFiles("tests/js/t*.nim"): for t in os.walkFiles("tests/js/t*.nim"):
test(t) test(t)
@ -259,14 +292,14 @@ proc jsTests(r: var TResults, cat: Category, options: string) =
proc testNimInAction(r: var TResults, cat: Category, options: string) = proc testNimInAction(r: var TResults, cat: Category, options: string) =
let options = options & " --nilseqs:on" let options = options & " --nilseqs:on"
template test(filename: untyped, action: untyped) = template test(filename: untyped) =
testSpec r, makeTest(filename, options, cat, action) testSpec r, makeTest(filename, options, cat)
template testJS(filename: untyped) = template testJS(filename: untyped) =
testSpec r, makeTest(filename, options, cat, actionCompile), targetJS testSpec r, makeTest(filename, options, cat), {targetJS}
template testCPP(filename: untyped) = template testCPP(filename: untyped) =
testSpec r, makeTest(filename, options, cat, actionCompile), targetCPP testSpec r, makeTest(filename, options, cat), {targetCPP}
let tests = [ let tests = [
"niminaction/Chapter1/various1", "niminaction/Chapter1/various1",
@ -298,39 +331,43 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) =
# edit when making a conscious breaking change, also please try to make your # edit when making a conscious breaking change, also please try to make your
# commit message clear and notify me so I can easily compile an errata later. # commit message clear and notify me so I can easily compile an errata later.
const refHashes = @[ const refHashes = @[
"51afdfa84b3ca3d810809d6c4e5037ba", "30f07e4cd5eaec981f67868d4e91cfcf", "51afdfa84b3ca3d810809d6c4e5037ba",
"d14e7c032de36d219c9548066a97e846", "2e40bfd5daadb268268727da91bb4e81", "30f07e4cd5eaec981f67868d4e91cfcf",
"c5d3853ed0aba04bf6d35ba28a98dca0", "058603145ff92d46c009006b06e5b228", "d14e7c032de36d219c9548066a97e846",
"7b94a029b94ddb7efafddd546c965ff6", "586d74514394e49f2370dfc01dd9e830", "b335635562ff26ec0301bdd86356ac0c",
"13febc363ed82585f2a60de40ddfefda", "c11a013db35e798f44077bc0763cc86d", "6c4add749fbf50860e2f523f548e6b0e",
"3e32e2c5e9a24bd13375e1cd0467079c", "0b9fe7ba159623d49ae60db18a15037c", "76de5833a7cc46f96b006ce51179aeb1",
"b2dd5293d7f784824bbf9792c6fb51ad", "4c19d8d9026bfe151b31d7007fa3c237", "705eff79844e219b47366bd431658961",
"9415c6a568cfceed08da8378e95b5cd5", "da520038c153f4054cb8cc5faa617714", "a1e87b881c5eb161553d119be8b52f64",
"e6c6e061b6f77b2475db6fec7abfb7f4", "9a8fe78c588d08018843b64b57409a02", "2d706a6ec68d2973ec7e733e6d5dce50",
"8b5d28e985c0542163927d253a3e4fc9", "783299b98179cc725f9c46b5e3b5381f", "c11a013db35e798f44077bc0763cc86d",
"bc523f9a9921299090bac1af6c958e73", "80f9c3e594a798225046e8a42e990daf" "3e32e2c5e9a24bd13375e1cd0467079c",
"a5452722b2841f0c1db030cf17708955",
"dc6c45eb59f8814aaaf7aabdb8962294",
"69d208d281a2e7bffd3eaf4bab2309b1",
"ec05666cfb60211bedc5e81d4c1caf3d",
"da520038c153f4054cb8cc5faa617714",
"59906c8cd819cae67476baa90a36b8c1",
"9a8fe78c588d08018843b64b57409a02",
"8b5d28e985c0542163927d253a3e4fc9",
"783299b98179cc725f9c46b5e3b5381f",
"1a2b3fba1187c68d6a9bfa66854f3318",
"80f9c3e594a798225046e8a42e990daf"
] ]
for i, test in tests: for i, test in tests:
let filename = "tests" / test.addFileExt("nim") let filename = "tests" / test.addFileExt("nim")
let testHash = getMD5(readFile(filename).string) let testHash = getMD5(readFile(filename).string)
doAssert testHash == refHashes[i], "Nim in Action test " & filename & " was changed." doAssert testHash == refHashes[i], "Nim in Action test " & filename & " was changed."
# Run the tests. # Run the tests.
for testfile in tests: for testfile in tests:
test "tests/" & testfile & ".nim", actionCompile test "tests/" & testfile & ".nim"
let jsFile = "tests/niminaction/Chapter8/canvas/canvas_test.nim" let jsFile = "tests/niminaction/Chapter8/canvas/canvas_test.nim"
testJS jsFile testJS jsFile
let cppFile = "tests/niminaction/Chapter8/sfml/sfml_test.nim" let cppFile = "tests/niminaction/Chapter8/sfml/sfml_test.nim"
testCPP cppFile testCPP cppFile
# ------------------------- manyloc ------------------------------------------- # ------------------------- manyloc -------------------------------------------
#proc runSpecialTests(r: var TResults, options: string) =
# for t in ["lib/packages/docutils/highlite"]:
# testSpec(r, t, options)
proc findMainFile(dir: string): string = proc findMainFile(dir: string): string =
# finds the file belonging to ".nim.cfg"; if there is no such file # finds the file belonging to ".nim.cfg"; if there is no such file
@ -354,24 +391,29 @@ proc manyLoc(r: var TResults, cat: Category, options: string) =
if dir.endsWith"named_argument_bug": continue if dir.endsWith"named_argument_bug": continue
let mainfile = findMainFile(dir) let mainfile = findMainFile(dir)
if mainfile != "": if mainfile != "":
testNoSpec r, makeTest(mainfile, options, cat) var test = makeTest(mainfile, options, cat)
test.spec.action = actionCompile
testSpec r, test
proc compileExample(r: var TResults, pattern, options: string, cat: Category) = proc compileExample(r: var TResults, pattern, options: string, cat: Category) =
for test in os.walkFiles(pattern): for test in os.walkFiles(pattern):
testNoSpec r, makeTest(test, options, cat) var test = makeTest(test, options, cat)
test.spec.action = actionCompile
testSpec r, test
proc testStdlib(r: var TResults, pattern, options: string, cat: Category) = proc testStdlib(r: var TResults, pattern, options: string, cat: Category) =
for test in os.walkFiles(pattern): for testFile in os.walkFiles(pattern):
let name = extractFilename(test) let name = extractFilename(testFile)
if name notin disabledFiles: if name notin disabledFiles:
let contents = readFile(test).string let contents = readFile(testFile).string
if contents.contains("when isMainModule"): var testObj = makeTest(testFile, options, cat)
testSpec r, makeTest(test, options, cat, actionRunNoSpec) if "when isMainModule" notin contents:
else: testObj.spec.action = actionCompile
testNoSpec r, makeTest(test, options, cat, actionCompile) testSpec r, testObj
# ----------------------------- nimble ---------------------------------------- # ----------------------------- nimble ----------------------------------------
type PackageFilter = enum type
PackageFilter = enum
pfCoreOnly pfCoreOnly
pfExtraOnly pfExtraOnly
pfAll pfAll
@ -404,7 +446,6 @@ proc getPackageDir(package: string): string =
iterator listPackages(filter: PackageFilter): tuple[name, url: string] = iterator listPackages(filter: PackageFilter): tuple[name, url: string] =
let packageList = parseFile(packageIndex) let packageList = parseFile(packageIndex)
for package in packageList.items(): for package in packageList.items():
let let
name = package["name"].str name = package["name"].str
@ -458,24 +499,99 @@ proc testNimblePackages(r: var TResults, cat: Category, filter: PackageFilter) =
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
const AdditionalCategories = ["debugger", "examples", "lib"] const AdditionalCategories = ["debugger", "examples", "lib", "megatest"]
proc `&.?`(a, b: string): string = proc `&.?`(a, b: string): string =
# candidate for the stdlib? # candidate for the stdlib?
result = if b.startswith(a): b else: a & b result = if b.startswith(a): b else: a & b
#proc `&?.`(a, b: string): string = # not used
# candidate for the stdlib?
#result = if a.endswith(b): a else: a & b
proc processSingleTest(r: var TResults, cat: Category, options, test: string) = proc processSingleTest(r: var TResults, cat: Category, options, test: string) =
let test = "tests" & DirSep &.? cat.string / test let test = "tests" & DirSep &.? cat.string / test
let target = if cat.string.normalize == "js": targetJS else: targetC let target = if cat.string.normalize == "js": targetJS else: targetC
if existsFile(test):
testSpec r, makeTest(test, options, cat), {target}
else:
echo "[Warning] - ", test, " test does not exist"
if existsFile(test): testSpec r, makeTest(test, options, cat), target proc isJoinableSpec(spec: TSpec): bool =
else: echo "[Warning] - ", test, " test does not exist" result = not spec.sortoutput and
spec.action == actionRun and
not fileExists(spec.file.changeFileExt("cfg")) and
not fileExists(parentDir(spec.file) / "nim.cfg") and
spec.cmd.len == 0 and
spec.err != reDisabled and
not spec.unjoinable and
spec.exitCode == 0 and
spec.input.len == 0 and
spec.nimout.len == 0 and
spec.outputCheck != ocSubstr and
spec.ccodeCheck.len == 0 and
(spec.targets == {} or spec.targets == {targetC})
proc processCategory(r: var TResults, cat: Category, options: string) = proc norm(s: var string) =
while true:
let tmp = s.replace("\n\n", "\n")
if tmp == s: break
s = tmp
s = s.strip
proc runJoinedTest(r: var TResults, cat: Category, testsDir: string) =
## returs a list of tests that have problems
var specs: seq[TSpec] = @[]
for kind, dir in walkDir(testsDir):
assert testsDir.startsWith(testsDir)
let cat = dir[testsDir.len .. ^1]
if kind == pcDir and cat notin specialCategories:
for file in os.walkFiles(testsDir / cat / "t*.nim"):
let spec = parseSpec(file)
if isJoinableSpec(spec):
specs.add spec
echo "joinable specs: ", specs.len
var megatest: string
for runSpec in specs:
megatest.add "import r\""
megatest.add runSpec.file
megatest.add "\"\n"
writeFile("megatest.nim", megatest)
const args = ["c", "-d:testing", "--listCmd", "megatest.nim"]
var (buf, exitCode) = execCmdEx2(command = "nim", args = args, options = {poStdErrToStdOut, poUsePath}, input = "")
if exitCode != 0:
echo buf
quit("megatest compilation failed")
(buf, exitCode) = execCmdEx("./megatest")
if exitCode != 0:
echo buf
quit("megatest execution failed")
norm buf
writeFile("outputGotten.txt", buf)
var outputExpected = ""
for i, runSpec in specs:
outputExpected.add runSpec.output.strip
outputExpected.add '\n'
norm outputExpected
if buf != outputExpected:
writeFile("outputExpected.txt", outputExpected)
discard execShellCmd("diff -uNdr outputExpected.txt outputGotten.txt")
echo "output different!"
quit 1
else:
echo "output OK"
removeFile("outputGotten.txt")
removeFile("megatest.nim")
#testSpec r, makeTest("megatest", options, cat)
# ---------------------------------------------------------------------------
proc processCategory(r: var TResults, cat: Category, options, testsDir: string,
runJoinableTests: bool) =
case cat.string.normalize case cat.string.normalize
of "rodfiles": of "rodfiles":
when false: when false:
@ -524,10 +640,17 @@ proc processCategory(r: var TResults, cat: Category, options: string) =
of "untestable": of "untestable":
# We can't test it because it depends on a third party. # We can't test it because it depends on a third party.
discard # TODO: Move untestable tests to someplace else, i.e. nimble repo. discard # TODO: Move untestable tests to someplace else, i.e. nimble repo.
of "megatest":
runJoinedTest(r, cat, testsDir)
else: else:
var testsRun = 0 var testsRun = 0
for name in os.walkFiles("tests" & DirSep &.? cat.string / "t*.nim"): for name in os.walkFiles("tests" & DirSep &.? cat.string / "t*.nim"):
testSpec r, makeTest(name, options, cat) var test = makeTest(name, options, cat)
if runJoinableTests or not isJoinableSpec(test.spec) or cat.string in specialCategories:
discard "run the test"
else:
test.spec.err = reJoined
testSpec r, test
inc testsRun inc testsRun
if testsRun == 0: if testsRun == 0:
echo "[Warning] - Invalid category specified \"", cat.string, "\", no tests were run" echo "[Warning] - Invalid category specified \"", cat.string, "\", no tests were run"

View file

@ -9,21 +9,21 @@
import parseutils, strutils, os, osproc, streams, parsecfg import parseutils, strutils, os, osproc, streams, parsecfg
var compilerPrefix* = "compiler" / "nim" var compilerPrefix* = "compiler" / "nim"
let isTravis* = existsEnv("TRAVIS") let isTravis* = existsEnv("TRAVIS")
let isAppVeyor* = existsEnv("APPVEYOR") let isAppVeyor* = existsEnv("APPVEYOR")
proc cmdTemplate*(): string =
compilerPrefix & " $target --hints:on -d:testing --nimblePath:tests/deps $options $file"
type type
TTestAction* = enum TTestAction* = enum
actionRun = "run" actionRun = "run"
actionCompile = "compile" actionCompile = "compile"
actionReject = "reject" actionReject = "reject"
actionRunNoSpec = "runNoSpec"
TOutputCheck* = enum
ocIgnore = "ignore"
ocEqual = "equal"
ocSubstr = "substr"
TResultEnum* = enum TResultEnum* = enum
reNimcCrash, # nim compiler seems to have crashed reNimcCrash, # nim compiler seems to have crashed
@ -38,8 +38,10 @@ type
reExeNotFound, reExeNotFound,
reInstallFailed # package installation failed reInstallFailed # package installation failed
reBuildFailed # package building failed reBuildFailed # package building failed
reIgnored, # test is ignored reDisabled, # test is disabled
reJoined, # test is disabled because it was joined into the megatest
reSuccess # test was successful reSuccess # test was successful
reInvalidSpec # test had problems to parse the spec
TTarget* = enum TTarget* = enum
targetC = "C" targetC = "C"
@ -51,7 +53,9 @@ type
action*: TTestAction action*: TTestAction
file*, cmd*: string file*, cmd*: string
input*: string input*: string
outp*: string outputCheck*: TOutputCheck
sortoutput*: bool
output*: string
line*, column*: int line*, column*: int
tfile*: string tfile*: string
tline*, tcolumn*: int tline*, tcolumn*: int
@ -60,9 +64,16 @@ type
ccodeCheck*: string ccodeCheck*: string
maxCodeSize*: int maxCodeSize*: int
err*: TResultEnum err*: TResultEnum
substr*, sortoutput*: bool
targets*: set[TTarget] targets*: set[TTarget]
nimout*: string nimout*: string
parseErrors*: string # when the spec definition is invalid, this is not empty.
unjoinable*: bool
proc getCmd*(s: TSpec): string =
if s.cmd.len == 0:
result = compilerPrefix & " $target --hints:on -d:testing --nimblePath:tests/deps $options $file"
else:
result = s.cmd
const const
targetToExt*: array[TTarget, string] = ["c", "cpp", "m", "js"] targetToExt*: array[TTarget, string] = ["c", "cpp", "m", "js"]
@ -91,33 +102,6 @@ proc extractSpec(filename: string): string =
when not defined(nimhygiene): when not defined(nimhygiene):
{.pragma: inject.} {.pragma: inject.}
template parseSpecAux(fillResult: untyped) =
var ss = newStringStream(extractSpec(filename))
var p {.inject.}: CfgParser
open(p, ss, filename, 1)
while true:
var e {.inject.} = next(p)
case e.kind
of cfgEof: break
of cfgSectionStart, cfgOption, cfgError:
echo ignoreMsg(p, e)
of cfgKeyValuePair:
fillResult
close(p)
proc specDefaults*(result: var TSpec) =
result.msg = ""
result.outp = ""
result.nimout = ""
result.ccodeCheck = ""
result.cmd = cmdTemplate()
result.line = 0
result.column = 0
result.tfile = ""
result.tline = 0
result.tcolumn = 0
result.maxCodeSize = 0
proc parseTargets*(value: string): set[TTarget] = proc parseTargets*(value: string): set[TTarget] =
for v in value.normalize.splitWhitespace: for v in value.normalize.splitWhitespace:
case v case v
@ -127,34 +111,67 @@ proc parseTargets*(value: string): set[TTarget] =
of "js": result.incl(targetJS) of "js": result.incl(targetJS)
else: echo "target ignored: " & v else: echo "target ignored: " & v
proc addLine*(self: var string; a: string) =
self.add a
self.add "\n"
proc addLine*(self: var string; a,b: string) =
self.add a
self.add b
self.add "\n"
proc parseSpec*(filename: string): TSpec = proc parseSpec*(filename: string): TSpec =
specDefaults(result)
result.file = filename result.file = filename
parseSpecAux: let specStr = extractSpec(filename)
var ss = newStringStream(specStr)
var p: CfgParser
open(p, ss, filename, 1)
while true:
var e = next(p)
case e.kind
of cfgKeyValuePair:
case normalize(e.key) case normalize(e.key)
of "action": of "action":
case e.value.normalize case e.value.normalize
of "compile": result.action = actionCompile of "compile":
of "run": result.action = actionRun result.action = actionCompile
of "reject": result.action = actionReject of "run":
else: echo ignoreMsg(p, e)
of "file": result.file = e.value
of "line": discard parseInt(e.value, result.line)
of "column": discard parseInt(e.value, result.column)
of "tfile": result.tfile = e.value
of "tline": discard parseInt(e.value, result.tline)
of "tcolumn": discard parseInt(e.value, result.tcolumn)
of "output":
result.action = actionRun result.action = actionRun
result.outp = e.value of "reject":
result.action = actionReject
else:
result.parseErrors.addLine "cannot interpret as action: ", e.value
of "file":
if result.msg.len == 0 and result.nimout.len == 0:
result.parseErrors.addLine "errormsg or msg needs to be specified before file"
result.file = e.value
of "line":
if result.msg.len == 0 and result.nimout.len == 0:
result.parseErrors.addLine "errormsg, msg or nimout needs to be specified before line"
discard parseInt(e.value, result.line)
of "column":
if result.msg.len == 0 and result.nimout.len == 0:
result.parseErrors.addLine "errormsg or msg needs to be specified before column"
discard parseInt(e.value, result.column)
of "tfile":
result.tfile = e.value
of "tline":
discard parseInt(e.value, result.tline)
of "tcolumn":
discard parseInt(e.value, result.tcolumn)
of "output":
result.outputCheck = ocEqual
result.output = strip(e.value)
of "input": of "input":
result.input = e.value result.input = e.value
of "outputsub": of "outputsub":
result.action = actionRun result.outputCheck = ocSubstr
result.outp = e.value result.output = strip(e.value)
result.substr = true
of "sortoutput": of "sortoutput":
try:
result.sortoutput = parseCfgBool(e.value) result.sortoutput = parseCfgBool(e.value)
except:
result.parseErrors.addLine getCurrentExceptionMsg()
of "exitcode": of "exitcode":
discard parseInt(e.value, result.exitCode) discard parseInt(e.value, result.exitCode)
result.action = actionRun result.action = actionRun
@ -167,41 +184,61 @@ proc parseSpec*(filename: string): TSpec =
result.action = actionReject result.action = actionReject
of "nimout": of "nimout":
result.nimout = e.value result.nimout = e.value
of "joinable":
result.unjoinable = not parseCfgBool(e.value)
of "disabled": of "disabled":
case e.value.normalize case e.value.normalize
of "y", "yes", "true", "1", "on": result.err = reIgnored of "y", "yes", "true", "1", "on": result.err = reDisabled
of "n", "no", "false", "0", "off": discard of "n", "no", "false", "0", "off": discard
of "win", "windows": of "win", "windows":
when defined(windows): result.err = reIgnored when defined(windows): result.err = reDisabled
of "linux": of "linux":
when defined(linux): result.err = reIgnored when defined(linux): result.err = reDisabled
of "bsd": of "bsd":
when defined(bsd): result.err = reIgnored when defined(bsd): result.err = reDisabled
of "macosx": of "macosx":
when defined(macosx): result.err = reIgnored when defined(macosx): result.err = reDisabled
of "unix": of "unix":
when defined(unix): result.err = reIgnored when defined(unix): result.err = reDisabled
of "posix": of "posix":
when defined(posix): result.err = reIgnored when defined(posix): result.err = reDisabled
of "travis": of "travis":
if isTravis: result.err = reIgnored if isTravis: result.err = reDisabled
of "appveyor": of "appveyor":
if isAppVeyor: result.err = reIgnored if isAppVeyor: result.err = reDisabled
else: else:
raise newException(ValueError, "cannot interpret as a bool: " & e.value) result.parseErrors.addLine "cannot interpret as a bool: ", e.value
of "cmd": of "cmd":
if e.value.startsWith("nim "): if e.value.startsWith("nim "):
result.cmd = compilerPrefix & e.value[3..^1] result.cmd = compilerPrefix & e.value[3..^1]
else: else:
result.cmd = e.value result.cmd = e.value
of "ccodecheck": result.ccodeCheck = e.value of "ccodecheck":
of "maxcodesize": discard parseInt(e.value, result.maxCodeSize) result.ccodeCheck = e.value
of "maxcodesize":
discard parseInt(e.value, result.maxCodeSize)
of "target", "targets": of "target", "targets":
for v in e.value.normalize.splitWhitespace: for v in e.value.normalize.splitWhitespace:
case v case v
of "c": result.targets.incl(targetC) of "c":
of "cpp", "c++": result.targets.incl(targetCpp) result.targets.incl(targetC)
of "objc": result.targets.incl(targetObjC) of "cpp", "c++":
of "js": result.targets.incl(targetJS) result.targets.incl(targetCpp)
else: echo ignoreMsg(p, e) of "objc":
else: echo ignoreMsg(p, e) result.targets.incl(targetObjC)
of "js":
result.targets.incl(targetJS)
else:
result.parseErrors.addLine "cannot interpret as a target: ", e.value
else:
result.parseErrors.addLine "invalid key for test spec: ", e.key
of cfgSectionStart:
result.parseErrors.addLine "section ignored: ", e.section
of cfgOption:
result.parseErrors.addLine "command ignored: ", e.key & ": " & e.value
of cfgError:
result.parseErrors.addLine e.msg
of cfgEof:
break
close(p)

View file

@ -15,6 +15,7 @@ import
algorithm, compiler/nodejs, times, sets, md5 algorithm, compiler/nodejs, times, sets, md5
var useColors = true var useColors = true
var backendLogging = true
const const
resultsFile = "testresults.html" resultsFile = "testresults.html"
@ -27,6 +28,7 @@ Command:
c|cat|category <category> run all the tests of a certain category c|cat|category <category> run all the tests of a certain category
r|run <test> run single test file r|run <test> run single test file
html generate $1 from the database html generate $1 from the database
stats generate statistics about test cases
Arguments: Arguments:
arguments are passed to the compiler arguments are passed to the compiler
Options: Options:
@ -35,7 +37,8 @@ Options:
--targets:"c c++ js objc" run tests for specified targets (default: all) --targets:"c c++ js objc" run tests for specified targets (default: all)
--nim:path use a particular nim executable (default: compiler/nim) --nim:path use a particular nim executable (default: compiler/nim)
--directory:dir Change to directory dir before reading the tests or doing anything else. --directory:dir Change to directory dir before reading the tests or doing anything else.
--colors:on|off turn messagescoloring on|off --colors:on|off Turn messagescoloring on|off.
--backendLogging:on|off Disable or enable backend logging. By default turned on.
""" % resultsFile """ % resultsFile
type type
@ -48,7 +51,7 @@ type
name: string name: string
cat: Category cat: Category
options: string options: string
action: TTestAction spec: TSpec
startTime: float startTime: float
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
@ -113,7 +116,7 @@ proc nimcacheDir(filename, options: string, target: TTarget): string =
proc callCompiler(cmdTemplate, filename, options: string, proc callCompiler(cmdTemplate, filename, options: string,
target: TTarget, extraOptions=""): TSpec = target: TTarget, extraOptions=""): TSpec =
let nimcache = nimcacheDir(filename, options, target) let nimcache = nimcacheDir(filename, options, target)
let options = options & " " & ("--nimCache:" & nimcache).quoteShell & extraOptions let options = options & " " & quoteShell("--nimCache:" & nimcache) & extraOptions
let c = parseCmdLine(cmdTemplate % ["target", targetToCmd[target], let c = parseCmdLine(cmdTemplate % ["target", targetToCmd[target],
"options", options, "file", filename.quoteShell, "options", options, "file", filename.quoteShell,
"filedir", filename.getFileDir()]) "filedir", filename.getFileDir()])
@ -138,7 +141,7 @@ proc callCompiler(cmdTemplate, filename, options: string,
close(p) close(p)
result.msg = "" result.msg = ""
result.file = "" result.file = ""
result.outp = "" result.output = ""
result.line = 0 result.line = 0
result.column = 0 result.column = 0
result.tfile = "" result.tfile = ""
@ -170,7 +173,7 @@ proc callCCompiler(cmdTemplate, filename, options: string,
result.nimout = "" result.nimout = ""
result.msg = "" result.msg = ""
result.file = "" result.file = ""
result.outp = "" result.output = ""
result.line = -1 result.line = -1
while outp.readLine(x.TaintedString) or running(p): while outp.readLine(x.TaintedString) or running(p):
result.nimout.add(x & "\n") result.nimout.add(x & "\n")
@ -219,18 +222,21 @@ proc addResult(r: var TResults, test: TTest, target: TTarget,
let name = test.name.extractFilename & " " & $target & test.options let name = test.name.extractFilename & " " & $target & test.options
let duration = epochTime() - test.startTime let duration = epochTime() - test.startTime
let durationStr = duration.formatFloat(ffDecimal, precision = 8).align(11) let durationStr = duration.formatFloat(ffDecimal, precision = 8).align(11)
if backendLogging:
backend.writeTestResult(name = name, backend.writeTestResult(name = name,
category = test.cat.string, category = test.cat.string,
target = $target, target = $target,
action = $test.action, action = $test.spec.action,
result = $success, result = $success,
expected = expected, expected = expected,
given = given) given = given)
r.data.addf("$#\t$#\t$#\t$#", name, expected, given, $success) r.data.addf("$#\t$#\t$#\t$#", name, expected, given, $success)
if success == reSuccess: if success == reSuccess:
maybeStyledEcho fgGreen, "PASS: ", fgCyan, alignLeft(name, 60), fgBlue, " (", durationStr, " secs)" maybeStyledEcho fgGreen, "PASS: ", fgCyan, alignLeft(name, 60), fgBlue, " (", durationStr, " secs)"
elif success == reIgnored: elif success == reDisabled:
maybeStyledEcho styleDim, fgYellow, "SKIP: ", styleBright, fgCyan, name maybeStyledEcho styleDim, fgYellow, "SKIP: ", styleBright, fgCyan, name
elif success == reJoined:
maybeStyledEcho styleDim, fgYellow, "JOINED: ", styleBright, fgCyan, name
else: else:
maybeStyledEcho styleBright, fgRed, "FAIL: ", fgCyan, name maybeStyledEcho styleBright, fgRed, "FAIL: ", fgCyan, name
maybeStyledEcho styleBright, fgCyan, "Test \"", test.name, "\"", " in category \"", test.cat.string, "\"" maybeStyledEcho styleBright, fgCyan, "Test \"", test.name, "\"", " in category \"", test.cat.string, "\""
@ -240,11 +246,11 @@ proc addResult(r: var TResults, test: TTest, target: TTarget,
maybeStyledEcho fgYellow, "Gotten:" maybeStyledEcho fgYellow, "Gotten:"
maybeStyledEcho styleBright, given, "\n" maybeStyledEcho styleBright, given, "\n"
if existsEnv("APPVEYOR"): if backendLogging and existsEnv("APPVEYOR"):
let (outcome, msg) = let (outcome, msg) =
if success == reSuccess: if success == reSuccess:
("Passed", "") ("Passed", "")
elif success == reIgnored: elif success in {reDisabled, reJoined}:
("Skipped", "") ("Skipped", "")
else: else:
("Failed", "Failure: " & $success & "\nExpected:\n" & expected & "\n\n" & "Gotten:\n" & given) ("Failed", "Failure: " & $success & "\nExpected:\n" & expected & "\n\n" & "Gotten:\n" & given)
@ -328,11 +334,6 @@ proc nimoutCheck(test: TTest; expectedNimout: string; given: var TSpec) =
given.err = reMsgsDiffer given.err = reMsgsDiffer
return return
proc makeDeterministic(s: string): string =
var x = splitLines(s)
sort(x, system.cmp)
result = join(x, "\n")
proc compilerOutputTests(test: TTest, target: TTarget, given: var TSpec, proc compilerOutputTests(test: TTest, target: TTarget, given: var TSpec,
expected: TSpec; r: var TResults) = expected: TSpec; r: var TResults) =
var expectedmsg: string = "" var expectedmsg: string = ""
@ -350,75 +351,58 @@ proc compilerOutputTests(test: TTest, target: TTarget, given: var TSpec,
if given.err == reSuccess: inc(r.passed) if given.err == reSuccess: inc(r.passed)
r.addResult(test, target, expectedmsg, givenmsg, given.err) r.addResult(test, target, expectedmsg, givenmsg, given.err)
proc testSpec(r: var TResults, test: TTest, target = targetC) = proc testSpec(r: var TResults, test: TTest, targets: set[TTarget] = {}) =
let tname = test.name.addFileExt(".nim") var expected = test.spec
var expected: TSpec if expected.parseErrors.len > 0:
if test.action != actionRunNoSpec: # targetC is a lie, but parameter is required
expected = parseSpec(tname) r.addResult(test, targetC, "", expected.parseErrors, reInvalidSpec)
if test.action == actionRun and expected.action == actionCompile: inc(r.total)
expected.action = actionRun return
else: if expected.err in {reDisabled, reJoined}:
specDefaults expected # targetC is a lie, but parameter is required
expected.action = actionRunNoSpec r.addResult(test, targetC, "", "", expected.err)
if expected.err == reIgnored:
r.addResult(test, target, "", "", reIgnored)
inc(r.skipped) inc(r.skipped)
inc(r.total) inc(r.total)
return return
expected.targets.incl targets
if getEnv("NIM_COMPILE_TO_CPP", "false").string == "true" and target == targetC and expected.targets == {}: # still no target specified at all
expected.targets.incl(targetCpp) if expected.targets == {}:
elif expected.targets == {}: if getEnv("NIM_COMPILE_TO_CPP", "false").string == "true":
expected.targets.incl(target) expected.targets = {targetCpp}
else:
expected.targets = {targetC}
for target in expected.targets: for target in expected.targets:
inc(r.total) inc(r.total)
if target notin targets:
r.addResult(test, target, "", "", reIgnored)
inc(r.skipped)
continue
case expected.action case expected.action
of actionCompile: of actionCompile:
var given = callCompiler(expected.cmd, test.name, test.options, target, var given = callCompiler(expected.getCmd, test.name, test.options, target,
extraOptions=" --stdout --hint[Path]:off --hint[Processing]:off") extraOptions=" --stdout --hint[Path]:off --hint[Processing]:off")
compilerOutputTests(test, target, given, expected, r) compilerOutputTests(test, target, given, expected, r)
of actionRun, actionRunNoSpec: of actionRun:
# In this branch of code "early return" pattern is clearer than deep # In this branch of code "early return" pattern is clearer than deep
# nested conditionals - the empty rows in between to clarify the "danger" # nested conditionals - the empty rows in between to clarify the "danger"
var given = callCompiler(expected.cmd, test.name, test.options, var given = callCompiler(expected.getCmd, test.name, test.options,
target) target)
# echo "expected.cmd: ", expected.cmd
# echo "nimout: ", given.nimout
# echo "outp: ", given.outp
# echo "msg: ", given.msg
# echo "err: ", given.err
if given.err != reSuccess: if given.err != reSuccess:
r.addResult(test, target, "", given.msg, given.err) r.addResult(test, target, "", given.msg, given.err)
continue continue
let isJsTarget = target == targetJS let isJsTarget = target == targetJS
var exeFile: string var exeFile: string
if isJsTarget: if isJsTarget:
let (_, file, _) = splitFile(tname) let file = test.name.lastPathPart.changeFileExt("js")
exeFile = nimcacheDir(test.name, test.options, target) / file & ".js" exeFile = nimcacheDir(test.name, test.options, target) / file
else: else:
exeFile = changeFileExt(tname, ExeExt) exeFile = changeFileExt(test.name, ExeExt)
if not existsFile(exeFile): if not existsFile(exeFile):
r.addResult(test, target, expected.outp, "executable not found", reExeNotFound) r.addResult(test, target, expected.output, "executable not found", reExeNotFound)
continue continue
let nodejs = if isJsTarget: findNodeJs() else: "" let nodejs = if isJsTarget: findNodeJs() else: ""
if isJsTarget and nodejs == "": if isJsTarget and nodejs == "":
r.addResult(test, target, expected.outp, "nodejs binary not in PATH", r.addResult(test, target, expected.output, "nodejs binary not in PATH",
reExeNotFound) reExeNotFound)
continue continue
var exeCmd: string var exeCmd: string
var args: seq[string] var args: seq[string]
if isJsTarget: if isJsTarget:
@ -426,55 +410,44 @@ proc testSpec(r: var TResults, test: TTest, target = targetC) =
args.add exeFile args.add exeFile
else: else:
exeCmd = exeFile exeCmd = exeFile
var (buf, exitCode) = execCmdEx2(exeCmd, args, options = {poStdErrToStdOut}, input = expected.input) var (buf, exitCode) = execCmdEx2(exeCmd, args, options = {poStdErrToStdOut}, input = expected.input)
# Treat all failure codes from nodejs as 1. Older versions of nodejs used # Treat all failure codes from nodejs as 1. Older versions of nodejs used
# to return other codes, but for us it is sufficient to know that it's not 0. # to return other codes, but for us it is sufficient to know that it's not 0.
if exitCode != 0: exitCode = 1 if exitCode != 0: exitCode = 1
let bufB =
let bufB = if expected.sortoutput: makeDeterministic(strip(buf.string)) if expected.sortoutput:
else: strip(buf.string) var x = splitLines(strip(buf.string))
let expectedOut = strip(expected.outp) sort(x, system.cmp)
join(x, "\n")
else:
strip(buf.string)
if exitCode != expected.exitCode: if exitCode != expected.exitCode:
r.addResult(test, target, "exitcode: " & $expected.exitCode, r.addResult(test, target, "exitcode: " & $expected.exitCode,
"exitcode: " & $exitCode & "\n\nOutput:\n" & "exitcode: " & $exitCode & "\n\nOutput:\n" &
bufB, reExitCodesDiffer) bufB, reExitCodesDiffer)
continue continue
if (expected.outputCheck == ocEqual and expected.output != bufB) or
if bufB != expectedOut and expected.action != actionRunNoSpec: (expected.outputCheck == ocSubstr and expected.output notin bufB):
if not (expected.substr and expectedOut in bufB):
given.err = reOutputsDiffer given.err = reOutputsDiffer
r.addResult(test, target, expected.outp, bufB, reOutputsDiffer) r.addResult(test, target, expected.output, bufB, reOutputsDiffer)
continue continue
compilerOutputTests(test, target, given, expected, r) compilerOutputTests(test, target, given, expected, r)
continue continue
of actionReject: of actionReject:
var given = callCompiler(expected.cmd, test.name, test.options, var given = callCompiler(expected.getCmd, test.name, test.options,
target) target)
cmpMsgs(r, expected, given, test, target) cmpMsgs(r, expected, given, test, target)
continue continue
proc testNoSpec(r: var TResults, test: TTest, target = targetC) = proc testC(r: var TResults, test: TTest, action: TTestAction) =
# does not extract the spec because the file is not supposed to have any
#let tname = test.name.addFileExt(".nim")
inc(r.total)
let given = callCompiler(cmdTemplate(), test.name, test.options, target)
r.addResult(test, target, "", given.msg, given.err)
if given.err == reSuccess: inc(r.passed)
proc testC(r: var TResults, test: TTest) =
# runs C code. Doesn't support any specs, just goes by exit code. # runs C code. Doesn't support any specs, just goes by exit code.
let tname = test.name.addFileExt(".c") let tname = test.name.addFileExt(".c")
inc(r.total) inc(r.total)
maybeStyledEcho "Processing ", fgCyan, extractFilename(tname) maybeStyledEcho "Processing ", fgCyan, extractFilename(tname)
var given = callCCompiler(cmdTemplate(), test.name & ".c", test.options, targetC) var given = callCCompiler(getCmd(TSpec()), test.name & ".c", test.options, targetC)
if given.err != reSuccess: if given.err != reSuccess:
r.addResult(test, targetC, "", given.msg, given.err) r.addResult(test, targetC, "", given.msg, given.err)
elif test.action == actionRun: elif action == actionRun:
let exeFile = changeFileExt(test.name, ExeExt) let exeFile = changeFileExt(test.name, ExeExt)
var (_, exitCode) = execCmdEx(exeFile, options = {poStdErrToStdOut, poUsePath}) var (_, exitCode) = execCmdEx(exeFile, options = {poStdErrToStdOut, poUsePath})
if exitCode != 0: given.err = reExitCodesDiffer if exitCode != 0: given.err = reExitCodesDiffer
@ -485,7 +458,6 @@ proc testExec(r: var TResults, test: TTest) =
inc(r.total) inc(r.total)
let (outp, errC) = execCmdEx(test.options.strip()) let (outp, errC) = execCmdEx(test.options.strip())
var given: TSpec var given: TSpec
specDefaults(given)
if errC == 0: if errC == 0:
given.err = reSuccess given.err = reSuccess
else: else:
@ -495,11 +467,12 @@ proc testExec(r: var TResults, test: TTest) =
if given.err == reSuccess: inc(r.passed) if given.err == reSuccess: inc(r.passed)
r.addResult(test, targetC, "", given.msg, given.err) r.addResult(test, targetC, "", given.msg, given.err)
proc makeTest(test, options: string, cat: Category, action = actionCompile, proc makeTest(test, options: string, cat: Category): TTest =
env: string = ""): TTest = result.cat = cat
# start with 'actionCompile', will be overwritten in the spec: result.name = test
result = TTest(cat: cat, name: test, options: options, result.options = options
action: action, startTime: epochTime()) result.spec = parseSpec(addFileExt(test, ".nim"))
result.startTime = epochTime()
when defined(windows): when defined(windows):
const const
@ -512,10 +485,7 @@ else:
include categories include categories
# proc runCaasTests(r: var TResults) = const testsDir = "tests" & DirSep
# for test, output, status, mode in caasTestsRunner():
# r.addResult(test, "", output & "-> " & $mode,
# if status: reSuccess else: reOutputsDiffer)
proc main() = proc main() =
os.putenv "NIMTEST_COLOR", "never" os.putenv "NIMTEST_COLOR", "never"
@ -524,10 +494,8 @@ proc main() =
backend.open() backend.open()
var optPrintResults = false var optPrintResults = false
var optFailing = false var optFailing = false
var targetsStr = "" var targetsStr = ""
var p = initOptParser() var p = initOptParser()
p.next() p.next()
while p.kind == cmdLongoption: while p.kind == cmdLongoption:
@ -538,26 +506,38 @@ proc main() =
of "targets": of "targets":
targetsStr = p.val.string targetsStr = p.val.string
targets = parseTargets(targetsStr) targets = parseTargets(targetsStr)
of "nim": compilerPrefix = p.val.string of "nim":
compilerPrefix = addFileExt(p.val.string, ExeExt)
of "directory": of "directory":
setCurrentDir(p.val.string) setCurrentDir(p.val.string)
of "colors": of "colors":
if p.val.string == "on": case p.val.string:
of "on":
useColors = true useColors = true
elif p.val.string == "off": of "off":
useColors = false useColors = false
else: else:
quit Usage quit Usage
of "backendlogging":
case p.val.string:
of "on":
backendLogging = true
of "off":
backendLogging = false
else:
quit Usage
else: else:
quit Usage quit Usage
p.next() p.next()
if p.kind != cmdArgument: quit Usage if p.kind != cmdArgument:
quit Usage
var action = p.key.string.normalize var action = p.key.string.normalize
p.next() p.next()
var r = initResults() var r = initResults()
case action case action
of "all": of "all":
let testsDir = "tests" & DirSep #processCategory(r, Category"megatest", p.cmdLineRest.string, testsDir, runJoinableTests = false)
var myself = quoteShell(findExe("testament" / "tester")) var myself = quoteShell(findExe("testament" / "tester"))
if targetsStr.len > 0: if targetsStr.len > 0:
myself &= " " & quoteShell("--targets:" & targetsStr) myself &= " " & quoteShell("--targets:" & targetsStr)
@ -570,14 +550,21 @@ proc main() =
assert testsDir.startsWith(testsDir) assert testsDir.startsWith(testsDir)
let cat = dir[testsDir.len .. ^1] let cat = dir[testsDir.len .. ^1]
if kind == pcDir and cat notin ["testdata", "nimcache"]: if kind == pcDir and cat notin ["testdata", "nimcache"]:
cmds.add(myself & " cat " & quoteShell(cat) & rest) cmds.add(myself & " pcat " & quoteShell(cat) & rest)
for cat in AdditionalCategories: for cat in AdditionalCategories:
cmds.add(myself & " cat " & quoteShell(cat) & rest) cmds.add(myself & " pcat " & quoteShell(cat) & rest)
quit osproc.execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams}) quit osproc.execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams})
of "c", "cat", "category": of "c", "cat", "category":
var cat = Category(p.key) var cat = Category(p.key)
p.next p.next
processCategory(r, cat, p.cmdLineRest.string) processCategory(r, cat, p.cmdLineRest.string, testsDir, runJoinableTests = true)
of "pcat":
# 'pcat' is used for running a category in parallel. Currently the only
# difference is that we don't want to run joinable tests here as they
# are covered by the 'megatest' category.
var cat = Category(p.key)
p.next
processCategory(r, cat, p.cmdLineRest.string, testsDir, runJoinableTests = false)
of "r", "run": of "r", "run":
let (dir, file) = splitPath(p.key.string) let (dir, file) = splitPath(p.key.string)
let (_, subdir) = splitPath(dir) let (_, subdir) = splitPath(dir)

View file

@ -1,7 +1,7 @@
discard """ discard """
errormsg: "undeclared identifier: 'undeclared'"
line: 8 line: 8
column: 7 column: 7
errormsg: "undeclared identifier: 'undeclared'"
""" """
# test should fail because the line directive is wrong # test should fail because the line directive is wrong

View file

@ -1,7 +1,7 @@
discard """ discard """
errormsg: "wrong error message"
line: 8 line: 8
column: 6 column: 6
errormsg: "wrong error message"
""" """
# test should fail because the line directive is wrong # test should fail because the line directive is wrong

View file

@ -1,6 +1,6 @@
discard """ discard """
file: "notthisfile.nim"
errmsg: "undeclared identifier: 'undefined'" errmsg: "undeclared identifier: 'undefined'"
file: "notthisfile.nim"
""" """
echo undefined echo undefined

View file

@ -1,7 +1,7 @@
discard """ discard """
errormsg: "undeclared identifier: 'undeclared'"
line: 9 line: 9
column: 6 column: 6
errormsg: "undeclared identifier: 'undeclared'"
""" """
# test should fail because the line directive is wrong # test should fail because the line directive is wrong

View file

@ -1,7 +1,7 @@
discard """ discard """
errormsg: "ambiguous identifier"
file: "tambsym.nim" file: "tambsym.nim"
line: 11 line: 11
errormsg: "ambiguous identifier"
""" """
# Test ambiguous symbols # Test ambiguous symbols
@ -11,5 +11,3 @@ var
v: TExport #ERROR_MSG ambiguous identifier v: TExport #ERROR_MSG ambiguous identifier
v = y v = y

View file

@ -1,5 +1,4 @@
discard """ discard """
file: "tambsym2.nim"
output: "7" output: "7"
""" """
# Test overloading of procs with locals # Test overloading of procs with locals
@ -20,5 +19,3 @@ m.len = 7
m.data = "1234" m.data = "1234"
x(m, 5) #OUT 7 x(m, 5) #OUT 7

View file

@ -1,7 +1,7 @@
discard """ discard """
errormsg: "ambiguous identifier"
file: "tambsym3.nim" file: "tambsym3.nim"
line: 11 line: 11
errormsg: "ambiguous identifier"
""" """
# Test ambiguous symbols # Test ambiguous symbols
@ -11,5 +11,3 @@ var
v = mDec #ERROR_MSG ambiguous identifier v = mDec #ERROR_MSG ambiguous identifier
writeLine(stdout, ord(v)) writeLine(stdout, ord(v))

View file

@ -1,5 +1,4 @@
discard """ discard """
file: "tambsys.nim"
output: "" output: ""
""" """
# Test ambiguous symbols # Test ambiguous symbols
@ -9,5 +8,3 @@ import mambsys1, mambsys2
var var
v: mambsys1.TExport v: mambsys1.TExport
mambsys2.foo(3) #OUT mambsys2.foo(3) #OUT

View file

@ -1,7 +1,5 @@
discard """ discard """
file: "tarray.nim" output: '''
output:
'''
[4, 5, 6] [4, 5, 6]
[16, 25, 36] [16, 25, 36]
@ -30,6 +28,7 @@ dflfdjkl__abcdefgasfsgdfgsgdfggsdfasdfsafewfkljdsfajsdf
kgdchlfniambejop kgdchlfniambejop
fjpmholcibdgeakn fjpmholcibdgeakn
''' '''
joinable: false
""" """
block tarray: block tarray:
@ -358,7 +357,7 @@ block troofregression:
echo testStr[testStr.len - 8 .. testStr.len - 1] & "__" & testStr[0 .. testStr.len - pred(rot)] echo testStr[testStr.len - 8 .. testStr.len - 1] & "__" & testStr[0 .. testStr.len - pred(rot)]
var var
instructions = readFile(getAppDir() / "troofregression2.txt").split(',') instructions = readFile(parentDir(currentSourcePath) / "troofregression2.txt").split(',')
programs = "abcdefghijklmnop" programs = "abcdefghijklmnop"
proc dance(dancers: string): string = proc dance(dancers: string): string =

View file

@ -1,7 +1,7 @@
discard """ discard """
errormsg: "invalid order in array constructor"
file: "tarraycons.nim" file: "tarraycons.nim"
line: 14 line: 14
errormsg: "invalid order in array constructor"
""" """
type type
@ -19,6 +19,3 @@ const
] ]
echo myMapping[eC][1] echo myMapping[eC][1]

View file

@ -1,7 +1,7 @@
discard """ discard """
errormsg: "type mismatch: got <ptr Hard[system.string]> but expected 'Book[system.string]'"
file: "tarraycons_ptr_generic2.nim" file: "tarraycons_ptr_generic2.nim"
line: 17 line: 17
errormsg: "type mismatch: got <ptr Hard[system.string]> but expected 'Book[system.string]'"
""" """
type type

View file

@ -1,5 +1,4 @@
discard """ discard """
file: "tassert.nim"
outputsub: "assertion failure!this shall be always written" outputsub: "assertion failure!this shall be always written"
exitcode: "1" exitcode: "1"
""" """
@ -19,5 +18,3 @@ finally:
system.write(stdout, "this shall be always written") system.write(stdout, "this shall be always written")
assert(false) #OUT assertion failure!this shall be always written assert(false) #OUT assertion failure!this shall be always written

View file

@ -1,5 +1,4 @@
discard """ discard """
file: "tassign.nim"
output: output:
''' '''
TEMP=C:\Programs\xyz\bin TEMP=C:\Programs\xyz\bin

View file

@ -1,7 +1,7 @@
discard """ discard """
file: "tvariantasgn.nim"
output: "came here" output: "came here"
""" """
#BUG #BUG
type type
TAnyKind = enum TAnyKind = enum
@ -26,5 +26,3 @@ nr.intVal = 78
# s = nr # works # s = nr # works
nr = s # fails! nr = s # fails!
echo "came here" echo "came here"

View file

@ -9,6 +9,6 @@ import asyncdispatch
proc testCallback() = proc testCallback() =
echo "testCallback()" echo "testCallback()"
when isMainModule: when true:
callSoon(testCallback) callSoon(testCallback)
poll() poll()

View file

@ -1,7 +1,3 @@
discard """
file: "t7758.nim"
exitcode: 0
"""
import asyncdispatch import asyncdispatch
proc task() {.async.} = proc task() {.async.} =

View file

@ -1,5 +1,4 @@
discard """ discard """
file: "tasyncRecvLine.nim"
output: ''' output: '''
Hello World Hello World
Hello World Hello World

View file

@ -1,7 +1,7 @@
discard """ discard """
errormsg: "'anotherGCSafeAsyncProcIter' is not GC-safe as it calls 'asyncGCUnsafeProc'"
cmd: "nim c --threads:on $file" cmd: "nim c --threads:on $file"
file: "asyncmacro.nim" file: "asyncmacro.nim"
errormsg: "'anotherGCSafeAsyncProcIter' is not GC-safe as it calls 'asyncGCUnsafeProc'"
""" """
assert compileOption("threads"), "this test will not do anything useful without --threads:on" assert compileOption("threads"), "this test will not do anything useful without --threads:on"

View file

@ -1,5 +1,4 @@
discard """ discard """
file: "tasyncall.nim"
exitcode: 0 exitcode: 0
""" """
import times, sequtils import times, sequtils

View file

@ -1,5 +1,4 @@
discard """ discard """
file: "tasyncawait.nim"
output: "5000" output: "5000"
""" """
import asyncdispatch, nativesockets, net, strutils, os import asyncdispatch, nativesockets, net, strutils, os

View file

@ -1,7 +1,6 @@
discard """ discard """
file: "tasyncconnect.nim"
exitcode: 1
outputsub: "Error: unhandled exception: Connection refused" outputsub: "Error: unhandled exception: Connection refused"
exitcode: 1
""" """
import import

View file

@ -1,5 +1,4 @@
discard """ discard """
file: "tasyncdial.nim"
output: ''' output: '''
OK AF_INET OK AF_INET
OK AF_INET6 OK AF_INET6

View file

@ -1,7 +1,6 @@
discard """ discard """
file: "tasyncexceptions.nim"
exitcode: 1
outputsub: "Error: unhandled exception: foobar" outputsub: "Error: unhandled exception: foobar"
exitcode: 1
""" """
import asyncdispatch import asyncdispatch
@ -28,7 +27,7 @@ proc serve() {.async.} =
var fut = await accept() var fut = await accept()
await processClient(fut) await processClient(fut)
when isMainModule: when true:
proc main = proc main =
var fut = serve() var fut = serve()
fut.callback = fut.callback =

View file

@ -1,10 +1,9 @@
discard """ discard """
output: '''13 output: '''
13
hello humans! hello humans!
13 13
''' '''
file: "tasyncfile.nim"
exitcode: 0
""" """
import asyncfile, asyncdispatch, os import asyncfile, asyncdispatch, os
@ -54,8 +53,7 @@ proc main() {.async.} =
# Issue #7347 # Issue #7347
block: block:
let appDir = getAppDir() var file = openAsync( parentDir(currentSourcePath) / "hello.txt")
var file = openAsync(appDir & DirSep & "hello.txt")
echo file.getFileSize() echo file.getFileSize()
echo await file.readAll() echo await file.readAll()
echo file.getFilePos() echo file.getFilePos()

View file

@ -1,5 +1,4 @@
discard """ discard """
file: "tasyncrecursion.nim"
output: "50005000" output: "50005000"
""" """
import asyncdispatch import asyncdispatch
@ -16,7 +15,7 @@ proc asyncRecursionTest*(): Future[int] {.async.} =
inc(result, await asyncRecursionCycle(i)) inc(result, await asyncRecursionCycle(i))
inc(i) inc(i)
when isMainModule: when true:
setGlobalDispatcher(newDispatcher()) setGlobalDispatcher(newDispatcher())
var i = waitFor asyncRecursionTest() var i = waitFor asyncRecursionTest()
echo i echo i

View file

@ -1,5 +1,4 @@
discard """ discard """
file: "tasyncsend4754.nim"
output: "Finished" output: "Finished"
""" """

View file

@ -1,5 +1,4 @@
discard """ discard """
file: "tasyncssl.nim"
cmd: "nim $target --hints:on --define:ssl $options $file" cmd: "nim $target --hints:on --define:ssl $options $file"
output: "500" output: "500"
disabled: "windows" disabled: "windows"

View file

@ -1,6 +1,4 @@
discard """ discard """
file: "tasynctry.nim"
exitcode: 0
output: ''' output: '''
Generic except: Test Generic except: Test
Specific except Specific except

View file

@ -1,6 +1,4 @@
discard """ discard """
file: "tawaitsemantics.nim"
exitcode: 0
output: ''' output: '''
Error can be caught using yield Error can be caught using yield
Infix `or` raises Infix `or` raises

View file

@ -1,6 +1,4 @@
discard """ discard """
file: "tfuturestream.nim"
exitcode: 0
output: ''' output: '''
0 0
1 1

View file

@ -1,5 +1,4 @@
discard """ discard """
file: "tioselectors.nim"
output: "All tests passed!" output: "All tests passed!"
""" """
import selectors import selectors

View file

@ -1,5 +1,4 @@
discard """ discard """
file: "tnewasyncudp.nim"
output: "5000" output: "5000"
""" """
import asyncdispatch, nativesockets, net, strutils, os import asyncdispatch, nativesockets, net, strutils, os

View file

@ -1,6 +1,4 @@
discard """ discard """
file: "tpendingcheck.nim"
exitcode: 0
output: "" output: ""
""" """
@ -18,4 +16,3 @@ while not f.finished:
f.read f.read
doAssert(not hasPendingOperations()) doAssert(not hasPendingOperations())

View file

@ -1,5 +1,4 @@
discard """ discard """
file: "twinasyncrw.nim"
output: "5000" output: "5000"
""" """
when defined(windows): when defined(windows):

View file

@ -1,47 +0,0 @@
#
#
# Nim Benchmark tool
# (c) Copyright 2012 Dominik Picheta
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This program runs benchmarks
import osproc, os, times, json
type
TBenchResult = tuple[file: string, success: bool, time: float]
proc compileBench(file: string) =
## Compiles ``file``.
doAssert(execCmdEx("nim c -d:release " & file).exitCode == QuitSuccess)
proc runBench(file: string): TBenchResult =
## Runs ``file`` and returns info on how long it took to run.
var start = epochTime()
if execCmdEx(file.addFileExt(ExeExt)).exitCode == QuitSuccess:
var t = epochTime() - start
result = (file, true, t)
else: result = (file, false, -1.0)
proc genOutput(benches: seq[TBenchResult]): PJsonNode =
result = newJObject()
for i in benches:
if i.success:
result[i.file.extractFilename] = newJFloat(i.time)
else:
result[i.file.extractFilename] = newJNull()
proc doBench(): seq[TBenchResult] =
result = @[]
for i in walkFiles("tests/benchmarks/*.nim"):
echo(i.extractFilename)
compileBench(i)
result.add(runBench(i))
when isMainModule:
var b = doBench()
var output = genOutput(b)
writeFile("benchmarkResults.json", pretty(output))

View file

@ -1,69 +0,0 @@
import os
import strutils
proc fannkuch(n: int): int =
var
count: seq[int]
maxFlips = 0
m = n-1
r = n
check = 0
perm1: seq[int]
perm: seq[int]
newSeq(count, n+1)
newSeq(perm1, n)
newSeq(perm, n)
for i in 0 .. n-1:
count[i] = i+1
perm1[i] = i
perm[i] = i
count[n] = n+1
while true:
if check < 30:
for i in items(perm1):
write(stdout, $(i+1))
echo("")
inc(check)
while r != 1:
count[r-1] = r
dec (r)
if perm1[0] != 0 and perm1[m] != m:
# perm = perm1
# The above line is between 3 and 4 times slower than the loop below!
for i in 0 .. n-1:
perm[i] = perm1[i]
var flipsCount = 0
var k = perm[0]
while k != 0:
for i in 0 .. (k div 2):
swap(perm[i], perm[k-i])
inc(flipsCount)
k = perm[0]
if flipsCount > maxFlips:
maxFlips = flipsCount
block makePerm:
while r != n:
var tmp = perm1[0]
# # perm1.delete (0)
# # perm1.insert (tmp, r)
# # The above is about twice as slow as the following:
# moveMem (addr (perm1[0]), addr (perm1[1]), r * sizeof (int))
# The call to moveMem is about 50% slower than the loop below!
for i in 0 .. r-1:
perm1[i] = perm1[i+1]
perm1[r] = tmp
dec(count[r])
if count[r] > 0:
break makePerm
inc(r)
return maxFlips
var n = 10
echo("Pfannkuchen(" & $n & ") = " & $fannkuch(n))

View file

@ -1,54 +0,0 @@
import os
import strutils
# Generate some pseudo-random data
var seed: tuple[s1, s2, s3: int32] = (2'i32, 8'i32, 16'i32)
proc random(): int32 =
seed = (((((((seed[0] and 0x0007_FFFF'i32) shl 13'i32) xor seed[0]) shr
19'i32) and 0x0000_1FFF'i32) xor
((seed[0] and 0x000F_FFFE'i32) shl 12'i32)),
((((((seed[1] and 0x3FFF_FFFF'i32) shl 2'i32) xor seed[1]) shr
25'i32) and 0x0000_007F'i32) xor
((seed[1] and 0x0FFF_FFF8'i32) shl 4'i32)),
((((((seed[2] and 0x1FFF_FFFF'i32) shl 3'i32) xor seed[2]) shr
11'i32) and 0x001F_FFFF'i32) xor
((seed[2] and 0x0000_7FF0'i32) shl 17'i32)))
return seed[0] xor seed[1] xor seed[2]
var n = 9999999
var data: seq[int32]
newSeq(data, n)
for i in 0 .. data.high():
data[i] = random()
proc `$`(d: seq[int32]): string =
result = "[ "
for i in items(d):
result.addSep(", ", 2)
result.add($(i and 0xFFFF_FFFF'i64))
result.add(" ]")
# Sort the data
proc sort(start, stop: int) =
if stop <= start+1:
return
var j = start
for i in start..stop-2:
if data[i] <% data[stop-1]:
swap(data[i], data[j])
inc(j)
swap(data[j], data[stop-1])
sort(start, j)
sort(j+1, stop)
sort(0, data.len)
echo(data[n div 2 - 1] and 0xFFFF_FFFF'i64, ", ",
data[n div 2] and 0xFFFF_FFFF'i64, ", ",
data[n div 2 + 1] and 0xFFFF_FFFF'i64)

View file

@ -1,7 +1,5 @@
discard """ discard """
file: "tbind.nim" output: '''
output:
'''
3 3
1 1
1 1

View file

@ -1,7 +1,7 @@
discard """ discard """
errormsg: "ambiguous call"
file: "tbind2.nim" file: "tbind2.nim"
line: 12 line: 12
errormsg: "ambiguous call"
""" """
# Test the new ``bind`` keyword for templates # Test the new ``bind`` keyword for templates
@ -12,6 +12,3 @@ template tempBind(x, y): untyped =
(bind p1(x, y)) #ERROR_MSG ambiguous call (bind p1(x, y)) #ERROR_MSG ambiguous call
echo tempBind(1'i8, 2'i8) echo tempBind(1'i8, 2'i8)

View file

@ -74,7 +74,7 @@ proc propertyBind*[T](p1: var TProperty[T], p2: var TProperty[T]) =
proc `->`[T](p1: var TProperty[T], p2: var TProperty[T]) = proc `->`[T](p1: var TProperty[T], p2: var TProperty[T]) =
propertyBind(p2,p1) propertyBind(p2,p1)
when isMainModule: when true:
# Initial value testing # Initial value testing
var myProp = newProperty(5) var myProp = newProperty(5)

View file

@ -1,6 +1,6 @@
discard """ discard """
line: 10
errormsg: "type mismatch: got <type float, string>" errormsg: "type mismatch: got <type float, string>"
line: 10
""" """
proc foo(T: typedesc; some: T) = proc foo(T: typedesc; some: T) =
@ -8,4 +8,3 @@ proc foo(T: typedesc; some: T) =
foo int, 4 foo int, 4
foo float, "bad" foo float, "bad"

View file

@ -1,6 +1,6 @@
discard """ discard """
line: 18
errormsg: "type mismatch: got <proc (s: TScgi: ScgiState or AsyncScgiState) | proc (client: AsyncSocket, headers: StringTableRef, input: string){.noSideEffect, gcsafe, locks: 0.}>" errormsg: "type mismatch: got <proc (s: TScgi: ScgiState or AsyncScgiState) | proc (client: AsyncSocket, headers: StringTableRef, input: string){.noSideEffect, gcsafe, locks: 0.}>"
line: 18
""" """
#bug #442 #bug #442

View file

@ -1,6 +1,6 @@
discard """ discard """
line: 11
errormsg: "no symbol to borrow from found" errormsg: "no symbol to borrow from found"
line: 11
""" """
# bug #516 # bug #516
@ -14,4 +14,3 @@ var
d, e: TAtom d, e: TAtom
echo( $(d == e) ) echo( $(d == e) )

View file

@ -1,29 +0,0 @@
# Tries to test the full ownership path generated by idetools.
proc lev1(t1: string) =
var temp = t1
for i in 0..len(temp)-1:
temp[i] = chr(int(temp[i]) + 1)
proc lev2(t2: string) =
var temp = t2
for i in 0..len(temp)-1:
temp[i] = chr(int(temp[i]) + 1)
proc lev3(t3: string) =
var temp = t3
for i in 0..len(temp)-1:
temp[i] = chr(int(temp[i]) + 1)
proc lev4(t4: string) =
var temp = t4
for i in 0..len(temp)-1:
temp[i] = chr(int(temp[i]) + 1)
echo temp & "(lev4)"
lev4(temp & "(lev3)")
lev3(temp & "(lev2)")
lev2(temp & "(lev1)")
when isMainModule:
lev1("abcd")

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