Merge branch 'devel' into araq

This commit is contained in:
Andreas Rumpf 2017-11-02 10:46:30 +01:00
commit 1eaeccc15d
616 changed files with 73547 additions and 36764 deletions

View file

@ -16,10 +16,12 @@ addons:
- libcurl4-openssl-dev - libcurl4-openssl-dev
- libsdl1.2-dev - libsdl1.2-dev
- libgc-dev - libgc-dev
- libsfml-dev
before_install: before_install:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install boehmgc; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install boehmgc; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install sfml; fi
before_script: before_script:
- set -e - set -e

View file

@ -5,3 +5,39 @@
- Removed basic2d/basic3d out of the stdlib and into Nimble packages. - Removed basic2d/basic3d out of the stdlib and into Nimble packages.
These packages deprecated however, use the ``glm``, ``arraymancer``, ``neo`` These packages deprecated however, use the ``glm``, ``arraymancer``, ``neo``
or another package. or another package.
- Arrays of char cannot be converted to ``cstring`` anymore, pointers to
arrays of char can! This means ``$`` for arrays can finally exist
in ``system.nim`` and do the right thing.
- ``echo`` now works with strings that contain ``\0`` (the binary zero is not
shown) and ``nil`` strings are equal to empty strings.
- JSON: Deprecated `getBVal`, `getFNum`, and `getNum` in favour to
`getBool`, `getFloat`, `getBiggestInt`. Also `getInt` procedure was added.
- `reExtended` is no longer default for the `re` constructor in the `re`
module.
- The overloading rules changed slightly so that constrained generics are
preferred over unconstrained generics. (Bug #6526)
- It is now possible to forward declare object types so that mutually
recursive types can be created across module boundaries. See
[package level objects](https://nim-lang.org/docs/manual.html#package-level-objects)
for more information.
- The **unary** ``<`` is now deprecated, for ``.. <`` use ``..<`` for other usages
use the ``pred`` proc.
- We changed how array accesses "from backwards" like ``a[^1]`` or ``a[0..^1]`` are
implemented. These are now implemented purely in ``system.nim`` without compiler
support. There is a new "heterogenous" slice type ``system.HSlice`` that takes 2
generic parameters which can be ``BackwardsIndex`` indices. ``BackwardsIndex`` is
produced by ``system.^``.
This means if you overload ``[]`` or ``[]=`` you need to ensure they also work
with ``system.BackwardsIndex`` (if applicable for the accessors).
- ``mod`` and bitwise ``and`` do not produce ``range`` subtypes anymore. This
turned out to be more harmful than helpful and the language is simpler
without this special typing rule.
- Added ``algorithm.rotateLeft``.
- ``rationals.toRational`` now uses an algorithm based on continued fractions.
This means its results are more precise and it can't run into an infinite loop
anymore.
- Added ``typetraits.$`` as an alias for ``typetraits.name``.
- ``os.getEnv`` now takes an optional ``default`` parameter that tells ``getEnv``
what to return if the environment variable does not exist.
- Removed PDCurses wrapper from the stdlib and published it as a separate
Nimble package.

View file

@ -874,7 +874,8 @@ type
# mean that there is no destructor. # mean that there is no destructor.
# see instantiateDestructor in semdestruct.nim # see instantiateDestructor in semdestruct.nim
deepCopy*: PSym # overriden 'deepCopy' operation deepCopy*: PSym # overriden 'deepCopy' operation
assignment*: PSym # overriden '=' operator assignment*: PSym # overriden '=' operation
sink*: PSym # overriden '=sink' operation
methods*: seq[(int,PSym)] # attached methods methods*: seq[(int,PSym)] # attached methods
size*: BiggestInt # the size of the type in bytes size*: BiggestInt # the size of the type in bytes
# -1 means that the size is unkwown # -1 means that the size is unkwown
@ -1019,16 +1020,11 @@ proc add*(father, son: PNode) =
type Indexable = PNode | PType type Indexable = PNode | PType
template `[]`*(n: Indexable, i: int): Indexable = template `[]`*(n: Indexable, i: int): Indexable = n.sons[i]
n.sons[i] template `[]=`*(n: Indexable, i: int; x: Indexable) = n.sons[i] = x
template `-|`*(b, s: untyped): untyped = template `[]`*(n: Indexable, i: BackwardsIndex): Indexable = n[n.len - i.int]
(if b >= 0: b else: s.len + b) template `[]=`*(n: Indexable, i: BackwardsIndex; x: Indexable) = n[n.len - i.int] = x
# son access operators with support for negative indices
template `{}`*(n: Indexable, i: int): untyped = n[i -| n]
template `{}=`*(n: Indexable, i: int, s: Indexable) =
n.sons[i -| n] = s
when defined(useNodeIds): when defined(useNodeIds):
const nodeIdToDebug* = -1 # 299750 # 300761 #300863 # 300879 const nodeIdToDebug* = -1 # 299750 # 300761 #300863 # 300879
@ -1050,6 +1046,8 @@ proc newNode*(kind: TNodeKind): PNode =
proc newTree*(kind: TNodeKind; children: varargs[PNode]): PNode = proc newTree*(kind: TNodeKind; children: varargs[PNode]): PNode =
result = newNode(kind) result = newNode(kind)
if children.len > 0:
result.info = children[0].info
result.sons = @children result.sons = @children
proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode = proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
@ -1293,6 +1291,7 @@ proc assignType*(dest, src: PType) =
dest.align = src.align dest.align = src.align
dest.destructor = src.destructor dest.destructor = src.destructor
dest.deepCopy = src.deepCopy dest.deepCopy = src.deepCopy
dest.sink = src.sink
dest.assignment = src.assignment dest.assignment = src.assignment
dest.lockLevel = src.lockLevel dest.lockLevel = src.lockLevel
# this fixes 'type TLock = TSysLock': # this fixes 'type TLock = TSysLock':
@ -1391,6 +1390,14 @@ proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
result = t result = t
while result.kind in kinds: result = lastSon(result) while result.kind in kinds: result = lastSon(result)
proc skipTypes*(t: PType, kinds: TTypeKinds; maxIters: int): PType =
result = t
var i = maxIters
while result.kind in kinds:
result = lastSon(result)
dec i
if i == 0: return nil
proc skipTypesOrNil*(t: PType, kinds: TTypeKinds): PType = proc skipTypesOrNil*(t: PType, kinds: TTypeKinds): PType =
## same as skipTypes but handles 'nil' ## same as skipTypes but handles 'nil'
result = t result = t
@ -1403,7 +1410,7 @@ proc isGCedMem*(t: PType): bool {.inline.} =
t.kind == tyProc and t.callConv == ccClosure t.kind == tyProc and t.callConv == ccClosure
proc propagateToOwner*(owner, elem: PType) = proc propagateToOwner*(owner, elem: PType) =
const HaveTheirOwnEmpty = {tySequence, tySet, tyPtr, tyRef, tyProc} const HaveTheirOwnEmpty = {tySequence, tyOpt, tySet, tyPtr, tyRef, tyProc}
owner.flags = owner.flags + (elem.flags * {tfHasMeta}) owner.flags = owner.flags + (elem.flags * {tfHasMeta})
if tfNotNil in elem.flags: if tfNotNil in elem.flags:
if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvocation}: if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvocation}:
@ -1419,9 +1426,9 @@ proc propagateToOwner*(owner, elem: PType) =
owner.flags.incl tfHasMeta owner.flags.incl tfHasMeta
if tfHasAsgn in elem.flags: if tfHasAsgn in elem.flags:
let o2 = elem.skipTypes({tyGenericInst, tyAlias}) let o2 = owner.skipTypes({tyGenericInst, tyAlias})
if o2.kind in {tyTuple, tyObject, tyArray, if o2.kind in {tyTuple, tyObject, tyArray,
tySequence, tySet, tyDistinct}: tySequence, tyOpt, tySet, tyDistinct}:
o2.flags.incl tfHasAsgn o2.flags.incl tfHasAsgn
owner.flags.incl tfHasAsgn owner.flags.incl tfHasAsgn
@ -1662,3 +1669,10 @@ when false:
if n.isNil: return true if n.isNil: return true
for i in 0 ..< n.safeLen: for i in 0 ..< n.safeLen:
if n[i].containsNil: return true if n[i].containsNil: return true
template hasDestructor*(t: PType): bool = tfHasAsgn in t.flags
template incompleteType*(t: PType): bool =
t.sym != nil and {sfForward, sfNoForward} * t.sym.flags == {sfForward}
template typeCompleted*(s: PSym) =
incl s.flags, sfNoForward

View file

@ -270,10 +270,10 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
addrLoc(dest), addrLoc(src), rdLoc(dest)) addrLoc(dest), addrLoc(src), rdLoc(dest))
else: else:
linefmt(p, cpsStmts, "#genericShallowAssign((void*)$1, (void*)$2, $3);$n", linefmt(p, cpsStmts, "#genericShallowAssign((void*)$1, (void*)$2, $3);$n",
addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t)) addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t, dest.lode.info))
else: else:
linefmt(p, cpsStmts, "#genericAssign((void*)$1, (void*)$2, $3);$n", linefmt(p, cpsStmts, "#genericAssign((void*)$1, (void*)$2, $3);$n",
addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t)) addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t, dest.lode.info))
proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
# This function replaces all other methods for generating # This function replaces all other methods for generating
@ -291,7 +291,8 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
genRefAssign(p, dest, src, flags) genRefAssign(p, dest, src, flags)
else: else:
linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n", linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n",
addrLoc(dest), rdLoc(src), genTypeInfo(p.module, dest.t)) addrLoc(dest), rdLoc(src),
genTypeInfo(p.module, dest.t, dest.lode.info))
of tyString: of tyString:
if (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode): if (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode):
genRefAssign(p, dest, src, flags) genRefAssign(p, dest, src, flags)
@ -352,7 +353,8 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
if needsComplexAssignment(dest.t): if needsComplexAssignment(dest.t):
linefmt(p, cpsStmts, # XXX: is this correct for arrays? linefmt(p, cpsStmts, # XXX: is this correct for arrays?
"#genericAssignOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n", "#genericAssignOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n",
addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t)) addrLoc(dest), addrLoc(src),
genTypeInfo(p.module, dest.t, dest.lode.info))
else: else:
useStringh(p.module) useStringh(p.module)
linefmt(p, cpsStmts, linefmt(p, cpsStmts,
@ -393,14 +395,17 @@ proc genDeepCopy(p: BProc; dest, src: TLoc) =
of tyPtr, tyRef, tyProc, tyTuple, tyObject, tyArray: of tyPtr, tyRef, tyProc, tyTuple, tyObject, tyArray:
# XXX optimize this # XXX optimize this
linefmt(p, cpsStmts, "#genericDeepCopy((void*)$1, (void*)$2, $3);$n", linefmt(p, cpsStmts, "#genericDeepCopy((void*)$1, (void*)$2, $3);$n",
addrLoc(dest), addrLocOrTemp(src), genTypeInfo(p.module, dest.t)) addrLoc(dest), addrLocOrTemp(src),
genTypeInfo(p.module, dest.t, dest.lode.info))
of tySequence, tyString: of tySequence, tyString:
linefmt(p, cpsStmts, "#genericSeqDeepCopy($1, $2, $3);$n", linefmt(p, cpsStmts, "#genericSeqDeepCopy($1, $2, $3);$n",
addrLoc(dest), rdLoc(src), genTypeInfo(p.module, dest.t)) addrLoc(dest), rdLoc(src),
genTypeInfo(p.module, dest.t, dest.lode.info))
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
linefmt(p, cpsStmts, linefmt(p, cpsStmts,
"#genericDeepCopyOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n", "#genericDeepCopyOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n",
addrLoc(dest), addrLocOrTemp(src), genTypeInfo(p.module, dest.t)) addrLoc(dest), addrLocOrTemp(src),
genTypeInfo(p.module, dest.t, dest.lode.info))
of tySet: of tySet:
if mapType(ty) == ctArray: if mapType(ty) == ctArray:
useStringh(p.module) useStringh(p.module)
@ -965,6 +970,8 @@ proc genEcho(p: BProc, n: PNode) =
# this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)`` # this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)``
# is threadsafe. # is threadsafe.
internalAssert n.kind == nkBracket internalAssert n.kind == nkBracket
if platform.targetOS == osGenode:
# bypass libc and print directly to the Genode LOG session
var args: Rope = nil var args: Rope = nil
var a: TLoc var a: TLoc
for i in countup(0, n.len-1): for i in countup(0, n.len-1):
@ -973,11 +980,16 @@ proc genEcho(p: BProc, n: PNode) =
else: else:
initLocExpr(p, n.sons[i], a) initLocExpr(p, n.sons[i], a)
addf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)]) addf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)])
if platform.targetOS == osGenode:
# bypass libc and print directly to the Genode LOG session
p.module.includeHeader("<base/log.h>") p.module.includeHeader("<base/log.h>")
linefmt(p, cpsStmts, """Genode::log(""$1);$n""", args) linefmt(p, cpsStmts, """Genode::log(""$1);$n""", args)
else: else:
if n.len == 0:
linefmt(p, cpsStmts, "#echoBinSafe(NIM_NIL, $1);$n", n.len.rope)
else:
var a: TLoc
initLocExpr(p, n, a)
linefmt(p, cpsStmts, "#echoBinSafe($1, $2);$n", a.rdLoc, n.len.rope)
when false:
p.module.includeHeader("<stdio.h>") p.module.includeHeader("<stdio.h>")
linefmt(p, cpsStmts, "printf($1$2);$n", linefmt(p, cpsStmts, "printf($1$2);$n",
makeCString(repeat("%s", n.len) & tnl), args) makeCString(repeat("%s", n.len) & tnl), args)
@ -1094,7 +1106,8 @@ proc genReset(p: BProc, n: PNode) =
var a: TLoc var a: TLoc
initLocExpr(p, n.sons[1], a) initLocExpr(p, n.sons[1], a)
linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n", linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
addrLoc(a), genTypeInfo(p.module, skipTypes(a.t, {tyVar}))) addrLoc(a),
genTypeInfo(p.module, skipTypes(a.t, {tyVar}), n.info))
proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) = proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) =
var sizeExpr = sizeExpr var sizeExpr = sizeExpr
@ -1108,7 +1121,7 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) =
sizeExpr = "sizeof($1)" % sizeExpr = "sizeof($1)" %
[getTypeDesc(p.module, bt)] [getTypeDesc(p.module, bt)]
let args = [getTypeDesc(p.module, typ), let args = [getTypeDesc(p.module, typ),
genTypeInfo(p.module, typ), genTypeInfo(p.module, typ, a.lode.info),
sizeExpr] sizeExpr]
if a.storage == OnHeap and usesNativeGC(): if a.storage == OnHeap and usesNativeGC():
# use newObjRC1 as an optimization # use newObjRC1 as an optimization
@ -1138,7 +1151,7 @@ proc genNew(p: BProc, e: PNode) =
proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope) = proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope) =
let seqtype = skipTypes(dest.t, abstractVarRange) let seqtype = skipTypes(dest.t, abstractVarRange)
let args = [getTypeDesc(p.module, seqtype), let args = [getTypeDesc(p.module, seqtype),
genTypeInfo(p.module, seqtype), length] genTypeInfo(p.module, seqtype, dest.lode.info), length]
var call: TLoc var call: TLoc
initLoc(call, locExpr, dest.lode, OnHeap) initLoc(call, locExpr, dest.lode, OnHeap)
if dest.storage == OnHeap and usesNativeGC(): if dest.storage == OnHeap and usesNativeGC():
@ -1166,7 +1179,7 @@ proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) =
putIntoDest(p, d, e, ropecg(p.module, putIntoDest(p, d, e, ropecg(p.module,
"($1)#nimNewSeqOfCap($2, $3)", [ "($1)#nimNewSeqOfCap($2, $3)", [
getTypeDesc(p.module, seqtype), getTypeDesc(p.module, seqtype),
genTypeInfo(p.module, seqtype), a.rdLoc])) genTypeInfo(p.module, seqtype, e.info), a.rdLoc]))
gcUsage(e) gcUsage(e)
proc genConstExpr(p: BProc, n: PNode): Rope proc genConstExpr(p: BProc, n: PNode): Rope
@ -1248,10 +1261,11 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) =
if d.k == locNone: if d.k == locNone:
getTemp(p, n.typ, d) getTemp(p, n.typ, d)
# generate call to newSeq before adding the elements per hand: # generate call to newSeq before adding the elements per hand:
var L = int(lengthOrd(n.sons[1].typ)) let L = int(lengthOrd(n.sons[1].typ))
genNewSeqAux(p, d, intLiteral(L)) genNewSeqAux(p, d, intLiteral(L))
initLocExpr(p, n.sons[1], a) initLocExpr(p, n.sons[1], a)
# bug #5007; do not produce excessive C source code:
if L < 10:
for i in countup(0, L - 1): for i in countup(0, L - 1):
initLoc(elem, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap) initLoc(elem, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap)
elem.r = rfmt(nil, "$1->data[$2]", rdLoc(d), intLiteral(i)) elem.r = rfmt(nil, "$1->data[$2]", rdLoc(d), intLiteral(i))
@ -1259,6 +1273,19 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) =
initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.sons[1].typ, abstractInst)), a.storage) initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.sons[1].typ, abstractInst)), a.storage)
arr.r = rfmt(nil, "$1[$2]", rdLoc(a), intLiteral(i)) arr.r = rfmt(nil, "$1[$2]", rdLoc(a), intLiteral(i))
genAssignment(p, elem, arr, {afDestIsNil, needToCopy}) genAssignment(p, elem, arr, {afDestIsNil, needToCopy})
else:
var i: TLoc
getTemp(p, getSysType(tyInt), i)
let oldCode = p.s(cpsStmts)
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", i.r, L.rope)
initLoc(elem, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap)
elem.r = rfmt(nil, "$1->data[$2]", rdLoc(d), rdLoc(i))
elem.storage = OnHeap # we know that sequences are on the heap
initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.sons[1].typ, abstractInst)), a.storage)
arr.r = rfmt(nil, "$1[$2]", rdLoc(a), rdLoc(i))
genAssignment(p, elem, arr, {afDestIsNil, needToCopy})
lineF(p, cpsStmts, "}$n", [])
proc genNewFinalize(p: BProc, e: PNode) = proc genNewFinalize(p: BProc, e: PNode) =
var var
@ -1269,7 +1296,7 @@ proc genNewFinalize(p: BProc, e: PNode) =
initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[1], a)
initLocExpr(p, e.sons[2], f) initLocExpr(p, e.sons[2], f)
initLoc(b, locExpr, a.lode, OnHeap) initLoc(b, locExpr, a.lode, OnHeap)
ti = genTypeInfo(p.module, refType) ti = genTypeInfo(p.module, refType, e.info)
addf(p.module.s[cfsTypeInit3], "$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)]) addf(p.module.s[cfsTypeInit3], "$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)])
b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [ b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [
getTypeDesc(p.module, refType), getTypeDesc(p.module, refType),
@ -1279,10 +1306,10 @@ proc genNewFinalize(p: BProc, e: PNode) =
genObjectInit(p, cpsStmts, bt, a, false) genObjectInit(p, cpsStmts, bt, a, false)
gcUsage(e) gcUsage(e)
proc genOfHelper(p: BProc; dest: PType; a: Rope): Rope = proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo): Rope =
# unfortunately 'genTypeInfo' sets tfObjHasKids as a side effect, so we # unfortunately 'genTypeInfo' sets tfObjHasKids as a side effect, so we
# have to call it here first: # have to call it here first:
let ti = genTypeInfo(p.module, dest) let ti = genTypeInfo(p.module, dest, info)
if tfFinal in dest.flags or (objHasKidsValid in p.module.flags and if tfFinal in dest.flags or (objHasKidsValid in p.module.flags and
tfObjHasKids notin dest.flags): tfObjHasKids notin dest.flags):
result = "$1.m_type == $2" % [a, ti] result = "$1.m_type == $2" % [a, ti]
@ -1295,7 +1322,7 @@ proc genOfHelper(p: BProc; dest: PType; a: Rope): Rope =
when false: when false:
# former version: # former version:
result = rfmt(p.module, "#isObj($1.m_type, $2)", result = rfmt(p.module, "#isObj($1.m_type, $2)",
a, genTypeInfo(p.module, dest)) a, genTypeInfo(p.module, dest, info))
proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) =
var a: TLoc var a: TLoc
@ -1317,9 +1344,9 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) =
globalError(x.info, errGenerated, globalError(x.info, errGenerated,
"no 'of' operator available for pure objects") "no 'of' operator available for pure objects")
if nilCheck != nil: if nilCheck != nil:
r = rfmt(p.module, "(($1) && ($2))", nilCheck, genOfHelper(p, dest, r)) r = rfmt(p.module, "(($1) && ($2))", nilCheck, genOfHelper(p, dest, r, x.info))
else: else:
r = rfmt(p.module, "($1)", genOfHelper(p, dest, r)) r = rfmt(p.module, "($1)", genOfHelper(p, dest, r, x.info))
putIntoDest(p, d, x, r, a.storage) putIntoDest(p, d, x, r, a.storage)
proc genOf(p: BProc, n: PNode, d: var TLoc) = proc genOf(p: BProc, n: PNode, d: var TLoc) =
@ -1342,12 +1369,12 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
of tyEnum, tyOrdinal: of tyEnum, tyOrdinal:
putIntoDest(p, d, e, putIntoDest(p, d, e,
ropecg(p.module, "#reprEnum((NI)$1, $2)", [ ropecg(p.module, "#reprEnum((NI)$1, $2)", [
rdLoc(a), genTypeInfo(p.module, t)]), a.storage) rdLoc(a), genTypeInfo(p.module, t, e.info)]), a.storage)
of tyString: of tyString:
putIntoDest(p, d, e, ropecg(p.module, "#reprStr($1)", [rdLoc(a)]), a.storage) putIntoDest(p, d, e, ropecg(p.module, "#reprStr($1)", [rdLoc(a)]), a.storage)
of tySet: of tySet:
putIntoDest(p, d, e, ropecg(p.module, "#reprSet($1, $2)", [ putIntoDest(p, d, e, ropecg(p.module, "#reprSet($1, $2)", [
addrLoc(a), genTypeInfo(p.module, t)]), a.storage) addrLoc(a), genTypeInfo(p.module, t, e.info)]), a.storage)
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
var b: TLoc var b: TLoc
case a.t.kind case a.t.kind
@ -1362,22 +1389,22 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
else: internalError(e.sons[0].info, "genRepr()") else: internalError(e.sons[0].info, "genRepr()")
putIntoDest(p, d, e, putIntoDest(p, d, e,
ropecg(p.module, "#reprOpenArray($1, $2)", [rdLoc(b), ropecg(p.module, "#reprOpenArray($1, $2)", [rdLoc(b),
genTypeInfo(p.module, elemType(t))]), a.storage) genTypeInfo(p.module, elemType(t), e.info)]), a.storage)
of tyCString, tyArray, tyRef, tyPtr, tyPointer, tyNil, tySequence: of tyCString, tyArray, tyRef, tyPtr, tyPointer, tyNil, tySequence:
putIntoDest(p, d, e, putIntoDest(p, d, e,
ropecg(p.module, "#reprAny($1, $2)", [ ropecg(p.module, "#reprAny($1, $2)", [
rdLoc(a), genTypeInfo(p.module, t)]), a.storage) rdLoc(a), genTypeInfo(p.module, t, e.info)]), a.storage)
of tyEmpty, tyVoid: of tyEmpty, tyVoid:
localError(e.info, "'repr' doesn't support 'void' type") localError(e.info, "'repr' doesn't support 'void' type")
else: else:
putIntoDest(p, d, e, ropecg(p.module, "#reprAny($1, $2)", putIntoDest(p, d, e, ropecg(p.module, "#reprAny($1, $2)",
[addrLoc(a), genTypeInfo(p.module, t)]), [addrLoc(a), genTypeInfo(p.module, t, e.info)]),
a.storage) a.storage)
gcUsage(e) gcUsage(e)
proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) = proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) =
let t = e.sons[1].typ let t = e.sons[1].typ
putIntoDest(p, d, e, genTypeInfo(p.module, t)) putIntoDest(p, d, e, genTypeInfo(p.module, t, e.info))
proc genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) = proc genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) =
var a: TLoc var a: TLoc
@ -1959,10 +1986,10 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) =
t = skipTypes(t.sons[0], skipPtrs) t = skipTypes(t.sons[0], skipPtrs)
if nilCheck != nil: if nilCheck != nil:
linefmt(p, cpsStmts, "if ($1) #chckObj($2.m_type, $3);$n", linefmt(p, cpsStmts, "if ($1) #chckObj($2.m_type, $3);$n",
nilCheck, r, genTypeInfo(p.module, dest)) nilCheck, r, genTypeInfo(p.module, dest, n.info))
else: else:
linefmt(p, cpsStmts, "#chckObj($1.m_type, $2);$n", linefmt(p, cpsStmts, "#chckObj($1.m_type, $2);$n",
r, genTypeInfo(p.module, dest)) r, genTypeInfo(p.module, dest, n.info))
if n.sons[0].typ.kind != tyObject: if n.sons[0].typ.kind != tyObject:
putIntoDest(p, d, n, putIntoDest(p, d, n,
"(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage) "(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage)
@ -2245,7 +2272,7 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope =
result = rope"{NIM_NIL, NIM_NIL}" result = rope"{NIM_NIL, NIM_NIL}"
of tyObject: of tyObject:
if not isObjLackingTypeField(t) and not p.module.compileToCpp: if not isObjLackingTypeField(t) and not p.module.compileToCpp:
result = "{{$1}}" % [genTypeInfo(p.module, t)] result = "{{$1}}" % [genTypeInfo(p.module, t, info)]
else: else:
result = rope"{}" result = rope"{}"
of tyArray, tyTuple: result = rope"{}" of tyArray, tyTuple: result = rope"{}"
@ -2290,7 +2317,7 @@ proc getNullValueAuxT(p: BProc; orig, t: PType; obj, cons: PNode, result: var Ro
base = skipTypes(base, skipPtrs) base = skipTypes(base, skipPtrs)
getNullValueAuxT(p, orig, base, base.n, cons, result, count) getNullValueAuxT(p, orig, base, base.n, cons, result, count)
elif not isObjLackingTypeField(t) and not p.module.compileToCpp: elif not isObjLackingTypeField(t) and not p.module.compileToCpp:
addf(result, "$1", [genTypeInfo(p.module, orig)]) addf(result, "$1", [genTypeInfo(p.module, orig, obj.info)])
inc count inc count
getNullValueAux(p, t, obj, cons, result, count) getNullValueAux(p, t, obj, cons, result, count)
# do not emit '{}' as that is not valid C: # do not emit '{}' as that is not valid C:

View file

@ -20,7 +20,7 @@ proc registerGcRoot(p: BProc, v: PSym) =
containsGarbageCollectedRef(v.loc.t): containsGarbageCollectedRef(v.loc.t):
# we register a specialized marked proc here; this has the advantage # we register a specialized marked proc here; this has the advantage
# that it works out of the box for thread local storage then :-) # that it works out of the box for thread local storage then :-)
let prc = genTraverseProcForGlobal(p.module, v) let prc = genTraverseProcForGlobal(p.module, v, v.info)
appcg(p.module, p.module.initProc.procSec(cpsInit), appcg(p.module, p.module.initProc.procSec(cpsInit),
"#nimRegisterGlobalMarker($1);$n", [prc]) "#nimRegisterGlobalMarker($1);$n", [prc])
@ -141,9 +141,13 @@ template preserveBreakIdx(body: untyped): untyped =
p.breakIdx = oldBreakIdx p.breakIdx = oldBreakIdx
proc genState(p: BProc, n: PNode) = proc genState(p: BProc, n: PNode) =
internalAssert n.len == 1 and n.sons[0].kind == nkIntLit internalAssert n.len == 1
let n0 = n[0]
if n0.kind == nkIntLit:
let idx = n.sons[0].intVal let idx = n.sons[0].intVal
linefmt(p, cpsStmts, "STATE$1: ;$n", idx.rope) linefmt(p, cpsStmts, "STATE$1: ;$n", idx.rope)
elif n0.kind == nkStrLit:
linefmt(p, cpsStmts, "$1: ;$n", n0.strVal.rope)
proc genGotoState(p: BProc, n: PNode) = proc genGotoState(p: BProc, n: PNode) =
# we resist the temptation to translate it into duff's device as it later # we resist the temptation to translate it into duff's device as it later
@ -156,8 +160,13 @@ proc genGotoState(p: BProc, n: PNode) =
lineF(p, cpsStmts, "switch ($1) {$n", [rdLoc(a)]) lineF(p, cpsStmts, "switch ($1) {$n", [rdLoc(a)])
p.beforeRetNeeded = true p.beforeRetNeeded = true
lineF(p, cpsStmts, "case -1: goto BeforeRet_;$n", []) lineF(p, cpsStmts, "case -1: goto BeforeRet_;$n", [])
for i in 0 .. lastOrd(n.sons[0].typ): var statesCounter = lastOrd(n.sons[0].typ)
lineF(p, cpsStmts, "case $1: goto STATE$1;$n", [rope(i)]) if n.len >= 2 and n[1].kind == nkIntLit:
statesCounter = n[1].intVal
let prefix = if n.len == 3 and n[2].kind == nkStrLit: n[2].strVal.rope
else: rope"STATE"
for i in 0 .. statesCounter:
lineF(p, cpsStmts, "case $2: goto $1$2;$n", [prefix, rope(i)])
lineF(p, cpsStmts, "}$n", []) lineF(p, cpsStmts, "}$n", [])
proc genBreakState(p: BProc, n: PNode) = proc genBreakState(p: BProc, n: PNode) =
@ -826,7 +835,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
if orExpr != nil: add(orExpr, "||") if orExpr != nil: add(orExpr, "||")
appcg(p.module, orExpr, appcg(p.module, orExpr,
"#isObj($1.exp->m_type, $2)", "#isObj($1.exp->m_type, $2)",
[exc, genTypeInfo(p.module, t.sons[i].sons[j].typ)]) [exc, genTypeInfo(p.module, t[i][j].typ, t[i][j].info)])
lineF(p, cpsStmts, "if ($1) ", [orExpr]) lineF(p, cpsStmts, "if ($1) ", [orExpr])
startBlock(p) startBlock(p)
expr(p, t.sons[i].sons[blen-1], d) expr(p, t.sons[i].sons[blen-1], d)
@ -935,7 +944,7 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
"#isObj(#getCurrentException()->Sup.m_type, $1)" "#isObj(#getCurrentException()->Sup.m_type, $1)"
else: "#isObj(#getCurrentException()->m_type, $1)" else: "#isObj(#getCurrentException()->m_type, $1)"
appcg(p.module, orExpr, isObjFormat, appcg(p.module, orExpr, isObjFormat,
[genTypeInfo(p.module, t.sons[i].sons[j].typ)]) [genTypeInfo(p.module, t[i][j].typ, t[i][j].info)])
if i > 1: line(p, cpsStmts, "else ") if i > 1: line(p, cpsStmts, "else ")
startBlock(p, "if ($1) {$n", [orExpr]) startBlock(p, "if ($1) {$n", [orExpr])
linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint) linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint)
@ -1053,7 +1062,7 @@ proc genWatchpoint(p: BProc, n: PNode) =
let typ = skipTypes(n.sons[1].typ, abstractVarRange) let typ = skipTypes(n.sons[1].typ, abstractVarRange)
lineCg(p, cpsStmts, "#dbgRegisterWatchpoint($1, (NCSTRING)$2, $3);$n", lineCg(p, cpsStmts, "#dbgRegisterWatchpoint($1, (NCSTRING)$2, $3);$n",
[a.addrLoc, makeCString(renderTree(n.sons[1])), [a.addrLoc, makeCString(renderTree(n.sons[1])),
genTypeInfo(p.module, typ)]) genTypeInfo(p.module, typ, n.info)])
proc genPragma(p: BProc, n: PNode) = proc genPragma(p: BProc, n: PNode) =
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
@ -1083,7 +1092,7 @@ proc genDiscriminantCheck(p: BProc, a, tmp: TLoc, objtype: PType,
field: PSym) = field: PSym) =
var t = skipTypes(objtype, abstractVar) var t = skipTypes(objtype, abstractVar)
assert t.kind == tyObject assert t.kind == tyObject
discard genTypeInfo(p.module, t) discard genTypeInfo(p.module, t, a.lode.info)
var L = lengthOrd(field.typ) var L = lengthOrd(field.typ)
if not containsOrIncl(p.module.declaredThings, field.id): if not containsOrIncl(p.module.declaredThings, field.id):
appcg(p.module, cfsVars, "extern $1", appcg(p.module, cfsVars, "extern $1",
@ -1103,19 +1112,46 @@ proc asgnFieldDiscriminant(p: BProc, e: PNode) =
genDiscriminantCheck(p, a, tmp, dotExpr.sons[0].typ, dotExpr.sons[1].sym) genDiscriminantCheck(p, a, tmp, dotExpr.sons[0].typ, dotExpr.sons[1].sym)
genAssignment(p, a, tmp, {}) genAssignment(p, a, tmp, {})
proc patchAsgnStmtListExpr(father, orig, n: PNode) =
case n.kind
of nkDerefExpr, nkHiddenDeref:
let asgn = copyNode(orig)
asgn.add orig[0]
asgn.add n
father.add asgn
of nkStmtList, nkStmtListExpr:
for x in n:
patchAsgnStmtListExpr(father, orig, x)
else:
father.add n
proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) = proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
if e.sons[0].kind == nkSym and sfGoto in e.sons[0].sym.flags: if e.sons[0].kind == nkSym and sfGoto in e.sons[0].sym.flags:
genLineDir(p, e) genLineDir(p, e)
genGotoVar(p, e.sons[1]) genGotoVar(p, e.sons[1])
elif not fieldDiscriminantCheckNeeded(p, e): elif not fieldDiscriminantCheckNeeded(p, e):
# this fixes bug #6422 but we really need to change the representation of
# arrays in the backend...
let le = e[0]
let ri = e[1]
var needsRepair = false
var it = ri
while it.kind in {nkStmtList, nkStmtListExpr}:
it = it.lastSon
needsRepair = true
if it.kind in {nkDerefExpr, nkHiddenDeref} and needsRepair:
var patchedTree = newNodeI(nkStmtList, e.info)
patchAsgnStmtListExpr(patchedTree, e, ri)
genStmts(p, patchedTree)
return
var a: TLoc var a: TLoc
if e[0].kind in {nkDerefExpr, nkHiddenDeref}: if le.kind in {nkDerefExpr, nkHiddenDeref}:
genDeref(p, e[0], a, enforceDeref=true) genDeref(p, le, a, enforceDeref=true)
else: else:
initLocExpr(p, e.sons[0], a) initLocExpr(p, le, a)
if fastAsgn: incl(a.flags, lfNoDeepCopy) if fastAsgn: incl(a.flags, lfNoDeepCopy)
assert(a.t != nil) assert(a.t != nil)
let ri = e.sons[1]
genLineDir(p, ri) genLineDir(p, ri)
loadInto(p, e.sons[0], ri, a) loadInto(p, e.sons[0], ri, a)
else: else:

View file

@ -66,7 +66,7 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, typ: PType) =
var p = c.p var p = c.p
case typ.kind case typ.kind
of tyGenericInst, tyGenericBody, tyTypeDesc, tyAlias, tyDistinct: of tyGenericInst, tyGenericBody, tyTypeDesc, tyAlias, tyDistinct, tyInferred:
genTraverseProc(c, accessor, lastSon(typ)) genTraverseProc(c, accessor, lastSon(typ))
of tyArray: of tyArray:
let arraySize = lengthOrd(typ.sons[0]) let arraySize = lengthOrd(typ.sons[0])
@ -151,8 +151,8 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash;
m.s[cfsProcHeaders].addf("$1;$n", [header]) m.s[cfsProcHeaders].addf("$1;$n", [header])
m.s[cfsProcs].add(generatedProc) m.s[cfsProcs].add(generatedProc)
proc genTraverseProcForGlobal(m: BModule, s: PSym): Rope = proc genTraverseProcForGlobal(m: BModule, s: PSym; info: TLineInfo): Rope =
discard genTypeInfo(m, s.loc.t) discard genTypeInfo(m, s.loc.t, info)
var c: TTraversalClosure var c: TTraversalClosure
var p = newProc(nil, m) var p = newProc(nil, m)

View file

@ -92,7 +92,7 @@ proc mangleParamName(m: BModule; s: PSym): Rope =
proc mangleLocalName(p: BProc; s: PSym): Rope = proc mangleLocalName(p: BProc; s: PSym): Rope =
assert s.kind in skLocalVars+{skTemp} assert s.kind in skLocalVars+{skTemp}
assert sfGlobal notin s.flags #assert sfGlobal notin s.flags
result = s.loc.r result = s.loc.r
if result == nil: if result == nil:
var key = s.name.s.mangle var key = s.name.s.mangle
@ -119,7 +119,7 @@ proc scopeMangledParam(p: BProc; param: PSym) =
const const
irrelevantForBackend = {tyGenericBody, tyGenericInst, tyGenericInvocation, irrelevantForBackend = {tyGenericBody, tyGenericInst, tyGenericInvocation,
tyDistinct, tyRange, tyStatic, tyAlias} tyDistinct, tyRange, tyStatic, tyAlias, tyInferred}
proc typeName(typ: PType): Rope = proc typeName(typ: PType): Rope =
let typ = typ.skipTypes(irrelevantForBackend) let typ = typ.skipTypes(irrelevantForBackend)
@ -278,7 +278,10 @@ proc ccgIntroducedPtr(s: PSym): bool =
elif tfByCopy in pt.flags: return false elif tfByCopy in pt.flags: return false
case pt.kind case pt.kind
of tyObject: of tyObject:
if (optByRef in s.options) or (getSize(pt) > platform.floatSize * 2): if s.typ.sym != nil and sfForward in s.typ.sym.flags:
# forwarded objects are *always* passed by pointers for consistency!
result = true
elif (optByRef in s.options) or (getSize(pt) > platform.floatSize * 3):
result = true # requested anyway result = true # requested anyway
elif (tfFinal in pt.flags) and (pt.sons[0] == nil): elif (tfFinal in pt.flags) and (pt.sons[0] == nil):
result = false # no need, because no subtyping possible result = false # no need, because no subtyping possible
@ -286,7 +289,7 @@ proc ccgIntroducedPtr(s: PSym): bool =
result = true # ordinary objects are always passed by reference, result = true # ordinary objects are always passed by reference,
# otherwise casting doesn't work # otherwise casting doesn't work
of tyTuple: of tyTuple:
result = (getSize(pt) > platform.floatSize*2) or (optByRef in s.options) result = (getSize(pt) > platform.floatSize*3) or (optByRef in s.options)
else: result = false else: result = false
proc fillResult(param: PNode) = proc fillResult(param: PNode) =
@ -806,7 +809,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
var chunkStart = 0 var chunkStart = 0
while i < cppName.data.len: while i < cppName.data.len:
if cppName.data[i] == '\'': if cppName.data[i] == '\'':
var chunkEnd = <i var chunkEnd = i-1
var idx, stars: int var idx, stars: int
if scanCppGenericSlot(cppName.data, i, idx, stars): if scanCppGenericSlot(cppName.data, i, idx, stars):
result.add cppName.data.substr(chunkStart, chunkEnd) result.add cppName.data.substr(chunkStart, chunkEnd)
@ -854,6 +857,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
[structOrUnion(t), result]) [structOrUnion(t), result])
assert m.forwTypeCache[sig] == result assert m.forwTypeCache[sig] == result
m.typeCache[sig] = result # always call for sideeffects: m.typeCache[sig] = result # always call for sideeffects:
if not incompleteType(t):
let recdesc = if t.kind != tyTuple: getRecordDesc(m, t, result, check) let recdesc = if t.kind != tyTuple: getRecordDesc(m, t, result, check)
else: getTupleDesc(m, t, result, check) else: getTupleDesc(m, t, result, check)
if not isImportedType(t): if not isImportedType(t):
@ -935,12 +939,13 @@ proc genProcHeader(m: BModule, prc: PSym): Rope =
# ------------------ type info generation ------------------------------------- # ------------------ type info generation -------------------------------------
proc genTypeInfo(m: BModule, t: PType): Rope proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope
proc getNimNode(m: BModule): Rope = proc getNimNode(m: BModule): Rope =
result = "$1[$2]" % [m.typeNodesName, rope(m.typeNodes)] result = "$1[$2]" % [m.typeNodesName, rope(m.typeNodes)]
inc(m.typeNodes) inc(m.typeNodes)
proc genTypeInfoAuxBase(m: BModule; typ, origType: PType; name, base: Rope) = proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
name, base: Rope; info: TLineInfo) =
var nimtypeKind: int var nimtypeKind: int
#allocMemTI(m, typ, name) #allocMemTI(m, typ, name)
if isObjLackingTypeField(typ): if isObjLackingTypeField(typ):
@ -970,15 +975,19 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType; name, base: Rope) =
[name]) [name])
addf(m.s[cfsVars], "TNimType $1;$n", [name]) addf(m.s[cfsVars], "TNimType $1;$n", [name])
proc genTypeInfoAux(m: BModule, typ, origType: PType, name: Rope) = proc genTypeInfoAux(m: BModule, typ, origType: PType, name: Rope;
info: TLineInfo) =
var base: Rope var base: Rope
if sonsLen(typ) > 0 and typ.lastSon != nil: if sonsLen(typ) > 0 and typ.lastSon != nil:
var x = typ.lastSon var x = typ.lastSon
if typ.kind == tyObject: x = x.skipTypes(skipPtrs) if typ.kind == tyObject: x = x.skipTypes(skipPtrs)
base = genTypeInfo(m, x) if typ.kind == tyPtr and x.kind == tyObject and incompleteType(x):
base = rope("0")
else:
base = genTypeInfo(m, x, info)
else: else:
base = rope("0") base = rope("0")
genTypeInfoAuxBase(m, typ, origType, name, base) genTypeInfoAuxBase(m, typ, origType, name, base, info)
proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): Rope = proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): Rope =
# bugfix: we need to search the type that contains the discriminator: # bugfix: we need to search the type that contains the discriminator:
@ -994,19 +1003,20 @@ proc discriminatorTableDecl(m: BModule, objtype: PType, d: PSym): Rope =
var tmp = discriminatorTableName(m, objtype, d) var tmp = discriminatorTableName(m, objtype, d)
result = "TNimNode* $1[$2];$n" % [tmp, rope(lengthOrd(d.typ)+1)] result = "TNimNode* $1[$2];$n" % [tmp, rope(lengthOrd(d.typ)+1)]
proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope) = proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope;
info: TLineInfo) =
case n.kind case n.kind
of nkRecList: of nkRecList:
var L = sonsLen(n) var L = sonsLen(n)
if L == 1: if L == 1:
genObjectFields(m, typ, origType, n.sons[0], expr) genObjectFields(m, typ, origType, n.sons[0], expr, info)
elif L > 0: elif L > 0:
var tmp = getTempName(m) var tmp = getTempName(m)
addf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n", [tmp, rope(L)]) addf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n", [tmp, rope(L)])
for i in countup(0, L-1): for i in countup(0, L-1):
var tmp2 = getNimNode(m) var tmp2 = getNimNode(m)
addf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, rope(i), tmp2]) addf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, rope(i), tmp2])
genObjectFields(m, typ, origType, n.sons[i], tmp2) genObjectFields(m, typ, origType, n.sons[i], tmp2, info)
addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n", addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n",
[expr, rope(L), tmp]) [expr, rope(L), tmp])
else: else:
@ -1024,14 +1034,14 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope) =
"$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" & "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" &
"$1.name = $5;$n" & "$1.sons = &$6[0];$n" & "$1.name = $5;$n" & "$1.sons = &$6[0];$n" &
"$1.len = $7;$n", [expr, getTypeDesc(m, origType), field.loc.r, "$1.len = $7;$n", [expr, getTypeDesc(m, origType), field.loc.r,
genTypeInfo(m, field.typ), genTypeInfo(m, field.typ, info),
makeCString(field.name.s), makeCString(field.name.s),
tmp, rope(L)]) tmp, rope(L)])
addf(m.s[cfsData], "TNimNode* $1[$2];$n", [tmp, rope(L+1)]) addf(m.s[cfsData], "TNimNode* $1[$2];$n", [tmp, rope(L+1)])
for i in countup(1, sonsLen(n)-1): for i in countup(1, sonsLen(n)-1):
var b = n.sons[i] # branch var b = n.sons[i] # branch
var tmp2 = getNimNode(m) var tmp2 = getNimNode(m)
genObjectFields(m, typ, origType, lastSon(b), tmp2) genObjectFields(m, typ, origType, lastSon(b), tmp2, info)
case b.kind case b.kind
of nkOfBranch: of nkOfBranch:
if sonsLen(b) < 2: if sonsLen(b) < 2:
@ -1059,15 +1069,20 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope) =
addf(m.s[cfsTypeInit3], "$1.kind = 1;$n" & addf(m.s[cfsTypeInit3], "$1.kind = 1;$n" &
"$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" & "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" &
"$1.name = $5;$n", [expr, getTypeDesc(m, origType), "$1.name = $5;$n", [expr, getTypeDesc(m, origType),
field.loc.r, genTypeInfo(m, field.typ), makeCString(field.name.s)]) field.loc.r, genTypeInfo(m, field.typ, info), makeCString(field.name.s)])
else: internalError(n.info, "genObjectFields") else: internalError(n.info, "genObjectFields")
proc genObjectInfo(m: BModule, typ, origType: PType, name: Rope) = proc genObjectInfo(m: BModule, typ, origType: PType, name: Rope; info: TLineInfo) =
if typ.kind == tyObject: genTypeInfoAux(m, typ, origType, name) if typ.kind == tyObject:
else: genTypeInfoAuxBase(m, typ, origType, name, rope("0")) if incompleteType(typ):
localError(info, "request for RTTI generation for incomplete object: " &
typeToString(typ))
genTypeInfoAux(m, typ, origType, name, info)
else:
genTypeInfoAuxBase(m, typ, origType, name, rope("0"), info)
var tmp = getNimNode(m) var tmp = getNimNode(m)
if not isImportedType(typ): if not isImportedType(typ):
genObjectFields(m, typ, origType, typ.n, tmp) genObjectFields(m, typ, origType, typ.n, tmp, info)
addf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [name, tmp]) addf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [name, tmp])
var t = typ.sons[0] var t = typ.sons[0]
while t != nil: while t != nil:
@ -1075,8 +1090,8 @@ proc genObjectInfo(m: BModule, typ, origType: PType, name: Rope) =
t.flags.incl tfObjHasKids t.flags.incl tfObjHasKids
t = t.sons[0] t = t.sons[0]
proc genTupleInfo(m: BModule, typ, origType: PType, name: Rope) = proc genTupleInfo(m: BModule, typ, origType: PType, name: Rope; info: TLineInfo) =
genTypeInfoAuxBase(m, typ, typ, name, rope("0")) genTypeInfoAuxBase(m, typ, typ, name, rope("0"), info)
var expr = getNimNode(m) var expr = getNimNode(m)
var length = sonsLen(typ) var length = sonsLen(typ)
if length > 0: if length > 0:
@ -1090,7 +1105,7 @@ proc genTupleInfo(m: BModule, typ, origType: PType, name: Rope) =
"$1.offset = offsetof($2, Field$3);$n" & "$1.offset = offsetof($2, Field$3);$n" &
"$1.typ = $4;$n" & "$1.typ = $4;$n" &
"$1.name = \"Field$3\";$n", "$1.name = \"Field$3\";$n",
[tmp2, getTypeDesc(m, origType), rope(i), genTypeInfo(m, a)]) [tmp2, getTypeDesc(m, origType), rope(i), genTypeInfo(m, a, info)])
addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n", addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n",
[expr, rope(length), tmp]) [expr, rope(length), tmp])
else: else:
@ -1098,12 +1113,12 @@ proc genTupleInfo(m: BModule, typ, origType: PType, name: Rope) =
[expr, rope(length)]) [expr, rope(length)])
addf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [name, expr]) addf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [name, expr])
proc genEnumInfo(m: BModule, typ: PType, name: Rope) = proc genEnumInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
# Type information for enumerations is quite heavy, so we do some # Type information for enumerations is quite heavy, so we do some
# optimizations here: The ``typ`` field is never set, as it is redundant # optimizations here: The ``typ`` field is never set, as it is redundant
# anyway. We generate a cstring array and a loop over it. Exceptional # anyway. We generate a cstring array and a loop over it. Exceptional
# positions will be reset after the loop. # positions will be reset after the loop.
genTypeInfoAux(m, typ, typ, name) genTypeInfoAux(m, typ, typ, name, info)
var nodePtrs = getTempName(m) var nodePtrs = getTempName(m)
var length = sonsLen(typ.n) var length = sonsLen(typ.n)
addf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n", addf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n",
@ -1141,15 +1156,15 @@ proc genEnumInfo(m: BModule, typ: PType, name: Rope) =
# 1 << 2 is {ntfEnumHole} # 1 << 2 is {ntfEnumHole}
addf(m.s[cfsTypeInit3], "$1.flags = 1<<2;$n", [name]) addf(m.s[cfsTypeInit3], "$1.flags = 1<<2;$n", [name])
proc genSetInfo(m: BModule, typ: PType, name: Rope) = proc genSetInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
assert(typ.sons[0] != nil) assert(typ.sons[0] != nil)
genTypeInfoAux(m, typ, typ, name) genTypeInfoAux(m, typ, typ, name, info)
var tmp = getNimNode(m) var tmp = getNimNode(m)
addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 0;$n" & "$3.node = &$1;$n", addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 0;$n" & "$3.node = &$1;$n",
[tmp, rope(firstOrd(typ)), name]) [tmp, rope(firstOrd(typ)), name])
proc genArrayInfo(m: BModule, typ: PType, name: Rope) = proc genArrayInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
genTypeInfoAuxBase(m, typ, typ, name, genTypeInfo(m, typ.sons[1])) genTypeInfoAuxBase(m, typ, typ, name, genTypeInfo(m, typ.sons[1], info), info)
proc fakeClosureType(owner: PSym): PType = proc fakeClosureType(owner: PSym): PType =
# we generate the same RTTI as for a tuple[pointer, ref tuple[]] # we generate the same RTTI as for a tuple[pointer, ref tuple[]]
@ -1171,11 +1186,11 @@ proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) =
addf(m.s[cfsTypeInit3], "$1.deepcopy =(void* (N_RAW_NIMCALL*)(void*))$2;$n", addf(m.s[cfsTypeInit3], "$1.deepcopy =(void* (N_RAW_NIMCALL*)(void*))$2;$n",
[result, s.loc.r]) [result, s.loc.r])
proc genTypeInfo(m: BModule, t: PType): Rope = proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
let origType = t let origType = t
var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses) var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses)
if t.kind == tyOpt: if t.kind == tyOpt:
return genTypeInfo(m, optLowering(t)) return genTypeInfo(m, optLowering(t), info)
let sig = hashType(origType) let sig = hashType(origType)
result = m.typeInfoMarker.getOrDefault(sig) result = m.typeInfoMarker.getOrDefault(sig)
@ -1197,7 +1212,7 @@ proc genTypeInfo(m: BModule, t: PType): Rope =
let owner = t.skipTypes(typedescPtrs).owner.getModule let owner = t.skipTypes(typedescPtrs).owner.getModule
if owner != m.module: if owner != m.module:
# make sure the type info is created in the owner module # make sure the type info is created in the owner module
discard genTypeInfo(m.g.modules[owner.position], origType) discard genTypeInfo(m.g.modules[owner.position], origType, info)
# reference the type info as extern here # reference the type info as extern here
discard cgsym(m, "TNimType") discard cgsym(m, "TNimType")
discard cgsym(m, "TNimNode") discard cgsym(m, "TNimNode")
@ -1208,35 +1223,35 @@ proc genTypeInfo(m: BModule, t: PType): Rope =
case t.kind case t.kind
of tyEmpty, tyVoid: result = rope"0" of tyEmpty, tyVoid: result = rope"0"
of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64, tyVar: of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64, tyVar:
genTypeInfoAuxBase(m, t, t, result, rope"0") genTypeInfoAuxBase(m, t, t, result, rope"0", info)
of tyStatic: of tyStatic:
if t.n != nil: result = genTypeInfo(m, lastSon t) if t.n != nil: result = genTypeInfo(m, lastSon t, info)
else: internalError("genTypeInfo(" & $t.kind & ')') else: internalError("genTypeInfo(" & $t.kind & ')')
of tyUserTypeClasses: of tyUserTypeClasses:
internalAssert t.isResolvedUserTypeClass internalAssert t.isResolvedUserTypeClass
return genTypeInfo(m, t.lastSon) return genTypeInfo(m, t.lastSon, info)
of tyProc: of tyProc:
if t.callConv != ccClosure: if t.callConv != ccClosure:
genTypeInfoAuxBase(m, t, t, result, rope"0") genTypeInfoAuxBase(m, t, t, result, rope"0", info)
else: else:
let x = fakeClosureType(t.owner) let x = fakeClosureType(t.owner)
genTupleInfo(m, x, x, result) genTupleInfo(m, x, x, result, info)
of tySequence, tyRef, tyOptAsRef: of tySequence, tyRef, tyOptAsRef:
genTypeInfoAux(m, t, t, result) genTypeInfoAux(m, t, t, result, info)
if gSelectedGC >= gcMarkAndSweep: if gSelectedGC >= gcMarkAndSweep:
let markerProc = genTraverseProc(m, origType, sig, tiNew) let markerProc = genTraverseProc(m, origType, sig, tiNew)
addf(m.s[cfsTypeInit3], "$1.marker = $2;$n", [result, markerProc]) addf(m.s[cfsTypeInit3], "$1.marker = $2;$n", [result, markerProc])
of tyPtr, tyRange: genTypeInfoAux(m, t, t, result) of tyPtr, tyRange: genTypeInfoAux(m, t, t, result, info)
of tyArray: genArrayInfo(m, t, result) of tyArray: genArrayInfo(m, t, result, info)
of tySet: genSetInfo(m, t, result) of tySet: genSetInfo(m, t, result, info)
of tyEnum: genEnumInfo(m, t, result) of tyEnum: genEnumInfo(m, t, result, info)
of tyObject: genObjectInfo(m, t, origType, result) of tyObject: genObjectInfo(m, t, origType, result, info)
of tyTuple: of tyTuple:
# if t.n != nil: genObjectInfo(m, t, result) # if t.n != nil: genObjectInfo(m, t, result)
# else: # else:
# BUGFIX: use consistently RTTI without proper field names; otherwise # BUGFIX: use consistently RTTI without proper field names; otherwise
# results are not deterministic! # results are not deterministic!
genTupleInfo(m, t, origType, result) genTupleInfo(m, t, origType, result, info)
else: internalError("genTypeInfo(" & $t.kind & ')') else: internalError("genTypeInfo(" & $t.kind & ')')
if t.deepCopy != nil: if t.deepCopy != nil:
genDeepCopyProc(m, t.deepCopy, result) genDeepCopyProc(m, t.deepCopy, result)

View file

@ -271,11 +271,11 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc,
while (s.kind == tyObject) and (s.sons[0] != nil): while (s.kind == tyObject) and (s.sons[0] != nil):
add(r, ".Sup") add(r, ".Sup")
s = skipTypes(s.sons[0], skipPtrs) s = skipTypes(s.sons[0], skipPtrs)
linefmt(p, section, "$1.m_type = $2;$n", r, genTypeInfo(p.module, t)) linefmt(p, section, "$1.m_type = $2;$n", r, genTypeInfo(p.module, t, a.lode.info))
of frEmbedded: of frEmbedded:
# worst case for performance: # worst case for performance:
var r = if takeAddr: addrLoc(a) else: rdLoc(a) var r = if takeAddr: addrLoc(a) else: rdLoc(a)
linefmt(p, section, "#objectInit($1, $2);$n", r, genTypeInfo(p.module, t)) linefmt(p, section, "#objectInit($1, $2);$n", r, genTypeInfo(p.module, t, a.lode.info))
type type
TAssignmentFlag = enum TAssignmentFlag = enum
@ -306,7 +306,7 @@ proc resetLoc(p: BProc, loc: var TLoc) =
linefmt(p, cpsStmts, "#chckNil((void*)$1);$n", addrLoc(loc)) linefmt(p, cpsStmts, "#chckNil((void*)$1);$n", addrLoc(loc))
if loc.storage != OnStack: if loc.storage != OnStack:
linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n", linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
addrLoc(loc), genTypeInfo(p.module, loc.t)) addrLoc(loc), genTypeInfo(p.module, loc.t, loc.lode.info))
# XXX: generated reset procs should not touch the m_type # XXX: generated reset procs should not touch the m_type
# field, so disabling this should be safe: # field, so disabling this should be safe:
genObjectInit(p, cpsStmts, loc.t, loc, true) genObjectInit(p, cpsStmts, loc.t, loc, true)
@ -381,7 +381,7 @@ proc localDebugInfo(p: BProc, s: PSym) =
lineF(p, cpsInit, lineF(p, cpsInit,
"FR_.s[$1].address = (void*)$3; FR_.s[$1].typ = $4; FR_.s[$1].name = $2;$n", "FR_.s[$1].address = (void*)$3; FR_.s[$1].typ = $4; FR_.s[$1].name = $2;$n",
[p.maxFrameLen.rope, makeCString(normalize(s.name.s)), a, [p.maxFrameLen.rope, makeCString(normalize(s.name.s)), a,
genTypeInfo(p.module, s.loc.t)]) genTypeInfo(p.module, s.loc.t, s.info)])
inc(p.maxFrameLen) inc(p.maxFrameLen)
inc p.blocks[p.blocks.len-1].frameLen inc p.blocks[p.blocks.len-1].frameLen
@ -451,7 +451,7 @@ proc assignGlobalVar(p: BProc, n: PNode) =
appcg(p.module, p.module.s[cfsDebugInit], appcg(p.module, p.module.s[cfsDebugInit],
"#dbgRegisterGlobal($1, &$2, $3);$n", "#dbgRegisterGlobal($1, &$2, $3);$n",
[makeCString(normalize(s.owner.name.s & '.' & s.name.s)), [makeCString(normalize(s.owner.name.s & '.' & s.name.s)),
s.loc.r, genTypeInfo(p.module, s.typ)]) s.loc.r, genTypeInfo(p.module, s.typ, n.info)])
proc assignParam(p: BProc, s: PSym) = proc assignParam(p: BProc, s: PSym) =
assert(s.loc.r != nil) assert(s.loc.r != nil)
@ -663,6 +663,13 @@ proc closureSetup(p: BProc, prc: PSym) =
linefmt(p, cpsStmts, "$1 = ($2) ClE_0;$n", linefmt(p, cpsStmts, "$1 = ($2) ClE_0;$n",
rdLoc(env.loc), getTypeDesc(p.module, env.typ)) rdLoc(env.loc), getTypeDesc(p.module, env.typ))
proc containsResult(n: PNode): bool =
if n.kind == nkSym and n.sym.kind == skResult:
result = true
else:
for i in 0..<n.safeLen:
if containsResult(n[i]): return true
proc easyResultAsgn(n: PNode): PNode = proc easyResultAsgn(n: PNode): PNode =
const harmless = {nkConstSection, nkTypeSection, nkEmpty, nkCommentStmt} + const harmless = {nkConstSection, nkTypeSection, nkEmpty, nkCommentStmt} +
declarativeDefs declarativeDefs
@ -672,7 +679,7 @@ proc easyResultAsgn(n: PNode): PNode =
while i < n.len and n[i].kind in harmless: inc i while i < n.len and n[i].kind in harmless: inc i
if i < n.len: result = easyResultAsgn(n[i]) if i < n.len: result = easyResultAsgn(n[i])
of nkAsgn, nkFastAsgn: of nkAsgn, nkFastAsgn:
if n[0].kind == nkSym and skResult == n[0].sym.kind: if n[0].kind == nkSym and n[0].sym.kind == skResult and not containsResult(n[1]):
incl n.flags, nfPreventCg incl n.flags, nfPreventCg
return n[1] return n[1]
of nkReturnStmt: of nkReturnStmt:
@ -1006,10 +1013,9 @@ proc genMainProc(m: BModule) =
ComponentConstruct = ComponentConstruct =
"void Libc::Component::construct(Libc::Env &env) {$N" & "void Libc::Component::construct(Libc::Env &env) {$N" &
"\tgenodeEnv = &env;$N" & "\tgenodeEnv = &env;$N" &
"\tLibc::with_libc([&] () {$n\t" & "\tLibc::with_libc([&] () {$N\t" &
MainProcs & MainProcs &
"\t});$N" & "\t});$N" &
"\tenv.parent().exit(0);$N" &
"}$N$N" "}$N$N"
var nimMain, otherMain: FormatStr var nimMain, otherMain: FormatStr

View file

@ -154,7 +154,7 @@ proc includeHeader*(this: BModule; header: string) =
proc s*(p: BProc, s: TCProcSection): var Rope {.inline.} = proc s*(p: BProc, s: TCProcSection): var Rope {.inline.} =
# section in the current block # section in the current block
result = p.blocks[^1].sections[s] result = p.blocks[p.blocks.len-1].sections[s]
proc procSec*(p: BProc, s: TCProcSection): var Rope {.inline.} = proc procSec*(p: BProc, s: TCProcSection): var Rope {.inline.} =
# top level proc sections # top level proc sections

View file

@ -81,15 +81,17 @@ proc writeVersionInfo(pass: TCmdLinePass) =
if pass == passCmd1: if pass == passCmd1:
msgWriteln(`%`(HelpMessage, [VersionAsString, msgWriteln(`%`(HelpMessage, [VersionAsString,
platform.OS[platform.hostOS].name, platform.OS[platform.hostOS].name,
CPU[platform.hostCPU].name])) CPU[platform.hostCPU].name]),
{msgStdout})
const gitHash = gorge("git log -n 1 --format=%H").strip const gitHash = gorge("git log -n 1 --format=%H").strip
when gitHash.len == 40: when gitHash.len == 40:
msgWriteln("git hash: " & gitHash) msgWriteln("git hash: " & gitHash, {msgStdout})
msgWriteln("active boot switches:" & usedRelease & usedAvoidTimeMachine & msgWriteln("active boot switches:" & usedRelease & usedAvoidTimeMachine &
usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas & usedTinyC & usedGnuReadline & usedNativeStacktrace & usedNoCaas &
usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedGoGC & usedNoGC) usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedGoGC & usedNoGC,
{msgStdout})
msgQuit(0) msgQuit(0)
var var
@ -249,6 +251,7 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool =
result = gOptions * {optNaNCheck, optInfCheck} == {optNaNCheck, optInfCheck} result = gOptions * {optNaNCheck, optInfCheck} == {optNaNCheck, optInfCheck}
of "infchecks": result = contains(gOptions, optInfCheck) of "infchecks": result = contains(gOptions, optInfCheck)
of "nanchecks": result = contains(gOptions, optNaNCheck) of "nanchecks": result = contains(gOptions, optNaNCheck)
of "nilchecks": result = contains(gOptions, optNilCheck)
of "objchecks": result = contains(gOptions, optObjCheck) of "objchecks": result = contains(gOptions, optObjCheck)
of "fieldchecks": result = contains(gOptions, optFieldCheck) of "fieldchecks": result = contains(gOptions, optFieldCheck)
of "rangechecks": result = contains(gOptions, optRangeCheck) of "rangechecks": result = contains(gOptions, optRangeCheck)
@ -340,7 +343,9 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
# keep the old name for compat # keep the old name for compat
if pass in {passCmd2, passPP} and not options.gNoNimblePath: if pass in {passCmd2, passPP} and not options.gNoNimblePath:
expectArg(switch, arg, pass, info) expectArg(switch, arg, pass, info)
let path = processPath(arg, info, notRelativeToProj=true) var path = processPath(arg, info, notRelativeToProj=true)
let nimbleDir = getEnv("NIMBLE_DIR")
if nimbleDir.len > 0 and pass == passPP: path = nimbleDir / "pkgs"
nimblePath(path, info) nimblePath(path, info)
of "nonimblepath", "nobabelpath": of "nonimblepath", "nobabelpath":
expectNoArg(switch, arg, pass, info) expectNoArg(switch, arg, pass, info)
@ -471,6 +476,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
processOnOffSwitch({optNaNCheck, optInfCheck}, arg, pass, info) processOnOffSwitch({optNaNCheck, optInfCheck}, arg, pass, info)
of "infchecks": processOnOffSwitch({optInfCheck}, arg, pass, info) of "infchecks": processOnOffSwitch({optInfCheck}, arg, pass, info)
of "nanchecks": processOnOffSwitch({optNaNCheck}, arg, pass, info) of "nanchecks": processOnOffSwitch({optNaNCheck}, arg, pass, info)
of "nilchecks": processOnOffSwitch({optNilCheck}, arg, pass, info)
of "objchecks": processOnOffSwitch({optObjCheck}, arg, pass, info) of "objchecks": processOnOffSwitch({optObjCheck}, arg, pass, info)
of "fieldchecks": processOnOffSwitch({optFieldCheck}, arg, pass, info) of "fieldchecks": processOnOffSwitch({optFieldCheck}, arg, pass, info)
of "rangechecks": processOnOffSwitch({optRangeCheck}, arg, pass, info) of "rangechecks": processOnOffSwitch({optRangeCheck}, arg, pass, info)
@ -662,6 +668,10 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
expectArg(switch, arg, pass, info) expectArg(switch, arg, pass, info)
if config != nil: if config != nil:
config.cppDefine(arg) config.cppDefine(arg)
of "newruntime":
expectNoArg(switch, arg, pass, info)
newDestructors = true
defineSymbol("nimNewRuntime")
else: else:
if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg) if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg)
else: invalidCmdLineOption(pass, switch, info) else: invalidCmdLineOption(pass, switch, info)

View file

@ -108,3 +108,4 @@ proc initDefines*() =
defineSymbol("nimHasCppDefine") defineSymbol("nimHasCppDefine")
defineSymbol("nimGenericInOutFlags") defineSymbol("nimGenericInOutFlags")
when false: defineSymbol("nimHasOpt") when false: defineSymbol("nimHasOpt")
defineSymbol("nimNoArrayToCstringConversion")

View file

@ -10,7 +10,7 @@
# This module implements a dependency file generator. # This module implements a dependency file generator.
import import
os, options, ast, astalgo, msgs, ropes, idents, passes, importer os, options, ast, astalgo, msgs, ropes, idents, passes, modulepaths
from modulegraphs import ModuleGraph from modulegraphs import ModuleGraph

318
compiler/destroyer.nim Normal file
View file

@ -0,0 +1,318 @@
#
#
# The Nim Compiler
# (c) Copyright 2017 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Injects destructor calls into Nim code as well as
## an optimizer that optimizes copies to moves. This is implemented as an
## AST to AST transformation so that every backend benefits from it.
## Rules for destructor injections:
##
## foo(bar(X(), Y()))
## X and Y get destroyed after bar completes:
##
## foo( (tmpX = X(); tmpY = Y(); tmpBar = bar(tmpX, tmpY);
## destroy(tmpX); destroy(tmpY);
## tmpBar))
## destroy(tmpBar)
##
## var x = f()
## body
##
## is the same as:
##
## var x;
## try:
## move(x, f())
## finally:
## destroy(x)
##
## But this really just an optimization that tries to avoid to
## introduce too many temporaries, the 'destroy' is caused by
## the 'f()' call. No! That is not true for 'result = f()'!
##
## x = y where y is read only once
## is the same as: move(x, y)
##
## Actually the more general rule is: The *last* read of ``y``
## can become a move if ``y`` is the result of a construction.
##
## We also need to keep in mind here that the number of reads is
## control flow dependent:
## let x = foo()
## while true:
## y = x # only one read, but the 2nd iteration will fail!
## This also affects recursions! Only usages that do not cross
## a loop boundary (scope) and are not used in function calls
## are safe.
##
##
## x = f() is the same as: move(x, f())
##
## x = y
## is the same as: copy(x, y)
##
## Reassignment works under this scheme:
## var x = f()
## x = y
##
## is the same as:
##
## var x;
## try:
## move(x, f())
## copy(x, y)
## finally:
## destroy(x)
##
## result = f() must not destroy 'result'!
##
## The produced temporaries clutter up the code and might lead to
## inefficiencies. A better strategy is to collect all the temporaries
## in a single object that we put into a single try-finally that
## surrounds the proc body. This means the code stays quite efficient
## when compiled to C. In fact, we do the same for variables, so
## destructors are called when the proc returns, not at scope exit!
## This makes certains idioms easier to support. (Taking the slice
## of a temporary object.)
##
## foo(bar(X(), Y()))
## X and Y get destroyed after bar completes:
##
## var tmp: object
## foo( (move tmp.x, X(); move tmp.y, Y(); tmp.bar = bar(tmpX, tmpY);
## tmp.bar))
## destroy(tmp.bar)
## destroy(tmp.x); destroy(tmp.y)
import
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
strutils, options, dfa, lowerings, rodread
const
InterestingSyms = {skVar, skResult, skLet}
type
Con = object
owner: PSym
g: ControlFlowGraph
jumpTargets: IntSet
tmpObj: PType
tmp: PSym
destroys, topLevelVars: PNode
proc isHarmlessVar*(s: PSym; c: Con): bool =
# 's' is harmless if it used only once and its
# definition/usage are not split by any labels:
#
# let s = foo()
# while true:
# a[i] = s
#
# produces:
#
# def s
# L1:
# use s
# goto L1
#
# let s = foo()
# if cond:
# a[i] = s
# else:
# a[j] = s
#
# produces:
#
# def s
# fork L2
# use s
# goto L3
# L2:
# use s
# L3
#
# So this analysis is for now overly conservative, but correct.
var defsite = -1
var usages = 0
for i in 0..<c.g.len:
case c.g[i].kind
of def:
if c.g[i].sym == s:
if defsite < 0: defsite = i
else: return false
of use:
if c.g[i].sym == s:
if defsite < 0: return false
for j in defsite .. i:
# not within the same basic block?
if j in c.jumpTargets: return false
# if we want to die after the first 'use':
if usages > 1: return false
inc usages
of useWithinCall:
if c.g[i].sym == s: return false
of goto, fork:
discard "we do not perform an abstract interpretation yet"
template interestingSym(s: PSym): bool =
s.owner == c.owner and s.kind in InterestingSyms and hasDestructor(s.typ)
proc patchHead(n: PNode) =
if n.kind in nkCallKinds and n[0].kind == nkSym and n.len > 1:
let s = n[0].sym
if sfFromGeneric in s.flags and s.name.s[0] == '=' and
s.name.s in ["=sink", "=", "=destroy"]:
excl(s.flags, sfFromGeneric)
patchHead(s.getBody)
let t = n[1].typ.skipTypes({tyVar, tyGenericInst, tyAlias, tyInferred})
template patch(op, field) =
if s.name.s == op and field != nil and field != s:
n.sons[0].sym = field
patch "=sink", t.sink
patch "=", t.assignment
patch "=destroy", t.destructor
for x in n:
patchHead(x)
proc genSink(t: PType; dest: PNode): PNode =
let t = t.skipTypes({tyGenericInst, tyAlias})
let op = if t.sink != nil: t.sink else: t.assignment
assert op != nil
patchHead op.ast[bodyPos]
result = newTree(nkCall, newSymNode(op), newTree(nkHiddenAddr, dest))
proc genCopy(t: PType; dest: PNode): PNode =
let t = t.skipTypes({tyGenericInst, tyAlias})
assert t.assignment != nil
patchHead t.assignment.ast[bodyPos]
result = newTree(nkCall, newSymNode(t.assignment), newTree(nkHiddenAddr, dest))
proc genDestroy(t: PType; dest: PNode): PNode =
let t = t.skipTypes({tyGenericInst, tyAlias})
assert t.destructor != nil
patchHead t.destructor.ast[bodyPos]
result = newTree(nkCall, newSymNode(t.destructor), newTree(nkHiddenAddr, dest))
proc addTopVar(c: var Con; v: PNode) =
c.topLevelVars.add newTree(nkIdentDefs, v, emptyNode, emptyNode)
proc p(n: PNode; c: var Con): PNode
template recurse(n, dest) =
for i in 0..<n.len:
dest.add p(n[i], c)
proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
if ri.kind in nkCallKinds:
result = genSink(ri.typ, dest)
# watch out and no not transform 'ri' twice if it's a call:
let ri2 = copyNode(ri)
recurse(ri, ri2)
result.add ri2
elif ri.kind == nkSym and isHarmlessVar(ri.sym, c):
result = genSink(ri.typ, dest)
result.add p(ri, c)
else:
result = genCopy(ri.typ, dest)
result.add p(ri, c)
proc p(n: PNode; c: var Con): PNode =
case n.kind
of nkVarSection, nkLetSection:
discard "transform; var x = y to var x; x op y where op is a move or copy"
result = newNodeI(nkStmtList, n.info)
for i in 0..<n.len:
let it = n[i]
let L = it.len-1
let ri = it[L]
if it.kind == nkVarTuple and hasDestructor(ri.typ):
let x = lowerTupleUnpacking(it, c.owner)
result.add p(x, c)
elif it.kind == nkIdentDefs and hasDestructor(it[0].typ):
for j in 0..L-2:
let v = it[j]
doAssert v.kind == nkSym
# move the variable declaration to the top of the frame:
c.addTopVar v
# make sure it's destroyed at the end of the proc:
c.destroys.add genDestroy(v.typ, v)
if ri.kind != nkEmpty:
let r = moveOrCopy(v, ri, c)
result.add r
else:
# keep it, but transform 'ri':
var varSection = copyNode(n)
var itCopy = copyNode(it)
for j in 0..L-1:
itCopy.add it[j]
itCopy.add p(ri, c)
varSection.add itCopy
result.add varSection
of nkCallKinds:
if n.typ != nil and hasDestructor(n.typ):
discard "produce temp creation"
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
let f = newSym(skField, getIdent(":d" & $c.tmpObj.n.len), c.owner, n.info)
f.typ = n.typ
rawAddField c.tmpObj, f
var m = genSink(n.typ, rawDirectAccess(c.tmp, f))
var call = copyNode(n)
recurse(n, call)
m.add call
result.add m
result.add rawDirectAccess(c.tmp, f)
c.destroys.add genDestroy(n.typ, rawDirectAccess(c.tmp, f))
else:
result = copyNode(n)
recurse(n, result)
of nkAsgn, nkFastAsgn:
if hasDestructor(n[0].typ):
result = moveOrCopy(n[0], n[1], c)
else:
result = copyNode(n)
recurse(n, result)
of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef,
nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef:
result = n
else:
result = copyNode(n)
recurse(n, result)
proc injectDestructorCalls*(owner: PSym; n: PNode): PNode =
var c: Con
c.owner = owner
c.tmp = newSym(skTemp, getIdent":d", owner, n.info)
c.tmpObj = createObj(owner, n.info)
c.tmp.typ = c.tmpObj
c.destroys = newNodeI(nkStmtList, n.info)
c.topLevelVars = newNodeI(nkVarSection, n.info)
let cfg = constructCfg(owner, n)
shallowCopy(c.g, cfg)
c.jumpTargets = initIntSet()
for i in 0..<c.g.len:
if c.g[i].kind in {goto, fork}:
c.jumpTargets.incl(i+c.g[i].dest)
let body = p(n, c)
if c.tmp.typ.n.len > 0:
c.addTopVar(newSymNode c.tmp)
result = newNodeI(nkStmtList, n.info)
if c.topLevelVars.len > 0:
result.add c.topLevelVars
if c.destroys.len > 0:
result.add newTryFinally(body, c.destroys)
else:
result.add body
when defined(nimDebugDestroys):
if owner.name.s == "createSeq":
echo "------------------------------------"
echo owner.name.s, " transformed to: "
echo result

439
compiler/dfa.nim Normal file
View file

@ -0,0 +1,439 @@
#
#
# The Nim Compiler
# (c) Copyright 2017 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Data flow analysis for Nim. For now the task is to prove that every
## usage of a local variable 'v' is covered by an initialization to 'v'
## first.
## We transform the AST into a linear list of instructions first to
## make this easier to handle: There are only 2 different branching
## instructions: 'goto X' is an unconditional goto, 'fork X'
## is a conditional goto (either the next instruction or 'X' can be
## taken). Exhaustive case statements are translated
## so that the last branch is transformed into an 'else' branch.
## ``return`` and ``break`` are all covered by 'goto'.
## The case to detect is ``use v`` that is not dominated by
## a ``def v``.
## The data structures and algorithms used here are inspired by
## "A Graph–Free Approach to Data–Flow Analysis" by Markus Mohnen.
## https://link.springer.com/content/pdf/10.1007/3-540-45937-5_6.pdf
import ast, astalgo, types, intsets, tables, msgs
type
InstrKind* = enum
goto, fork, def, use,
useWithinCall # this strange special case is used to get more
# move optimizations out of regular code
# XXX This is still overly pessimistic in
# call(let x = foo; bar(x))
Instr* = object
n*: PNode
case kind*: InstrKind
of def, use, useWithinCall: sym*: PSym
of goto, fork: dest*: int
ControlFlowGraph* = seq[Instr]
TPosition = distinct int
TBlock = object
label: PSym
fixups: seq[TPosition]
ValueKind = enum
undef, value, valueOrUndef
Con = object
code: ControlFlowGraph
inCall: int
blocks: seq[TBlock]
proc debugInfo(info: TLineInfo): string =
result = info.toFilename & ":" & $info.line
proc codeListing(c: ControlFlowGraph, result: var string, start=0; last = -1) =
# for debugging purposes
# first iteration: compute all necessary labels:
var jumpTargets = initIntSet()
let last = if last < 0: c.len-1 else: min(last, c.len-1)
for i in start..last:
if c[i].kind in {goto, fork}:
jumpTargets.incl(i+c[i].dest)
var i = start
while i <= last:
if i in jumpTargets: result.add("L" & $i & ":\n")
result.add "\t"
result.add $c[i].kind
result.add "\t"
case c[i].kind
of def, use, useWithinCall:
result.add c[i].sym.name.s
of goto, fork:
result.add "L"
result.add c[i].dest+i
result.add("\t#")
result.add(debugInfo(c[i].n.info))
result.add("\n")
inc i
if i in jumpTargets: result.add("L" & $i & ": End\n")
proc echoCfg*(c: ControlFlowGraph; start=0; last = -1) {.deprecated.} =
## echos the ControlFlowGraph for debugging purposes.
var buf = ""
codeListing(c, buf, start, last)
echo buf
proc forkI(c: var Con; n: PNode): TPosition =
result = TPosition(c.code.len)
c.code.add Instr(n: n, kind: fork, dest: 0)
proc gotoI(c: var Con; n: PNode): TPosition =
result = TPosition(c.code.len)
c.code.add Instr(n: n, kind: goto, dest: 0)
proc genLabel(c: Con): TPosition =
result = TPosition(c.code.len)
proc jmpBack(c: var Con, n: PNode, p = TPosition(0)) =
let dist = p.int - c.code.len
internalAssert(-0x7fff < dist and dist < 0x7fff)
c.code.add Instr(n: n, kind: goto, dest: dist)
proc patch(c: var Con, p: TPosition) =
# patch with current index
let p = p.int
let diff = c.code.len - p
internalAssert(-0x7fff < diff and diff < 0x7fff)
c.code[p].dest = diff
proc popBlock(c: var Con; oldLen: int) =
for f in c.blocks[oldLen].fixups:
c.patch(f)
c.blocks.setLen(oldLen)
template withBlock(labl: PSym; body: untyped) {.dirty.} =
var oldLen {.gensym.} = c.blocks.len
c.blocks.add TBlock(label: labl, fixups: @[])
body
popBlock(c, oldLen)
proc isTrue(n: PNode): bool =
n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
n.kind == nkIntLit and n.intVal != 0
proc gen(c: var Con; n: PNode) # {.noSideEffect.}
proc genWhile(c: var Con; n: PNode) =
# L1:
# cond, tmp
# fjmp tmp, L2
# body
# jmp L1
# L2:
let L1 = c.genLabel
withBlock(nil):
if isTrue(n.sons[0]):
c.gen(n.sons[1])
c.jmpBack(n, L1)
else:
c.gen(n.sons[0])
let L2 = c.forkI(n)
c.gen(n.sons[1])
c.jmpBack(n, L1)
c.patch(L2)
proc genBlock(c: var Con; n: PNode) =
withBlock(n.sons[0].sym):
c.gen(n.sons[1])
proc genBreak(c: var Con; n: PNode) =
let L1 = c.gotoI(n)
if n.sons[0].kind == nkSym:
#echo cast[int](n.sons[0].sym)
for i in countdown(c.blocks.len-1, 0):
if c.blocks[i].label == n.sons[0].sym:
c.blocks[i].fixups.add L1
return
globalError(n.info, errGenerated, "VM problem: cannot find 'break' target")
else:
c.blocks[c.blocks.high].fixups.add L1
proc genIf(c: var Con, n: PNode) =
var endings: seq[TPosition] = @[]
for i in countup(0, len(n) - 1):
var it = n.sons[i]
if it.len == 2:
c.gen(it.sons[0].sons[1])
var elsePos = c.forkI(it.sons[0].sons[1])
c.gen(it.sons[1])
if i < sonsLen(n)-1:
endings.add(c.gotoI(it.sons[1]))
c.patch(elsePos)
else:
c.gen(it.sons[0])
for endPos in endings: c.patch(endPos)
proc genAndOr(c: var Con; n: PNode) =
# asgn dest, a
# fork L1
# asgn dest, b
# L1:
c.gen(n.sons[1])
let L1 = c.forkI(n)
c.gen(n.sons[2])
c.patch(L1)
proc genCase(c: var Con; n: PNode) =
# if (!expr1) goto L1;
# thenPart
# goto LEnd
# L1:
# if (!expr2) goto L2;
# thenPart2
# goto LEnd
# L2:
# elsePart
# Lend:
var endings: seq[TPosition] = @[]
c.gen(n.sons[0])
for i in 1 ..< n.len:
let it = n.sons[i]
if it.len == 1:
c.gen(it.sons[0])
else:
let elsePos = c.forkI(it.lastSon)
c.gen(it.lastSon)
if i < sonsLen(n)-1:
endings.add(c.gotoI(it.lastSon))
c.patch(elsePos)
for endPos in endings: c.patch(endPos)
proc genTry(c: var Con; n: PNode) =
var endings: seq[TPosition] = @[]
let elsePos = c.forkI(n)
c.gen(n.sons[0])
c.patch(elsePos)
for i in 1 ..< n.len:
let it = n.sons[i]
if it.kind != nkFinally:
var blen = len(it)
let endExcept = c.forkI(it)
c.gen(it.lastSon)
if i < sonsLen(n)-1:
endings.add(c.gotoI(it))
c.patch(endExcept)
for endPos in endings: c.patch(endPos)
let fin = lastSon(n)
if fin.kind == nkFinally:
c.gen(fin.sons[0])
proc genRaise(c: var Con; n: PNode) =
gen(c, n.sons[0])
c.code.add Instr(n: n, kind: goto, dest: high(int) - c.code.len)
proc genReturn(c: var Con; n: PNode) =
if n.sons[0].kind != nkEmpty: gen(c, n.sons[0])
c.code.add Instr(n: n, kind: goto, dest: high(int) - c.code.len)
const
InterestingSyms = {skVar, skResult, skLet}
proc genUse(c: var Con; n: PNode) =
var n = n
while n.kind in {nkDotExpr, nkCheckedFieldExpr,
nkBracketExpr, nkDerefExpr, nkHiddenDeref,
nkAddr, nkHiddenAddr}:
n = n[0]
if n.kind == nkSym and n.sym.kind in InterestingSyms:
if c.inCall > 0:
c.code.add Instr(n: n, kind: useWithinCall, sym: n.sym)
else:
c.code.add Instr(n: n, kind: use, sym: n.sym)
proc genDef(c: var Con; n: PNode) =
if n.kind == nkSym and n.sym.kind in InterestingSyms:
c.code.add Instr(n: n, kind: def, sym: n.sym)
proc genCall(c: var Con; n: PNode) =
gen(c, n[0])
var t = n[0].typ
if t != nil: t = t.skipTypes(abstractInst)
inc c.inCall
for i in 1..<n.len:
gen(c, n[i])
if t != nil and i < t.len and t.sons[i].kind == tyVar:
genDef(c, n[i])
dec c.inCall
proc genMagic(c: var Con; n: PNode; m: TMagic) =
case m
of mAnd, mOr: c.genAndOr(n)
of mNew, mNewFinalize:
genDef(c, n[1])
for i in 2..<n.len: gen(c, n[i])
of mExit:
genCall(c, n)
c.code.add Instr(n: n, kind: goto, dest: high(int) - c.code.len)
else:
genCall(c, n)
proc genVarSection(c: var Con; n: PNode) =
for a in n:
if a.kind == nkCommentStmt: continue
if a.kind == nkVarTuple:
gen(c, a.lastSon)
for i in 0 .. a.len-3: genDef(c, a[i])
else:
gen(c, a.lastSon)
if a.lastSon.kind != nkEmpty:
genDef(c, a.sons[0])
proc gen(c: var Con; n: PNode) =
case n.kind
of nkSym: genUse(c, n)
of nkCallKinds:
if n.sons[0].kind == nkSym:
let s = n.sons[0].sym
if s.magic != mNone:
genMagic(c, n, s.magic)
else:
genCall(c, n)
else:
genCall(c, n)
of nkCharLit..nkNilLit: discard
of nkAsgn, nkFastAsgn:
gen(c, n[1])
genDef(c, n[0])
of nkDotExpr, nkCheckedFieldExpr, nkBracketExpr,
nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr:
gen(c, n[0])
of nkIfStmt, nkIfExpr: genIf(c, n)
of nkWhenStmt:
# This is "when nimvm" node. Chose the first branch.
gen(c, n.sons[0].sons[1])
of nkCaseStmt: genCase(c, n)
of nkWhileStmt: genWhile(c, n)
of nkBlockExpr, nkBlockStmt: genBlock(c, n)
of nkReturnStmt: genReturn(c, n)
of nkRaiseStmt: genRaise(c, n)
of nkBreakStmt: genBreak(c, n)
of nkTryStmt: genTry(c, n)
of nkStmtList, nkStmtListExpr, nkChckRangeF, nkChckRange64, nkChckRange,
nkBracket, nkCurly, nkPar, nkClosure, nkObjConstr:
for x in n: gen(c, x)
of nkPragmaBlock: gen(c, n.lastSon)
of nkDiscardStmt: gen(c, n.sons[0])
of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkExprColonExpr, nkExprEqExpr,
nkCast:
gen(c, n.sons[1])
of nkObjDownConv, nkStringToCString, nkCStringToString: gen(c, n.sons[0])
of nkVarSection, nkLetSection: genVarSection(c, n)
else: discard
proc dfa(code: seq[Instr]) =
# We aggressively push 'undef' values for every 'use v' instruction
# until they are eliminated via a 'def v' instructions.
# If we manage to push one 'undef' to a 'use' instruction, we produce
# an error:
var undef = initIntSet()
for i in 0..<code.len:
if code[i].kind == use: undef.incl(code[i].sym.id)
var s = newSeq[IntSet](code.len)
for i in 0..<code.len:
assign(s[i], undef)
# In the original paper, W := {0,...,n} is done. This is wasteful, we
# have no intention to analyse a program like
#
# return 3
# echo a + b
#
# any further than necessary.
var w = @[0]
while w.len > 0:
var pc = w[^1]
# this simulates a single linear control flow execution:
while true:
# according to the paper, it is better to shrink the working set here
# in this inner loop:
let widx = w.find(pc)
if widx >= 0: w.del(widx)
# our interpretation ![I!]:
var sid = -1
case code[pc].kind
of goto, fork: discard
of use, useWithinCall:
let sym = code[pc].sym
if s[pc].contains(sym.id):
localError(code[pc].n.info, "variable read before initialized: " & sym.name.s)
of def:
sid = code[pc].sym.id
var pc2: int
if code[pc].kind == goto:
pc2 = pc + code[pc].dest
else:
pc2 = pc + 1
if code[pc].kind == fork:
let l = pc + code[pc].dest
if sid >= 0 and s[l].missingOrExcl(sid):
w.add l
if sid >= 0 and s[pc2].missingOrExcl(sid):
pc = pc2
else:
break
if pc >= code.len: break
when false:
case code[pc].kind
of use:
let s = code[pc].sym
if undefB.contains(s.id):
localError(code[pc].n.info, "variable read before initialized: " & s.name.s)
break
inc pc
of def:
let s = code[pc].sym
# exclude 'undef' for s for this path through the graph.
if not undefB.missingOrExcl(s.id):
inc pc
else:
break
#undefB.excl s.id
#inc pc
when false:
let prev = bindings.getOrDefault(s.id)
if prev != value:
# well now it has a value and we made progress, so
bindings[s.id] = value
inc pc
else:
break
of fork:
let diff = code[pc].dest
# we follow pc + 1 and remember the label for later:
w.add pc+diff
inc pc
of goto:
let diff = code[pc].dest
pc = pc + diff
if pc >= code.len: break
proc dataflowAnalysis*(s: PSym; body: PNode) =
var c = Con(code: @[], blocks: @[])
gen(c, body)
#echoCfg(c.code)
dfa(c.code)
proc constructCfg*(s: PSym; body: PNode): ControlFlowGraph =
## constructs a control flow graph for ``body``.
var c = Con(code: @[], blocks: @[])
shallowCopy(result, c.code)

View file

@ -15,8 +15,8 @@ import
ast, strutils, strtabs, options, msgs, os, ropes, idents, ast, strutils, strtabs, options, msgs, os, ropes, idents,
wordrecg, syntaxes, renderer, lexer, packages/docutils/rstast, wordrecg, syntaxes, renderer, lexer, packages/docutils/rstast,
packages/docutils/rst, packages/docutils/rstgen, times, packages/docutils/rst, packages/docutils/rstgen, times,
packages/docutils/highlite, importer, sempass2, json, xmltree, cgi, packages/docutils/highlite, sempass2, json, xmltree, cgi,
typesrenderer, astalgo typesrenderer, astalgo, modulepaths
type type
TSections = array[TSymKind, Rope] TSections = array[TSymKind, Rope]

View file

@ -77,7 +77,7 @@ proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode =
# now that we have working untyped parameters. # now that we have working untyped parameters.
genericParams = if sfImmediate in s.flags or fromHlo: 0 genericParams = if sfImmediate in s.flags or fromHlo: 0
else: s.ast[genericParamsPos].len else: s.ast[genericParamsPos].len
expectedRegularParams = <s.typ.len expectedRegularParams = s.typ.len-1
givenRegularParams = totalParams - genericParams givenRegularParams = totalParams - genericParams
if givenRegularParams < 0: givenRegularParams = 0 if givenRegularParams < 0: givenRegularParams = 0

View file

@ -13,14 +13,10 @@ import
llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options, llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
renderer, filters renderer, filters
proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode): PLLStream
# #! template(subsChar='$', metaChar='#') | standard(version="0.7.2")
# implementation
type type
TParseState = enum TParseState = enum
psDirective, psTempl psDirective, psTempl
TTmplParser{.final.} = object TTmplParser = object
inp: PLLStream inp: PLLStream
state: TParseState state: TParseState
info: TLineInfo info: TLineInfo
@ -61,6 +57,10 @@ proc scanPar(p: var TTmplParser, d: int) =
proc withInExpr(p: TTmplParser): bool {.inline.} = proc withInExpr(p: TTmplParser): bool {.inline.} =
result = p.par > 0 or p.bracket > 0 or p.curly > 0 result = p.par > 0 or p.bracket > 0 or p.curly > 0
const
LineContinuationOprs = {'+', '-', '*', '/', '\\', '<', '>', '^',
'|', '%', '&', '$', '@', '~', ','}
proc parseLine(p: var TTmplParser) = proc parseLine(p: var TTmplParser) =
var j = 0 var j = 0
while p.x[j] == ' ': inc(j) while p.x[j] == ' ': inc(j)
@ -77,7 +77,7 @@ proc parseLine(p: var TTmplParser) =
inc(j) inc(j)
scanPar(p, j) scanPar(p, j)
p.pendingExprLine = withInExpr(p) or llstream.endsWithOpr(p.x) p.pendingExprLine = withInExpr(p) or p.x.endsWith(LineContinuationOprs)
case keyw case keyw
of "end": of "end":
if p.indent >= 2: if p.indent >= 2:
@ -88,14 +88,14 @@ proc parseLine(p: var TTmplParser) =
llStreamWrite(p.outp, spaces(p.indent)) llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, "#end") llStreamWrite(p.outp, "#end")
of "if", "when", "try", "while", "for", "block", "case", "proc", "iterator", of "if", "when", "try", "while", "for", "block", "case", "proc", "iterator",
"converter", "macro", "template", "method": "converter", "macro", "template", "method", "func":
llStreamWrite(p.outp, spaces(p.indent)) llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, substr(p.x, d)) llStreamWrite(p.outp, substr(p.x, d))
inc(p.indent, 2) inc(p.indent, 2)
of "elif", "of", "else", "except", "finally": of "elif", "of", "else", "except", "finally":
llStreamWrite(p.outp, spaces(p.indent - 2)) llStreamWrite(p.outp, spaces(p.indent - 2))
llStreamWrite(p.outp, substr(p.x, d)) llStreamWrite(p.outp, substr(p.x, d))
of "wLet", "wVar", "wConst", "wType": of "let", "var", "const", "type":
llStreamWrite(p.outp, spaces(p.indent)) llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, substr(p.x, d)) llStreamWrite(p.outp, substr(p.x, d))
if not p.x.contains({':', '='}): if not p.x.contains({':', '='}):
@ -199,7 +199,7 @@ proc parseLine(p: var TTmplParser) =
inc(j) inc(j)
llStreamWrite(p.outp, "\\n\"") llStreamWrite(p.outp, "\\n\"")
proc filterTmpl(stdin: PLLStream, filename: string, call: PNode): PLLStream = proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode): PLLStream =
var p: TTmplParser var p: TTmplParser
p.info = newLineInfo(filename, 0, 0) p.info = newLineInfo(filename, 0, 0)
p.outp = llStreamOpen("") p.outp = llStreamOpen("")

View file

@ -13,14 +13,6 @@ import
llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options, llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
renderer renderer
proc filterReplace*(stdin: PLLStream, filename: string, call: PNode): PLLStream
proc filterStrip*(stdin: PLLStream, filename: string, call: PNode): PLLStream
# helpers to retrieve arguments:
proc charArg*(n: PNode, name: string, pos: int, default: char): char
proc strArg*(n: PNode, name: string, pos: int, default: string): string
proc boolArg*(n: PNode, name: string, pos: int, default: bool): bool
# implementation
proc invalidPragma(n: PNode) = proc invalidPragma(n: PNode) =
localError(n.info, errXNotAllowedHere, renderTree(n, {renderNoComments})) localError(n.info, errXNotAllowedHere, renderTree(n, {renderNoComments}))
@ -35,26 +27,26 @@ proc getArg(n: PNode, name: string, pos: int): PNode =
elif i == pos: elif i == pos:
return n.sons[i] return n.sons[i]
proc charArg(n: PNode, name: string, pos: int, default: char): char = proc charArg*(n: PNode, name: string, pos: int, default: char): char =
var x = getArg(n, name, pos) var x = getArg(n, name, pos)
if x == nil: result = default if x == nil: result = default
elif x.kind == nkCharLit: result = chr(int(x.intVal)) elif x.kind == nkCharLit: result = chr(int(x.intVal))
else: invalidPragma(n) else: invalidPragma(n)
proc strArg(n: PNode, name: string, pos: int, default: string): string = proc strArg*(n: PNode, name: string, pos: int, default: string): string =
var x = getArg(n, name, pos) var x = getArg(n, name, pos)
if x == nil: result = default if x == nil: result = default
elif x.kind in {nkStrLit..nkTripleStrLit}: result = x.strVal elif x.kind in {nkStrLit..nkTripleStrLit}: result = x.strVal
else: invalidPragma(n) else: invalidPragma(n)
proc boolArg(n: PNode, name: string, pos: int, default: bool): bool = proc boolArg*(n: PNode, name: string, pos: int, default: bool): bool =
var x = getArg(n, name, pos) var x = getArg(n, name, pos)
if x == nil: result = default if x == nil: result = default
elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "true") == 0: result = true elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "true") == 0: result = true
elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "false") == 0: result = false elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "false") == 0: result = false
else: invalidPragma(n) else: invalidPragma(n)
proc filterStrip(stdin: PLLStream, filename: string, call: PNode): PLLStream = proc filterStrip*(stdin: PLLStream, filename: string, call: PNode): PLLStream =
var pattern = strArg(call, "startswith", 1, "") var pattern = strArg(call, "startswith", 1, "")
var leading = boolArg(call, "leading", 2, true) var leading = boolArg(call, "leading", 2, true)
var trailing = boolArg(call, "trailing", 3, true) var trailing = boolArg(call, "trailing", 3, true)
@ -68,7 +60,7 @@ proc filterStrip(stdin: PLLStream, filename: string, call: PNode): PLLStream =
llStreamWriteln(result, line) llStreamWriteln(result, line)
llStreamClose(stdin) llStreamClose(stdin)
proc filterReplace(stdin: PLLStream, filename: string, call: PNode): PLLStream = proc filterReplace*(stdin: PLLStream, filename: string, call: PNode): PLLStream =
var sub = strArg(call, "sub", 1, "") var sub = strArg(call, "sub", 1, "")
if len(sub) == 0: invalidPragma(call) if len(sub) == 0: invalidPragma(call)
var by = strArg(call, "by", 2, "") var by = strArg(call, "by", 2, "")

View file

@ -7,8 +7,7 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
## Module that implements ``gorge`` for the compiler as well as ## Module that implements ``gorge`` for the compiler.
## the scriptable import mechanism.
import msgs, securehash, os, osproc, streams, strutils, options import msgs, securehash, os, osproc, streams, strutils, options
@ -56,28 +55,3 @@ proc opGorge*(cmd, input, cache: string, info: TLineInfo): (string, int) =
result = p.readOutput result = p.readOutput
except IOError, OSError: except IOError, OSError:
result = ("", -1) result = ("", -1)
proc scriptableImport*(pkg, subdir: string; info: TLineInfo): string =
var cmd = getConfigVar("resolver.exe")
if cmd.len == 0: cmd = "nimresolve"
else: cmd = quoteShell(cmd)
cmd.add " --source:"
cmd.add quoteShell(info.toFullPath())
cmd.add " --stdlib:"
cmd.add quoteShell(options.libpath)
cmd.add " --project:"
cmd.add quoteShell(gProjectFull)
if subdir.len != 0:
cmd.add " --subdir:"
cmd.add quoteShell(subdir)
if options.gNoNimblePath:
cmd.add " --nonimblepath"
cmd.add ' '
cmd.add quoteShell(pkg)
let (res, exitCode) = opGorge(cmd, "", cmd, info)
if exitCode == 0:
result = res.strip()
elif res.len > 0:
localError(info, res)
else:
localError(info, "cannot resolve: " & (pkg / subdir))

View file

@ -36,7 +36,7 @@ proc applyPatterns(c: PContext, n: PNode): PNode =
# we apply the last pattern first, so that pattern overriding is possible; # we apply the last pattern first, so that pattern overriding is possible;
# however the resulting AST would better not trigger the old rule then # however the resulting AST would better not trigger the old rule then
# anymore ;-) # anymore ;-)
for i in countdown(<c.patterns.len, 0): for i in countdown(c.patterns.len-1, 0):
let pattern = c.patterns[i] let pattern = c.patterns[i]
if not isNil(pattern): if not isNil(pattern):
let x = applyRule(c, pattern, result) let x = applyRule(c, pattern, result)

View file

@ -11,83 +11,11 @@
import import
intsets, strutils, os, ast, astalgo, msgs, options, idents, rodread, lookups, intsets, strutils, os, ast, astalgo, msgs, options, idents, rodread, lookups,
semdata, passes, renderer, gorgeimpl semdata, passes, renderer, modulepaths
proc evalImport*(c: PContext, n: PNode): PNode proc evalImport*(c: PContext, n: PNode): PNode
proc evalFrom*(c: PContext, n: PNode): PNode proc evalFrom*(c: PContext, n: PNode): PNode
proc lookupPackage(pkg, subdir: PNode): string =
let sub = if subdir != nil: renderTree(subdir, {renderNoComments}).replace(" ") else: ""
case pkg.kind
of nkStrLit, nkRStrLit, nkTripleStrLit:
result = scriptableImport(pkg.strVal, sub, pkg.info)
of nkIdent:
result = scriptableImport(pkg.ident.s, sub, pkg.info)
else:
localError(pkg.info, "package name must be an identifier or string literal")
result = ""
proc getModuleName*(n: PNode): string =
# This returns a short relative module name without the nim extension
# e.g. like "system", "importer" or "somepath/module"
# The proc won't perform any checks that the path is actually valid
case n.kind
of nkStrLit, nkRStrLit, nkTripleStrLit:
try:
result = pathSubs(n.strVal, n.info.toFullPath().splitFile().dir)
except ValueError:
localError(n.info, "invalid path: " & n.strVal)
result = n.strVal
of nkIdent:
result = n.ident.s
of nkSym:
result = n.sym.name.s
of nkInfix:
let n0 = n[0]
let n1 = n[1]
if n0.kind == nkIdent and n0.ident.id == getIdent("as").id:
# XXX hack ahead:
n.kind = nkImportAs
n.sons[0] = n.sons[1]
n.sons[1] = n.sons[2]
n.sons.setLen(2)
return getModuleName(n.sons[0])
if n1.kind == nkPrefix and n1[0].kind == nkIdent and n1[0].ident.s == "$":
if n0.kind == nkIdent and n0.ident.s == "/":
result = lookupPackage(n1[1], n[2])
else:
localError(n.info, "only '/' supported with $package notation")
result = ""
else:
# hacky way to implement 'x / y /../ z':
result = getModuleName(n1)
result.add renderTree(n0, {renderNoComments})
result.add getModuleName(n[2])
of nkPrefix:
if n.sons[0].kind == nkIdent and n.sons[0].ident.s == "$":
result = lookupPackage(n[1], nil)
else:
# hacky way to implement 'x / y /../ z':
result = renderTree(n, {renderNoComments}).replace(" ")
of nkDotExpr:
result = renderTree(n, {renderNoComments}).replace(".", "/")
of nkImportAs:
result = getModuleName(n.sons[0])
else:
localError(n.info, errGenerated, "invalid module name: '$1'" % n.renderTree)
result = ""
proc checkModuleName*(n: PNode; doLocalError=true): int32 =
# This returns the full canonical path for a given module import
let modulename = n.getModuleName
let fullPath = findModule(modulename, n.info.toFullPath)
if fullPath.len == 0:
if doLocalError:
localError(n.info, errCannotOpenFile, modulename)
result = InvalidFileIDX
else:
result = fullPath.fileInfoIdx
proc importPureEnumField*(c: PContext; s: PSym) = proc importPureEnumField*(c: PContext; s: PSym) =
var check = strTableGet(c.importTable.symbols, s.name) var check = strTableGet(c.importTable.symbols, s.name)
if check == nil: if check == nil:

View file

@ -126,6 +126,10 @@ type
literal*: string # the parsed (string) literal; and literal*: string # the parsed (string) literal; and
# documentation comments are here too # documentation comments are here too
line*, col*: int line*, col*: int
when defined(nimpretty):
offsetA*, offsetB*: int # used for pretty printing so that literals
# like 0b01 or r"\L" are unaffected
commentOffsetA*, commentOffsetB*: int
TErrorHandler* = proc (info: TLineInfo; msg: TMsgKind; arg: string) TErrorHandler* = proc (info: TLineInfo; msg: TMsgKind; arg: string)
TLexer* = object of TBaseLexer TLexer* = object of TBaseLexer
@ -141,10 +145,19 @@ type
when defined(nimsuggest): when defined(nimsuggest):
previousToken: TLineInfo previousToken: TLineInfo
when defined(nimpretty):
var
gIndentationWidth*: int
var gLinesCompiled*: int # all lines that have been compiled var gLinesCompiled*: int # all lines that have been compiled
proc getLineInfo*(L: TLexer, tok: TToken): TLineInfo {.inline.} = proc getLineInfo*(L: TLexer, tok: TToken): TLineInfo {.inline.} =
newLineInfo(L.fileIdx, tok.line, tok.col) result = newLineInfo(L.fileIdx, tok.line, tok.col)
when defined(nimpretty):
result.offsetA = tok.offsetA
result.offsetB = tok.offsetB
result.commentOffsetA = tok.commentOffsetA
result.commentOffsetB = tok.commentOffsetB
proc isKeyword*(kind: TTokType): bool = proc isKeyword*(kind: TTokType): bool =
result = (kind >= tokKeywordLow) and (kind <= tokKeywordHigh) result = (kind >= tokKeywordLow) and (kind <= tokKeywordHigh)
@ -192,6 +205,9 @@ proc initToken*(L: var TToken) =
L.fNumber = 0.0 L.fNumber = 0.0
L.base = base10 L.base = base10
L.ident = nil L.ident = nil
when defined(nimpretty):
L.commentOffsetA = 0
L.commentOffsetB = 0
proc fillToken(L: var TToken) = proc fillToken(L: var TToken) =
L.tokType = tkInvalid L.tokType = tkInvalid
@ -202,6 +218,9 @@ proc fillToken(L: var TToken) =
L.fNumber = 0.0 L.fNumber = 0.0
L.base = base10 L.base = base10
L.ident = nil L.ident = nil
when defined(nimpretty):
L.commentOffsetA = 0
L.commentOffsetB = 0
proc openLexer*(lex: var TLexer, fileIdx: int32, inputstream: PLLStream; proc openLexer*(lex: var TLexer, fileIdx: int32, inputstream: PLLStream;
cache: IdentCache) = cache: IdentCache) =
@ -245,11 +264,13 @@ proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") =
proc matchTwoChars(L: TLexer, first: char, second: set[char]): bool = proc matchTwoChars(L: TLexer, first: char, second: set[char]): bool =
result = (L.buf[L.bufpos] == first) and (L.buf[L.bufpos + 1] in second) result = (L.buf[L.bufpos] == first) and (L.buf[L.bufpos + 1] in second)
template tokenBegin(pos) {.dirty.} = template tokenBegin(tok, pos) {.dirty.} =
when defined(nimsuggest): when defined(nimsuggest):
var colA = getColNumber(L, pos) var colA = getColNumber(L, pos)
when defined(nimpretty):
tok.offsetA = L.offsetBase + pos
template tokenEnd(pos) {.dirty.} = template tokenEnd(tok, pos) {.dirty.} =
when defined(nimsuggest): when defined(nimsuggest):
let colB = getColNumber(L, pos)+1 let colB = getColNumber(L, pos)+1
if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and
@ -257,8 +278,10 @@ template tokenEnd(pos) {.dirty.} =
L.cursor = CursorPosition.InToken L.cursor = CursorPosition.InToken
gTrackPos.col = colA.int16 gTrackPos.col = colA.int16
colA = 0 colA = 0
when defined(nimpretty):
tok.offsetB = L.offsetBase + pos
template tokenEndIgnore(pos) = template tokenEndIgnore(tok, pos) =
when defined(nimsuggest): when defined(nimsuggest):
let colB = getColNumber(L, pos) let colB = getColNumber(L, pos)
if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and
@ -266,8 +289,10 @@ template tokenEndIgnore(pos) =
gTrackPos.fileIndex = trackPosInvalidFileIdx gTrackPos.fileIndex = trackPosInvalidFileIdx
gTrackPos.line = -1 gTrackPos.line = -1
colA = 0 colA = 0
when defined(nimpretty):
tok.offsetB = L.offsetBase + pos
template tokenEndPrevious(pos) = template tokenEndPrevious(tok, pos) =
when defined(nimsuggest): when defined(nimsuggest):
# when we detect the cursor in whitespace, we attach the track position # when we detect the cursor in whitespace, we attach the track position
# to the token that came before that, but only if we haven't detected # to the token that came before that, but only if we haven't detected
@ -279,6 +304,8 @@ template tokenEndPrevious(pos) =
gTrackPos = L.previousToken gTrackPos = L.previousToken
gTrackPosAttached = true gTrackPosAttached = true
colA = 0 colA = 0
when defined(nimpretty):
tok.offsetB = L.offsetBase + pos
{.push overflowChecks: off.} {.push overflowChecks: off.}
# We need to parse the largest uint literal without overflow checks # We need to parse the largest uint literal without overflow checks
@ -363,7 +390,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
result.literal = "" result.literal = ""
result.base = base10 result.base = base10
startpos = L.bufpos startpos = L.bufpos
tokenBegin(startPos) tokenBegin(result, startPos)
# First stage: find out base, make verifications, build token literal string # First stage: find out base, make verifications, build token literal string
if L.buf[L.bufpos] == '0' and L.buf[L.bufpos + 1] in baseCodeChars + {'O'}: if L.buf[L.bufpos] == '0' and L.buf[L.bufpos + 1] in baseCodeChars + {'O'}:
@ -573,7 +600,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
lexMessageLitNum(L, errInvalidNumber, startpos) lexMessageLitNum(L, errInvalidNumber, startpos)
except OverflowError, RangeError: except OverflowError, RangeError:
lexMessageLitNum(L, errNumberOutOfRange, startpos) lexMessageLitNum(L, errNumberOutOfRange, startpos)
tokenEnd(postPos-1) tokenEnd(result, postPos-1)
L.bufpos = postPos L.bufpos = postPos
proc handleHexChar(L: var TLexer, xi: var int) = proc handleHexChar(L: var TLexer, xi: var int) =
@ -691,10 +718,11 @@ proc handleCRLF(L: var TLexer, pos: int): int =
else: result = pos else: result = pos
proc getString(L: var TLexer, tok: var TToken, rawMode: bool) = proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
var pos = L.bufpos + 1 # skip " var pos = L.bufpos
var buf = L.buf # put `buf` in a register var buf = L.buf # put `buf` in a register
var line = L.lineNumber # save linenumber for better error message var line = L.lineNumber # save linenumber for better error message
tokenBegin(pos) tokenBegin(tok, pos)
inc pos # skip "
if buf[pos] == '\"' and buf[pos+1] == '\"': if buf[pos] == '\"' and buf[pos+1] == '\"':
tok.tokType = tkTripleStrLit # long string literal: tok.tokType = tkTripleStrLit # long string literal:
inc(pos, 2) # skip "" inc(pos, 2) # skip ""
@ -710,18 +738,18 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
of '\"': of '\"':
if buf[pos+1] == '\"' and buf[pos+2] == '\"' and if buf[pos+1] == '\"' and buf[pos+2] == '\"' and
buf[pos+3] != '\"': buf[pos+3] != '\"':
tokenEndIgnore(pos+2) tokenEndIgnore(tok, pos+2)
L.bufpos = pos + 3 # skip the three """ L.bufpos = pos + 3 # skip the three """
break break
add(tok.literal, '\"') add(tok.literal, '\"')
inc(pos) inc(pos)
of CR, LF: of CR, LF:
tokenEndIgnore(pos) tokenEndIgnore(tok, pos)
pos = handleCRLF(L, pos) pos = handleCRLF(L, pos)
buf = L.buf buf = L.buf
add(tok.literal, tnl) add(tok.literal, tnl)
of nimlexbase.EndOfFile: of nimlexbase.EndOfFile:
tokenEndIgnore(pos) tokenEndIgnore(tok, pos)
var line2 = L.lineNumber var line2 = L.lineNumber
L.lineNumber = line L.lineNumber = line
lexMessagePos(L, errClosingTripleQuoteExpected, L.lineStart) lexMessagePos(L, errClosingTripleQuoteExpected, L.lineStart)
@ -742,11 +770,11 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
inc(pos, 2) inc(pos, 2)
add(tok.literal, '"') add(tok.literal, '"')
else: else:
tokenEndIgnore(pos) tokenEndIgnore(tok, pos)
inc(pos) # skip '"' inc(pos) # skip '"'
break break
elif c in {CR, LF, nimlexbase.EndOfFile}: elif c in {CR, LF, nimlexbase.EndOfFile}:
tokenEndIgnore(pos) tokenEndIgnore(tok, pos)
lexMessage(L, errClosingQuoteExpected) lexMessage(L, errClosingQuoteExpected)
break break
elif (c == '\\') and not rawMode: elif (c == '\\') and not rawMode:
@ -759,7 +787,7 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
L.bufpos = pos L.bufpos = pos
proc getCharacter(L: var TLexer, tok: var TToken) = proc getCharacter(L: var TLexer, tok: var TToken) =
tokenBegin(L.bufpos) tokenBegin(tok, L.bufpos)
inc(L.bufpos) # skip ' inc(L.bufpos) # skip '
var c = L.buf[L.bufpos] var c = L.buf[L.bufpos]
case c case c
@ -769,14 +797,14 @@ proc getCharacter(L: var TLexer, tok: var TToken) =
tok.literal = $c tok.literal = $c
inc(L.bufpos) inc(L.bufpos)
if L.buf[L.bufpos] != '\'': lexMessage(L, errMissingFinalQuote) if L.buf[L.bufpos] != '\'': lexMessage(L, errMissingFinalQuote)
tokenEndIgnore(L.bufpos) tokenEndIgnore(tok, L.bufpos)
inc(L.bufpos) # skip ' inc(L.bufpos) # skip '
proc getSymbol(L: var TLexer, tok: var TToken) = proc getSymbol(L: var TLexer, tok: var TToken) =
var h: Hash = 0 var h: Hash = 0
var pos = L.bufpos var pos = L.bufpos
var buf = L.buf var buf = L.buf
tokenBegin(pos) tokenBegin(tok, pos)
while true: while true:
var c = buf[pos] var c = buf[pos]
case c case c
@ -793,7 +821,7 @@ proc getSymbol(L: var TLexer, tok: var TToken) =
break break
inc(pos) inc(pos)
else: break else: break
tokenEnd(pos-1) tokenEnd(tok, pos-1)
h = !$h h = !$h
tok.ident = L.cache.getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h) tok.ident = L.cache.getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
L.bufpos = pos L.bufpos = pos
@ -814,7 +842,7 @@ proc endOperator(L: var TLexer, tok: var TToken, pos: int,
proc getOperator(L: var TLexer, tok: var TToken) = proc getOperator(L: var TLexer, tok: var TToken) =
var pos = L.bufpos var pos = L.bufpos
var buf = L.buf var buf = L.buf
tokenBegin(pos) tokenBegin(tok, pos)
var h: Hash = 0 var h: Hash = 0
while true: while true:
var c = buf[pos] var c = buf[pos]
@ -822,7 +850,7 @@ proc getOperator(L: var TLexer, tok: var TToken) =
h = h !& ord(c) h = h !& ord(c)
inc(pos) inc(pos)
endOperator(L, tok, pos, h) endOperator(L, tok, pos, h)
tokenEnd(pos-1) tokenEnd(tok, pos-1)
# advance pos but don't store it in L.bufpos so the next token (which might # advance pos but don't store it in L.bufpos so the next token (which might
# be an operator too) gets the preceding spaces: # be an operator too) gets the preceding spaces:
tok.strongSpaceB = 0 tok.strongSpaceB = 0
@ -837,7 +865,7 @@ proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int;
var pos = start var pos = start
var buf = L.buf var buf = L.buf
var toStrip = 0 var toStrip = 0
tokenBegin(pos) tokenBegin(tok, pos)
# detect the amount of indentation: # detect the amount of indentation:
if isDoc: if isDoc:
toStrip = getColNumber(L, pos) toStrip = getColNumber(L, pos)
@ -864,36 +892,37 @@ proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int;
if isDoc: if isDoc:
if buf[pos+1] == '#' and buf[pos+2] == '#': if buf[pos+1] == '#' and buf[pos+2] == '#':
if nesting == 0: if nesting == 0:
tokenEndIgnore(pos+2) tokenEndIgnore(tok, pos+2)
inc(pos, 3) inc(pos, 3)
break break
dec nesting dec nesting
tok.literal.add ']' tok.literal.add ']'
elif buf[pos+1] == '#': elif buf[pos+1] == '#':
if nesting == 0: if nesting == 0:
tokenEndIgnore(pos+1) tokenEndIgnore(tok, pos+1)
inc(pos, 2) inc(pos, 2)
break break
dec nesting dec nesting
inc pos inc pos
of CR, LF: of CR, LF:
tokenEndIgnore(pos) tokenEndIgnore(tok, pos)
pos = handleCRLF(L, pos) pos = handleCRLF(L, pos)
buf = L.buf buf = L.buf
# strip leading whitespace: # strip leading whitespace:
when defined(nimpretty): tok.literal.add "\L"
if isDoc: if isDoc:
tok.literal.add "\n" when not defined(nimpretty): tok.literal.add "\n"
inc tok.iNumber inc tok.iNumber
var c = toStrip var c = toStrip
while buf[pos] == ' ' and c > 0: while buf[pos] == ' ' and c > 0:
inc pos inc pos
dec c dec c
of nimlexbase.EndOfFile: of nimlexbase.EndOfFile:
tokenEndIgnore(pos) tokenEndIgnore(tok, pos)
lexMessagePos(L, errGenerated, pos, "end of multiline comment expected") lexMessagePos(L, errGenerated, pos, "end of multiline comment expected")
break break
else: else:
if isDoc: tok.literal.add buf[pos] if isDoc or defined(nimpretty): tok.literal.add buf[pos]
inc(pos) inc(pos)
L.bufpos = pos L.bufpos = pos
@ -907,7 +936,7 @@ proc scanComment(L: var TLexer, tok: var TToken) =
if buf[pos+2] == '[': if buf[pos+2] == '[':
skipMultiLineComment(L, tok, pos+3, true) skipMultiLineComment(L, tok, pos+3, true)
return return
tokenBegin(pos) tokenBegin(tok, pos)
inc(pos, 2) inc(pos, 2)
var toStrip = 0 var toStrip = 0
@ -921,7 +950,7 @@ proc scanComment(L: var TLexer, tok: var TToken) =
if buf[pos] == '\\': lastBackslash = pos+1 if buf[pos] == '\\': lastBackslash = pos+1
add(tok.literal, buf[pos]) add(tok.literal, buf[pos])
inc(pos) inc(pos)
tokenEndIgnore(pos) tokenEndIgnore(tok, pos)
pos = handleCRLF(L, pos) pos = handleCRLF(L, pos)
buf = L.buf buf = L.buf
var indent = 0 var indent = 0
@ -940,14 +969,14 @@ proc scanComment(L: var TLexer, tok: var TToken) =
else: else:
if buf[pos] > ' ': if buf[pos] > ' ':
L.indentAhead = indent L.indentAhead = indent
tokenEndIgnore(pos) tokenEndIgnore(tok, pos)
break break
L.bufpos = pos L.bufpos = pos
proc skip(L: var TLexer, tok: var TToken) = proc skip(L: var TLexer, tok: var TToken) =
var pos = L.bufpos var pos = L.bufpos
var buf = L.buf var buf = L.buf
tokenBegin(pos) tokenBegin(tok, pos)
tok.strongSpaceA = 0 tok.strongSpaceA = 0
while true: while true:
case buf[pos] case buf[pos]
@ -958,7 +987,7 @@ proc skip(L: var TLexer, tok: var TToken) =
if not L.allowTabs: lexMessagePos(L, errTabulatorsAreNotAllowed, pos) if not L.allowTabs: lexMessagePos(L, errTabulatorsAreNotAllowed, pos)
inc(pos) inc(pos)
of CR, LF: of CR, LF:
tokenEndPrevious(pos) tokenEndPrevious(tok, pos)
pos = handleCRLF(L, pos) pos = handleCRLF(L, pos)
buf = L.buf buf = L.buf
var indent = 0 var indent = 0
@ -980,18 +1009,27 @@ proc skip(L: var TLexer, tok: var TToken) =
of '#': of '#':
# do not skip documentation comment: # do not skip documentation comment:
if buf[pos+1] == '#': break if buf[pos+1] == '#': break
when defined(nimpretty):
tok.commentOffsetA = L.offsetBase + pos
if buf[pos+1] == '[': if buf[pos+1] == '[':
skipMultiLineComment(L, tok, pos+2, false) skipMultiLineComment(L, tok, pos+2, false)
pos = L.bufpos pos = L.bufpos
buf = L.buf buf = L.buf
when defined(nimpretty):
tok.commentOffsetB = L.offsetBase + pos
else: else:
tokenBegin(pos) tokenBegin(tok, pos)
while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}: inc(pos) while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}: inc(pos)
tokenEndIgnore(pos+1) tokenEndIgnore(tok, pos+1)
when defined(nimpretty):
tok.commentOffsetB = L.offsetBase + pos + 1
else: else:
break # EndOfFile also leaves the loop break # EndOfFile also leaves the loop
tokenEndPrevious(pos-1) tokenEndPrevious(tok, pos-1)
L.bufpos = pos L.bufpos = pos
when defined(nimpretty):
if gIndentationWidth <= 0:
gIndentationWidth = tok.indent
proc rawGetTok*(L: var TLexer, tok: var TToken) = proc rawGetTok*(L: var TLexer, tok: var TToken) =
template atTokenEnd() {.dirty.} = template atTokenEnd() {.dirty.} =
@ -1014,7 +1052,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
var c = L.buf[L.bufpos] var c = L.buf[L.bufpos]
tok.line = L.lineNumber tok.line = L.lineNumber
tok.col = getColNumber(L, L.bufpos) tok.col = getColNumber(L, L.bufpos)
if c in SymStartChars - {'r', 'R', 'l'}: if c in SymStartChars - {'r', 'R'}:
getSymbol(L, tok) getSymbol(L, tok)
else: else:
case c case c
@ -1031,12 +1069,6 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
of ',': of ',':
tok.tokType = tkComma tok.tokType = tkComma
inc(L.bufpos) inc(L.bufpos)
of 'l':
# if we parsed exactly one character and its a small L (l), this
# is treated as a warning because it may be confused with the number 1
if L.buf[L.bufpos+1] notin (SymChars + {'_'}):
lexMessage(L, warnSmallLshouldNotBeUsed)
getSymbol(L, tok)
of 'r', 'R': of 'r', 'R':
if L.buf[L.bufpos + 1] == '\"': if L.buf[L.bufpos + 1] == '\"':
inc(L.bufpos) inc(L.bufpos)

View file

@ -84,7 +84,6 @@ const
AdditionalLineContinuationOprs = {'#', ':', '='} AdditionalLineContinuationOprs = {'#', ':', '='}
proc endsWithOpr*(x: string): bool = proc endsWithOpr*(x: string): bool =
# also used by the standard template filter:
result = x.endsWith(LineContinuationOprs) result = x.endsWith(LineContinuationOprs)
proc continueLine(line: string, inTripleString: bool): bool {.inline.} = proc continueLine(line: string, inTripleString: bool): bool {.inline.} =

View file

@ -46,7 +46,11 @@ proc considerQuotedIdent*(n: PNode, origin: PNode = nil): PIdent =
of nkSym: id.add(x.sym.name.s) of nkSym: id.add(x.sym.name.s)
else: handleError(n, origin) else: handleError(n, origin)
result = getIdent(id) result = getIdent(id)
of nkOpenSymChoice, nkClosedSymChoice: result = n.sons[0].sym.name of nkOpenSymChoice, nkClosedSymChoice:
if n[0].kind == nkSym:
result = n.sons[0].sym.name
else:
handleError(n, origin)
else: else:
handleError(n, origin) handleError(n, origin)
@ -155,7 +159,7 @@ proc ensureNoMissingOrUnusedSymbols(scope: PScope) =
var s = initTabIter(it, scope.symbols) var s = initTabIter(it, scope.symbols)
var missingImpls = 0 var missingImpls = 0
while s != nil: while s != nil:
if sfForward in s.flags: if sfForward in s.flags and s.kind != skType:
# too many 'implementation of X' errors are annoying # too many 'implementation of X' errors are annoying
# and slow 'suggest' down: # and slow 'suggest' down:
if missingImpls == 0: if missingImpls == 0:
@ -379,7 +383,11 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
result = errorSym(c, n.sons[1]) result = errorSym(c, n.sons[1])
of nkClosedSymChoice, nkOpenSymChoice: of nkClosedSymChoice, nkOpenSymChoice:
o.mode = oimSymChoice o.mode = oimSymChoice
if n[0].kind == nkSym:
result = n.sons[0].sym result = n.sons[0].sym
else:
o.mode = oimDone
return nil
o.symChoiceIndex = 1 o.symChoiceIndex = 1
o.inSymChoice = initIntSet() o.inSymChoice = initIntSet()
incl(o.inSymChoice, result.id) incl(o.inSymChoice, result.id)

View file

@ -70,6 +70,9 @@ proc newTupleAccessRaw*(tup: PNode, i: int): PNode =
lit.intVal = i lit.intVal = i
addSon(result, lit) addSon(result, lit)
proc newTryFinally*(body, final: PNode): PNode =
result = newTree(nkTryStmt, body, newTree(nkFinally, final))
proc lowerTupleUnpackingForAsgn*(n: PNode; owner: PSym): PNode = proc lowerTupleUnpackingForAsgn*(n: PNode; owner: PSym): PNode =
let value = n.lastSon let value = n.lastSon
result = newNodeI(nkStmtList, n.info) result = newNodeI(nkStmtList, n.info)
@ -139,6 +142,14 @@ proc rawIndirectAccess*(a: PNode; field: PSym; info: TLineInfo): PNode =
addSon(result, newSymNode(field)) addSon(result, newSymNode(field))
result.typ = field.typ result.typ = field.typ
proc rawDirectAccess*(obj, field: PSym): PNode =
# returns a.field as a node
assert field.kind == skField
result = newNodeI(nkDotExpr, field.info)
addSon(result, newSymNode obj)
addSon(result, newSymNode field)
result.typ = field.typ
proc lookupInRecord(n: PNode, id: int): PSym = proc lookupInRecord(n: PNode, id: int): PSym =
result = nil result = nil
case n.kind case n.kind
@ -171,8 +182,9 @@ proc addField*(obj: PType; s: PSym) =
field.position = sonsLen(obj.n) field.position = sonsLen(obj.n)
addSon(obj.n, newSymNode(field)) addSon(obj.n, newSymNode(field))
proc addUniqueField*(obj: PType; s: PSym) = proc addUniqueField*(obj: PType; s: PSym): PSym {.discardable.} =
if lookupInRecord(obj.n, s.id) == nil: result = lookupInRecord(obj.n, s.id)
if result == nil:
var field = newSym(skField, getIdent(s.name.s & $obj.n.len), s.owner, s.info) var field = newSym(skField, getIdent(s.name.s & $obj.n.len), s.owner, s.info)
field.id = -s.id field.id = -s.id
let t = skipIntLit(s.typ) let t = skipIntLit(s.typ)
@ -180,6 +192,7 @@ proc addUniqueField*(obj: PType; s: PSym) =
assert t.kind != tyStmt assert t.kind != tyStmt
field.position = sonsLen(obj.n) field.position = sonsLen(obj.n)
addSon(obj.n, newSymNode(field)) addSon(obj.n, newSymNode(field))
result = field
proc newDotExpr(obj, b: PSym): PNode = proc newDotExpr(obj, b: PSym): PNode =
result = newNodeI(nkDotExpr, obj.info) result = newNodeI(nkDotExpr, obj.info)

172
compiler/modulepaths.nim Normal file
View file

@ -0,0 +1,172 @@
#
#
# The Nim Compiler
# (c) Copyright 2017 Contributors
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
import ast, renderer, strutils, msgs, options, idents, os
import nimblecmd
const
considerParentDirs = not defined(noParentProjects)
considerNimbleDirs = not defined(noNimbleDirs)
proc findInNimbleDir(pkg, subdir, dir: string): string =
var best = ""
var bestv = ""
for k, p in os.walkDir(dir, relative=true):
if k == pcDir and p.len > pkg.len+1 and
p[pkg.len] == '-' and p.startsWith(pkg):
let (_, a) = getPathVersion(p)
if bestv.len == 0 or bestv < a:
bestv = a
best = dir / p
if best.len > 0:
var f: File
if open(f, best / changeFileExt(pkg, ".nimble-link")):
# the second line contains what we're interested in, see:
# https://github.com/nim-lang/nimble#nimble-link
var override = ""
discard readLine(f, override)
discard readLine(f, override)
close(f)
if not override.isAbsolute():
best = best / override
else:
best = override
let f = if subdir.len == 0: pkg else: subdir
let res = addFileExt(best / f, "nim")
if best.len > 0 and fileExists(res):
result = res
const stdlibDirs = [
"pure", "core", "arch",
"pure/collections",
"pure/concurrency", "impure",
"wrappers", "wrappers/linenoise",
"windows", "posix", "js"]
proc resolveDollar(project, source, pkg, subdir: string; info: TLineInfo): string =
template attempt(a) =
let x = addFileExt(a, "nim")
if fileExists(x): return x
case pkg
of "stdlib":
if subdir.len == 0:
return options.libpath
else:
for candidate in stdlibDirs:
attempt(options.libpath / candidate / subdir)
of "root":
let root = project.splitFile.dir
if subdir.len == 0:
return root
else:
attempt(root / subdir)
else:
when considerParentDirs:
var p = parentDir(source.splitFile.dir)
# support 'import $karax':
let f = if subdir.len == 0: pkg else: subdir
while p.len > 0:
let dir = p / pkg
if dirExists(dir):
attempt(dir / f)
# 2nd attempt: try to use 'karax/karax'
attempt(dir / pkg / f)
# 3rd attempt: try to use 'karax/src/karax'
attempt(dir / "src" / f)
attempt(dir / "src" / pkg / f)
p = parentDir(p)
when considerNimbleDirs:
if not options.gNoNimblePath:
var nimbleDir = getEnv("NIMBLE_DIR")
if nimbleDir.len == 0: nimbleDir = getHomeDir() / ".nimble"
result = findInNimbleDir(pkg, subdir, nimbleDir / "pkgs")
if result.len > 0: return result
when not defined(windows):
result = findInNimbleDir(pkg, subdir, "/opt/nimble/pkgs")
if result.len > 0: return result
proc scriptableImport(pkg, sub: string; info: TLineInfo): string =
result = resolveDollar(gProjectFull, info.toFullPath(), pkg, sub, info)
proc lookupPackage(pkg, subdir: PNode): string =
let sub = if subdir != nil: renderTree(subdir, {renderNoComments}).replace(" ") else: ""
case pkg.kind
of nkStrLit, nkRStrLit, nkTripleStrLit:
result = scriptableImport(pkg.strVal, sub, pkg.info)
of nkIdent:
result = scriptableImport(pkg.ident.s, sub, pkg.info)
else:
localError(pkg.info, "package name must be an identifier or string literal")
result = ""
proc getModuleName*(n: PNode): string =
# This returns a short relative module name without the nim extension
# e.g. like "system", "importer" or "somepath/module"
# The proc won't perform any checks that the path is actually valid
case n.kind
of nkStrLit, nkRStrLit, nkTripleStrLit:
try:
result = pathSubs(n.strVal, n.info.toFullPath().splitFile().dir)
except ValueError:
localError(n.info, "invalid path: " & n.strVal)
result = n.strVal
of nkIdent:
result = n.ident.s
of nkSym:
result = n.sym.name.s
of nkInfix:
let n0 = n[0]
let n1 = n[1]
if n0.kind == nkIdent and n0.ident.id == getIdent("as").id:
# XXX hack ahead:
n.kind = nkImportAs
n.sons[0] = n.sons[1]
n.sons[1] = n.sons[2]
n.sons.setLen(2)
return getModuleName(n.sons[0])
if n1.kind == nkPrefix and n1[0].kind == nkIdent and n1[0].ident.s == "$":
if n0.kind == nkIdent and n0.ident.s == "/":
result = lookupPackage(n1[1], n[2])
else:
localError(n.info, "only '/' supported with $package notation")
result = ""
else:
# hacky way to implement 'x / y /../ z':
result = getModuleName(n1)
result.add renderTree(n0, {renderNoComments})
result.add getModuleName(n[2])
of nkPrefix:
if n.sons[0].kind == nkIdent and n.sons[0].ident.s == "$":
result = lookupPackage(n[1], nil)
else:
# hacky way to implement 'x / y /../ z':
result = renderTree(n, {renderNoComments}).replace(" ")
of nkDotExpr:
result = renderTree(n, {renderNoComments}).replace(".", "/")
of nkImportAs:
result = getModuleName(n.sons[0])
else:
localError(n.info, errGenerated, "invalid module name: '$1'" % n.renderTree)
result = ""
proc checkModuleName*(n: PNode; doLocalError=true): int32 =
# This returns the full canonical path for a given module import
let modulename = n.getModuleName
let fullPath = findModule(modulename, n.info.toFullPath)
if fullPath.len == 0:
if doLocalError:
localError(n.info, errCannotOpenFile, modulename)
result = InvalidFileIDX
else:
result = fullPath.fileInfoIdx

View file

@ -498,6 +498,9 @@ type
# only 8 bytes. # only 8 bytes.
line*, col*: int16 line*, col*: int16
fileIndex*: int32 fileIndex*: int32
when defined(nimpretty):
offsetA*, offsetB*: int
commentOffsetA*, commentOffsetB*: int
TErrorOutput* = enum TErrorOutput* = enum
eStdOut eStdOut

View file

@ -46,6 +46,7 @@ type
# private data: # private data:
sentinel*: int sentinel*: int
lineStart*: int # index of last line start in buffer lineStart*: int # index of last line start in buffer
offsetBase*: int # use ``offsetBase + bufpos`` to get the offset
proc openBaseLexer*(L: var TBaseLexer, inputstream: PLLStream, proc openBaseLexer*(L: var TBaseLexer, inputstream: PLLStream,
@ -122,7 +123,8 @@ proc fillBaseLexer(L: var TBaseLexer, pos: int): int =
result = pos + 1 # nothing to do result = pos + 1 # nothing to do
else: else:
fillBuffer(L) fillBuffer(L)
L.bufpos = 0 # XXX: is this really correct? L.offsetBase += pos + 1
L.bufpos = 0
result = 0 result = 0
L.lineStart = result L.lineStart = result
@ -146,6 +148,7 @@ proc skipUTF8BOM(L: var TBaseLexer) =
proc openBaseLexer(L: var TBaseLexer, inputstream: PLLStream, bufLen = 8192) = proc openBaseLexer(L: var TBaseLexer, inputstream: PLLStream, bufLen = 8192) =
assert(bufLen > 0) assert(bufLen > 0)
L.bufpos = 0 L.bufpos = 0
L.offsetBase = 0
L.bufLen = bufLen L.bufLen = bufLen
L.buf = cast[cstring](alloc(bufLen * chrSize)) L.buf = cast[cstring](alloc(bufLen * chrSize))
L.sentinel = bufLen - 1 L.sentinel = bufLen - 1

View file

@ -13,5 +13,5 @@
const const
MaxSetElements* = 1 shl 16 # (2^16) to support unicode character sets? MaxSetElements* = 1 shl 16 # (2^16) to support unicode character sets?
VersionAsString* = system.NimVersion VersionAsString* = system.NimVersion
RodFileVersion* = "1222" # modify this if the rod-format changes! RodFileVersion* = "1223" # modify this if the rod-format changes!

View file

@ -144,6 +144,7 @@ var
gPreciseStack*: bool = false gPreciseStack*: bool = false
gNoNimblePath* = false gNoNimblePath* = false
gExperimentalMode*: bool gExperimentalMode*: bool
newDestructors*: bool
proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools} proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools}
proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc

View file

@ -28,7 +28,7 @@ import
llstream, lexer, idents, strutils, ast, astalgo, msgs llstream, lexer, idents, strutils, ast, astalgo, msgs
type type
TParser*{.final.} = object # A TParser object represents a file that TParser* = object # A TParser object represents a file that
# is being parsed # is being parsed
currInd: int # current indentation level currInd: int # current indentation level
firstTok, strongSpaces: bool # Has the first token been read? firstTok, strongSpaces: bool # Has the first token been read?
@ -1905,7 +1905,7 @@ proc parseVariable(p: var TParser): PNode =
#| variable = (varTuple / identColonEquals) colonBody? indAndComment #| variable = (varTuple / identColonEquals) colonBody? indAndComment
if p.tok.tokType == tkParLe: result = parseVarTuple(p) if p.tok.tokType == tkParLe: result = parseVarTuple(p)
else: result = parseIdentColonEquals(p, {withPragma, withDot}) else: result = parseIdentColonEquals(p, {withPragma, withDot})
result{-1} = postExprBlocks(p, result{-1}) result[^1] = postExprBlocks(p, result[^1])
indAndComment(p, result) indAndComment(p, result)
proc parseBind(p: var TParser, k: TNodeKind): PNode = proc parseBind(p: var TParser, k: TNodeKind): PNode =

View file

@ -15,6 +15,7 @@ import
condsyms, idents, renderer, types, extccomp, math, magicsys, nversion, condsyms, idents, renderer, types, extccomp, math, magicsys, nversion,
nimsets, syntaxes, times, rodread, idgen, modulegraphs, reorder nimsets, syntaxes, times, rodread, idgen, modulegraphs, reorder
type type
TPassContext* = object of RootObj # the pass's context TPassContext* = object of RootObj # the pass's context
fromCache*: bool # true if created by "openCached" fromCache*: bool # true if created by "openCached"
@ -211,7 +212,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream,
if n.kind == nkEmpty: break if n.kind == nkEmpty: break
sl.add n sl.add n
if sfReorder in module.flags: if sfReorder in module.flags:
sl = reorder sl sl = reorder(graph, sl, module, cache)
discard processTopLevelStmt(sl, a) discard processTopLevelStmt(sl, a)
break break
elif not processTopLevelStmt(n, a): break elif not processTopLevelStmt(n, a): break

View file

@ -25,7 +25,7 @@ const
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC, wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl, wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl,
wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe, wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe,
wOverride, wConstructor, wExportNims, wUsed} wOverride, wConstructor, wExportNims, wUsed, wLiftLocals}
converterPragmas* = procPragmas converterPragmas* = procPragmas
methodPragmas* = procPragmas+{wBase}-{wImportCpp} methodPragmas* = procPragmas+{wBase}-{wImportCpp}
templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty, templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty,
@ -55,7 +55,7 @@ const
wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow, wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow,
wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef, wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef,
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked, wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
wBorrow, wGcSafe, wExportNims, wPartial, wUsed, wExplain} wBorrow, wGcSafe, wExportNims, wPartial, wUsed, wExplain, wPackage}
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern, fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
wImportCpp, wImportObjC, wError, wGuard, wBitsize, wUsed} wImportCpp, wImportObjC, wError, wGuard, wBitsize, wUsed}
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
@ -70,6 +70,14 @@ const
wThread, wRaises, wLocks, wTags, wGcSafe} wThread, wRaises, wLocks, wTags, wGcSafe}
allRoutinePragmas* = methodPragmas + iteratorPragmas + lambdaPragmas allRoutinePragmas* = methodPragmas + iteratorPragmas + lambdaPragmas
proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode =
let p = procAst[pragmasPos]
if p.kind == nkEmpty: return nil
for it in p:
if it.kind == nkExprColonExpr and it[0].kind == nkIdent and
it[0].ident.id == ord(name):
return it[1]
proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords)
# implementation # implementation
@ -799,6 +807,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
noVal(it) noVal(it)
if sym.typ == nil or tfFinal in sym.typ.flags: invalidPragma(it) if sym.typ == nil or tfFinal in sym.typ.flags: invalidPragma(it)
else: incl(sym.typ.flags, tfInheritable) else: incl(sym.typ.flags, tfInheritable)
of wPackage:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.flags, sfForward)
of wAcyclic: of wAcyclic:
noVal(it) noVal(it)
if sym.typ == nil: invalidPragma(it) if sym.typ == nil: invalidPragma(it)
@ -974,6 +986,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
noVal(it) noVal(it)
if sym == nil: invalidPragma(it) if sym == nil: invalidPragma(it)
else: sym.flags.incl sfUsed else: sym.flags.incl sfUsed
of wLiftLocals: discard
else: invalidPragma(it) else: invalidPragma(it)
else: invalidPragma(it) else: invalidPragma(it)

View file

@ -17,12 +17,12 @@ type
renderNone, renderNoBody, renderNoComments, renderDocComments, renderNone, renderNoBody, renderNoComments, renderDocComments,
renderNoPragmas, renderIds, renderNoProcDefs renderNoPragmas, renderIds, renderNoProcDefs
TRenderFlags* = set[TRenderFlag] TRenderFlags* = set[TRenderFlag]
TRenderTok*{.final.} = object TRenderTok* = object
kind*: TTokType kind*: TTokType
length*: int16 length*: int16
TRenderTokSeq* = seq[TRenderTok] TRenderTokSeq* = seq[TRenderTok]
TSrcGen*{.final.} = object TSrcGen* = object
indent*: int indent*: int
lineLen*: int lineLen*: int
pos*: int # current position for iteration over the buffer pos*: int # current position for iteration over the buffer
@ -37,15 +37,11 @@ type
inGenericParams: bool inGenericParams: bool
checkAnon: bool # we're in a context that can contain sfAnon checkAnon: bool # we're in a context that can contain sfAnon
inPragma: int inPragma: int
when defined(nimpretty):
pendingNewlineCount: int
origContent: string
proc renderModule*(n: PNode, filename: string, renderFlags: TRenderFlags = {})
proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string
proc initTokRender*(r: var TSrcGen, n: PNode, renderFlags: TRenderFlags = {})
proc getNextTok*(r: var TSrcGen, kind: var TTokType, literal: var string)
proc `$`*(n: PNode): string = n.renderTree
# implementation
# We render the source code in a two phases: The first # We render the source code in a two phases: The first
# determines how long the subtree will likely be, the second # determines how long the subtree will likely be, the second
# phase appends to a buffer that will be the output. # phase appends to a buffer that will be the output.
@ -67,9 +63,31 @@ proc renderDefinitionName*(s: PSym, noQuotes = false): string =
else: else:
result = '`' & x & '`' result = '`' & x & '`'
when not defined(nimpretty):
const const
IndentWidth = 2 IndentWidth = 2
longIndentWid = 4 longIndentWid = IndentWidth * 2
else:
template IndentWidth: untyped = lexer.gIndentationWidth
template longIndentWid: untyped = IndentWidth() * 2
proc minmaxLine(n: PNode): (int, int) =
case n.kind
of nkTripleStrLit:
result = (n.info.line.int, n.info.line.int + countLines(n.strVal))
of nkCommentStmt:
result = (n.info.line.int, n.info.line.int + countLines(n.comment))
else:
result = (n.info.line.int, n.info.line.int)
for i in 0 ..< safeLen(n):
let (currMin, currMax) = minmaxLine(n[i])
if currMin < result[0]: result[0] = currMin
if currMax > result[1]: result[1] = currMax
proc lineDiff(a, b: PNode): int =
result = minmaxLine(b)[0] - minmaxLine(a)[1]
const
MaxLineLen = 80 MaxLineLen = 80
LineCommentColumn = 30 LineCommentColumn = 30
@ -95,7 +113,11 @@ proc addTok(g: var TSrcGen, kind: TTokType, s: string) =
proc addPendingNL(g: var TSrcGen) = proc addPendingNL(g: var TSrcGen) =
if g.pendingNL >= 0: if g.pendingNL >= 0:
addTok(g, tkSpaces, "\n" & spaces(g.pendingNL)) when defined(nimpretty):
let newlines = repeat("\n", clamp(g.pendingNewlineCount, 1, 3))
else:
const newlines = "\n"
addTok(g, tkSpaces, newlines & spaces(g.pendingNL))
g.lineLen = g.pendingNL g.lineLen = g.pendingNL
g.pendingNL = - 1 g.pendingNL = - 1
g.pendingWhitespace = -1 g.pendingWhitespace = -1
@ -119,11 +141,17 @@ proc putNL(g: var TSrcGen) =
proc optNL(g: var TSrcGen, indent: int) = proc optNL(g: var TSrcGen, indent: int) =
g.pendingNL = indent g.pendingNL = indent
g.lineLen = indent # BUGFIX g.lineLen = indent
when defined(nimpretty): g.pendingNewlineCount = 0
proc optNL(g: var TSrcGen) = proc optNL(g: var TSrcGen) =
optNL(g, g.indent) optNL(g, g.indent)
proc optNL(g: var TSrcGen; a, b: PNode) =
g.pendingNL = g.indent
g.lineLen = g.indent
when defined(nimpretty): g.pendingNewlineCount = lineDiff(a, b)
proc indentNL(g: var TSrcGen) = proc indentNL(g: var TSrcGen) =
inc(g.indent, IndentWidth) inc(g.indent, IndentWidth)
g.pendingNL = g.indent g.pendingNL = g.indent
@ -284,8 +312,8 @@ proc gcoms(g: var TSrcGen) =
for i in countup(0, high(g.comStack)): gcom(g, g.comStack[i]) for i in countup(0, high(g.comStack)): gcom(g, g.comStack[i])
popAllComs(g) popAllComs(g)
proc lsub(n: PNode): int proc lsub(g: TSrcGen; n: PNode): int
proc litAux(n: PNode, x: BiggestInt, size: int): string = proc litAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string =
proc skip(t: PType): PType = proc skip(t: PType): PType =
result = t result = t
while result.kind in {tyGenericInst, tyRange, tyVar, tyDistinct, while result.kind in {tyGenericInst, tyRange, tyVar, tyDistinct,
@ -302,14 +330,23 @@ proc litAux(n: PNode, x: BiggestInt, size: int): string =
elif nfBase16 in n.flags: result = "0x" & toHex(x, size * 2) elif nfBase16 in n.flags: result = "0x" & toHex(x, size * 2)
else: result = $x else: result = $x
proc ulitAux(n: PNode, x: BiggestInt, size: int): string = proc ulitAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string =
if nfBase2 in n.flags: result = "0b" & toBin(x, size * 8) if nfBase2 in n.flags: result = "0b" & toBin(x, size * 8)
elif nfBase8 in n.flags: result = "0o" & toOct(x, size * 3) elif nfBase8 in n.flags: result = "0o" & toOct(x, size * 3)
elif nfBase16 in n.flags: result = "0x" & toHex(x, size * 2) elif nfBase16 in n.flags: result = "0x" & toHex(x, size * 2)
else: result = $x else: result = $x
# XXX proper unsigned output! # XXX proper unsigned output!
proc atom(n: PNode): string = proc atom(g: TSrcGen; n: PNode): string =
when defined(nimpretty):
let comment = if n.info.commentOffsetA < n.info.commentOffsetB:
" " & substr(g.origContent, n.info.commentOffsetA, n.info.commentOffsetB)
else:
""
if n.info.offsetA <= n.info.offsetB:
# for some constructed tokens this can not be the case and we're better
# off to not mess with the offset then.
return substr(g.origContent, n.info.offsetA, n.info.offsetB) & comment
var f: float32 var f: float32
case n.kind case n.kind
of nkEmpty: result = "" of nkEmpty: result = ""
@ -319,30 +356,30 @@ proc atom(n: PNode): string =
of nkRStrLit: result = "r\"" & replace(n.strVal, "\"", "\"\"") & '\"' of nkRStrLit: result = "r\"" & replace(n.strVal, "\"", "\"\"") & '\"'
of nkTripleStrLit: result = "\"\"\"" & n.strVal & "\"\"\"" of nkTripleStrLit: result = "\"\"\"" & n.strVal & "\"\"\""
of nkCharLit: result = '\'' & toNimChar(chr(int(n.intVal))) & '\'' of nkCharLit: result = '\'' & toNimChar(chr(int(n.intVal))) & '\''
of nkIntLit: result = litAux(n, n.intVal, 4) of nkIntLit: result = litAux(g, n, n.intVal, 4)
of nkInt8Lit: result = litAux(n, n.intVal, 1) & "\'i8" of nkInt8Lit: result = litAux(g, n, n.intVal, 1) & "\'i8"
of nkInt16Lit: result = litAux(n, n.intVal, 2) & "\'i16" of nkInt16Lit: result = litAux(g, n, n.intVal, 2) & "\'i16"
of nkInt32Lit: result = litAux(n, n.intVal, 4) & "\'i32" of nkInt32Lit: result = litAux(g, n, n.intVal, 4) & "\'i32"
of nkInt64Lit: result = litAux(n, n.intVal, 8) & "\'i64" of nkInt64Lit: result = litAux(g, n, n.intVal, 8) & "\'i64"
of nkUIntLit: result = ulitAux(n, n.intVal, 4) & "\'u" of nkUIntLit: result = ulitAux(g, n, n.intVal, 4) & "\'u"
of nkUInt8Lit: result = ulitAux(n, n.intVal, 1) & "\'u8" of nkUInt8Lit: result = ulitAux(g, n, n.intVal, 1) & "\'u8"
of nkUInt16Lit: result = ulitAux(n, n.intVal, 2) & "\'u16" of nkUInt16Lit: result = ulitAux(g, n, n.intVal, 2) & "\'u16"
of nkUInt32Lit: result = ulitAux(n, n.intVal, 4) & "\'u32" of nkUInt32Lit: result = ulitAux(g, n, n.intVal, 4) & "\'u32"
of nkUInt64Lit: result = ulitAux(n, n.intVal, 8) & "\'u64" of nkUInt64Lit: result = ulitAux(g, n, n.intVal, 8) & "\'u64"
of nkFloatLit: of nkFloatLit:
if n.flags * {nfBase2, nfBase8, nfBase16} == {}: result = $(n.floatVal) if n.flags * {nfBase2, nfBase8, nfBase16} == {}: result = $(n.floatVal)
else: result = litAux(n, (cast[PInt64](addr(n.floatVal)))[] , 8) else: result = litAux(g, n, (cast[PInt64](addr(n.floatVal)))[] , 8)
of nkFloat32Lit: of nkFloat32Lit:
if n.flags * {nfBase2, nfBase8, nfBase16} == {}: if n.flags * {nfBase2, nfBase8, nfBase16} == {}:
result = $n.floatVal & "\'f32" result = $n.floatVal & "\'f32"
else: else:
f = n.floatVal.float32 f = n.floatVal.float32
result = litAux(n, (cast[PInt32](addr(f)))[], 4) & "\'f32" result = litAux(g, n, (cast[PInt32](addr(f)))[], 4) & "\'f32"
of nkFloat64Lit: of nkFloat64Lit:
if n.flags * {nfBase2, nfBase8, nfBase16} == {}: if n.flags * {nfBase2, nfBase8, nfBase16} == {}:
result = $n.floatVal & "\'f64" result = $n.floatVal & "\'f64"
else: else:
result = litAux(n, (cast[PInt64](addr(n.floatVal)))[], 8) & "\'f64" result = litAux(g, n, (cast[PInt64](addr(n.floatVal)))[], 8) & "\'f64"
of nkNilLit: result = "nil" of nkNilLit: result = "nil"
of nkType: of nkType:
if (n.typ != nil) and (n.typ.sym != nil): result = n.typ.sym.name.s if (n.typ != nil) and (n.typ.sym != nil): result = n.typ.sym.name.s
@ -351,21 +388,21 @@ proc atom(n: PNode): string =
internalError("rnimsyn.atom " & $n.kind) internalError("rnimsyn.atom " & $n.kind)
result = "" result = ""
proc lcomma(n: PNode, start: int = 0, theEnd: int = - 1): int = proc lcomma(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int =
assert(theEnd < 0) assert(theEnd < 0)
result = 0 result = 0
for i in countup(start, sonsLen(n) + theEnd): for i in countup(start, sonsLen(n) + theEnd):
inc(result, lsub(n.sons[i])) inc(result, lsub(g, n.sons[i]))
inc(result, 2) # for ``, `` inc(result, 2) # for ``, ``
if result > 0: if result > 0:
dec(result, 2) # last does not get a comma! dec(result, 2) # last does not get a comma!
proc lsons(n: PNode, start: int = 0, theEnd: int = - 1): int = proc lsons(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int =
assert(theEnd < 0) assert(theEnd < 0)
result = 0 result = 0
for i in countup(start, sonsLen(n) + theEnd): inc(result, lsub(n.sons[i])) for i in countup(start, sonsLen(n) + theEnd): inc(result, lsub(g, n.sons[i]))
proc lsub(n: PNode): int = proc lsub(g: TSrcGen; n: PNode): int =
# computes the length of a tree # computes the length of a tree
if isNil(n): return 0 if isNil(n): return 0
if n.comment != nil: return MaxLineLen + 1 if n.comment != nil: return MaxLineLen + 1
@ -373,108 +410,108 @@ proc lsub(n: PNode): int =
of nkEmpty: result = 0 of nkEmpty: result = 0
of nkTripleStrLit: of nkTripleStrLit:
if containsNL(n.strVal): result = MaxLineLen + 1 if containsNL(n.strVal): result = MaxLineLen + 1
else: result = len(atom(n)) else: result = len(atom(g, n))
of succ(nkEmpty)..pred(nkTripleStrLit), succ(nkTripleStrLit)..nkNilLit: of succ(nkEmpty)..pred(nkTripleStrLit), succ(nkTripleStrLit)..nkNilLit:
result = len(atom(n)) result = len(atom(g, n))
of nkCall, nkBracketExpr, nkCurlyExpr, nkConv, nkPattern, nkObjConstr: of nkCall, nkBracketExpr, nkCurlyExpr, nkConv, nkPattern, nkObjConstr:
result = lsub(n.sons[0]) + lcomma(n, 1) + 2 result = lsub(g, n.sons[0]) + lcomma(g, n, 1) + 2
of nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv: result = lsub(n[1]) of nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv: result = lsub(g, n[1])
of nkCast: result = lsub(n.sons[0]) + lsub(n.sons[1]) + len("cast[]()") of nkCast: result = lsub(g, n.sons[0]) + lsub(g, n.sons[1]) + len("cast[]()")
of nkAddr: result = (if n.len>0: lsub(n.sons[0]) + len("addr()") else: 4) of nkAddr: result = (if n.len>0: lsub(g, n.sons[0]) + len("addr()") else: 4)
of nkStaticExpr: result = lsub(n.sons[0]) + len("static_") of nkStaticExpr: result = lsub(g, n.sons[0]) + len("static_")
of nkHiddenAddr, nkHiddenDeref: result = lsub(n.sons[0]) of nkHiddenAddr, nkHiddenDeref: result = lsub(g, n.sons[0])
of nkCommand: result = lsub(n.sons[0]) + lcomma(n, 1) + 1 of nkCommand: result = lsub(g, n.sons[0]) + lcomma(g, n, 1) + 1
of nkExprEqExpr, nkAsgn, nkFastAsgn: result = lsons(n) + 3 of nkExprEqExpr, nkAsgn, nkFastAsgn: result = lsons(g, n) + 3
of nkPar, nkCurly, nkBracket, nkClosure: result = lcomma(n) + 2 of nkPar, nkCurly, nkBracket, nkClosure: result = lcomma(g, n) + 2
of nkArgList: result = lcomma(n) of nkArgList: result = lcomma(g, n)
of nkTableConstr: of nkTableConstr:
result = if n.len > 0: lcomma(n) + 2 else: len("{:}") result = if n.len > 0: lcomma(g, n) + 2 else: len("{:}")
of nkClosedSymChoice, nkOpenSymChoice: of nkClosedSymChoice, nkOpenSymChoice:
result = lsons(n) + len("()") + sonsLen(n) - 1 result = lsons(g, n) + len("()") + sonsLen(n) - 1
of nkTupleTy: result = lcomma(n) + len("tuple[]") of nkTupleTy: result = lcomma(g, n) + len("tuple[]")
of nkTupleClassTy: result = len("tuple") of nkTupleClassTy: result = len("tuple")
of nkDotExpr: result = lsons(n) + 1 of nkDotExpr: result = lsons(g, n) + 1
of nkBind: result = lsons(n) + len("bind_") of nkBind: result = lsons(g, n) + len("bind_")
of nkBindStmt: result = lcomma(n) + len("bind_") of nkBindStmt: result = lcomma(g, n) + len("bind_")
of nkMixinStmt: result = lcomma(n) + len("mixin_") of nkMixinStmt: result = lcomma(g, n) + len("mixin_")
of nkCheckedFieldExpr: result = lsub(n.sons[0]) of nkCheckedFieldExpr: result = lsub(g, n.sons[0])
of nkLambda: result = lsons(n) + len("proc__=_") of nkLambda: result = lsons(g, n) + len("proc__=_")
of nkDo: result = lsons(n) + len("do__:_") of nkDo: result = lsons(g, n) + len("do__:_")
of nkConstDef, nkIdentDefs: of nkConstDef, nkIdentDefs:
result = lcomma(n, 0, - 3) result = lcomma(g, n, 0, - 3)
var L = sonsLen(n) var L = sonsLen(n)
if n.sons[L - 2].kind != nkEmpty: result = result + lsub(n.sons[L - 2]) + 2 if n.sons[L - 2].kind != nkEmpty: result = result + lsub(g, n.sons[L - 2]) + 2
if n.sons[L - 1].kind != nkEmpty: result = result + lsub(n.sons[L - 1]) + 3 if n.sons[L - 1].kind != nkEmpty: result = result + lsub(g, n.sons[L - 1]) + 3
of nkVarTuple: result = lcomma(n, 0, - 3) + len("() = ") + lsub(lastSon(n)) of nkVarTuple: result = lcomma(g, n, 0, - 3) + len("() = ") + lsub(g, lastSon(n))
of nkChckRangeF: result = len("chckRangeF") + 2 + lcomma(n) of nkChckRangeF: result = len("chckRangeF") + 2 + lcomma(g, n)
of nkChckRange64: result = len("chckRange64") + 2 + lcomma(n) of nkChckRange64: result = len("chckRange64") + 2 + lcomma(g, n)
of nkChckRange: result = len("chckRange") + 2 + lcomma(n) of nkChckRange: result = len("chckRange") + 2 + lcomma(g, n)
of nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString: of nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString:
result = 2 result = 2
if sonsLen(n) >= 1: result = result + lsub(n.sons[0]) if sonsLen(n) >= 1: result = result + lsub(g, n.sons[0])
result = result + lcomma(n, 1) result = result + lcomma(g, n, 1)
of nkExprColonExpr: result = lsons(n) + 2 of nkExprColonExpr: result = lsons(g, n) + 2
of nkInfix: result = lsons(n) + 2 of nkInfix: result = lsons(g, n) + 2
of nkPrefix: of nkPrefix:
result = lsons(n)+1+(if n.len > 0 and n.sons[1].kind == nkInfix: 2 else: 0) result = lsons(g, n)+1+(if n.len > 0 and n.sons[1].kind == nkInfix: 2 else: 0)
of nkPostfix: result = lsons(n) of nkPostfix: result = lsons(g, n)
of nkCallStrLit: result = lsons(n) of nkCallStrLit: result = lsons(g, n)
of nkPragmaExpr: result = lsub(n.sons[0]) + lcomma(n, 1) of nkPragmaExpr: result = lsub(g, n.sons[0]) + lcomma(g, n, 1)
of nkRange: result = lsons(n) + 2 of nkRange: result = lsons(g, n) + 2
of nkDerefExpr: result = lsub(n.sons[0]) + 2 of nkDerefExpr: result = lsub(g, n.sons[0]) + 2
of nkAccQuoted: result = lsons(n) + 2 of nkAccQuoted: result = lsons(g, n) + 2
of nkIfExpr: of nkIfExpr:
result = lsub(n.sons[0].sons[0]) + lsub(n.sons[0].sons[1]) + lsons(n, 1) + result = lsub(g, n.sons[0].sons[0]) + lsub(g, n.sons[0].sons[1]) + lsons(g, n, 1) +
len("if_:_") len("if_:_")
of nkElifExpr: result = lsons(n) + len("_elif_:_") of nkElifExpr: result = lsons(g, n) + len("_elif_:_")
of nkElseExpr: result = lsub(n.sons[0]) + len("_else:_") # type descriptions of nkElseExpr: result = lsub(g, n.sons[0]) + len("_else:_") # type descriptions
of nkTypeOfExpr: result = (if n.len > 0: lsub(n.sons[0]) else: 0)+len("type()") of nkTypeOfExpr: result = (if n.len > 0: lsub(g, n.sons[0]) else: 0)+len("type()")
of nkRefTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ref") of nkRefTy: result = (if n.len > 0: lsub(g, n.sons[0])+1 else: 0) + len("ref")
of nkPtrTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ptr") of nkPtrTy: result = (if n.len > 0: lsub(g, n.sons[0])+1 else: 0) + len("ptr")
of nkVarTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("var") of nkVarTy: result = (if n.len > 0: lsub(g, n.sons[0])+1 else: 0) + len("var")
of nkDistinctTy: of nkDistinctTy:
result = len("distinct") + (if n.len > 0: lsub(n.sons[0])+1 else: 0) result = len("distinct") + (if n.len > 0: lsub(g, n.sons[0])+1 else: 0)
if n.len > 1: if n.len > 1:
result += (if n[1].kind == nkWith: len("_with_") else: len("_without_")) result += (if n[1].kind == nkWith: len("_with_") else: len("_without_"))
result += lcomma(n[1]) result += lcomma(g, n[1])
of nkStaticTy: result = (if n.len > 0: lsub(n.sons[0]) else: 0) + of nkStaticTy: result = (if n.len > 0: lsub(g, n.sons[0]) else: 0) +
len("static[]") len("static[]")
of nkTypeDef: result = lsons(n) + 3 of nkTypeDef: result = lsons(g, n) + 3
of nkOfInherit: result = lsub(n.sons[0]) + len("of_") of nkOfInherit: result = lsub(g, n.sons[0]) + len("of_")
of nkProcTy: result = lsons(n) + len("proc_") of nkProcTy: result = lsons(g, n) + len("proc_")
of nkIteratorTy: result = lsons(n) + len("iterator_") of nkIteratorTy: result = lsons(g, n) + len("iterator_")
of nkSharedTy: result = lsons(n) + len("shared_") of nkSharedTy: result = lsons(g, n) + len("shared_")
of nkEnumTy: of nkEnumTy:
if sonsLen(n) > 0: if sonsLen(n) > 0:
result = lsub(n.sons[0]) + lcomma(n, 1) + len("enum_") result = lsub(g, n.sons[0]) + lcomma(g, n, 1) + len("enum_")
else: else:
result = len("enum") result = len("enum")
of nkEnumFieldDef: result = lsons(n) + 3 of nkEnumFieldDef: result = lsons(g, n) + 3
of nkVarSection, nkLetSection: of nkVarSection, nkLetSection:
if sonsLen(n) > 1: result = MaxLineLen + 1 if sonsLen(n) > 1: result = MaxLineLen + 1
else: result = lsons(n) + len("var_") else: result = lsons(g, n) + len("var_")
of nkUsingStmt: of nkUsingStmt:
if sonsLen(n) > 1: result = MaxLineLen + 1 if sonsLen(n) > 1: result = MaxLineLen + 1
else: result = lsons(n) + len("using_") else: result = lsons(g, n) + len("using_")
of nkReturnStmt: result = lsub(n.sons[0]) + len("return_") of nkReturnStmt: result = lsub(g, n.sons[0]) + len("return_")
of nkRaiseStmt: result = lsub(n.sons[0]) + len("raise_") of nkRaiseStmt: result = lsub(g, n.sons[0]) + len("raise_")
of nkYieldStmt: result = lsub(n.sons[0]) + len("yield_") of nkYieldStmt: result = lsub(g, n.sons[0]) + len("yield_")
of nkDiscardStmt: result = lsub(n.sons[0]) + len("discard_") of nkDiscardStmt: result = lsub(g, n.sons[0]) + len("discard_")
of nkBreakStmt: result = lsub(n.sons[0]) + len("break_") of nkBreakStmt: result = lsub(g, n.sons[0]) + len("break_")
of nkContinueStmt: result = lsub(n.sons[0]) + len("continue_") of nkContinueStmt: result = lsub(g, n.sons[0]) + len("continue_")
of nkPragma: result = lcomma(n) + 4 of nkPragma: result = lcomma(g, n) + 4
of nkCommentStmt: result = if n.comment.isNil: 0 else: len(n.comment) of nkCommentStmt: result = if n.comment.isNil: 0 else: len(n.comment)
of nkOfBranch: result = lcomma(n, 0, - 2) + lsub(lastSon(n)) + len("of_:_") of nkOfBranch: result = lcomma(g, n, 0, - 2) + lsub(g, lastSon(n)) + len("of_:_")
of nkImportAs: result = lsub(n.sons[0]) + len("_as_") + lsub(n.sons[1]) of nkImportAs: result = lsub(g, n.sons[0]) + len("_as_") + lsub(g, n.sons[1])
of nkElifBranch: result = lsons(n) + len("elif_:_") of nkElifBranch: result = lsons(g, n) + len("elif_:_")
of nkElse: result = lsub(n.sons[0]) + len("else:_") of nkElse: result = lsub(g, n.sons[0]) + len("else:_")
of nkFinally: result = lsub(n.sons[0]) + len("finally:_") of nkFinally: result = lsub(g, n.sons[0]) + len("finally:_")
of nkGenericParams: result = lcomma(n) + 2 of nkGenericParams: result = lcomma(g, n) + 2
of nkFormalParams: of nkFormalParams:
result = lcomma(n, 1) + 2 result = lcomma(g, n, 1) + 2
if n.sons[0].kind != nkEmpty: result = result + lsub(n.sons[0]) + 2 if n.sons[0].kind != nkEmpty: result = result + lsub(g, n.sons[0]) + 2
of nkExceptBranch: of nkExceptBranch:
result = lcomma(n, 0, -2) + lsub(lastSon(n)) + len("except_:_") result = lcomma(g, n, 0, -2) + lsub(g, lastSon(n)) + len("except_:_")
else: result = MaxLineLen + 1 else: result = MaxLineLen + 1
proc fits(g: TSrcGen, x: int): bool = proc fits(g: TSrcGen, x: int): bool =
@ -517,7 +554,7 @@ proc gcommaAux(g: var TSrcGen, n: PNode, ind: int, start: int = 0,
theEnd: int = - 1, separator = tkComma) = theEnd: int = - 1, separator = tkComma) =
for i in countup(start, sonsLen(n) + theEnd): for i in countup(start, sonsLen(n) + theEnd):
var c = i < sonsLen(n) + theEnd var c = i < sonsLen(n) + theEnd
var sublen = lsub(n.sons[i]) + ord(c) var sublen = lsub(g, n.sons[i]) + ord(c)
if not fits(g, sublen) and (ind + sublen < MaxLineLen): optNL(g, ind) if not fits(g, sublen) and (ind + sublen < MaxLineLen): optNL(g, ind)
let oldLen = g.tokens.len let oldLen = g.tokens.len
gsub(g, n.sons[i]) gsub(g, n.sons[i])
@ -564,12 +601,12 @@ proc gsection(g: var TSrcGen, n: PNode, c: TContext, kind: TTokType,
gcoms(g) gcoms(g)
dedent(g) dedent(g)
proc longMode(n: PNode, start: int = 0, theEnd: int = - 1): bool = proc longMode(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): bool =
result = n.comment != nil result = n.comment != nil
if not result: if not result:
# check further # check further
for i in countup(start, sonsLen(n) + theEnd): for i in countup(start, sonsLen(n) + theEnd):
if (lsub(n.sons[i]) > MaxLineLen): if (lsub(g, n.sons[i]) > MaxLineLen):
result = true result = true
break break
@ -577,12 +614,16 @@ proc gstmts(g: var TSrcGen, n: PNode, c: TContext, doIndent=true) =
if n.kind == nkEmpty: return if n.kind == nkEmpty: return
if n.kind in {nkStmtList, nkStmtListExpr, nkStmtListType}: if n.kind in {nkStmtList, nkStmtListExpr, nkStmtListType}:
if doIndent: indentNL(g) if doIndent: indentNL(g)
for i in countup(0, sonsLen(n) - 1): let L = n.len
optNL(g) for i in 0 .. L-1:
if n.sons[i].kind in {nkStmtList, nkStmtListExpr, nkStmtListType}: if i > 0:
gstmts(g, n.sons[i], c, doIndent=false) optNL(g, n[i-1], n[i])
else: else:
gsub(g, n.sons[i]) optNL(g)
if n[i].kind in {nkStmtList, nkStmtListExpr, nkStmtListType}:
gstmts(g, n[i], c, doIndent=false)
else:
gsub(g, n[i])
gcoms(g) gcoms(g)
if doIndent: dedent(g) if doIndent: dedent(g)
else: else:
@ -597,7 +638,7 @@ proc gif(g: var TSrcGen, n: PNode) =
gsub(g, n.sons[0].sons[0]) gsub(g, n.sons[0].sons[0])
initContext(c) initContext(c)
putWithSpace(g, tkColon, ":") putWithSpace(g, tkColon, ":")
if longMode(n) or (lsub(n.sons[0].sons[1]) + g.lineLen > MaxLineLen): if longMode(g, n) or (lsub(g, n.sons[0].sons[1]) + g.lineLen > MaxLineLen):
incl(c.flags, rfLongMode) incl(c.flags, rfLongMode)
gcoms(g) # a good place for comments gcoms(g) # a good place for comments
gstmts(g, n.sons[0].sons[1], c) gstmts(g, n.sons[0].sons[1], c)
@ -612,7 +653,7 @@ proc gwhile(g: var TSrcGen, n: PNode) =
gsub(g, n.sons[0]) gsub(g, n.sons[0])
putWithSpace(g, tkColon, ":") putWithSpace(g, tkColon, ":")
initContext(c) initContext(c)
if longMode(n) or (lsub(n.sons[1]) + g.lineLen > MaxLineLen): if longMode(g, n) or (lsub(g, n.sons[1]) + g.lineLen > MaxLineLen):
incl(c.flags, rfLongMode) incl(c.flags, rfLongMode)
gcoms(g) # a good place for comments gcoms(g) # a good place for comments
gstmts(g, n.sons[1], c) gstmts(g, n.sons[1], c)
@ -621,7 +662,7 @@ proc gpattern(g: var TSrcGen, n: PNode) =
var c: TContext var c: TContext
put(g, tkCurlyLe, "{") put(g, tkCurlyLe, "{")
initContext(c) initContext(c)
if longMode(n) or (lsub(n.sons[0]) + g.lineLen > MaxLineLen): if longMode(g, n) or (lsub(g, n.sons[0]) + g.lineLen > MaxLineLen):
incl(c.flags, rfLongMode) incl(c.flags, rfLongMode)
gcoms(g) # a good place for comments gcoms(g) # a good place for comments
gstmts(g, n, c) gstmts(g, n, c)
@ -632,7 +673,7 @@ proc gpragmaBlock(g: var TSrcGen, n: PNode) =
gsub(g, n.sons[0]) gsub(g, n.sons[0])
putWithSpace(g, tkColon, ":") putWithSpace(g, tkColon, ":")
initContext(c) initContext(c)
if longMode(n) or (lsub(n.sons[1]) + g.lineLen > MaxLineLen): if longMode(g, n) or (lsub(g, n.sons[1]) + g.lineLen > MaxLineLen):
incl(c.flags, rfLongMode) incl(c.flags, rfLongMode)
gcoms(g) # a good place for comments gcoms(g) # a good place for comments
gstmts(g, n.sons[1], c) gstmts(g, n.sons[1], c)
@ -642,7 +683,7 @@ proc gtry(g: var TSrcGen, n: PNode) =
put(g, tkTry, "try") put(g, tkTry, "try")
putWithSpace(g, tkColon, ":") putWithSpace(g, tkColon, ":")
initContext(c) initContext(c)
if longMode(n) or (lsub(n.sons[0]) + g.lineLen > MaxLineLen): if longMode(g, n) or (lsub(g, n.sons[0]) + g.lineLen > MaxLineLen):
incl(c.flags, rfLongMode) incl(c.flags, rfLongMode)
gcoms(g) # a good place for comments gcoms(g) # a good place for comments
gstmts(g, n.sons[0], c) gstmts(g, n.sons[0], c)
@ -653,8 +694,8 @@ proc gfor(g: var TSrcGen, n: PNode) =
var length = sonsLen(n) var length = sonsLen(n)
putWithSpace(g, tkFor, "for") putWithSpace(g, tkFor, "for")
initContext(c) initContext(c)
if longMode(n) or if longMode(g, n) or
(lsub(n.sons[length - 1]) + lsub(n.sons[length - 2]) + 6 + g.lineLen > (lsub(g, n.sons[length - 1]) + lsub(g, n.sons[length - 2]) + 6 + g.lineLen >
MaxLineLen): MaxLineLen):
incl(c.flags, rfLongMode) incl(c.flags, rfLongMode)
gcomma(g, n, c, 0, - 3) gcomma(g, n, c, 0, - 3)
@ -670,7 +711,7 @@ proc gcase(g: var TSrcGen, n: PNode) =
initContext(c) initContext(c)
var length = sonsLen(n) var length = sonsLen(n)
var last = if n.sons[length-1].kind == nkElse: -2 else: -1 var last = if n.sons[length-1].kind == nkElse: -2 else: -1
if longMode(n, 0, last): incl(c.flags, rfLongMode) if longMode(g, n, 0, last): incl(c.flags, rfLongMode)
putWithSpace(g, tkCase, "case") putWithSpace(g, tkCase, "case")
gsub(g, n.sons[0]) gsub(g, n.sons[0])
gcoms(g) gcoms(g)
@ -678,7 +719,7 @@ proc gcase(g: var TSrcGen, n: PNode) =
gsons(g, n, c, 1, last) gsons(g, n, c, 1, last)
if last == - 2: if last == - 2:
initContext(c) initContext(c)
if longMode(n.sons[length - 1]): incl(c.flags, rfLongMode) if longMode(g, n.sons[length - 1]): incl(c.flags, rfLongMode)
gsub(g, n.sons[length - 1], c) gsub(g, n.sons[length - 1], c)
proc gproc(g: var TSrcGen, n: PNode) = proc gproc(g: var TSrcGen, n: PNode) =
@ -740,7 +781,7 @@ proc gblock(g: var TSrcGen, n: PNode) =
else: else:
put(g, tkBlock, "block") put(g, tkBlock, "block")
putWithSpace(g, tkColon, ":") putWithSpace(g, tkColon, ":")
if longMode(n) or (lsub(n.sons[1]) + g.lineLen > MaxLineLen): if longMode(g, n) or (lsub(g, n.sons[1]) + g.lineLen > MaxLineLen):
incl(c.flags, rfLongMode) incl(c.flags, rfLongMode)
gcoms(g) gcoms(g)
# XXX I don't get why this is needed here! gstmts should already handle this! # XXX I don't get why this is needed here! gstmts should already handle this!
@ -753,7 +794,7 @@ proc gstaticStmt(g: var TSrcGen, n: PNode) =
putWithSpace(g, tkStatic, "static") putWithSpace(g, tkStatic, "static")
putWithSpace(g, tkColon, ":") putWithSpace(g, tkColon, ":")
initContext(c) initContext(c)
if longMode(n) or (lsub(n.sons[0]) + g.lineLen > MaxLineLen): if longMode(g, n) or (lsub(g, n.sons[0]) + g.lineLen > MaxLineLen):
incl(c.flags, rfLongMode) incl(c.flags, rfLongMode)
gcoms(g) # a good place for comments gcoms(g) # a good place for comments
gstmts(g, n.sons[0], c) gstmts(g, n.sons[0], c)
@ -771,7 +812,7 @@ proc gident(g: var TSrcGen, n: PNode) =
(n.typ != nil and tfImplicitTypeParam in n.typ.flags): return (n.typ != nil and tfImplicitTypeParam in n.typ.flags): return
var t: TTokType var t: TTokType
var s = atom(n) var s = atom(g, n)
if (s[0] in lexer.SymChars): if (s[0] in lexer.SymChars):
if (n.kind == nkIdent): if (n.kind == nkIdent):
if (n.ident.id < ord(tokKeywordLow) - ord(tkSymbol)) or if (n.ident.id < ord(tokKeywordLow) - ord(tkSymbol)) or
@ -818,26 +859,26 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
case n.kind # atoms: case n.kind # atoms:
of nkTripleStrLit: putRawStr(g, tkTripleStrLit, n.strVal) of nkTripleStrLit: putRawStr(g, tkTripleStrLit, n.strVal)
of nkEmpty: discard of nkEmpty: discard
of nkType: put(g, tkInvalid, atom(n)) of nkType: put(g, tkInvalid, atom(g, n))
of nkSym, nkIdent: gident(g, n) of nkSym, nkIdent: gident(g, n)
of nkIntLit: put(g, tkIntLit, atom(n)) of nkIntLit: put(g, tkIntLit, atom(g, n))
of nkInt8Lit: put(g, tkInt8Lit, atom(n)) of nkInt8Lit: put(g, tkInt8Lit, atom(g, n))
of nkInt16Lit: put(g, tkInt16Lit, atom(n)) of nkInt16Lit: put(g, tkInt16Lit, atom(g, n))
of nkInt32Lit: put(g, tkInt32Lit, atom(n)) of nkInt32Lit: put(g, tkInt32Lit, atom(g, n))
of nkInt64Lit: put(g, tkInt64Lit, atom(n)) of nkInt64Lit: put(g, tkInt64Lit, atom(g, n))
of nkUIntLit: put(g, tkUIntLit, atom(n)) of nkUIntLit: put(g, tkUIntLit, atom(g, n))
of nkUInt8Lit: put(g, tkUInt8Lit, atom(n)) of nkUInt8Lit: put(g, tkUInt8Lit, atom(g, n))
of nkUInt16Lit: put(g, tkUInt16Lit, atom(n)) of nkUInt16Lit: put(g, tkUInt16Lit, atom(g, n))
of nkUInt32Lit: put(g, tkUInt32Lit, atom(n)) of nkUInt32Lit: put(g, tkUInt32Lit, atom(g, n))
of nkUInt64Lit: put(g, tkUInt64Lit, atom(n)) of nkUInt64Lit: put(g, tkUInt64Lit, atom(g, n))
of nkFloatLit: put(g, tkFloatLit, atom(n)) of nkFloatLit: put(g, tkFloatLit, atom(g, n))
of nkFloat32Lit: put(g, tkFloat32Lit, atom(n)) of nkFloat32Lit: put(g, tkFloat32Lit, atom(g, n))
of nkFloat64Lit: put(g, tkFloat64Lit, atom(n)) of nkFloat64Lit: put(g, tkFloat64Lit, atom(g, n))
of nkFloat128Lit: put(g, tkFloat128Lit, atom(n)) of nkFloat128Lit: put(g, tkFloat128Lit, atom(g, n))
of nkStrLit: put(g, tkStrLit, atom(n)) of nkStrLit: put(g, tkStrLit, atom(g, n))
of nkRStrLit: put(g, tkRStrLit, atom(n)) of nkRStrLit: put(g, tkRStrLit, atom(g, n))
of nkCharLit: put(g, tkCharLit, atom(n)) of nkCharLit: put(g, tkCharLit, atom(g, n))
of nkNilLit: put(g, tkNil, atom(n)) # complex expressions of nkNilLit: put(g, tkNil, atom(g, n)) # complex expressions
of nkCall, nkConv, nkDotCall, nkPattern, nkObjConstr: of nkCall, nkConv, nkDotCall, nkPattern, nkObjConstr:
if n.len > 0 and isBracket(n[0]): if n.len > 0 and isBracket(n[0]):
gsub(g, n, 1) gsub(g, n, 1)
@ -1003,7 +1044,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
gsub(g, n, 1) gsub(g, n, 1)
put(g, tkSpaces, Space) put(g, tkSpaces, Space)
gsub(g, n, 0) # binary operator gsub(g, n, 0) # binary operator
if not fits(g, lsub(n.sons[2]) + lsub(n.sons[0]) + 1): if not fits(g, lsub(g, n.sons[2]) + lsub(g, n.sons[0]) + 1):
optNL(g, g.indent + longIndentWid) optNL(g, g.indent + longIndentWid)
else: else:
put(g, tkSpaces, Space) put(g, tkSpaces, Space)
@ -1011,6 +1052,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
of nkPrefix: of nkPrefix:
gsub(g, n, 0) gsub(g, n, 0)
if n.len > 1: if n.len > 1:
let opr = if n[0].kind == nkIdent: n[0].ident
elif n[0].kind == nkSym: n[0].sym.name
else: nil
if n[1].kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):
put(g, tkSpaces, Space) put(g, tkSpaces, Space)
if n.sons[1].kind == nkInfix: if n.sons[1].kind == nkInfix:
put(g, tkParLe, "(") put(g, tkParLe, "(")
@ -1316,7 +1361,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
gstmts(g, n.sons[0], c) gstmts(g, n.sons[0], c)
of nkExceptBranch: of nkExceptBranch:
optNL(g) optNL(g)
if n.len != 1:
putWithSpace(g, tkExcept, "except") putWithSpace(g, tkExcept, "except")
else:
put(g, tkExcept, "except")
gcomma(g, n, 0, -2) gcomma(g, n, 0, -2)
putWithSpace(g, tkColon, ":") putWithSpace(g, tkColon, ":")
gcoms(g) gcoms(g)
@ -1363,7 +1411,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
#nkNone, nkExplicitTypeListCall: #nkNone, nkExplicitTypeListCall:
internalError(n.info, "rnimsyn.gsub(" & $n.kind & ')') internalError(n.info, "rnimsyn.gsub(" & $n.kind & ')')
proc renderTree(n: PNode, renderFlags: TRenderFlags = {}): string = proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string =
var g: TSrcGen var g: TSrcGen
initSrcGen(g, renderFlags) initSrcGen(g, renderFlags)
# do not indent the initial statement list so that # do not indent the initial statement list so that
@ -1375,12 +1423,20 @@ proc renderTree(n: PNode, renderFlags: TRenderFlags = {}): string =
gsub(g, n) gsub(g, n)
result = g.buf result = g.buf
proc renderModule(n: PNode, filename: string, proc `$`*(n: PNode): string = n.renderTree
proc renderModule*(n: PNode, infile, outfile: string,
renderFlags: TRenderFlags = {}) = renderFlags: TRenderFlags = {}) =
var var
f: File f: File
g: TSrcGen g: TSrcGen
initSrcGen(g, renderFlags) initSrcGen(g, renderFlags)
when defined(nimpretty):
try:
g.origContent = readFile(infile)
except IOError:
rawMessage(errCannotOpenFile, infile)
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
gsub(g, n.sons[i]) gsub(g, n.sons[i])
optNL(g) optNL(g)
@ -1391,17 +1447,17 @@ proc renderModule(n: PNode, filename: string,
gcoms(g) gcoms(g)
if optStdout in gGlobalOptions: if optStdout in gGlobalOptions:
write(stdout, g.buf) write(stdout, g.buf)
elif open(f, filename, fmWrite): elif open(f, outfile, fmWrite):
write(f, g.buf) write(f, g.buf)
close(f) close(f)
else: else:
rawMessage(errCannotOpenFile, filename) rawMessage(errCannotOpenFile, outfile)
proc initTokRender(r: var TSrcGen, n: PNode, renderFlags: TRenderFlags = {}) = proc initTokRender*(r: var TSrcGen, n: PNode, renderFlags: TRenderFlags = {}) =
initSrcGen(r, renderFlags) initSrcGen(r, renderFlags)
gsub(r, n) gsub(r, n)
proc getNextTok(r: var TSrcGen, kind: var TTokType, literal: var string) = proc getNextTok*(r: var TSrcGen, kind: var TTokType, literal: var string) =
if r.idx < len(r.tokens): if r.idx < len(r.tokens):
kind = r.tokens[r.idx].kind kind = r.tokens[r.idx].kind
var length = r.tokens[r.idx].length.int var length = r.tokens[r.idx].length.int

View file

@ -1,9 +1,36 @@
import intsets, tables, ast, idents, renderer import
intsets, ast, idents, algorithm, renderer, parser, ospaths, strutils,
sequtils, msgs, modulegraphs, syntaxes, options, modulepaths, tables
const type
nfTempMark = nfTransf DepN = ref object
nfPermMark = nfNoRewrite pnode: PNode
id, idx, lowLink: int
onStack: bool
kids: seq[DepN]
hAQ, hIS, hB, hCmd: int
when not defined(release):
expls: seq[string]
DepG = seq[DepN]
when not defined(release):
var idNames = newTable[int, string]()
proc newDepN(id: int, pnode: PNode): DepN =
new(result)
result.id = id
result.pnode = pnode
result.idx = -1
result.lowLink = -1
result.onStack = false
result.kids = @[]
result.hAQ = -1
result.hIS = -1
result.hB = -1
result.hCmd = -1
when not defined(release):
result.expls = @[]
proc accQuoted(n: PNode): PIdent = proc accQuoted(n: PNode): PIdent =
var id = "" var id = ""
@ -21,10 +48,19 @@ proc addDecl(n: PNode; declares: var IntSet) =
of nkPragmaExpr: addDecl(n[0], declares) of nkPragmaExpr: addDecl(n[0], declares)
of nkIdent: of nkIdent:
declares.incl n.ident.id declares.incl n.ident.id
when not defined(release):
idNames[n.ident.id] = n.ident.s
of nkSym: of nkSym:
declares.incl n.sym.name.id declares.incl n.sym.name.id
when not defined(release):
idNames[n.sym.name.id] = n.sym.name.s
of nkAccQuoted: of nkAccQuoted:
declares.incl accQuoted(n).id let a = accQuoted(n)
declares.incl a.id
when not defined(release):
idNames[a.id] = a.s
of nkEnumFieldDef:
addDecl(n[0], declares)
else: discard else: discard
proc computeDeps(n: PNode, declares, uses: var IntSet; topLevel: bool) = proc computeDeps(n: PNode, declares, uses: var IntSet; topLevel: bool) =
@ -32,7 +68,7 @@ proc computeDeps(n: PNode, declares, uses: var IntSet; topLevel: bool) =
template decl(n) = template decl(n) =
if topLevel: addDecl(n, declares) if topLevel: addDecl(n, declares)
case n.kind case n.kind
of procDefs: of procDefs, nkMacroDef, nkTemplateDef:
decl(n[0]) decl(n[0])
for i in 1..bodyPos: deps(n[i]) for i in 1..bodyPos: deps(n[i])
of nkLetSection, nkVarSection, nkUsingStmt: of nkLetSection, nkVarSection, nkUsingStmt:
@ -44,43 +80,358 @@ proc computeDeps(n: PNode, declares, uses: var IntSet; topLevel: bool) =
for a in n: for a in n:
if a.len >= 3: if a.len >= 3:
decl(a[0]) decl(a[0])
for i in 1..<a.len: deps(a[i]) for i in 1..<a.len:
if a[i].kind == nkEnumTy:
# declare enum members
for b in a[i]:
decl(b)
else:
deps(a[i])
of nkIdentDefs:
for i in 1..<n.len: # avoid members identifiers in object definition
deps(n[i])
of nkIdent: uses.incl n.ident.id of nkIdent: uses.incl n.ident.id
of nkSym: uses.incl n.sym.name.id of nkSym: uses.incl n.sym.name.id
of nkAccQuoted: uses.incl accQuoted(n).id of nkAccQuoted: uses.incl accQuoted(n).id
of nkOpenSymChoice, nkClosedSymChoice: of nkOpenSymChoice, nkClosedSymChoice:
uses.incl n.sons[0].sym.name.id uses.incl n.sons[0].sym.name.id
of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse: of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse, nkStaticStmt:
for i in 0..<len(n): computeDeps(n[i], declares, uses, topLevel) for i in 0..<len(n): computeDeps(n[i], declares, uses, topLevel)
of nkPragma:
let a = n.sons[0]
if a.kind == nkExprColonExpr and a.sons[0].kind == nkIdent and
a.sons[0].ident.s == "pragma":
# user defined pragma
decl(a.sons[1])
else:
for i in 0..<safeLen(n): deps(n[i])
else: else:
for i in 0..<safeLen(n): deps(n[i]) for i in 0..<safeLen(n): deps(n[i])
proc visit(i: int; all, res: PNode; deps: var seq[(IntSet, IntSet)]): bool = proc cleanPath(s: string): string =
let n = all[i] # Here paths may have the form A / B or "A/B"
if nfTempMark in n.flags: result = ""
# not a DAG! for c in s:
if c != ' ' and c != '\"':
result.add c
proc joinPath(parts: seq[string]): string =
let nb = parts.len
assert nb > 0
if nb == 1:
return parts[0]
result = parts[0] / parts[1]
for i in 2..<parts.len:
result = result / parts[i]
proc getIncludePath(n: PNode, modulePath: string): string =
let istr = n.renderTree.cleanPath
let (pdir, _) = modulePath.splitPath
let p = istr.split('/').joinPath.addFileExt("nim")
result = pdir / p
proc hasIncludes(n:PNode): bool =
for a in n:
if a.kind == nkIncludeStmt:
return true return true
if nfPermMark notin n.flags:
incl n.flags, nfTempMark proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: int32;
var uses = deps[i][1] cache: IdentCache): PNode {.procvar.} =
for j in 0..<all.len: result = syntaxes.parseFile(fileIdx, cache)
if j != i: graph.addDep(s, fileIdx)
graph.addIncludeDep(s.position.int32, fileIdx)
proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode,
modulePath: string, includedFiles: var IntSet,
cache: IdentCache): PNode =
# Parses includes and injects them in the current tree
if not n.hasIncludes:
return n
result = newNodeI(nkStmtList, n.info)
for a in n:
if a.kind == nkIncludeStmt:
for i in 0..<a.len:
var f = checkModuleName(a.sons[i])
if f != InvalidFileIDX:
if containsOrIncl(includedFiles, f):
localError(a.info, errRecursiveDependencyX, f.toFilename)
else:
let nn = includeModule(graph, module, f, cache)
let nnn = expandIncludes(graph, module, nn, modulePath,
includedFiles, cache)
excl(includedFiles, f)
for b in nnn:
result.add b
else:
result.add a
proc splitSections(n: PNode): PNode =
# Split typeSections and ConstSections into
# sections that contain only one definition
assert n.kind == nkStmtList
result = newNodeI(nkStmtList, n.info)
for a in n:
if a.kind in {nkTypeSection, nkConstSection} and a.len > 1:
for b in a:
var s = newNode(a.kind)
s.info = b.info
s.add b
result.add s
else:
result.add a
proc haveSameKind(dns: seq[DepN]): bool =
# Check if all the nodes in a strongly connected
# component have the same kind
result = true
let kind = dns[0].pnode.kind
for dn in dns:
if dn.pnode.kind != kind:
return false
proc mergeSections(comps: seq[seq[DepN]], res: PNode) =
# Merges typeSections and ConstSections when they form
# a strong component (ex: circular type definition)
for c in comps:
assert c.len > 0
if c.len == 1:
res.add c[0].pnode
else:
let fstn = c[0].pnode
let kind = fstn.kind
# always return to the original order when we got circular dependencies
let cs = c.sortedByIt(it.id)
if kind in {nkTypeSection, nkConstSection} and haveSameKind(cs):
# Circular dependency between type or const sections, we just
# need to merge them
var sn = newNode(kind)
for dn in cs:
sn.add dn.pnode.sons[0]
res.add sn
else:
# Problematic circular dependency, we arrange the nodes into
# their original relative order and make sure to re-merge
# consecutive type and const sections
var wmsg = "Circular dependency detected. reorder pragma may not be able to" &
" reorder some nodes properely"
when not defined(release):
wmsg &= ":\n"
for i in 0..<cs.len-1:
for j in i..<cs.len:
for ci in 0..<cs[i].kids.len:
if cs[i].kids[ci].id == cs[j].id:
wmsg &= "line " & $cs[i].pnode.info.line &
" depends on line " & $cs[j].pnode.info.line &
": " & cs[i].expls[ci] & "\n"
for j in 0..<cs.len-1:
for ci in 0..<cs[^1].kids.len:
if cs[^1].kids[ci].id == cs[j].id:
wmsg &= "line " & $cs[^1].pnode.info.line &
" depends on line " & $cs[j].pnode.info.line &
": " & cs[^1].expls[ci] & "\n"
message(cs[0].pnode.info, warnUser, wmsg)
var i = 0
while i < cs.len:
if cs[i].pnode.kind in {nkTypeSection, nkConstSection}:
let ckind = cs[i].pnode.kind
var sn = newNode(ckind)
sn.add cs[i].pnode[0]
inc i
while i < cs.len and cs[i].pnode.kind == ckind :
sn.add cs[i].pnode[0]
inc i
res.add sn
else:
res.add cs[i].pnode
inc i
proc hasImportStmt(n: PNode): bool =
# Checks if the node is an import statement or
# i it contains one
case n.kind
of nkImportStmt, nkFromStmt, nkImportExceptStmt:
return true
of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse, nkStaticStmt:
for a in n:
if a.hasImportStmt:
return true
else:
result = false
proc hasImportStmt(n: DepN): bool =
if n.hIS < 0:
n.hIS = ord(n.pnode.hasImportStmt)
result = bool(n.hIS)
proc hasCommand(n: PNode): bool =
# Checks if the node is a command or a call
# or if it contains one
case n.kind
of nkCommand, nkCall:
result = true
of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse,
nkStaticStmt, nkLetSection, nkConstSection, nkVarSection,
nkIdentDefs:
for a in n:
if a.hasCommand:
return true
else:
return false
proc hasCommand(n: DepN): bool =
if n.hCmd < 0:
n.hCmd = ord(n.pnode.hasCommand)
result = bool(n.hCmd)
proc hasAccQuoted(n: PNode): bool =
if n.kind == nkAccQuoted:
return true
for a in n:
if hasAccQuoted(a):
return true
const extandedProcDefs = procDefs + {nkMacroDef, nkTemplateDef}
proc hasAccQuotedDef(n: PNode): bool =
# Checks if the node is a function, macro, template ...
# with a quoted name or if it contains one
case n.kind
of extandedProcDefs:
result = n[0].hasAccQuoted
of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse, nkStaticStmt:
for a in n:
if a.hasAccQuotedDef:
return true
else:
result = false
proc hasAccQuotedDef(n: DepN): bool =
if n.hAQ < 0:
n.hAQ = ord(n.pnode.hasAccQuotedDef)
result = bool(n.hAQ)
proc hasBody(n: PNode): bool =
# Checks if the node is a function, macro, template ...
# with a body or if it contains one
case n.kind
of nkCommand, nkCall:
result = true
of extandedProcDefs:
result = n[^1].kind == nkStmtList
of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse, nkStaticStmt:
for a in n:
if a.hasBody:
return true
else:
result = false
proc hasBody(n: DepN): bool =
if n.hB < 0:
n.hB = ord(n.pnode.hasBody)
result = bool(n.hB)
proc intersects(s1, s2: IntSet): bool =
for a in s1:
if s2.contains(a):
return true
proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG =
# Build a dependency graph
result = newSeqOfCap[DepN](deps.len)
for i in 0..<deps.len:
result.add newDepN(i, n.sons[i])
for i in 0..<deps.len:
var ni = result[i]
let uses = deps[i][1]
let niHasBody = ni.hasBody
let niHasCmd = ni.hasCommand
for j in 0..<deps.len:
if i == j: continue
var nj = result[j]
let declares = deps[j][0] let declares = deps[j][0]
if j < i and nj.hasCommand and niHasCmd:
# Preserve order for commands and calls
ni.kids.add nj
when not defined(release):
ni.expls.add "both have commands and one comes after the other"
elif j < i and nj.hasImportStmt:
# Every node that comes after an import statement must
# depend on that import
ni.kids.add nj
when not defined(release):
ni.expls.add "parent is, or contains, an import statement and child comes after it"
elif j < i and niHasBody and nj.hasAccQuotedDef:
# Every function, macro, template... with a body depends
# on precedent function declarations that have quoted names.
# That's because it is hard to detect the use of functions
# like "[]=", "[]", "or" ... in their bodies.
ni.kids.add nj
when not defined(release):
ni.expls.add "one declares a quoted identifier and the other has a body and comes after it"
elif j < i and niHasBody and not nj.hasBody and
intersects(deps[i][0], declares):
# Keep function declaration before function definition
ni.kids.add nj
when not defined(release):
for dep in deps[i][0]:
if dep in declares:
ni.expls.add "one declares \"" & idNames[dep] & "\" and the other defines it"
else:
for d in declares: for d in declares:
if uses.contains(d): if uses.contains(d):
let oldLen = res.len ni.kids.add nj
if visit(j, all, res, deps): when not defined(release):
result = true ni.expls.add "one declares \"" & idNames[d] & "\" and the other uses it"
# rollback what we did, it turned out to be a dependency that caused
# trouble:
for k in oldLen..<res.len:
res.sons[k].flags = res.sons[k].flags - {nfPermMark, nfTempMark}
if oldLen != res.len: res.sons.setLen oldLen
break
n.flags = n.flags + {nfPermMark} - {nfTempMark}
res.add n
proc reorder*(n: PNode): PNode = proc strongConnect(v: var DepN, idx: var int, s: var seq[DepN],
res: var seq[seq[DepN]]) =
# Recursive part of trajan's algorithm
v.idx = idx
v.lowLink = idx
inc idx
s.add v
v.onStack = true
for w in v.kids.mitems:
if w.idx < 0:
strongConnect(w, idx, s, res)
v.lowLink = min(v.lowLink, w.lowLink)
elif w.onStack:
v.lowLink = min(v.lowLink, w.idx)
if v.lowLink == v.idx:
var comp = newSeq[DepN]()
while true:
var w = s.pop
w.onStack = false
comp.add w
if w.id == v.id: break
res.add comp
proc getStrongComponents(g: var DepG): seq[seq[DepN]] =
## Tarjan's algorithm. Performs a topological sort
## and detects strongly connected components.
result = newSeq[seq[DepN]]()
var s = newSeq[DepN]()
var idx = 0
for v in g.mitems:
if v.idx < 0:
strongConnect(v, idx, s, result)
proc hasForbiddenPragma(n: PNode): bool =
# Checks if the tree node has some pragmas that do not
# play well with reordering, like the push/pop pragma
for a in n:
if a.kind == nkPragma and a[0].kind == nkIdent and
a[0].ident.s == "push":
return true
proc reorder*(graph: ModuleGraph, n: PNode, module: PSym, cache: IdentCache): PNode =
if n.hasForbiddenPragma:
return n
var includedFiles = initIntSet()
let mpath = module.fileIdx.toFullPath
let n = expandIncludes(graph, module, n, mpath,
includedFiles, cache).splitSections
result = newNodeI(nkStmtList, n.info) result = newNodeI(nkStmtList, n.info)
var deps = newSeq[(IntSet, IntSet)](n.len) var deps = newSeq[(IntSet, IntSet)](n.len)
for i in 0..<n.len: for i in 0..<n.len:
@ -88,15 +439,6 @@ proc reorder*(n: PNode): PNode =
deps[i][1] = initIntSet() deps[i][1] = initIntSet()
computeDeps(n[i], deps[i][0], deps[i][1], true) computeDeps(n[i], deps[i][0], deps[i][1], true)
for i in 0 .. n.len-1: var g = buildGraph(n, deps)
discard visit(i, n, result, deps) let comps = getStrongComponents(g)
for i in 0..<result.len: mergeSections(comps, result)
result.sons[i].flags = result.sons[i].flags - {nfTempMark, nfPermMark}
when false:
# reverse the result:
let L = result.len-1
for i in 0 .. result.len div 2:
result.sons[i].flags = result.sons[i].flags - {nfTempMark, nfPermMark}
result.sons[L - i].flags = result.sons[L - i].flags - {nfTempMark, nfPermMark}
swap(result.sons[i], result.sons[L - i])
#echo result

View file

@ -336,10 +336,13 @@ proc decodeType(r: PRodReader, info: TLineInfo): PType =
if r.s[r.pos] == '\17': if r.s[r.pos] == '\17':
inc(r.pos) inc(r.pos)
result.assignment = rrGetSym(r, decodeVInt(r.s, r.pos), info) result.assignment = rrGetSym(r, decodeVInt(r.s, r.pos), info)
while r.s[r.pos] == '\18': if r.s[r.pos] == '\18':
inc(r.pos)
result.sink = rrGetSym(r, decodeVInt(r.s, r.pos), info)
while r.s[r.pos] == '\19':
inc(r.pos) inc(r.pos)
let x = decodeVInt(r.s, r.pos) let x = decodeVInt(r.s, r.pos)
doAssert r.s[r.pos] == '\19' doAssert r.s[r.pos] == '\20'
inc(r.pos) inc(r.pos)
let y = rrGetSym(r, decodeVInt(r.s, r.pos), info) let y = rrGetSym(r, decodeVInt(r.s, r.pos), info)
result.methods.safeAdd((x, y)) result.methods.safeAdd((x, y))

View file

@ -10,7 +10,7 @@
## Serialization utilities for the compiler. ## Serialization utilities for the compiler.
import strutils import strutils
proc c_sprintf(buf, frmt: cstring) {.importc: "sprintf", header: "<stdio.h>", nodecl, varargs.} proc c_snprintf(s: cstring; n:uint; frmt: cstring): cint {.importc: "snprintf", header: "<stdio.h>", nodecl, varargs.}
proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string = proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string =
if f != f: if f != f:
@ -20,10 +20,15 @@ proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string =
elif f == 0.5 * f: elif f == 0.5 * f:
if f > 0.0: result = "INF" if f > 0.0: result = "INF"
else: result = "-INF" else: result = "-INF"
else:
when defined(nimNoArrayToCstringConversion):
result = newString(81)
let n = c_snprintf(result.cstring, result.len.uint, "%#.16e%s", f, literalPostfix.cstring)
setLen(result, n)
else: else:
var buf: array[0..80, char] var buf: array[0..80, char]
c_sprintf(buf, "%#.16e" & literalPostfix, f) discard c_snprintf(buf.cstring, buf.len.uint, "%#.16e%s", f, literalPostfix.cstring)
result = $buf result = $buf.cstring
proc encodeStr*(s: string, result: var string) = proc encodeStr*(s: string, result: var string) =
for i in countup(0, len(s) - 1): for i in countup(0, len(s) - 1):
@ -133,4 +138,3 @@ iterator decodeStrArray*(s: cstring): string =
while s[i] != '\0': while s[i] != '\0':
yield decodeStr(s, i) yield decodeStr(s, i)
if s[i] == ' ': inc i if s[i] == ' ': inc i

View file

@ -13,8 +13,8 @@
import import
intsets, os, options, strutils, nversion, ast, astalgo, msgs, platform, intsets, os, options, strutils, nversion, ast, astalgo, msgs, platform,
condsyms, ropes, idents, securehash, rodread, passes, importer, idgen, condsyms, ropes, idents, securehash, rodread, passes, idgen,
rodutils rodutils, modulepaths
from modulegraphs import ModuleGraph from modulegraphs import ModuleGraph
@ -245,10 +245,14 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
add(result, '\17') add(result, '\17')
encodeVInt(t.assignment.id, result) encodeVInt(t.assignment.id, result)
pushSym(w, t.assignment) pushSym(w, t.assignment)
for i, s in items(t.methods): if t.sink != nil:
add(result, '\18') add(result, '\18')
encodeVInt(i, result) encodeVInt(t.sink.id, result)
pushSym(w, t.sink)
for i, s in items(t.methods):
add(result, '\19') add(result, '\19')
encodeVInt(i, result)
add(result, '\20')
encodeVInt(s.id, result) encodeVInt(s.id, result)
pushSym(w, s) pushSym(w, s)
encodeLoc(w, t.loc, result) encodeLoc(w, t.loc, result)

View file

@ -79,7 +79,7 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
setResult(a, osproc.execCmd getString(a, 0)) setResult(a, osproc.execCmd getString(a, 0))
cbconf getEnv: cbconf getEnv:
setResult(a, os.getEnv(a.getString 0)) setResult(a, os.getEnv(a.getString 0, a.getString 1))
cbconf existsEnv: cbconf existsEnv:
setResult(a, os.existsEnv(a.getString 0)) setResult(a, os.existsEnv(a.getString 0))
cbconf dirExists: cbconf dirExists:

View file

@ -12,7 +12,7 @@
import import
ast, strutils, hashes, options, lexer, astalgo, trees, treetab, ast, strutils, hashes, options, lexer, astalgo, trees, treetab,
wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math, wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math,
magicsys, parser, nversion, nimsets, semfold, importer, magicsys, parser, nversion, nimsets, semfold, modulepaths, importer,
procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch, procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch,
intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting, intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
evaltempl, patterns, parampatterns, sempass2, nimfix.pretty, semmacrosanity, evaltempl, patterns, parampatterns, sempass2, nimfix.pretty, semmacrosanity,
@ -522,6 +522,10 @@ proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
else: else:
result = n result = n
result = semStmt(c, result) result = semStmt(c, result)
when false:
# Code generators are lazy now and can deal with undeclared procs, so these
# steps are not required anymore and actually harmful for the upcoming
# destructor support.
# BUGFIX: process newly generated generics here, not at the end! # BUGFIX: process newly generated generics here, not at the end!
if c.lastGenericIdx < c.generics.len: if c.lastGenericIdx < c.generics.len:
var a = newNodeI(nkStmtList, n.info) var a = newNodeI(nkStmtList, n.info)

View file

@ -22,7 +22,8 @@ type
recurse: bool recurse: bool
proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode)
proc liftBody(c: PContext; typ: PType; info: TLineInfo): PSym proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
info: TLineInfo): PSym {.discardable.}
proc at(a, i: PNode, elemType: PType): PNode = proc at(a, i: PNode, elemType: PType): PNode =
result = newNodeI(nkBracketExpr, a.info, 2) result = newNodeI(nkBracketExpr, a.info, 2)
@ -92,24 +93,24 @@ proc newAsgnStmt(le, ri: PNode): PNode =
result.sons[0] = le result.sons[0] = le
result.sons[1] = ri result.sons[1] = ri
proc newDestructorCall(op: PSym; x: PNode): PNode = proc newOpCall(op: PSym; x: PNode): PNode =
result = newNodeIT(nkCall, x.info, op.typ.sons[0]) result = newNodeIT(nkCall, x.info, op.typ.sons[0])
result.add(newSymNode(op)) result.add(newSymNode(op))
result.add x result.add x
proc newDeepCopyCall(op: PSym; x, y: PNode): PNode = proc destructorCall(c: PContext; op: PSym; x: PNode): PNode =
result = newAsgnStmt(x, newDestructorCall(op, y)) result = newNodeIT(nkCall, x.info, op.typ.sons[0])
result.add(newSymNode(op))
if newDestructors:
result.add genAddr(c, x)
else:
result.add x
proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = proc newDeepCopyCall(op: PSym; x, y: PNode): PNode =
case c.kind result = newAsgnStmt(x, newOpCall(op, y))
of attachedDestructor:
let op = t.destructor proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
if op != nil: field: PSym): bool =
markUsed(c.info, op, c.c.graph.usageSym)
styleCheckUse(c.info, op)
body.add newDestructorCall(op, x)
result = true
of attachedAsgn:
if tfHasAsgn in t.flags: if tfHasAsgn in t.flags:
var op: PSym var op: PSym
if sameType(t, c.asgnForType): if sameType(t, c.asgnForType):
@ -120,13 +121,27 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
c.recurse = true c.recurse = true
return false return false
else: else:
op = t.assignment op = field
if op == nil: if op == nil:
op = liftBody(c.c, t, c.info) op = liftBody(c.c, t, c.kind, c.info)
markUsed(c.info, op, c.c.graph.usageSym) markUsed(c.info, op, c.c.graph.usageSym)
styleCheckUse(c.info, op) styleCheckUse(c.info, op)
body.add newAsgnCall(c.c, op, x, y) body.add newAsgnCall(c.c, op, x, y)
result = true result = true
proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
case c.kind
of attachedDestructor:
let op = t.destructor
if op != nil:
markUsed(c.info, op, c.c.graph.usageSym)
styleCheckUse(c.info, op)
body.add destructorCall(c.c, op, x)
result = true
of attachedAsgn:
result = considerAsgnOrSink(c, t, body, x, y, t.assignment)
of attachedSink:
result = considerAsgnOrSink(c, t, body, x, y, t.sink)
of attachedDeepCopy: of attachedDeepCopy:
let op = t.deepCopy let op = t.deepCopy
if op != nil: if op != nil:
@ -188,7 +203,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
tyPtr, tyString, tyRef, tyOpt: tyPtr, tyString, tyRef, tyOpt:
defaultOp(c, t, body, x, y) defaultOp(c, t, body, x, y)
of tyArray, tySequence: of tyArray, tySequence:
if tfHasAsgn in t.flags: if {tfHasAsgn, tfUncheckedArray} * t.flags == {tfHasAsgn}:
if t.kind == tySequence: if t.kind == tySequence:
# XXX add 'nil' handling here # XXX add 'nil' handling here
body.add newSeqCall(c.c, x, y) body.add newSeqCall(c.c, x, y)
@ -245,12 +260,20 @@ proc addParam(procType: PType; param: PSym) =
addSon(procType.n, newSymNode(param)) addSon(procType.n, newSymNode(param))
rawAddSon(procType, param.typ) rawAddSon(procType, param.typ)
proc liftBody(c: PContext; typ: PType; info: TLineInfo): PSym = proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
info: TLineInfo): PSym {.discardable.} =
var a: TLiftCtx var a: TLiftCtx
a.info = info a.info = info
a.c = c a.c = c
a.kind = kind
let body = newNodeI(nkStmtList, info) let body = newNodeI(nkStmtList, info)
result = newSym(skProc, getIdent":lifted=", typ.owner, info) let procname = case kind
of attachedAsgn: getIdent"="
of attachedSink: getIdent"=sink"
of attachedDeepCopy: getIdent"=deepcopy"
of attachedDestructor: getIdent"=destroy"
result = newSym(skProc, procname, typ.owner, info)
a.fn = result a.fn = result
a.asgnForType = typ a.asgnForType = typ
@ -261,9 +284,17 @@ proc liftBody(c: PContext; typ: PType; info: TLineInfo): PSym =
result.typ = newProcType(info, typ.owner) result.typ = newProcType(info, typ.owner)
result.typ.addParam dest result.typ.addParam dest
if kind != attachedDestructor:
result.typ.addParam src result.typ.addParam src
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
# before 'liftBodyAux':
case kind
of attachedAsgn: typ.assignment = result
of attachedSink: typ.sink = result
of attachedDeepCopy: typ.deepCopy = result
of attachedDestructor: typ.destructor = result
var n = newNodeI(nkProcDef, info, bodyPos+1) var n = newNodeI(nkProcDef, info, bodyPos+1)
for i in 0 ..< n.len: n.sons[i] = emptyNode for i in 0 ..< n.len: n.sons[i] = emptyNode
@ -272,16 +303,29 @@ proc liftBody(c: PContext; typ: PType; info: TLineInfo): PSym =
n.sons[bodyPos] = body n.sons[bodyPos] = body
result.ast = n result.ast = n
# register late as recursion is handled differently
typ.assignment = result
#echo "Produced this ", n
proc getAsgnOrLiftBody(c: PContext; typ: PType; info: TLineInfo): PSym = proc getAsgnOrLiftBody(c: PContext; typ: PType; info: TLineInfo): PSym =
let t = typ.skipTypes({tyGenericInst, tyVar, tyAlias}) let t = typ.skipTypes({tyGenericInst, tyVar, tyAlias})
result = t.assignment result = t.assignment
if result.isNil: if result.isNil:
result = liftBody(c, t, info) result = liftBody(c, t, attachedAsgn, info)
proc overloadedAsgn(c: PContext; dest, src: PNode): PNode = proc overloadedAsgn(c: PContext; dest, src: PNode): PNode =
let a = getAsgnOrLiftBody(c, dest.typ, dest.info) let a = getAsgnOrLiftBody(c, dest.typ, dest.info)
result = newAsgnCall(c, a, dest, src) result = newAsgnCall(c, a, dest, src)
proc liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
## In the semantic pass this is called in strategic places
## to ensure we lift assignment, destructors and moves properly.
## The later 'destroyer' pass depends on it.
if not newDestructors or not hasDestructor(typ): return
# do not produce wrong liftings while we're still instantiating generics:
if c.typesWithOps.len > 0: return
let typ = typ.skipTypes({tyGenericInst, tyAlias})
# we generate the destructor first so that other operators can depend on it:
if typ.destructor == nil:
liftBody(c, typ, attachedDestructor, info)
if typ.assignment == nil:
liftBody(c, typ, attachedAsgn, info)
if typ.sink == nil:
liftBody(c, typ, attachedSink, info)

View file

@ -27,7 +27,7 @@ proc sameMethodDispatcher(a, b: PSym): bool =
# method collide[T](a: TThing, b: TUnit[T]) is instantiated and not # method collide[T](a: TThing, b: TUnit[T]) is instantiated and not
# method collide[T](a: TUnit[T], b: TThing)! This means we need to # method collide[T](a: TUnit[T], b: TThing)! This means we need to
# *instantiate* every candidate! However, we don't keep more than 2-3 # *instantiate* every candidate! However, we don't keep more than 2-3
# candidated around so we cannot implement that for now. So in order # candidates around so we cannot implement that for now. So in order
# to avoid subtle problems, the call remains ambiguous and needs to # to avoid subtle problems, the call remains ambiguous and needs to
# be disambiguated by the programmer; this way the right generic is # be disambiguated by the programmer; this way the right generic is
# instantiated. # instantiated.
@ -90,6 +90,10 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
if c.currentScope.symbols.counter == counterInitial or syms != nil: if c.currentScope.symbols.counter == counterInitial or syms != nil:
matches(c, n, orig, z) matches(c, n, orig, z)
if z.state == csMatch: if z.state == csMatch:
#if sym.name.s == "==" and (n.info ?? "temp3"):
# echo typeToString(sym.typ)
# writeMatches(z)
# little hack so that iterators are preferred over everything else: # little hack so that iterators are preferred over everything else:
if sym.kind == skIterator: inc(z.exactMatches, 200) if sym.kind == skIterator: inc(z.exactMatches, 200)
case best.state case best.state

View file

@ -37,7 +37,6 @@ type
# in standalone ``except`` and ``finally`` # in standalone ``except`` and ``finally``
next*: PProcCon # used for stacking procedure contexts next*: PProcCon # used for stacking procedure contexts
wasForwarded*: bool # whether the current proc has a separate header wasForwarded*: bool # whether the current proc has a separate header
bracketExpr*: PNode # current bracket expression (for ^ support)
mapping*: TIdTable mapping*: TIdTable
TMatchedConcept* = object TMatchedConcept* = object
@ -70,6 +69,7 @@ type
TTypeAttachedOp* = enum TTypeAttachedOp* = enum
attachedAsgn, attachedAsgn,
attachedSink,
attachedDeepCopy, attachedDeepCopy,
attachedDestructor attachedDestructor
@ -131,6 +131,11 @@ type
recursiveDep*: string recursiveDep*: string
suggestionsMade*: bool suggestionsMade*: bool
inTypeContext*: int inTypeContext*: int
typesWithOps*: seq[(PType, PType)] #\
# We need to instantiate the type bound ops lazily after
# the generic type has been constructed completely. See
# tests/destructor/topttree.nim for an example that
# would otherwise fail.
proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair = proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
result.genericSym = s result.genericSym = s
@ -218,6 +223,7 @@ proc newContext*(graph: ModuleGraph; module: PSym; cache: IdentCache): PContext
result.cache = cache result.cache = cache
result.graph = graph result.graph = graph
initStrTable(result.signatures) initStrTable(result.signatures)
result.typesWithOps = @[]
proc inclSym(sq: var TSymSeq, s: PSym) = proc inclSym(sq: var TSymSeq, s: PSym) =
@ -333,7 +339,7 @@ proc makeNotType*(c: PContext, t1: PType): PType =
proc nMinusOne*(n: PNode): PNode = proc nMinusOne*(n: PNode): PNode =
result = newNode(nkCall, n.info, @[ result = newNode(nkCall, n.info, @[
newSymNode(getSysMagic("<", mUnaryLt)), newSymNode(getSysMagic("pred", mPred)),
n]) n])
# Remember to fix the procs below this one when you make changes! # Remember to fix the procs below this one when you make changes!

View file

@ -184,62 +184,3 @@ proc createDestructorCall(c: PContext, s: PSym): PNode =
useSym(destructableT.destructor, c.graph.usageSym), useSym(destructableT.destructor, c.graph.usageSym),
useSym(s, c.graph.usageSym)])) useSym(s, c.graph.usageSym)]))
result = newNode(nkDefer, s.info, @[call]) result = newNode(nkDefer, s.info, @[call])
proc insertDestructors(c: PContext,
varSection: PNode): tuple[outer, inner: PNode] =
# Accepts a var or let section.
#
# When a var section has variables with destructors
# the var section is split up and finally blocks are inserted
# immediately after all "destructable" vars
#
# In case there were no destrucable variables, the proc returns
# (nil, nil) and the enclosing stmt-list requires no modifications.
#
# Otherwise, after the try blocks are created, the rest of the enclosing
# stmt-list should be inserted in the most `inner` such block (corresponding
# to the last variable).
#
# `outer` is a statement list that should replace the original var section.
# It will include the new truncated var section followed by the outermost
# try block.
let totalVars = varSection.sonsLen
for j in countup(0, totalVars - 1):
let
varId = varSection[j][0]
varTyp = varId.sym.typ
info = varId.info
if varTyp == nil or sfGlobal in varId.sym.flags: continue
let destructableT = instantiateDestructor(c, varTyp)
if destructableT != nil:
var tryStmt = newNodeI(nkTryStmt, info)
if j < totalVars - 1:
var remainingVars = newNodeI(varSection.kind, info)
remainingVars.sons = varSection.sons[(j+1)..varSection.len-1]
let (outer, inner) = insertDestructors(c, remainingVars)
if outer != nil:
tryStmt.addSon(outer)
result.inner = inner
else:
result.inner = newNodeI(nkStmtList, info)
result.inner.addSon(remainingVars)
tryStmt.addSon(result.inner)
else:
result.inner = newNodeI(nkStmtList, info)
tryStmt.addSon(result.inner)
tryStmt.addSon(
newNode(nkFinally, info, @[
semStmt(c, newNode(nkCall, info, @[
useSym(destructableT.destructor, c.graph.usageSym),
useSym(varId.sym, c.graph.usageSym)]))]))
result.outer = newNodeI(nkStmtList, info)
varSection.sons.setLen(j+1)
result.outer.addSon(varSection)
result.outer.addSon(tryStmt)
return

View file

@ -669,6 +669,7 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode =
analyseIfAddressTakenInCall(c, result) analyseIfAddressTakenInCall(c, result)
if callee.magic != mNone: if callee.magic != mNone:
result = magicsAfterOverloadResolution(c, result, flags) result = magicsAfterOverloadResolution(c, result, flags)
if result.typ != nil: liftTypeBoundOps(c, result.typ, n.info)
if c.matchedConcept == nil: if c.matchedConcept == nil:
result = evalAtCompileTime(c, result) result = evalAtCompileTime(c, result)
@ -1187,7 +1188,6 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
tyCString: tyCString:
if n.len != 2: return nil if n.len != 2: return nil
n.sons[0] = makeDeref(n.sons[0]) n.sons[0] = makeDeref(n.sons[0])
c.p.bracketExpr = n.sons[0]
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
n.sons[i] = semExprWithType(c, n.sons[i], n.sons[i] = semExprWithType(c, n.sons[i],
flags*{efInTypeof, efDetermineType}) flags*{efInTypeof, efDetermineType})
@ -1208,7 +1208,6 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
of tyTuple: of tyTuple:
if n.len != 2: return nil if n.len != 2: return nil
n.sons[0] = makeDeref(n.sons[0]) n.sons[0] = makeDeref(n.sons[0])
c.p.bracketExpr = n.sons[0]
# [] operator for tuples requires constant expression: # [] operator for tuples requires constant expression:
n.sons[1] = semConstExpr(c, n.sons[1]) n.sons[1] = semConstExpr(c, n.sons[1])
if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal, tyAlias}).kind in if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal, tyAlias}).kind in
@ -1246,17 +1245,13 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
of skType: of skType:
result = symNodeFromType(c, semTypeNode(c, n, nil), n.info) result = symNodeFromType(c, semTypeNode(c, n, nil), n.info)
else: else:
c.p.bracketExpr = n.sons[0] discard
else:
c.p.bracketExpr = n.sons[0]
proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
let oldBracketExpr = c.p.bracketExpr
result = semSubscript(c, n, flags) result = semSubscript(c, n, flags)
if result == nil: if result == nil:
# overloaded [] operator: # overloaded [] operator:
result = semExpr(c, buildOverloadedSubscripts(n, getIdent"[]")) result = semExpr(c, buildOverloadedSubscripts(n, getIdent"[]"))
c.p.bracketExpr = oldBracketExpr
proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode = proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
var id = considerQuotedIdent(a[1], a) var id = considerQuotedIdent(a[1], a)
@ -1295,9 +1290,12 @@ proc takeImplicitAddr(c: PContext, n: PNode): PNode =
proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} = proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} =
if le.kind == nkHiddenDeref: if le.kind == nkHiddenDeref:
var x = le.sons[0] var x = le.sons[0]
if x.typ.kind == tyVar and x.kind == nkSym and x.sym.kind == skResult: if x.typ.kind == tyVar and x.kind == nkSym:
if x.sym.kind == skResult:
n.sons[0] = x # 'result[]' --> 'result' n.sons[0] = x # 'result[]' --> 'result'
n.sons[1] = takeImplicitAddr(c, ri) n.sons[1] = takeImplicitAddr(c, ri)
if x.sym.kind != skParam:
# XXX This is hacky. See bug #4910.
x.typ.flags.incl tfVarIsPtr x.typ.flags.incl tfVarIsPtr
#echo x.info, " setting it for this type ", typeToString(x.typ), " ", n.info #echo x.info, " setting it for this type ", typeToString(x.typ), " ", n.info
@ -1325,7 +1323,6 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
of nkBracketExpr: of nkBracketExpr:
# a[i] = x # a[i] = x
# --> `[]=`(a, i, x) # --> `[]=`(a, i, x)
let oldBracketExpr = c.p.bracketExpr
a = semSubscript(c, a, {efLValue}) a = semSubscript(c, a, {efLValue})
if a == nil: if a == nil:
result = buildOverloadedSubscripts(n.sons[0], getIdent"[]=") result = buildOverloadedSubscripts(n.sons[0], getIdent"[]=")
@ -1335,9 +1332,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
return n return n
else: else:
result = semExprNoType(c, result) result = semExprNoType(c, result)
c.p.bracketExpr = oldBracketExpr
return result return result
c.p.bracketExpr = oldBracketExpr
of nkCurlyExpr: of nkCurlyExpr:
# a{i} = x --> `{}=`(a, i, x) # a{i} = x --> `{}=`(a, i, x)
result = buildOverloadedSubscripts(n.sons[0], getIdent"{}=") result = buildOverloadedSubscripts(n.sons[0], getIdent"{}=")
@ -1382,9 +1377,12 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
typeMismatch(n.info, lhs.typ, rhs.typ) typeMismatch(n.info, lhs.typ, rhs.typ)
n.sons[1] = fitNode(c, le, rhs, n.info) n.sons[1] = fitNode(c, le, rhs, n.info)
if not newDestructors:
if tfHasAsgn in lhs.typ.flags and not lhsIsResult and if tfHasAsgn in lhs.typ.flags and not lhsIsResult and
mode != noOverloadedAsgn: mode != noOverloadedAsgn:
return overloadedAsgn(c, lhs, n.sons[1]) return overloadedAsgn(c, lhs, n.sons[1])
else:
liftTypeBoundOps(c, lhs.typ, lhs.info)
fixAbstractType(c, n) fixAbstractType(c, n)
asgnToResultVar(c, n, n.sons[0], n.sons[1]) asgnToResultVar(c, n, n.sons[0], n.sons[1])
@ -1657,7 +1655,7 @@ proc semQuoteAst(c: PContext, n: PNode): PNode =
# We transform the do block into a template with a param for # We transform the do block into a template with a param for
# each interpolation. We'll pass this template to getAst. # each interpolation. We'll pass this template to getAst.
var var
quotedBlock = n{-1} quotedBlock = n[^1]
op = if n.len == 3: expectString(c, n[1]) else: "``" op = if n.len == 3: expectString(c, n[1]) else: "``"
quotes = newSeq[PNode](1) quotes = newSeq[PNode](1)
# the quotes will be added to a nkCall statement # the quotes will be added to a nkCall statement
@ -2099,7 +2097,7 @@ proc shouldBeBracketExpr(n: PNode): bool =
let b = a[0] let b = a[0]
if b.kind in nkSymChoices: if b.kind in nkSymChoices:
for i in 0..<b.len: for i in 0..<b.len:
if b[i].sym.magic == mArrGet: if b[i].kind == nkSym and b[i].sym.magic == mArrGet:
let be = newNodeI(nkBracketExpr, n.info) let be = newNodeI(nkBracketExpr, n.info)
for i in 1..<a.len: be.add(a[i]) for i in 1..<a.len: be.add(a[i])
n.sons[0] = be n.sons[0] = be
@ -2356,6 +2354,10 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
if not n.sons[0].typ.isEmptyType and not implicitlyDiscardable(n.sons[0]): if not n.sons[0].typ.isEmptyType and not implicitlyDiscardable(n.sons[0]):
localError(n.info, errGenerated, "'defer' takes a 'void' expression") localError(n.info, errGenerated, "'defer' takes a 'void' expression")
#localError(n.info, errGenerated, "'defer' not allowed in this context") #localError(n.info, errGenerated, "'defer' not allowed in this context")
of nkGotoState, nkState:
if n.len != 1 and n.len != 2: illFormedAst(n)
for i in 0 ..< n.len:
n.sons[i] = semExpr(c, n.sons[i])
else: else:
localError(n.info, errInvalidExpressionX, localError(n.info, errInvalidExpressionX,
renderTree(n, {renderNoComments})) renderTree(n, {renderNoComments}))

View file

@ -38,9 +38,7 @@ proc skipAddr(n: PNode): PNode {.inline.} =
proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode = proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode =
result = newNodeI(nkBracketExpr, n.info) result = newNodeI(nkBracketExpr, n.info)
for i in 1..<n.len: result.add(n[i]) for i in 1..<n.len: result.add(n[i])
let oldBracketExpr = c.p.bracketExpr
result = semSubscript(c, result, flags) result = semSubscript(c, result, flags)
c.p.bracketExpr = oldBracketExpr
if result.isNil: if result.isNil:
let x = copyTree(n) let x = copyTree(n)
x.sons[0] = newIdentNode(getIdent"[]", n.info) x.sons[0] = newIdentNode(getIdent"[]", n.info)
@ -146,8 +144,14 @@ proc evalTypeTrait(traitCall: PNode, operand: PType, context: PSym): PNode =
result = res.base.toNode(traitCall.info) result = res.base.toNode(traitCall.info)
of "stripGenericParams": of "stripGenericParams":
result = uninstantiate(operand).toNode(traitCall.info) result = uninstantiate(operand).toNode(traitCall.info)
of "supportsCopyMem":
let t = operand.skipTypes({tyVar, tyGenericInst, tyAlias, tyInferred})
let complexObj = containsGarbageCollectedRef(t) or
hasDestructor(t)
result = newIntNodeT(ord(not complexObj), traitCall)
else: else:
internalAssert false localError(traitCall.info, "unknown trait")
result = emptyNode
proc semTypeTraits(c: PContext, n: PNode): PNode = proc semTypeTraits(c: PContext, n: PNode): PNode =
checkMinSonsLen(n, 2) checkMinSonsLen(n, 2)
@ -246,7 +250,11 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
result = semTypeOf(c, n.sons[1]) result = semTypeOf(c, n.sons[1])
of mArrGet: result = semArrGet(c, n, flags) of mArrGet: result = semArrGet(c, n, flags)
of mArrPut: result = semArrPut(c, n, flags) of mArrPut: result = semArrPut(c, n, flags)
of mAsgn: result = semAsgnOpr(c, n) of mAsgn:
if n[0].sym.name.s == "=":
result = semAsgnOpr(c, n)
else:
result = n
of mIsPartOf: result = semIsPartOf(c, n, flags) of mIsPartOf: result = semIsPartOf(c, n, flags)
of mTypeTrait: result = semTypeTraits(c, n) of mTypeTrait: result = semTypeTraits(c, n)
of mAstToStr: of mAstToStr:
@ -264,35 +272,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
of mDotDot: of mDotDot:
result = n result = n
of mRoof: of mRoof:
let bracketExpr = if n.len == 3: n.sons[2] else: c.p.bracketExpr localError(n.info, "builtin roof operator is not supported anymore")
if bracketExpr.isNil:
localError(n.info, "no surrounding array access context for '^'")
result = n.sons[1]
elif bracketExpr.checkForSideEffects != seNoSideEffect:
localError(n.info, "invalid context for '^' as '$#' has side effects" %
renderTree(bracketExpr))
result = n.sons[1]
elif bracketExpr.typ.isStrangeArray:
localError(n.info, "invalid context for '^' as len!=high+1 for '$#'" %
renderTree(bracketExpr))
result = n.sons[1]
else:
# ^x is rewritten to: len(a)-x
let lenExpr = newNodeI(nkCall, n.info)
lenExpr.add newIdentNode(getIdent"len", n.info)
lenExpr.add bracketExpr
let lenExprB = semExprWithType(c, lenExpr)
if lenExprB.typ.isNil or not isOrdinalType(lenExprB.typ):
localError(n.info, "'$#' has to be of an ordinal type for '^'" %
renderTree(lenExpr))
result = n.sons[1]
else:
result = newNodeIT(nkCall, n.info, getSysType(tyInt))
let subi = getSysMagic("-", mSubI)
#echo "got ", typeToString(subi.typ)
result.add newSymNode(subi, n.info)
result.add lenExprB
result.add n.sons[1]
of mPlugin: of mPlugin:
let plugin = getPlugin(n[0].sym) let plugin = getPlugin(n[0].sym)
if plugin.isNil: if plugin.isNil:

View file

@ -78,13 +78,13 @@ proc caseBranchMatchesExpr(branch, matched: PNode): bool =
proc pickCaseBranch(caseExpr, matched: PNode): PNode = proc pickCaseBranch(caseExpr, matched: PNode): PNode =
# XXX: Perhaps this proc already exists somewhere # XXX: Perhaps this proc already exists somewhere
let endsWithElse = caseExpr{-1}.kind == nkElse let endsWithElse = caseExpr[^1].kind == nkElse
for i in 1 .. caseExpr.len - 1 - int(endsWithElse): for i in 1 .. caseExpr.len - 1 - int(endsWithElse):
if caseExpr[i].caseBranchMatchesExpr(matched): if caseExpr[i].caseBranchMatchesExpr(matched):
return caseExpr[i] return caseExpr[i]
if endsWithElse: if endsWithElse:
return caseExpr{-1} return caseExpr[^1]
iterator directFieldsInRecList(recList: PNode): PNode = iterator directFieldsInRecList(recList: PNode): PNode =
# XXX: We can remove this case by making all nkOfBranch nodes # XXX: We can remove this case by making all nkOfBranch nodes
@ -136,17 +136,20 @@ proc semConstructFields(c: PContext, recNode: PNode,
of nkRecCase: of nkRecCase:
template fieldsPresentInBranch(branchIdx: int): string = template fieldsPresentInBranch(branchIdx: int): string =
fieldsPresentInInitExpr(recNode[branchIdx]{-1}, initExpr) let branch = recNode[branchIdx]
let fields = branch[branch.len - 1]
fieldsPresentInInitExpr(fields, initExpr)
template checkMissingFields(branchNode: PNode) = template checkMissingFields(branchNode: PNode) =
checkForMissingFields(branchNode{-1}, initExpr) let fields = branchNode[branchNode.len - 1]
checkForMissingFields(fields, initExpr)
let discriminator = recNode.sons[0]; let discriminator = recNode.sons[0];
internalAssert discriminator.kind == nkSym internalAssert discriminator.kind == nkSym
var selectedBranch = -1 var selectedBranch = -1
for i in 1 ..< recNode.len: for i in 1 ..< recNode.len:
let innerRecords = recNode[i]{-1} let innerRecords = recNode[i][^1]
let status = semConstructFields(c, innerRecords, initExpr, flags) let status = semConstructFields(c, innerRecords, initExpr, flags)
if status notin {initNone, initUnknown}: if status notin {initNone, initUnknown}:
mergeInitStatus(result, status) mergeInitStatus(result, status)

View file

@ -11,23 +11,14 @@ import
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
wordrecg, strutils, options, guards, writetracking wordrecg, strutils, options, guards, writetracking
when defined(useDfa):
import dfa
# Second semantic checking pass over the AST. Necessary because the old # Second semantic checking pass over the AST. Necessary because the old
# way had some inherent problems. Performs: # way had some inherent problems. Performs:
# #
# * effect+exception tracking # * effect+exception tracking
# * "usage before definition" checking # * "usage before definition" checking
# * checks for invalid usages of compiletime magics (not implemented)
# * checks for invalid usages of NimNode (not implemented)
# * later: will do an escape analysis for closures at least
# Predefined effects:
# io, time (time dependent), gc (performs GC'ed allocation), exceptions,
# side effect (accesses global), store (stores into *type*),
# store_unknown (performs some store) --> store(any)|store(x)
# load (loads from *type*), recursive (recursive call), unsafe,
# endless (has endless loops), --> user effects are defined over *patterns*
# --> a TR macro can annotate the proc with user defined annotations
# --> the effect system can access these
# ------------------------ exception and tag tracking ------------------------- # ------------------------ exception and tag tracking -------------------------
@ -248,6 +239,7 @@ proc useVar(a: PEffects, n: PNode) =
(tfHasGCedMem in s.typ.flags or s.typ.isGCedMem): (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem):
#if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n) #if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
markGcUnsafe(a, s) markGcUnsafe(a, s)
markSideEffect(a, s)
else: else:
markSideEffect(a, s) markSideEffect(a, s)
@ -590,6 +582,12 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
if paramType != nil and paramType.kind == tyVar: if paramType != nil and paramType.kind == tyVar:
if n.kind == nkSym and isLocalVar(tracked, n.sym): if n.kind == nkSym and isLocalVar(tracked, n.sym):
makeVolatile(tracked, n.sym) makeVolatile(tracked, n.sym)
if paramType != nil and paramType.kind == tyProc and tfGcSafe in paramType.flags:
let argtype = skipTypes(a.typ, abstractInst)
# XXX figure out why this can be a non tyProc here. See httpclient.nim for an
# example that triggers it.
if argtype.kind == tyProc and notGcSafe(argtype) and not tracked.inEnforcedGcSafe:
localError(n.info, $n & " is not GC safe")
notNilCheck(tracked, n, paramType) notNilCheck(tracked, n, paramType)
proc breaksBlock(n: PNode): bool = proc breaksBlock(n: PNode): bool =
@ -979,7 +977,10 @@ proc trackProc*(s: PSym, body: PNode) =
message(s.info, warnLockLevel, message(s.info, warnLockLevel,
"declared lock level is $1, but real lock level is $2" % "declared lock level is $1, but real lock level is $2" %
[$s.typ.lockLevel, $t.maxLockLevel]) [$s.typ.lockLevel, $t.maxLockLevel])
if s.kind == skFunc: trackWrites(s, body) when false:
if s.kind == skFunc:
when defined(dfa): dataflowAnalysis(s, body)
trackWrites(s, body)
proc trackTopLevelStmt*(module: PSym; n: PNode) = proc trackTopLevelStmt*(module: PSym; n: PNode) =
if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef, if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef,

View file

@ -100,6 +100,7 @@ proc semProc(c: PContext, n: PNode): PNode
include semdestruct include semdestruct
proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} = proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} =
if not newDestructors:
if efAllowDestructor notin flags and if efAllowDestructor notin flags and
n.kind in nkCallKinds+{nkObjConstr,nkBracket}: n.kind in nkCallKinds+{nkObjConstr,nkBracket}:
if instantiateDestructor(c, n.typ) != nil: if instantiateDestructor(c, n.typ) != nil:
@ -384,7 +385,7 @@ proc checkNilable(v: PSym) =
{tfNotNil, tfNeedsInit} * v.typ.flags != {}: {tfNotNil, tfNeedsInit} * v.typ.flags != {}:
if v.ast.isNil: if v.ast.isNil:
message(v.info, warnProveInit, v.name.s) message(v.info, warnProveInit, v.name.s)
elif tfNeedsInit in v.typ.flags and tfNotNil notin v.ast.typ.flags: elif tfNotNil in v.typ.flags and tfNotNil notin v.ast.typ.flags:
message(v.info, warnProveInit, v.name.s) message(v.info, warnProveInit, v.name.s)
include semasgn include semasgn
@ -399,7 +400,7 @@ proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) =
# in order for this transformation to be correct. # in order for this transformation to be correct.
let L = identDefs.len let L = identDefs.len
let value = identDefs[L-1] let value = identDefs[L-1]
if value.typ != nil and tfHasAsgn in value.typ.flags: if value.typ != nil and tfHasAsgn in value.typ.flags and not newDestructors:
# the spec says we need to rewrite 'var x = T()' to 'var x: T; x = T()': # the spec says we need to rewrite 'var x = T()' to 'var x: T; x = T()':
identDefs.sons[L-1] = emptyNode identDefs.sons[L-1] = emptyNode
if result.kind != nkStmtList: if result.kind != nkStmtList:
@ -552,6 +553,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
# this can only happen for errornous var statements: # this can only happen for errornous var statements:
if typ == nil: continue if typ == nil: continue
typeAllowedCheck(a.info, typ, symkind) typeAllowedCheck(a.info, typ, symkind)
liftTypeBoundOps(c, typ, a.info)
var tup = skipTypes(typ, {tyGenericInst, tyAlias}) var tup = skipTypes(typ, {tyGenericInst, tyAlias})
if a.kind == nkVarTuple: if a.kind == nkVarTuple:
if tup.kind != tyTuple: if tup.kind != tyTuple:
@ -607,7 +609,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
if def.kind == nkPar: v.ast = def[j] if def.kind == nkPar: v.ast = def[j]
setVarType(v, tup.sons[j]) setVarType(v, tup.sons[j])
b.sons[j] = newSymNode(v) b.sons[j] = newSymNode(v)
addDefer(c, result, v) if not newDestructors: addDefer(c, result, v)
checkNilable(v) checkNilable(v)
if sfCompileTime in v.flags: hasCompileTime = true if sfCompileTime in v.flags: hasCompileTime = true
if hasCompileTime: vm.setupCompileTimeVar(c.module, c.cache, result) if hasCompileTime: vm.setupCompileTimeVar(c.module, c.cache, result)
@ -773,24 +775,55 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
checkSonsLen(a, 3) checkSonsLen(a, 3)
let name = a.sons[0] let name = a.sons[0]
var s: PSym var s: PSym
if name.kind == nkDotExpr: if name.kind == nkDotExpr and a[2].kind == nkObjectTy:
s = qualifiedLookUp(c, name, {checkUndeclared, checkModule}) let pkgName = considerQuotedIdent(name[0])
if s.kind != skType or let typName = considerQuotedIdent(name[1])
s.typ.skipTypes(abstractPtrs).kind != tyObject or let pkg = c.graph.packageSyms.strTableGet(pkgName)
tfPartial notin s.typ.skipTypes(abstractPtrs).flags: if pkg.isNil or pkg.kind != skPackage:
localError(name.info, "only .partial objects can be extended") localError(name.info, "unknown package name: " & pkgName.s)
else:
let typsym = pkg.tab.strTableGet(typName)
if typsym.isNil:
s = semIdentDef(c, name[1], skType)
s.typ = newTypeS(tyObject, c)
s.typ.sym = s
s.flags.incl sfForward
pkg.tab.strTableAdd s
addInterfaceDecl(c, s)
elif typsym.kind == skType and sfForward in typsym.flags:
s = typsym
addInterfaceDecl(c, s)
else:
localError(name.info, typsym.name.s & " is not a type that can be forwarded")
s = typsym
else: else:
s = semIdentDef(c, name, skType) s = semIdentDef(c, name, skType)
s.typ = newTypeS(tyForward, c) s.typ = newTypeS(tyForward, c)
s.typ.sym = s # process pragmas: s.typ.sym = s # process pragmas:
if name.kind == nkPragmaExpr: if name.kind == nkPragmaExpr:
pragma(c, s, name.sons[1], typePragmas) pragma(c, s, name.sons[1], typePragmas)
if sfForward in s.flags:
# check if the symbol already exists:
let pkg = c.module.owner
if not isTopLevel(c) or pkg.isNil:
localError(name.info, "only top level types in a package can be 'package'")
else:
let typsym = pkg.tab.strTableGet(s.name)
if typsym != nil:
if sfForward notin typsym.flags or sfNoForward notin typsym.flags:
typeCompleted(typsym)
typsym.info = s.info
else:
localError(name.info, "cannot complete type '" & s.name.s & "' twice; " &
"previous type completion was here: " & $typsym.info)
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)
a.sons[0] = newSymNode(s) a.sons[0] = newSymNode(s)
proc checkCovariantParamsUsages(genericType: PType) = proc checkCovariantParamsUsages(genericType: PType) =
var body = genericType{-1} var body = genericType[^1]
proc traverseSubTypes(t: PType): bool = proc traverseSubTypes(t: PType): bool =
template error(msg) = localError(genericType.sym.info, msg) template error(msg) = localError(genericType.sym.info, msg)
@ -1112,7 +1145,7 @@ proc semProcAnnotation(c: PContext, prc: PNode;
validPragmas: TSpecialWords): PNode = validPragmas: TSpecialWords): PNode =
var n = prc.sons[pragmasPos] var n = prc.sons[pragmasPos]
if n == nil or n.kind == nkEmpty: return if n == nil or n.kind == nkEmpty: return
for i in countup(0, <n.len): for i in countup(0, n.len-1):
var it = n.sons[i] var it = n.sons[i]
var key = if it.kind == nkExprColonExpr: it.sons[0] else: it var key = if it.kind == nkExprColonExpr: it.sons[0] else: it
let m = lookupMacro(c, key) let m = lookupMacro(c, key)
@ -1276,6 +1309,27 @@ proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
proc semOverride(c: PContext, s: PSym, n: PNode) = proc semOverride(c: PContext, s: PSym, n: PNode) =
case s.name.s.normalize case s.name.s.normalize
of "destroy", "=destroy": of "destroy", "=destroy":
if newDestructors:
let t = s.typ
var noError = false
if t.len == 2 and t.sons[0] == nil and t.sons[1].kind == tyVar:
var obj = t.sons[1].sons[0]
while true:
incl(obj.flags, tfHasAsgn)
if obj.kind == tyGenericBody: obj = obj.lastSon
elif obj.kind == tyGenericInvocation: obj = obj.sons[0]
else: break
if obj.kind in {tyObject, tyDistinct}:
if obj.destructor.isNil:
obj.destructor = s
else:
localError(n.info, errGenerated,
"cannot bind another '" & s.name.s & "' to: " & typeToString(obj))
noError = true
if not noError and sfSystemModule notin s.owner.flags:
localError(n.info, errGenerated,
"signature for '" & s.name.s & "' must be proc[T: object](x: var T)")
else:
doDestructorStuff(c, s, n) doDestructorStuff(c, s, n)
if not experimentalMode(c): if not experimentalMode(c):
localError n.info, "use the {.experimental.} pragma to enable destructors" localError n.info, "use the {.experimental.} pragma to enable destructors"
@ -1303,7 +1357,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
localError(n.info, errGenerated, localError(n.info, errGenerated,
"signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T") "signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T")
incl(s.flags, sfUsed) incl(s.flags, sfUsed)
of "=": of "=", "=sink":
if s.magic == mAsgn: return if s.magic == mAsgn: return
incl(s.flags, sfUsed) incl(s.flags, sfUsed)
let t = s.typ let t = s.typ
@ -1321,14 +1375,16 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
objB = objB.sons[0] objB = objB.sons[0]
else: break else: break
if obj.kind in {tyObject, tyDistinct} and sameType(obj, objB): if obj.kind in {tyObject, tyDistinct} and sameType(obj, objB):
if obj.assignment.isNil: let opr = if s.name.s == "=": addr(obj.assignment) else: addr(obj.sink)
obj.assignment = s if opr[].isNil:
opr[] = s
else: else:
localError(n.info, errGenerated, localError(n.info, errGenerated,
"cannot bind another '=' to: " & typeToString(obj)) "cannot bind another '" & s.name.s & "' to: " & typeToString(obj))
return return
if sfSystemModule notin s.owner.flags:
localError(n.info, errGenerated, localError(n.info, errGenerated,
"signature for '=' must be proc[T: object](x: var T; y: T)") "signature for '" & s.name.s & "' must be proc[T: object](x: var T; y: T)")
else: else:
if sfOverriden in s.flags: if sfOverriden in s.flags:
localError(n.info, errGenerated, localError(n.info, errGenerated,
@ -1808,7 +1864,8 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
of LastBlockStmts: of LastBlockStmts:
for j in countup(i + 1, length - 1): for j in countup(i + 1, length - 1):
case n.sons[j].kind case n.sons[j].kind
of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: discard of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr,
nkBlockStmt, nkState: discard
else: localError(n.sons[j].info, errStmtInvalidAfterReturn) else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
else: discard else: discard

View file

@ -113,13 +113,9 @@ type
owner: PSym owner: PSym
cursorInBody: bool # only for nimsuggest cursorInBody: bool # only for nimsuggest
scopeN: int scopeN: int
bracketExpr: PNode
template withBracketExpr(ctx, x, body: untyped) = template withBracketExpr(ctx, x, body: untyped) =
let old = ctx.bracketExpr
ctx.bracketExpr = x
body body
ctx.bracketExpr = old
proc getIdentNode(c: var TemplCtx, n: PNode): PNode = proc getIdentNode(c: var TemplCtx, n: PNode): PNode =
case n.kind case n.kind
@ -304,18 +300,6 @@ proc semTemplBodySons(c: var TemplCtx, n: PNode): PNode =
for i in 0 ..< n.len: for i in 0 ..< n.len:
result.sons[i] = semTemplBody(c, n.sons[i]) result.sons[i] = semTemplBody(c, n.sons[i])
proc oprIsRoof(n: PNode): bool =
const roof = "^"
case n.kind
of nkIdent: result = n.ident.s == roof
of nkSym: result = n.sym.name.s == roof
of nkAccQuoted:
if n.len == 1:
result = oprIsRoof(n.sons[0])
of nkOpenSymChoice, nkClosedSymChoice:
result = oprIsRoof(n.sons[0])
else: discard
proc semTemplBody(c: var TemplCtx, n: PNode): PNode = proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
result = n result = n
semIdeForTemplateOrGenericCheck(n, c.cursorInBody) semIdeForTemplateOrGenericCheck(n, c.cursorInBody)
@ -506,8 +490,6 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
result = semTemplBodySons(c, n) result = semTemplBodySons(c, n)
of nkCallKinds-{nkPostfix}: of nkCallKinds-{nkPostfix}:
result = semTemplBodySons(c, n) result = semTemplBodySons(c, n)
if c.bracketExpr != nil and n.len == 2 and oprIsRoof(n.sons[0]):
result.add c.bracketExpr
of nkDotExpr, nkAccQuoted: of nkDotExpr, nkAccQuoted:
# dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam', # dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam',
# so we use the generic code for nkDotExpr too # so we use the generic code for nkDotExpr too

View file

@ -1167,7 +1167,10 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
# special check for generic object with # special check for generic object with
# generic/partial specialized parent # generic/partial specialized parent
let tx = result.skipTypes(abstractPtrs) let tx = result.skipTypes(abstractPtrs, 50)
if tx.isNil:
localError(n.info, "invalid recursion in type '$1'" % typeToString(result[0]))
return errorType(c)
if tx != result and tx.kind == tyObject and tx.sons[0] != nil: if tx != result and tx.kind == tyObject and tx.sons[0] != nil:
semObjectTypeForInheritedGenericInst(c, n, tx) semObjectTypeForInheritedGenericInst(c, n, tx)
@ -1295,7 +1298,6 @@ proc symFromExpectedTypeNode(c: PContext, n: PNode): PSym =
proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = nil result = nil
when defined(nimsuggest):
inc c.inTypeContext inc c.inTypeContext
if gCmd == cmdIdeTools: suggestExpr(c, n) if gCmd == cmdIdeTools: suggestExpr(c, n)
@ -1511,8 +1513,13 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
localError(n.info, errTypeExpected) localError(n.info, errTypeExpected)
result = newOrPrevType(tyError, prev, c) result = newOrPrevType(tyError, prev, c)
n.typ = result n.typ = result
when defined(nimsuggest):
dec c.inTypeContext dec c.inTypeContext
if c.inTypeContext == 0: instAllTypeBoundOp(c, n.info)
when false:
proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = semTypeNodeInner(c, n, prev)
instAllTypeBoundOp(c, n.info)
proc setMagicType(m: PSym, kind: TTypeKind, size: int) = proc setMagicType(m: PSym, kind: TTypeKind, size: int) =
# source : https://en.wikipedia.org/wiki/Data_structure_alignment#x86 # source : https://en.wikipedia.org/wiki/Data_structure_alignment#x86
@ -1614,8 +1621,8 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
var a = n.sons[i] var a = n.sons[i]
if a.kind != nkIdentDefs: illFormedAst(n) if a.kind != nkIdentDefs: illFormedAst(n)
let L = a.len let L = a.len
var def = a{-1} var def = a[^1]
let constraint = a{-2} let constraint = a[^2]
var typ: PType var typ: PType
if constraint.kind != nkEmpty: if constraint.kind != nkEmpty:

View file

@ -77,7 +77,7 @@ type
topLayer*: TIdTable topLayer*: TIdTable
nextLayer*: ptr LayeredIdTable nextLayer*: ptr LayeredIdTable
TReplTypeVars* {.final.} = object TReplTypeVars* = object
c*: PContext c*: PContext
typeMap*: ptr LayeredIdTable # map PType to PType typeMap*: ptr LayeredIdTable # map PType to PType
symMap*: TIdTable # map PSym to PSym symMap*: TIdTable # map PSym to PSym
@ -261,6 +261,17 @@ proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
if not (t.kind in tyMetaTypes or if not (t.kind in tyMetaTypes or
(t.kind == tyStatic and t.n == nil)): (t.kind == tyStatic and t.n == nil)):
result.flags.excl tfInstClearedFlags result.flags.excl tfInstClearedFlags
when false:
if newDestructors:
result.assignment = nil
#result.destructor = nil
result.sink = nil
template typeBound(c, newty, oldty, field, info) =
let opr = newty.field
if opr != nil and sfFromGeneric notin opr.flags:
# '=' needs to be instantiated for generics when the type is constructed:
newty.field = c.instTypeBoundOp(c, opr, oldty, info, attachedAsgn, 1)
proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
# tyGenericInvocation[A, tyGenericInvocation[A, B]] # tyGenericInvocation[A, tyGenericInvocation[A, B]]
@ -357,11 +368,10 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
assert newbody.kind in {tyRef, tyPtr} assert newbody.kind in {tyRef, tyPtr}
assert newbody.lastSon.typeInst == nil assert newbody.lastSon.typeInst == nil
newbody.lastSon.typeInst = result newbody.lastSon.typeInst = result
let asgn = newbody.assignment if newDestructors:
if asgn != nil and sfFromGeneric notin asgn.flags: cl.c.typesWithOps.add((newbody, result))
# '=' needs to be instantiated for generics when the type is constructed: else:
newbody.assignment = cl.c.instTypeBoundOp(cl.c, asgn, result, cl.info, typeBound(cl.c, newbody, result, assignment, cl.info)
attachedAsgn, 1)
let methods = skipTypes(bbody, abstractPtrs).methods let methods = skipTypes(bbody, abstractPtrs).methods
for col, meth in items(methods): for col, meth in items(methods):
# we instantiate the known methods belonging to that type, this causes # we instantiate the known methods belonging to that type, this causes
@ -529,6 +539,17 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
else: discard else: discard
proc instAllTypeBoundOp*(c: PContext, info: TLineInfo) =
if not newDestructors: return
var i = 0
while i < c.typesWithOps.len:
let (newty, oldty) = c.typesWithOps[i]
typeBound(c, newty, oldty, destructor, info)
typeBound(c, newty, oldty, sink, info)
typeBound(c, newty, oldty, assignment, info)
inc i
setLen(c.typesWithOps, 0)
proc initTypeVars*(p: PContext, typeMap: ptr LayeredIdTable, info: TLineInfo; proc initTypeVars*(p: PContext, typeMap: ptr LayeredIdTable, info: TLineInfo;
owner: PSym): TReplTypeVars = owner: PSym): TReplTypeVars =
initIdTable(result.symMap) initIdTable(result.symMap)

View file

@ -55,6 +55,7 @@ type
# a distrinct type # a distrinct type
typedescMatched*: bool typedescMatched*: bool
isNoCall*: bool # misused for generic type instantiations C[T] isNoCall*: bool # misused for generic type instantiations C[T]
mutabilityProblem*: uint8 # tyVar mismatch
inferredTypes: seq[PType] # inferred types during the current signature inferredTypes: seq[PType] # inferred types during the current signature
# matching. they will be reset if the matching # matching. they will be reset if the matching
# is not successful. may replace the bindings # is not successful. may replace the bindings
@ -66,7 +67,6 @@ type
# or when the explain pragma is used. may be # or when the explain pragma is used. may be
# triggered with an idetools command in the # triggered with an idetools command in the
# future. # future.
mutabilityProblem*: uint8 # tyVar mismatch
inheritancePenalty: int # to prefer closest father object type inheritancePenalty: int # to prefer closest father object type
TTypeRelFlag* = enum TTypeRelFlag* = enum
@ -224,7 +224,8 @@ proc complexDisambiguation(a, b: PType): int =
let x = a.sons[i].sumGeneric let x = a.sons[i].sumGeneric
let y = b.sons[i].sumGeneric let y = b.sons[i].sumGeneric
#if ggDebug: #if ggDebug:
# echo "came her ", typeToString(a.sons[i]), " ", typeToString(b.sons[i]) #echo "came herA ", typeToString(a.sons[i]), " ", x
#echo "came herB ", typeToString(b.sons[i]), " ", y
if x != y: if x != y:
if winner == 0: if winner == 0:
if x > y: winner = 1 if x > y: winner = 1
@ -389,7 +390,16 @@ proc isConvertibleToRange(f, a: PType): bool =
# be less picky for tyRange, as that it is used for array indexing: # be less picky for tyRange, as that it is used for array indexing:
if f.kind in {tyInt..tyInt64, tyUInt..tyUInt64} and if f.kind in {tyInt..tyInt64, tyUInt..tyUInt64} and
a.kind in {tyInt..tyInt64, tyUInt..tyUInt64}: a.kind in {tyInt..tyInt64, tyUInt..tyUInt64}:
result = true case f.kind
of tyInt, tyInt64: result = true
of tyInt8: result = a.kind in {tyInt8, tyInt}
of tyInt16: result = a.kind in {tyInt8, tyInt16, tyInt}
of tyInt32: result = a.kind in {tyInt8, tyInt16, tyInt32, tyInt}
of tyUInt, tyUInt64: result = true
of tyUInt8: result = a.kind in {tyUInt8, tyUInt}
of tyUInt16: result = a.kind in {tyUInt8, tyUInt16, tyUInt}
of tyUInt32: result = a.kind in {tyUInt8, tyUInt16, tyUInt32, tyUInt}
else: result = false
elif f.kind in {tyFloat..tyFloat128} and elif f.kind in {tyFloat..tyFloat128} and
a.kind in {tyFloat..tyFloat128}: a.kind in {tyFloat..tyFloat128}:
result = true result = true
@ -1288,11 +1298,12 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
of tyString: result = isConvertible of tyString: result = isConvertible
of tyPtr: of tyPtr:
# ptr[Tag, char] is not convertible to 'cstring' for now: # ptr[Tag, char] is not convertible to 'cstring' for now:
if a.len == 1 and a.sons[0].kind == tyChar: result = isConvertible if a.len == 1:
of tyArray: let pointsTo = a.sons[0].skipTypes(abstractInst)
if (firstOrd(a.sons[0]) == 0) and if pointsTo.kind == tyChar: result = isConvertible
(skipTypes(a.sons[0], {tyRange}).kind in {tyInt..tyInt64}) and elif pointsTo.kind == tyArray and firstOrd(pointsTo.sons[0]) == 0 and
(a.sons[1].kind == tyChar): skipTypes(pointsTo.sons[0], {tyRange}).kind in {tyInt..tyInt64} and
pointsTo.sons[1].kind == tyChar:
result = isConvertible result = isConvertible
else: discard else: discard
@ -1450,8 +1461,13 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
of tyOr: of tyOr:
considerPreviousT: considerPreviousT:
result = isNone result = isNone
let oldInheritancePenalty = c.inheritancePenalty
var maxInheritance = 0
for branch in f.sons: for branch in f.sons:
c.inheritancePenalty = 0
let x = typeRel(c, branch, aOrig) let x = typeRel(c, branch, aOrig)
maxInheritance = max(maxInheritance, c.inheritancePenalty)
# 'or' implies maximum matching result: # 'or' implies maximum matching result:
if x > result: result = x if x > result: result = x
if result >= isSubtype: if result >= isSubtype:
@ -1459,6 +1475,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
bindingRet result bindingRet result
else: else:
result = isNone result = isNone
c.inheritancePenalty = oldInheritancePenalty + maxInheritance
of tyNot: of tyNot:
considerPreviousT: considerPreviousT:
@ -1549,11 +1566,19 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
result = isNone result = isNone
else: else:
if f.sonsLen > 0 and f.sons[0].kind != tyNone: if f.sonsLen > 0 and f.sons[0].kind != tyNone:
let oldInheritancePenalty = c.inheritancePenalty
result = typeRel(c, f.lastSon, a, flags + {trDontBind}) result = typeRel(c, f.lastSon, a, flags + {trDontBind})
if doBind and result notin {isNone, isGeneric}: if doBind and result notin {isNone, isGeneric}:
let concrete = concreteType(c, a) let concrete = concreteType(c, a)
if concrete == nil: return isNone if concrete == nil: return isNone
put(c, f, concrete) put(c, f, concrete)
# bug #6526
if result in {isEqual, isSubtype}:
# 'T: Class' is a *better* match than just 'T'
# but 'T: Subclass' is even better:
c.inheritancePenalty = oldInheritancePenalty - c.inheritancePenalty -
100 * ord(result == isEqual)
result = isGeneric
else: else:
result = isGeneric result = isGeneric
@ -2287,6 +2312,7 @@ proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo;
localError(info, errGenerated, "cannot instantiate '" & dc.name.s & "'") localError(info, errGenerated, "cannot instantiate '" & dc.name.s & "'")
else: else:
result = c.semGenerateInstance(c, dc, m.bindings, info) result = c.semGenerateInstance(c, dc, m.bindings, info)
if op == attachedDeepCopy:
assert sfFromGeneric in result.flags assert sfFromGeneric in result.flags
include suggest include suggest

View file

@ -21,9 +21,7 @@
import import
intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os, intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os,
idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread, idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread,
lambdalifting, sempass2, lowerings, lookups lambdalifting, sempass2, lowerings, lookups, destroyer, liftlocals
# implementation
type type
PTransNode* = distinct PNode PTransNode* = distinct PNode
@ -45,7 +43,7 @@ type
inlining: int # > 0 if we are in inlining context (copy vars) inlining: int # > 0 if we are in inlining context (copy vars)
nestedProcs: int # > 0 if we are in a nested proc nestedProcs: int # > 0 if we are in a nested proc
contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break' contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break'
deferDetected, tooEarly: bool deferDetected, tooEarly, needsDestroyPass: bool
PTransf = ref TTransfContext PTransf = ref TTransfContext
proc newTransNode(a: PNode): PTransNode {.inline.} = proc newTransNode(a: PNode): PTransNode {.inline.} =
@ -277,7 +275,7 @@ proc transformWhile(c: PTransf; n: PNode): PTransNode =
var body = newTransNode(n) var body = newTransNode(n)
for i in 0..n.len-2: for i in 0..n.len-2:
body[i] = transform(c, n.sons[i]) body[i] = transform(c, n.sons[i])
body[<n.len] = transformLoopBody(c, n.sons[<n.len]) body[n.len-1] = transformLoopBody(c, n.sons[n.len-1])
result[1] = body result[1] = body
discard c.breakSyms.pop discard c.breakSyms.pop
@ -367,16 +365,22 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
# addr ( nkConv ( deref ( x ) ) ) --> nkConv(x) # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
n.sons[0].sons[0] = m.sons[0] n.sons[0].sons[0] = m.sons[0]
result = PTransNode(n.sons[0]) result = PTransNode(n.sons[0])
if n.typ.kind != tyOpenArray:
PNode(result).typ = n.typ
of nkHiddenStdConv, nkHiddenSubConv, nkConv: of nkHiddenStdConv, nkHiddenSubConv, nkConv:
var m = n.sons[0].sons[1] var m = n.sons[0].sons[1]
if m.kind == a or m.kind == b: if m.kind == a or m.kind == b:
# addr ( nkConv ( deref ( x ) ) ) --> nkConv(x) # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
n.sons[0].sons[1] = m.sons[0] n.sons[0].sons[1] = m.sons[0]
result = PTransNode(n.sons[0]) result = PTransNode(n.sons[0])
if n.typ.kind != tyOpenArray:
PNode(result).typ = n.typ
else: else:
if n.sons[0].kind == a or n.sons[0].kind == b: if n.sons[0].kind == a or n.sons[0].kind == b:
# addr ( deref ( x )) --> x # addr ( deref ( x )) --> x
result = PTransNode(n.sons[0].sons[0]) result = PTransNode(n.sons[0].sons[0])
if n.typ.kind != tyOpenArray:
PNode(result).typ = n.typ
proc generateThunk(prc: PNode, dest: PType): PNode = proc generateThunk(prc: PNode, dest: PType): PNode =
## Converts 'prc' into '(thunk, nil)' so that it's compatible with ## Converts 'prc' into '(thunk, nil)' so that it's compatible with
@ -782,7 +786,8 @@ proc transform(c: PTransf, n: PNode): PTransNode =
nkBlockStmt, nkBlockExpr}: nkBlockStmt, nkBlockExpr}:
oldDeferAnchor = c.deferAnchor oldDeferAnchor = c.deferAnchor
c.deferAnchor = n c.deferAnchor = n
if n.typ != nil and tfHasAsgn in n.typ.flags:
c.needsDestroyPass = true
case n.kind case n.kind
of nkSym: of nkSym:
result = transformSym(c, n) result = transformSym(c, n)
@ -972,8 +977,11 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode =
result = processTransf(c, result, prc) result = processTransf(c, result, prc)
liftDefer(c, result) liftDefer(c, result)
#result = liftLambdas(prc, result) #result = liftLambdas(prc, result)
incl(result.flags, nfTransf)
when useEffectSystem: trackProc(prc, result) when useEffectSystem: trackProc(prc, result)
liftLocalsIfRequested(prc)
if c.needsDestroyPass and newDestructors:
result = injectDestructorCalls(prc, result)
incl(result.flags, nfTransf)
#if prc.name.s == "testbody": #if prc.name.s == "testbody":
# echo renderTree(result) # echo renderTree(result)
@ -985,10 +993,12 @@ proc transformStmt*(module: PSym, n: PNode): PNode =
result = processTransf(c, n, module) result = processTransf(c, n, module)
liftDefer(c, result) liftDefer(c, result)
#result = liftLambdasForTopLevel(module, result) #result = liftLambdasForTopLevel(module, result)
incl(result.flags, nfTransf)
when useEffectSystem: trackTopLevelStmt(module, result) when useEffectSystem: trackTopLevelStmt(module, result)
#if n.info ?? "temp.nim": #if n.info ?? "temp.nim":
# echo renderTree(result, {renderIds}) # echo renderTree(result, {renderIds})
if c.needsDestroyPass and newDestructors:
result = injectDestructorCalls(module, result)
incl(result.flags, nfTransf)
proc transformExpr*(module: PSym, n: PNode): PNode = proc transformExpr*(module: PSym, n: PNode): PNode =
if nfTransf in n.flags: if nfTransf in n.flags:
@ -997,4 +1007,6 @@ proc transformExpr*(module: PSym, n: PNode): PNode =
var c = openTransf(module, "") var c = openTransf(module, "")
result = processTransf(c, n, module) result = processTransf(c, n, module)
liftDefer(c, result) liftDefer(c, result)
if c.needsDestroyPass and newDestructors:
result = injectDestructorCalls(module, result)
incl(result.flags, nfTransf) incl(result.flags, nfTransf)

View file

@ -667,14 +667,6 @@ proc lengthOrd*(t: PType): BiggestInt =
else: else:
result = lastOrd(t) - firstOrd(t) + 1 result = lastOrd(t) - firstOrd(t) + 1
proc isCompatibleToCString*(a: PType): bool =
if a.kind == tyArray:
if (firstOrd(a.sons[0]) == 0) and
(skipTypes(a.sons[0], {tyRange, tyGenericInst, tyAlias}).kind in
{tyInt..tyInt64, tyUInt..tyUInt64}) and
(a.sons[1].kind == tyChar):
result = true
# -------------- type equality ----------------------------------------------- # -------------- type equality -----------------------------------------------
type type

View file

@ -20,7 +20,7 @@ proc renderPlainSymbolName*(n: PNode): string =
result = "" result = ""
case n.kind case n.kind
of nkPostfix, nkAccQuoted: of nkPostfix, nkAccQuoted:
result = renderPlainSymbolName(n[<n.len]) result = renderPlainSymbolName(n[n.len-1])
of nkIdent: of nkIdent:
result = n.ident.s result = n.ident.s
of nkSym: of nkSym:
@ -59,7 +59,7 @@ proc renderType(n: PNode): string =
assert len(params) > 0 assert len(params) > 0
result = "proc(" result = "proc("
for i in 1 ..< len(params): result.add(renderType(params[i]) & ',') for i in 1 ..< len(params): result.add(renderType(params[i]) & ',')
result[<len(result)] = ')' result[len(result)-1] = ')'
else: else:
result = "proc" result = "proc"
of nkIdentDefs: of nkIdentDefs:
@ -73,12 +73,12 @@ proc renderType(n: PNode): string =
of nkTupleTy: of nkTupleTy:
result = "tuple[" result = "tuple["
for i in 0 ..< len(n): result.add(renderType(n[i]) & ',') for i in 0 ..< len(n): result.add(renderType(n[i]) & ',')
result[<len(result)] = ']' result[len(result)-1] = ']'
of nkBracketExpr: of nkBracketExpr:
assert len(n) >= 2 assert len(n) >= 2
result = renderType(n[0]) & '[' result = renderType(n[0]) & '['
for i in 1 ..< len(n): result.add(renderType(n[i]) & ',') for i in 1 ..< len(n): result.add(renderType(n[i]) & ',')
result[<len(result)] = ']' result[len(result)-1] = ']'
else: result = "" else: result = ""
assert(not result.isNil) assert(not result.isNil)

View file

@ -1688,8 +1688,7 @@ proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode,
# arity here too: # arity here too:
if sym.typ.len > n.safeLen and sym.typ.len > 1: if sym.typ.len > n.safeLen and sym.typ.len > 1:
globalError(n.info, "in call '$#' got $#, but expected $# argument(s)" % [ globalError(n.info, "in call '$#' got $#, but expected $# argument(s)" % [
n.renderTree, n.renderTree, $(n.safeLen-1), $(sym.typ.len-1)])
$ <n.safeLen, $ <sym.typ.len])
setupGlobalCtx(module, cache) setupGlobalCtx(module, cache)
var c = globalCtx var c = globalCtx

View file

@ -47,6 +47,11 @@ template wrap1s_ospaths(op) {.dirty.} =
setResult(a, op(getString(a, 0))) setResult(a, op(getString(a, 0)))
ospathsop op ospathsop op
template wrap2s_ospaths(op) {.dirty.} =
proc `op Wrapper`(a: VmArgs) {.nimcall.} =
setResult(a, op(getString(a, 0), getString(a, 1)))
ospathsop op
template wrap1s_system(op) {.dirty.} = template wrap1s_system(op) {.dirty.} =
proc `op Wrapper`(a: VmArgs) {.nimcall.} = proc `op Wrapper`(a: VmArgs) {.nimcall.} =
setResult(a, op(getString(a, 0))) setResult(a, op(getString(a, 0)))
@ -96,7 +101,7 @@ proc registerAdditionalOps*(c: PCtx) =
wrap1f_math(ceil) wrap1f_math(ceil)
wrap2f_math(fmod) wrap2f_math(fmod)
wrap1s_ospaths(getEnv) wrap2s_ospaths(getEnv)
wrap1s_ospaths(existsEnv) wrap1s_ospaths(existsEnv)
wrap1s_os(dirExists) wrap1s_os(dirExists)
wrap1s_os(fileExists) wrap1s_os(fileExists)

View file

@ -55,7 +55,7 @@ type
wFloatchecks, wNanChecks, wInfChecks, wFloatchecks, wNanChecks, wInfChecks,
wAssertions, wPatterns, wWarnings, wAssertions, wPatterns, wWarnings,
wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects, wTags, wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects, wTags,
wDeadCodeElim, wSafecode, wNoForward, wReorder, wNoRewrite, wDeadCodeElim, wSafecode, wPackage, wNoForward, wReorder, wNoRewrite,
wPragma, wPragma,
wCompileTime, wNoInit, wCompileTime, wNoInit,
wPassc, wPassl, wBorrow, wDiscardable, wPassc, wPassl, wBorrow, wDiscardable,
@ -66,7 +66,7 @@ type
wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit, wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit,
wAsmNoStackFrame, wAsmNoStackFrame,
wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked, wGuard, wLocks, wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked, wGuard, wLocks,
wPartial, wExplain, wPartial, wExplain, wLiftLocals,
wAuto, wBool, wCatch, wChar, wClass, wAuto, wBool, wCatch, wChar, wClass,
wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast, wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast,
@ -143,7 +143,7 @@ const
"assertions", "patterns", "warnings", "hints", "assertions", "patterns", "warnings", "hints",
"optimization", "raises", "writes", "reads", "size", "effects", "tags", "optimization", "raises", "writes", "reads", "size", "effects", "tags",
"deadcodeelim", "safecode", "noforward", "reorder", "norewrite", "deadcodeelim", "safecode", "package", "noforward", "reorder", "norewrite",
"pragma", "pragma",
"compiletime", "noinit", "compiletime", "noinit",
"passc", "passl", "borrow", "discardable", "fieldchecks", "passc", "passl", "borrow", "discardable", "fieldchecks",
@ -152,7 +152,7 @@ const
"computedgoto", "injectstmt", "experimental", "computedgoto", "injectstmt", "experimental",
"write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit", "write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit",
"asmnostackframe", "implicitstatic", "global", "codegendecl", "unchecked", "asmnostackframe", "implicitstatic", "global", "codegendecl", "unchecked",
"guard", "locks", "partial", "explain", "guard", "locks", "partial", "explain", "liftlocals",
"auto", "bool", "catch", "char", "class", "auto", "bool", "catch", "char", "class",
"const_cast", "default", "delete", "double", "const_cast", "default", "delete", "double",

View file

@ -202,6 +202,11 @@ vcc.cpp.linkerexe = "vccexe.exe"
# set the options for specific platforms: # set the options for specific platforms:
vcc.options.always = "/nologo" vcc.options.always = "/nologo"
@if release:
# no debug symbols in release builds
@else:
vcc.options.always %= "${vcc.options.always} /Z7" # Get VCC to output full debug symbols in the obj file
@end
vcc.cpp.options.always %= "${vcc.options.always} /EHsc" vcc.cpp.options.always %= "${vcc.options.always} /EHsc"
vcc.options.linker = "/nologo /DEBUG /Zi /F33554432" # set the stack size to 32 MiB vcc.options.linker = "/nologo /DEBUG /Zi /F33554432" # set the stack size to 32 MiB
vcc.cpp.options.linker %= "${vcc.options.linker}" vcc.cpp.options.linker %= "${vcc.options.linker}"
@ -222,8 +227,8 @@ vcc.options.linker %= "--platform:arm ${vcc.options.linker}"
vcc.cpp.options.linker %= "--platform:arm ${vcc.cpp.options.linker}" vcc.cpp.options.linker %= "--platform:arm ${vcc.cpp.options.linker}"
@end @end
vcc.options.debug = "/Zi /FS /Od" vcc.options.debug = "/Od"
vcc.cpp.options.debug = "/Zi /FS /Od" vcc.cpp.options.debug = "/Od"
vcc.options.speed = "/O2" vcc.options.speed = "/O2"
vcc.cpp.options.speed = "/O2" vcc.cpp.options.speed = "/O2"
vcc.options.size = "/O1" vcc.options.size = "/O1"

View file

@ -20,10 +20,12 @@ to compile to C++, Objective-C or JavaScript. This document tries to
concentrate in a single place all the backend and interfacing options. concentrate in a single place all the backend and interfacing options.
The Nim compiler supports mainly two backend families: the C, C++ and The Nim compiler supports mainly two backend families: the C, C++ and
Objective-C targets and the JavaScript target. `The C like targets`_ creates Objective-C targets and the JavaScript target. `The C like targets
source files which can be compiled into a library or a final executable. `The <#backends-the-c-like-targets>`_ creates source files which can be compiled
JavaScript target`_ can generate a ``.js`` file which you reference from an into a library or a final executable. `The JavaScript target
HTML file or create a `standalone nodejs program <http://nodejs.org>`_. <#backends-the-javascript-target>`_ can generate a ``.js`` file which you
reference from an HTML file or create a `standalone nodejs program
<http://nodejs.org>`_.
On top of generating libraries or standalone applications, Nim offers On top of generating libraries or standalone applications, Nim offers
bidirectional interfacing with the backend targets through generic and bidirectional interfacing with the backend targets through generic and

View file

@ -29,6 +29,7 @@ Options:
--floatChecks:on|off turn all floating point (NaN/Inf) checks on|off --floatChecks:on|off turn all floating point (NaN/Inf) checks on|off
--nanChecks:on|off turn NaN checks on|off --nanChecks:on|off turn NaN checks on|off
--infChecks:on|off turn Inf checks on|off --infChecks:on|off turn Inf checks on|off
--nilChecks:on|off turn nil checks on|off
--deadCodeElim:on|off whole program dead code elimination on|off --deadCodeElim:on|off whole program dead code elimination on|off
--opt:none|speed|size optimize not at all or for speed|size --opt:none|speed|size optimize not at all or for speed|size
Note: use -d:release for a release build! Note: use -d:release for a release build!

View file

@ -206,7 +206,7 @@ strings, because they are precompiled.
**Note**: Passing variables to the ``dynlib`` pragma will fail at runtime **Note**: Passing variables to the ``dynlib`` pragma will fail at runtime
because of order of initialization problems. because of order of initialization problems.
**Note**: A ``dynlib`` import can be overriden with **Note**: A ``dynlib`` import can be overridden with
the ``--dynlibOverride:name`` command line option. The Compiler User Guide the ``--dynlibOverride:name`` command line option. The Compiler User Guide
contains further information. contains further information.

View file

@ -2,7 +2,7 @@ Guards and locks
================ ================
Apart from ``spawn`` and ``parallel`` Nim also provides all the common low level Apart from ``spawn`` and ``parallel`` Nim also provides all the common low level
concurrency mechanisms like locks, atomic intristics or condition variables. concurrency mechanisms like locks, atomic intrinsics or condition variables.
Nim significantly improves on the safety of these features via additional Nim significantly improves on the safety of these features via additional
pragmas: pragmas:
@ -74,7 +74,7 @@ model low level lockfree mechanisms:
The ``locks`` pragma takes a list of lock expressions ``locks: [a, b, ...]`` The ``locks`` pragma takes a list of lock expressions ``locks: [a, b, ...]``
in order to support *multi lock* statements. Why these are essential is in order to support *multi lock* statements. Why these are essential is
explained in the `lock levels`_ section. explained in the `lock levels <#guards-and-locks-lock-levels>`_ section.
Protecting general locations Protecting general locations

View file

@ -105,7 +105,7 @@ From import statement
~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~
After the ``from`` statement a module name follows followed by After the ``from`` statement a module name follows followed by
an ``import`` to list the symbols one likes to use without explict an ``import`` to list the symbols one likes to use without explicit
full qualification: full qualification:
.. code-block:: nim .. code-block:: nim
@ -123,7 +123,7 @@ in ``module``.
Export statement Export statement
~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
An ``export`` statement can be used for symbol fowarding so that client An ``export`` statement can be used for symbol forwarding so that client
modules don't need to import a module's dependencies: modules don't need to import a module's dependencies:
.. code-block:: nim .. code-block:: nim

View file

@ -70,7 +70,7 @@ compileTime pragma
The ``compileTime`` pragma is used to mark a proc or variable to be used at The ``compileTime`` pragma is used to mark a proc or variable to be used at
compile time only. No code will be generated for it. Compile time procs are compile time only. No code will be generated for it. Compile time procs are
useful as helpers for macros. Since version 0.12.0 of the language, a proc useful as helpers for macros. Since version 0.12.0 of the language, a proc
that uses ``system.NimNode`` within its parameter types is implictly declared that uses ``system.NimNode`` within its parameter types is implicitly declared
``compileTime``: ``compileTime``:
.. code-block:: nim .. code-block:: nim
@ -733,7 +733,8 @@ about the ``importcpp`` pragma pattern language. It is not necessary
to know all the details described here. to know all the details described here.
Similar to the `importc pragma for C <manual.html#importc-pragma>`_, the Similar to the `importc pragma for C
<#foreign-function-interface-importc-pragma>`_, the
``importcpp`` pragma can be used to import `C++`:idx: methods or C++ symbols ``importcpp`` pragma can be used to import `C++`:idx: methods or C++ symbols
in general. The generated code then uses the C++ method calling in general. The generated code then uses the C++ method calling
syntax: ``obj->method(arg)``. In combination with the ``header`` and ``emit`` syntax: ``obj->method(arg)``. In combination with the ``header`` and ``emit``
@ -955,10 +956,11 @@ Produces:
ImportObjC pragma ImportObjC pragma
----------------- -----------------
Similar to the `importc pragma for C <manual.html#importc-pragma>`_, the Similar to the `importc pragma for C
``importobjc`` pragma can be used to import `Objective C`:idx: methods. The <#foreign-function-interface-importc-pragma>`_, the ``importobjc`` pragma can
generated code then uses the Objective C method calling syntax: ``[obj method be used to import `Objective C`:idx: methods. The generated code then uses the
param1: arg]``. In addition with the ``header`` and ``emit`` pragmas this Objective C method calling syntax: ``[obj method param1: arg]``.
In addition with the ``header`` and ``emit`` pragmas this
allows *sloppy* interfacing with libraries written in Objective C: allows *sloppy* interfacing with libraries written in Objective C:
.. code-block:: Nim .. code-block:: Nim

View file

@ -142,10 +142,11 @@ The method call syntax conflicts with explicit generic instantiations:
parsed as ``(x.p)[T]``. parsed as ``(x.p)[T]``.
**Future directions**: ``p[.T.]`` might be introduced as an alternative syntax **Future directions**: ``p[.T.]`` might be introduced as an alternative syntax
to pass explict types to a generic and then ``x.p[.T.]`` can be parsed as to pass explicit types to a generic and then ``x.p[.T.]`` can be parsed as
``x.(p[.T.])``. ``x.(p[.T.])``.
See also: `Limitations of the method call syntax`_. See also: `Limitations of the method call syntax
<#templates-limitations-of-the-method-call-syntax>`_.
Properties Properties
@ -178,7 +179,7 @@ different; for this a special setter syntax is needed:
Command invocation syntax Command invocation syntax
------------------------- -------------------------
Routines can be invoked without the ``()`` if the call is syntatically Routines can be invoked without the ``()`` if the call is syntactically
a statement. This command invocation syntax also works for a statement. This command invocation syntax also works for
expressions, but then only a single argument may follow. This restriction expressions, but then only a single argument may follow. This restriction
means ``echo f 1, f 2`` is parsed as ``echo(f(1), f(2))`` and not as means ``echo f 1, f 2`` is parsed as ``echo(f(1), f(2))`` and not as

View file

@ -199,7 +199,7 @@ The rules for compile-time computability are:
(possibly empty) list of compile-time computable arguments. (possibly empty) list of compile-time computable arguments.
Constants cannot be of type ``ptr``, ``ref``, ``var`` or ``object``, nor can Constants cannot be of type ``ptr``, ``ref`` or ``var``, nor can
they contain such a type. they contain such a type.

View file

@ -293,7 +293,7 @@ Limitations of the method call syntax
The expression ``x`` in ``x.f`` needs to be semantically checked (that means The expression ``x`` in ``x.f`` needs to be semantically checked (that means
symbol lookup and type checking) before it can be decided that it needs to be symbol lookup and type checking) before it can be decided that it needs to be
rewritten to ``f(x)``. Therefore the dot syntax has some limiations when it rewritten to ``f(x)``. Therefore the dot syntax has some limitations when it
is used to invoke templates/macros: is used to invoke templates/macros:
.. code-block:: nim .. code-block:: nim

View file

@ -5,7 +5,7 @@ To enable thread support the ``--threads:on`` command line switch needs to
be used. The ``system`` module then contains several threading primitives. be used. The ``system`` module then contains several threading primitives.
See the `threads <threads.html>`_ and `channels <channels.html>`_ modules See the `threads <threads.html>`_ and `channels <channels.html>`_ modules
for the low level thread API. There are also high level parallelism constructs for the low level thread API. There are also high level parallelism constructs
available. See `spawn`_ for further details. available. See `spawn <#parallel-spawn>`_ for further details.
Nim's memory model for threads is quite different than that of other common Nim's memory model for threads is quite different than that of other common
programming languages (C, Pascal, Java): Each thread has its own (garbage programming languages (C, Pascal, Java): Each thread has its own (garbage

View file

@ -7,12 +7,12 @@ There are 3 operations that are bound to a type:
2. Destruction 2. Destruction
3. Deep copying for communication between threads 3. Deep copying for communication between threads
These operations can be *overriden* instead of *overloaded*. This means the These operations can be *overridden* instead of *overloaded*. This means the
implementation is automatically lifted to structured types. For instance if type implementation is automatically lifted to structured types. For instance if type
``T`` has an overriden assignment operator ``=`` this operator is also used ``T`` has an overridden assignment operator ``=`` this operator is also used
for assignments of the type ``seq[T]``. Since these operations are bound to a for assignments of the type ``seq[T]``. Since these operations are bound to a
type they have to be bound to a nominal type for reasons of simplicity of type they have to be bound to a nominal type for reasons of simplicity of
implementation: This means an overriden ``deepCopy`` for ``ref T`` is really implementation: This means an overridden ``deepCopy`` for ``ref T`` is really
bound to ``T`` and not to ``ref T``. This also means that one cannot override bound to ``T`` and not to ``ref T``. This also means that one cannot override
``deepCopy`` for both ``ptr T`` and ``ref T`` at the same time; instead a ``deepCopy`` for both ``ptr T`` and ``ref T`` at the same time; instead a
helper distinct or object type has to be used for one pointer type. helper distinct or object type has to be used for one pointer type.

View file

@ -491,4 +491,4 @@ metatypes ``typed`` and ``typedesc`` are not lazy.
Varargs matching Varargs matching
---------------- ----------------
See `Varargs`_. See `Varargs <#types-varargs>`_.

View file

@ -114,7 +114,8 @@ if the literal's value fits this smaller type and such a conversion is less
expensive than other implicit conversions, so ``myInt16 + 34`` produces expensive than other implicit conversions, so ``myInt16 + 34`` produces
an ``int16`` result. an ``int16`` result.
For further details, see `Convertible relation`_. For further details, see `Convertible relation
<#type-relations-convertible-relation>`_.
Subrange types Subrange types
@ -677,6 +678,44 @@ branch switch ``system.reset`` has to be used. Also, when the fields of a
particular branch are specified during object construction, the correct value particular branch are specified during object construction, the correct value
for the discriminator must be supplied at compile-time. for the discriminator must be supplied at compile-time.
Package level objects
---------------------
Every Nim module resides in a (nimble) package. An object type can be attached
to the package it resides in. If that is done, the type can be referenced from
other modules as an `incomplete`:idx: object type. This features allows to
break up recursive type dependencies accross module boundaries. Incomplete
object types are always passed ``byref`` and can only be used in pointer like
contexts (``var/ref/ptr IncompleteObject``) in general since the compiler does
not yet know the size of the object. To complete an incomplete object
the ``package`` pragma has to be used. ``package`` implies ``byref``.
As long as a type ``T`` is incomplete ``sizeof(T)`` or "runtime type
information" for ``T`` is not available.
Example:
.. code-block:: nim
# module A (in an arbitrary package)
type
Pack.SomeObject = object ## declare as incomplete object of package 'Pack'
Triple = object
a, b, c: ref SomeObject ## pointers to incomplete objects are allowed
## Incomplete objects can be used as parameters:
proc myproc(x: SomeObject) = discard
.. code-block:: nim
# module B (in package "Pack")
type
SomeObject* {.package.} = object ## Use 'package' to complete the object
s, t: string
x, y: int
Set type Set type
-------- --------
@ -885,8 +924,8 @@ not compatible to ``pointer`` to prevent the following from compiling:
Future directions: Future directions:
* Memory regions might become available for ``string`` and ``seq`` too. * Memory regions might become available for ``string`` and ``seq`` too.
* Builtin regions like ``private``, ``global`` and ``local`` will * Builtin regions like ``private``, ``global`` and ``local`` might be
prove very useful for the upcoming OpenCL target. useful for an OpenCL target.
* Builtin "regions" can model ``lent`` and ``unique`` pointers. * Builtin "regions" can model ``lent`` and ``unique`` pointers.
* An assignment operator can be attached to a region so that proper write * An assignment operator can be attached to a region so that proper write
barriers can be generated. This would imply that the GC can be implemented barriers can be generated. This would imply that the GC can be implemented

View file

@ -263,7 +263,7 @@ proc buildTools(latest: bool) =
nimexec "c -o:" & nimgrepExe & " tools/nimgrep.nim" nimexec "c -o:" & nimgrepExe & " tools/nimgrep.nim"
when defined(windows): buildVccTool() when defined(windows): buildVccTool()
nimexec "c -o:" & ("bin/nimresolve".exe) & " tools/nimresolve.nim" #nimexec "c -o:" & ("bin/nimresolve".exe) & " tools/nimresolve.nim"
buildNimble(latest) buildNimble(latest)
@ -472,7 +472,8 @@ proc temp(args: string) =
# 125 is the magic number to tell git bisect to skip the current # 125 is the magic number to tell git bisect to skip the current
# commit. # commit.
let (bootArgs, programArgs) = splitArgs(args) let (bootArgs, programArgs) = splitArgs(args)
exec("nim c " & bootArgs & " compiler" / "nim", 125) let nimexec = findNim()
exec(nimexec & " c " & bootArgs & " compiler" / "nim", 125)
copyExe(output, finalDest) copyExe(output, finalDest)
if programArgs.len > 0: exec(finalDest & " " & programArgs) if programArgs.len > 0: exec(finalDest & " " & programArgs)

View file

@ -129,13 +129,6 @@ const
nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand, nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
nnkCallStrLit} nnkCallStrLit}
proc `[]`*(n: NimNode, i: int): NimNode {.magic: "NChild", noSideEffect.}
## get `n`'s `i`'th child.
proc `[]=`*(n: NimNode, i: int, child: NimNode) {.magic: "NSetChild",
noSideEffect.}
## set `n`'s `i`'th child to `child`.
proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect.} proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect.}
## constructs an identifier from the string `s` ## constructs an identifier from the string `s`
@ -162,6 +155,20 @@ proc sameType*(a, b: NimNode): bool {.magic: "SameNodeType", noSideEffect.} =
proc len*(n: NimNode): int {.magic: "NLen", noSideEffect.} proc len*(n: NimNode): int {.magic: "NLen", noSideEffect.}
## returns the number of children of `n`. ## returns the number of children of `n`.
proc `[]`*(n: NimNode, i: int): NimNode {.magic: "NChild", noSideEffect.}
## get `n`'s `i`'th child.
proc `[]`*(n: NimNode, i: BackwardsIndex): NimNode = n[n.len - i.int]
## get `n`'s `i`'th child.
proc `[]=`*(n: NimNode, i: int, child: NimNode) {.magic: "NSetChild",
noSideEffect.}
## set `n`'s `i`'th child to `child`.
proc `[]=`*(n: NimNode, i: BackwardsIndex, child: NimNode) =
## set `n`'s `i`'th child to `child`.
n[n.len - i.int] = child
proc add*(father, child: NimNode): NimNode {.magic: "NAdd", discardable, proc add*(father, child: NimNode): NimNode {.magic: "NAdd", discardable,
noSideEffect, locks: 0.} noSideEffect, locks: 0.}
## Adds the `child` to the `father` node. Returns the ## Adds the `child` to the `father` node. Returns the
@ -839,7 +846,7 @@ proc newNilLit*(): NimNode {.compileTime.} =
## New nil literal shortcut ## New nil literal shortcut
result = newNimNode(nnkNilLit) result = newNimNode(nnkNilLit)
proc last*(node: NimNode): NimNode {.compileTime.} = node[<node.len] proc last*(node: NimNode): NimNode {.compileTime.} = node[node.len-1]
## Return the last item in nodes children. Same as `node[^1]` ## Return the last item in nodes children. Same as `node[^1]`
@ -986,6 +993,11 @@ iterator items*(n: NimNode): NimNode {.inline.} =
for i in 0 ..< n.len: for i in 0 ..< n.len:
yield n[i] yield n[i]
iterator pairs*(n: NimNode): (int, NimNode) {.inline.} =
## Iterates over the children of the NimNode ``n`` and its indices.
for i in 0 ..< n.len:
yield (i, n[i])
iterator children*(n: NimNode): NimNode {.inline.} = iterator children*(n: NimNode): NimNode {.inline.} =
## Iterates over the children of the NimNode ``n``. ## Iterates over the children of the NimNode ``n``.
for i in 0 ..< n.len: for i in 0 ..< n.len:

View file

@ -98,6 +98,9 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
var a = 0 var a = 0
if args.len > 0 and not string(formatstr).contains("?"): if args.len > 0 and not string(formatstr).contains("?"):
dbError("""parameter substitution expects "?" """) dbError("""parameter substitution expects "?" """)
if args.len == 0:
return string(formatstr)
else:
for c in items(string(formatstr)): for c in items(string(formatstr)):
if c == '?': if c == '?':
if args[a] == nil: if args[a] == nil:

View file

@ -155,7 +155,7 @@ type
## - ``"abc".match(re"(?<letter>\w)").captures["letter"] == "a"`` ## - ``"abc".match(re"(?<letter>\w)").captures["letter"] == "a"``
## - ``"abc".match(re"(\w)\w").captures[-1] == "ab"`` ## - ``"abc".match(re"(\w)\w").captures[-1] == "ab"``
## ##
## ``captureBounds[]: Option[Slice[int]]`` ## ``captureBounds[]: Option[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.
@ -167,7 +167,7 @@ type
## ``match: string`` ## ``match: string``
## the full text of the match. ## the full text of the match.
## ##
## ``matchBounds: Slice[int]`` ## ``matchBounds: HSlice[int, int]``
## the bounds of the match, as in ``captureBounds[]`` ## the bounds of the match, as in ``captureBounds[]``
## ##
## ``(captureBounds|captures).toTable`` ## ``(captureBounds|captures).toTable``
@ -182,7 +182,7 @@ type
## Not nil. ## Not nil.
str*: string ## The string that was matched against. str*: string ## The string that was matched against.
## Not nil. ## Not nil.
pcreMatchBounds: seq[Slice[cint]] ## First item is the bounds of the match pcreMatchBounds: seq[HSlice[cint, cint]] ## First item is the bounds of the match
## Other items are the captures ## Other items are the captures
## `a` is inclusive start, `b` is exclusive end ## `a` is inclusive start, `b` is exclusive end
@ -251,13 +251,13 @@ proc captureBounds*(pattern: RegexMatch): CaptureBounds = return CaptureBounds(p
proc captures*(pattern: RegexMatch): Captures = return Captures(pattern) proc captures*(pattern: RegexMatch): Captures = return Captures(pattern)
proc `[]`*(pattern: CaptureBounds, i: int): Option[Slice[int]] = proc `[]`*(pattern: CaptureBounds, i: int): Option[HSlice[int, int]] =
let pattern = RegexMatch(pattern) let pattern = RegexMatch(pattern)
if pattern.pcreMatchBounds[i + 1].a != -1: if pattern.pcreMatchBounds[i + 1].a != -1:
let bounds = pattern.pcreMatchBounds[i + 1] let bounds = pattern.pcreMatchBounds[i + 1]
return some(int(bounds.a) .. int(bounds.b-1)) return some(int(bounds.a) .. int(bounds.b-1))
else: else:
return none(Slice[int]) return none(HSlice[int, int])
proc `[]`*(pattern: Captures, i: int): string = proc `[]`*(pattern: Captures, i: int): string =
let pattern = RegexMatch(pattern) let pattern = RegexMatch(pattern)
@ -272,10 +272,10 @@ proc `[]`*(pattern: Captures, i: int): string =
proc match*(pattern: RegexMatch): string = proc match*(pattern: RegexMatch): string =
return pattern.captures[-1] return pattern.captures[-1]
proc matchBounds*(pattern: RegexMatch): Slice[int] = proc matchBounds*(pattern: RegexMatch): HSlice[int, int] =
return pattern.captureBounds[-1].get return pattern.captureBounds[-1].get
proc `[]`*(pattern: CaptureBounds, name: string): Option[Slice[int]] = proc `[]`*(pattern: CaptureBounds, name: string): Option[HSlice[int, int]] =
let pattern = RegexMatch(pattern) let pattern = RegexMatch(pattern)
return pattern.captureBounds[pattern.pattern.captureNameToId.fget(name)] return pattern.captureBounds[pattern.pattern.captureNameToId.fget(name)]
@ -295,9 +295,9 @@ proc toTable*(pattern: Captures, default: string = nil): Table[string, string] =
result = initTable[string, string]() result = initTable[string, string]()
toTableImpl(nextVal == nil) toTableImpl(nextVal == nil)
proc toTable*(pattern: CaptureBounds, default = none(Slice[int])): proc toTable*(pattern: CaptureBounds, default = none(HSlice[int, int])):
Table[string, Option[Slice[int]]] = Table[string, Option[HSlice[int, int]]] =
result = initTable[string, Option[Slice[int]]]() result = initTable[string, Option[HSlice[int, int]]]()
toTableImpl(nextVal.isNone) toTableImpl(nextVal.isNone)
template itemsImpl(cond: untyped) {.dirty.} = template itemsImpl(cond: untyped) {.dirty.} =
@ -309,13 +309,13 @@ template itemsImpl(cond: untyped) {.dirty.} =
yield nextYieldVal yield nextYieldVal
iterator items*(pattern: CaptureBounds, default = none(Slice[int])): Option[Slice[int]] = iterator items*(pattern: CaptureBounds, default = none(HSlice[int, int])): Option[HSlice[int, int]] =
itemsImpl(nextVal.isNone) itemsImpl(nextVal.isNone)
iterator items*(pattern: Captures, default: string = nil): string = iterator items*(pattern: Captures, default: string = nil): string =
itemsImpl(nextVal == nil) itemsImpl(nextVal == nil)
proc toSeq*(pattern: CaptureBounds, default = none(Slice[int])): seq[Option[Slice[int]]] = proc toSeq*(pattern: CaptureBounds, default = none(HSlice[int, int])): seq[Option[HSlice[int, int]]] =
accumulateResult(pattern.items(default)) accumulateResult(pattern.items(default))
proc toSeq*(pattern: Captures, default: string = nil): seq[string] = proc toSeq*(pattern: Captures, default: string = nil): seq[string] =
@ -396,8 +396,6 @@ proc extractOptions(pattern: string): tuple[pattern: string, flags: int, study:
# }}} # }}}
type UncheckedArray {.unchecked.}[T] = array[0 .. 0, T]
proc destroyRegex(pattern: Regex) = proc destroyRegex(pattern: Regex) =
pcre.free_substring(cast[cstring](pattern.pcreObj)) pcre.free_substring(cast[cstring](pattern.pcreObj))
pattern.pcreObj = nil pattern.pcreObj = nil
@ -464,7 +462,7 @@ proc matchImpl(str: string, pattern: Regex, start, endpos: int, flags: int): Opt
# 1x capture count as slack space for PCRE # 1x capture count as slack space for PCRE
let vecsize = (pattern.captureCount() + 1) * 3 let vecsize = (pattern.captureCount() + 1) * 3
# div 2 because each element is 2 cints long # div 2 because each element is 2 cints long
myResult.pcreMatchBounds = newSeq[Slice[cint]](ceil(vecsize / 2).int) myResult.pcreMatchBounds = newSeq[HSlice[cint, cint]](ceil(vecsize / 2).int)
myResult.pcreMatchBounds.setLen(vecsize div 3) myResult.pcreMatchBounds.setLen(vecsize div 3)
let strlen = if endpos == int.high: str.len else: endpos+1 let strlen = if endpos == int.high: str.len else: endpos+1

View file

@ -13,10 +13,6 @@
## We had to de-deprecate this module since too much code relies on it ## We had to de-deprecate this module since too much code relies on it
## and many people prefer its API over ``nre``'s. ## and many people prefer its API over ``nre``'s.
## ##
## **Note:** The 're' proc defaults to the **extended regular expression
## syntax** which lets you use whitespace freely to make your regexes readable.
## However, this means matching whitespace requires ``\s`` or something similar.
##
## This module is implemented by providing a wrapper around the ## This module is implemented by providing a wrapper around the
## `PRCE (Perl-Compatible Regular Expressions) <http://www.pcre.org>`_ ## `PRCE (Perl-Compatible Regular Expressions) <http://www.pcre.org>`_
## C library. This means that your application will depend on the PRCE ## C library. This means that your application will depend on the PRCE
@ -78,7 +74,7 @@ proc finalizeRegEx(x: Regex) =
if not isNil(x.e): if not isNil(x.e):
pcre.free_substring(cast[cstring](x.e)) pcre.free_substring(cast[cstring](x.e))
proc re*(s: string, flags = {reExtended, reStudy}): Regex = proc re*(s: string, flags = {reStudy}): Regex =
## Constructor of regular expressions. ## Constructor of regular expressions.
## ##
## Note that Nim's ## Note that Nim's
@ -96,6 +92,13 @@ proc re*(s: string, flags = {reExtended, reStudy}): Regex =
result.e = pcre.study(result.h, options, addr msg) result.e = pcre.study(result.h, options, addr msg)
if not isNil(msg): raiseInvalidRegex($msg) if not isNil(msg): raiseInvalidRegex($msg)
proc rex*(s: string, flags = {reStudy, reExtended}): Regex =
## Constructor for extended regular expressions.
##
## The extended means that comments starting with `#` and
## whitespace are ignored.
result = re(s, flags)
proc bufSubstr(b: cstring, sPos, ePos: int): string {.inline.} = proc bufSubstr(b: cstring, sPos, ePos: int): string {.inline.} =
## Return a Nim string built from a slice of a cstring buffer. ## Return a Nim string built from a slice of a cstring buffer.
## Don't assume cstring is '\0' terminated ## Don't assume cstring is '\0' terminated

View file

@ -63,16 +63,16 @@ proc recvLine*(sock: SecureSocket, line: var TaintedString): bool =
setLen(line.string, 0) setLen(line.string, 0)
while true: while true:
var c: array[0..0, char] var c: array[0..0, char]
var n = BIO_read(sock.bio, c, c.len.cint) var n = BIO_read(sock.bio, addr c, c.len.cint)
if n <= 0: return false if n <= 0: return false
if c[0] == '\r': if c[0] == '\r':
n = BIO_read(sock.bio, c, c.len.cint) n = BIO_read(sock.bio, addr c, c.len.cint)
if n > 0 and c[0] == '\L': if n > 0 and c[0] == '\L':
return true return true
elif n <= 0: elif n <= 0:
return false return false
elif c[0] == '\L': return true elif c[0] == '\L': return true
add(line.string, c) add(line.string, c[0])
proc send*(sock: SecureSocket, data: string) = proc send*(sock: SecureSocket, data: string) =

View file

@ -396,15 +396,13 @@ typedef struct TStringDesc* string;
#define GenericSeqSize sizeof(TGenericSeq) #define GenericSeqSize sizeof(TGenericSeq)
#define paramCount() cmdCount #define paramCount() cmdCount
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__i386__) // NAN definition copied from math.h included in the Windows SDK version 10.0.14393.0
#ifndef NAN #ifndef NAN
static unsigned long nimNaN[2]={0xffffffff, 0x7fffffff}; #ifndef _HUGE_ENUF
# define NAN (*(double*) nimNaN) #define _HUGE_ENUF 1e+300 // _HUGE_ENUF*_HUGE_ENUF must overflow
#endif #endif
#endif #define NAN_INFINITY ((float)(_HUGE_ENUF * _HUGE_ENUF))
#define NAN ((float)(NAN_INFINITY * 0.0F))
#ifndef NAN
# define NAN (0.0 / 0.0)
#endif #endif
#ifndef INF #ifndef INF

View file

@ -26,3 +26,5 @@ const
proc clone*(fn: pointer; child_stack: pointer; flags: cint; proc clone*(fn: pointer; child_stack: pointer; flags: cint;
arg: pointer; ptid: ptr Pid; tls: pointer; arg: pointer; ptid: ptr Pid; tls: pointer;
ctid: ptr Pid): cint {.importc, header: "<sched.h>".} ctid: ptr Pid): cint {.importc, header: "<sched.h>".}
proc pipe2*(a: array[0..1, cint], flags: cint): cint {.importc, header: "<unistd.h>".}

View file

@ -146,7 +146,7 @@ type
Mode* {.importc: "mode_t", header: "<sys/types.h>".} = cint Mode* {.importc: "mode_t", header: "<sys/types.h>".} = cint
Nlink* {.importc: "nlink_t", header: "<sys/types.h>".} = int Nlink* {.importc: "nlink_t", header: "<sys/types.h>".} = int
Off* {.importc: "off_t", header: "<sys/types.h>".} = int64 Off* {.importc: "off_t", header: "<sys/types.h>".} = int64
Pid* {.importc: "pid_t", header: "<sys/types.h>".} = int Pid* {.importc: "pid_t", header: "<sys/types.h>".} = int32
Pthread_attr* {.importc: "pthread_attr_t", header: "<sys/types.h>".} = int Pthread_attr* {.importc: "pthread_attr_t", header: "<sys/types.h>".} = int
Pthread_barrier* {.importc: "pthread_barrier_t", Pthread_barrier* {.importc: "pthread_barrier_t",
header: "<sys/types.h>".} = int header: "<sys/types.h>".} = int

View file

@ -371,3 +371,126 @@ when isMainModule:
for i in 0 .. high(arr1): for i in 0 .. high(arr1):
assert arr1.reversed(0, i) == arr1.reversed()[high(arr1) - i .. high(arr1)] assert arr1.reversed(0, i) == arr1.reversed()[high(arr1) - i .. high(arr1)]
assert arr1.reversed(i, high(arr1)) == arr1.reversed()[0 .. high(arr1) - i] assert arr1.reversed(i, high(arr1)) == arr1.reversed()[0 .. high(arr1) - i]
proc rotateInternal[T](arg: var openarray[T]; first, middle, last: int): int =
## A port of std::rotate from c++. Ported from `this reference <http://www.cplusplus.com/reference/algorithm/rotate/>`_.
result = first + last - middle
if first == middle or middle == last:
return
assert first < middle
assert middle < last
# m prefix for mutable
var
mFirst = first
mMiddle = middle
next = middle
swap(arg[mFirst], arg[next])
mFirst += 1
next += 1
if mFirst == mMiddle:
mMiddle = next
while next != last:
swap(arg[mFirst], arg[next])
mFirst += 1
next += 1
if mFirst == mMiddle:
mMiddle = next
next = mMiddle
while next != last:
swap(arg[mFirst], arg[next])
mFirst += 1
next += 1
if mFirst == mMiddle:
mMiddle = next
elif next == last:
next = mMiddle
proc rotatedInternal[T](arg: openarray[T]; first, middle, last: int): seq[T] =
result = newSeq[T](arg.len)
for i in 0 ..< first:
result[i] = arg[i]
let N = last - middle
let M = middle - first
for i in 0 ..< N:
result[first+i] = arg[middle+i]
for i in 0 ..< M:
result[first+N+i] = arg[first+i]
for i in last ..< arg.len:
result[i] = arg[i]
proc rotateLeft*[T](arg: var openarray[T]; slice: HSlice[int, int]; dist: int): int =
## Performs a left rotation on a range of elements. If you want to rotate right, use a negative ``dist``.
## Specifically, ``rotateLeft`` rotates the elements at ``slice`` by ``dist`` positions.
## The element at index ``slice.a + dist`` will be at index ``slice.a``.
## The element at index ``slice.b`` will be at ``slice.a + dist -1``.
## The element at index ``slice.a`` will be at ``slice.b + 1 - dist``.
## The element at index ``slice.a + dist - 1`` will be at ``slice.b``.
#
## Elements outsize of ``slice`` will be left unchanged.
## The time complexity is linear to ``slice.b - slice.a + 1``.
##
## ``slice``
## the indices of the element range that should be rotated.
##
## ``dist``
## the distance in amount of elements that the data should be rotated. Can be negative, can be any number.
##
## .. code-block:: nim
## var list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
## list.rotateLeft(1 .. 8, 3)
## doAssert list == [0, 4, 5, 6, 7, 8, 1, 2, 3, 9, 10]
let sliceLen = slice.b + 1 - slice.a
let distLeft = ((dist mod sliceLen) + sliceLen) mod sliceLen
arg.rotateInternal(slice.a, slice.a+distLeft, slice.b + 1)
proc rotateLeft*[T](arg: var openarray[T]; dist: int): int =
## default arguments for slice, so that this procedure operates on the entire
## ``arg``, and not just on a part of it.
let arglen = arg.len
let distLeft = ((dist mod arglen) + arglen) mod arglen
arg.rotateInternal(0, distLeft, arglen)
proc rotatedLeft*[T](arg: openarray[T]; slice: HSlice[int, int], dist: int): seq[T] =
## same as ``rotateLeft``, just with the difference that it does
## not modify the argument. It creates a new ``seq`` instead
let sliceLen = slice.b + 1 - slice.a
let distLeft = ((dist mod sliceLen) + sliceLen) mod sliceLen
arg.rotatedInternal(slice.a, slice.a+distLeft, slice.b+1)
proc rotatedLeft*[T](arg: openarray[T]; dist: int): seq[T] =
## same as ``rotateLeft``, just with the difference that it does
## not modify the argument. It creates a new ``seq`` instead
let arglen = arg.len
let distLeft = ((dist mod arglen) + arglen) mod arglen
arg.rotatedInternal(0, distLeft, arg.len)
when isMainModule:
var list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let list2 = list.rotatedLeft(1 ..< 9, 3)
let expected = [0, 4, 5, 6, 7, 8, 1, 2, 3, 9, 10]
doAssert list.rotateLeft(1 ..< 9, 3) == 6
doAssert list == expected
doAssert list2 == @expected
var s0,s1,s2,s3,s4,s5 = "xxxabcdefgxxx"
doAssert s0.rotateLeft(3 ..< 10, 3) == 7
doAssert s0 == "xxxdefgabcxxx"
doAssert s1.rotateLeft(3 ..< 10, 2) == 8
doAssert s1 == "xxxcdefgabxxx"
doAssert s2.rotateLeft(3 ..< 10, 4) == 6
doAssert s2 == "xxxefgabcdxxx"
doAssert s3.rotateLeft(3 ..< 10, -3) == 6
doAssert s3 == "xxxefgabcdxxx"
doAssert s4.rotateLeft(3 ..< 10, -10) == 6
doAssert s4 == "xxxefgabcdxxx"
doAssert s5.rotateLeft(3 ..< 10, 11) == 6
doAssert s5 == "xxxefgabcdxxx"

View file

@ -207,9 +207,9 @@ when isMainModule:
assert($deq == "[4, 56, 6, 789]") assert($deq == "[4, 56, 6, 789]")
assert deq[0] == deq.peekFirst and deq.peekFirst == 4 assert deq[0] == deq.peekFirst and deq.peekFirst == 4
assert deq[^1] == deq.peekLast and deq.peekLast == 789 #assert deq[^1] == deq.peekLast and deq.peekLast == 789
deq[0] = 42 deq[0] = 42
deq[^1] = 7 deq[deq.len - 1] = 7
assert 6 in deq and 789 notin deq assert 6 in deq and 789 notin deq
assert deq.find(6) >= 0 assert deq.find(6) >= 0

View file

@ -37,6 +37,14 @@ type
DoublyLinkedRing*[T] = object ## a doubly linked ring DoublyLinkedRing*[T] = object ## a doubly linked ring
head*: DoublyLinkedNode[T] head*: DoublyLinkedNode[T]
SomeLinkedList*[T] = SinglyLinkedList[T] | DoublyLinkedList[T]
SomeLinkedRing*[T] = SinglyLinkedRing[T] | DoublyLinkedRing[T]
SomeLinkedCollection*[T] = SomeLinkedList[T] | SomeLinkedRing[T]
SomeLinkedNode*[T] = SinglyLinkedNode[T] | DoublyLinkedNode[T]
{.deprecated: [TDoublyLinkedNode: DoublyLinkedNodeObj, {.deprecated: [TDoublyLinkedNode: DoublyLinkedNodeObj,
PDoublyLinkedNode: DoublyLinkedNode, PDoublyLinkedNode: DoublyLinkedNode,
TSinglyLinkedNode: SinglyLinkedNodeObj, TSinglyLinkedNode: SinglyLinkedNodeObj,
@ -86,14 +94,34 @@ template itemsRingImpl() {.dirty.} =
it = it.next it = it.next
if it == L.head: break if it == L.head: break
template nodesListImpl() {.dirty.} = iterator items*[T](L: SomeLinkedList[T]): T =
## yields every value of `L`.
itemsListImpl()
iterator items*[T](L: SomeLinkedRing[T]): T =
## yields every value of `L`.
itemsRingImpl()
iterator mitems*[T](L: var SomeLinkedList[T]): var T =
## yields every value of `L` so that you can modify it.
itemsListImpl()
iterator mitems*[T](L: var SomeLinkedRing[T]): var T =
## yields every value of `L` so that you can modify it.
itemsRingImpl()
iterator nodes*[T](L: SomeLinkedList[T]): SomeLinkedNode[T] =
## iterates over every node of `x`. Removing the current node from the
## list during traversal is supported.
var it = L.head var it = L.head
while it != nil: while it != nil:
var nxt = it.next var nxt = it.next
yield it yield it
it = nxt it = nxt
template nodesRingImpl() {.dirty.} = iterator nodes*[T](L: SomeLinkedRing[T]): SomeLinkedNode[T] =
## iterates over every node of `x`. Removing the current node from the
## list during traversal is supported.
var it = L.head var it = L.head
if it != nil: if it != nil:
while true: while true:
@ -102,121 +130,21 @@ template nodesRingImpl() {.dirty.} =
it = nxt it = nxt
if it == L.head: break if it == L.head: break
template findImpl() {.dirty.} = proc `$`*[T](L: SomeLinkedCollection[T]): string =
for x in nodes(L): ## turns a list into its string representation.
if x.value == value: return x
iterator items*[T](L: DoublyLinkedList[T]): T =
## yields every value of `L`.
itemsListImpl()
iterator items*[T](L: SinglyLinkedList[T]): T =
## yields every value of `L`.
itemsListImpl()
iterator items*[T](L: SinglyLinkedRing[T]): T =
## yields every value of `L`.
itemsRingImpl()
iterator items*[T](L: DoublyLinkedRing[T]): T =
## yields every value of `L`.
itemsRingImpl()
iterator mitems*[T](L: var DoublyLinkedList[T]): var T =
## yields every value of `L` so that you can modify it.
itemsListImpl()
iterator mitems*[T](L: var SinglyLinkedList[T]): var T =
## yields every value of `L` so that you can modify it.
itemsListImpl()
iterator mitems*[T](L: var SinglyLinkedRing[T]): var T =
## yields every value of `L` so that you can modify it.
itemsRingImpl()
iterator mitems*[T](L: var DoublyLinkedRing[T]): var T =
## yields every value of `L` so that you can modify it.
itemsRingImpl()
iterator nodes*[T](L: SinglyLinkedList[T]): SinglyLinkedNode[T] =
## iterates over every node of `x`. Removing the current node from the
## list during traversal is supported.
nodesListImpl()
iterator nodes*[T](L: DoublyLinkedList[T]): DoublyLinkedNode[T] =
## iterates over every node of `x`. Removing the current node from the
## list during traversal is supported.
nodesListImpl()
iterator nodes*[T](L: SinglyLinkedRing[T]): SinglyLinkedNode[T] =
## iterates over every node of `x`. Removing the current node from the
## list during traversal is supported.
nodesRingImpl()
iterator nodes*[T](L: DoublyLinkedRing[T]): DoublyLinkedNode[T] =
## iterates over every node of `x`. Removing the current node from the
## list during traversal is supported.
nodesRingImpl()
template dollarImpl() {.dirty.} =
result = "[" result = "["
for x in nodes(L): for x in nodes(L):
if result.len > 1: result.add(", ") if result.len > 1: result.add(", ")
result.add($x.value) result.add($x.value)
result.add("]") result.add("]")
proc `$`*[T](L: SinglyLinkedList[T]): string = proc find*[T](L: SomeLinkedCollection[T], value: T): SomeLinkedNode[T] =
## turns a list into its string representation.
dollarImpl()
proc `$`*[T](L: DoublyLinkedList[T]): string =
## turns a list into its string representation.
dollarImpl()
proc `$`*[T](L: SinglyLinkedRing[T]): string =
## turns a list into its string representation.
dollarImpl()
proc `$`*[T](L: DoublyLinkedRing[T]): string =
## turns a list into its string representation.
dollarImpl()
proc find*[T](L: SinglyLinkedList[T], value: T): SinglyLinkedNode[T] =
## searches in the list for a value. Returns nil if the value does not ## searches in the list for a value. Returns nil if the value does not
## exist. ## exist.
findImpl() for x in nodes(L):
if x.value == value: return x
proc find*[T](L: DoublyLinkedList[T], value: T): DoublyLinkedNode[T] = proc contains*[T](L: SomeLinkedCollection[T], value: T): bool {.inline.} =
## searches in the list for a value. Returns nil if the value does not
## exist.
findImpl()
proc find*[T](L: SinglyLinkedRing[T], value: T): SinglyLinkedNode[T] =
## searches in the list for a value. Returns nil if the value does not
## exist.
findImpl()
proc find*[T](L: DoublyLinkedRing[T], value: T): DoublyLinkedNode[T] =
## searches in the list for a value. Returns nil if the value does not
## exist.
findImpl()
proc contains*[T](L: SinglyLinkedList[T], value: T): bool {.inline.} =
## searches in the list for a value. Returns false if the value does not
## exist, true otherwise.
result = find(L, value) != nil
proc contains*[T](L: DoublyLinkedList[T], value: T): bool {.inline.} =
## searches in the list for a value. Returns false if the value does not
## exist, true otherwise.
result = find(L, value) != nil
proc contains*[T](L: SinglyLinkedRing[T], value: T): bool {.inline.} =
## searches in the list for a value. Returns false if the value does not
## exist, true otherwise.
result = find(L, value) != nil
proc contains*[T](L: DoublyLinkedRing[T], value: T): bool {.inline.} =
## searches in the list for a value. Returns false if the value does not ## searches in the list for a value. Returns false if the value does not
## exist, true otherwise. ## exist, true otherwise.
result = find(L, value) != nil result = find(L, value) != nil
@ -266,7 +194,6 @@ proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
if n.next != nil: n.next.prev = n.prev if n.next != nil: n.next.prev = n.prev
if n.prev != nil: n.prev.next = n.next if n.prev != nil: n.prev.next = n.next
proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) = proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
## appends a node `n` to `L`. Efficiency: O(1). ## appends a node `n` to `L`. Efficiency: O(1).
if L.head != nil: if L.head != nil:

View file

@ -198,9 +198,8 @@ when isMainModule:
assert($q == "[4, 56, 6, 789]") assert($q == "[4, 56, 6, 789]")
assert q[0] == q.front and q.front == 4 assert q[0] == q.front and q.front == 4
assert q[^1] == q.back and q.back == 789
q[0] = 42 q[0] = 42
q[^1] = 7 q[q.len - 1] = 7
assert 6 in q and 789 notin q assert 6 in q and 789 notin q
assert q.find(6) >= 0 assert q.find(6) >= 0

View file

@ -13,8 +13,9 @@
## were inspired by functional programming languages. ## were inspired by functional programming languages.
## ##
## For functional style programming you may want to pass `anonymous procs ## For functional style programming you may want to pass `anonymous procs
## <manual.html#anonymous-procs>`_ to procs like ``filter`` to reduce typing. ## <manual.html#procedures-anonymous-procs>`_ to procs like ``filter`` to
## Anonymous procs can use `the special do notation <manual.html#do-notation>`_ ## reduce typing. Anonymous procs can use `the special do notation
## <manual.html#procedures-do-notation>`_
## which is more convenient in certain situations. ## which is more convenient in certain situations.
include "system/inclrtl" include "system/inclrtl"
@ -43,8 +44,23 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
result[i] = itm result[i] = itm
inc(i) inc(i)
proc cycle*[T](s: seq[T], n: Natural): seq[T] = proc count*[T](s: openArray[T], x: T): int =
## Returns a new sequence with the items of `s` repeated `n` times. ## Returns the number of occurrences of the item `x` in the container `s`.
##
## Example:
##
## .. code-block::
## let
## s = @[1, 2, 2, 3, 2, 4, 2]
## c = count(s, 2)
## assert c == 4
for itm in items(s):
if itm == x:
inc result
proc cycle*[T](s: openArray[T], n: Natural): seq[T] =
## Returns a new sequence with the items of the container `s` repeated
## `n` times.
## ##
## Example: ## Example:
## ##
@ -75,9 +91,11 @@ proc repeat*[T](x: T, n: Natural): seq[T] =
for i in 0 ..< n: for i in 0 ..< n:
result[i] = x result[i] = x
proc deduplicate*[T](seq1: seq[T]): seq[T] = proc deduplicate*[T](s: openArray[T]): seq[T] =
## Returns a new sequence without duplicates. ## Returns a new sequence without duplicates.
## ##
## Example:
##
## .. code-block:: ## .. code-block::
## let ## let
## dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4] ## dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4]
@ -87,17 +105,19 @@ proc deduplicate*[T](seq1: seq[T]): seq[T] =
## assert unique1 == @[1, 3, 4, 2, 8] ## assert unique1 == @[1, 3, 4, 2, 8]
## assert unique2 == @["a", "c", "d"] ## assert unique2 == @["a", "c", "d"]
result = @[] result = @[]
for itm in items(seq1): for itm in items(s):
if not result.contains(itm): result.add(itm) if not result.contains(itm): result.add(itm)
{.deprecated: [distnct: deduplicate].} {.deprecated: [distnct: deduplicate].}
proc zip*[S, T](seq1: seq[S], seq2: seq[T]): seq[tuple[a: S, b: T]] = proc zip*[S, T](s1: openArray[S], s2: openArray[T]): seq[tuple[a: S, b: T]] =
## Returns a new sequence with a combination of the two input sequences. ## Returns a new sequence with a combination of the two input containers.
## ##
## For convenience you can access the returned tuples through the named ## For convenience you can access the returned tuples through the named
## fields `a` and `b`. If one sequence is shorter, the remaining items in the ## fields `a` and `b`. If one container is shorter, the remaining items in
## longer sequence are discarded. Example: ## the longer container are discarded.
##
## Example:
## ##
## .. code-block:: ## .. code-block::
## let ## let
@ -110,9 +130,10 @@ proc zip*[S, T](seq1: seq[S], seq2: seq[T]): seq[tuple[a: S, b: T]] =
## assert zip2 == @[(1, "one"), (2, "two"), (3, "three")] ## assert zip2 == @[(1, "one"), (2, "two"), (3, "three")]
## assert zip1[2].b == 4 ## assert zip1[2].b == 4
## assert zip2[2].b == "three" ## assert zip2[2].b == "three"
var m = min(seq1.len, seq2.len) var m = min(s1.len, s2.len)
newSeq(result, m) newSeq(result, m)
for i in 0 .. m-1: result[i] = (seq1[i], seq2[i]) for i in 0 ..< m:
result[i] = (s1[i], s2[i])
proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] = proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
## Splits and distributes a sequence `s` into `num` sub sequences. ## Splits and distributes a sequence `s` into `num` sub sequences.
@ -178,29 +199,33 @@ proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
result[i].add(s[g]) result[i].add(s[g])
first = last first = last
proc map*[T, S](s: openArray[T], op: proc (x: T): S {.closure.}):
proc map*[T, S](data: openArray[T], op: proc (x: T): S {.closure.}):
seq[S]{.inline.} = seq[S]{.inline.} =
## Returns a new sequence with the results of `op` applied to every item in ## Returns a new sequence with the results of `op` applied to every item in
## `data`. ## the container `s`.
## ##
## Since the input is not modified you can use this version of ``map`` to ## Since the input is not modified you can use this version of ``map`` to
## transform the type of the elements in the input sequence. Example: ## transform the type of the elements in the input container.
##
## Example:
## ##
## .. code-block:: nim ## .. code-block:: nim
## let ## let
## a = @[1, 2, 3, 4] ## a = @[1, 2, 3, 4]
## b = map(a, proc(x: int): string = $x) ## b = map(a, proc(x: int): string = $x)
## assert b == @["1", "2", "3", "4"] ## assert b == @["1", "2", "3", "4"]
newSeq(result, data.len) newSeq(result, s.len)
for i in 0..data.len-1: result[i] = op(data[i]) for i in 0 ..< s.len:
result[i] = op(s[i])
proc map*[T](data: var openArray[T], op: proc (x: var T) {.closure.}) proc map*[T](s: var openArray[T], op: proc (x: var T) {.closure.})
{.deprecated.} = {.deprecated.} =
## Applies `op` to every item in `data` modifying it directly. ## Applies `op` to every item in `s` modifying it directly.
## ##
## Note that this version of ``map`` requires your input and output types to ## Note that this version of ``map`` requires your input and output types to
## be the same, since they are modified in-place. Example: ## be the same, since they are modified in-place.
##
## Example:
## ##
## .. code-block:: nim ## .. code-block:: nim
## var a = @["1", "2", "3", "4"] ## var a = @["1", "2", "3", "4"]
@ -210,15 +235,16 @@ proc map*[T](data: var openArray[T], op: proc (x: var T) {.closure.})
## echo repr(a) ## echo repr(a)
## # --> ["142", "242", "342", "442"] ## # --> ["142", "242", "342", "442"]
## **Deprecated since version 0.12.0:** Use the ``apply`` proc instead. ## **Deprecated since version 0.12.0:** Use the ``apply`` proc instead.
for i in 0..data.len-1: op(data[i]) for i in 0 ..< s.len: op(s[i])
proc apply*[T](data: var seq[T], op: proc (x: var T) {.closure.}) proc apply*[T](s: var openArray[T], op: proc (x: var T) {.closure.})
{.inline.} = {.inline.} =
## Applies `op` to every item in `data` modifying it directly. ## Applies `op` to every item in `s` modifying it directly.
## ##
## Note that this requires your input and output types to ## Note that this requires your input and output types to
## be the same, since they are modified in-place. ## be the same, since they are modified in-place.
## The parameter function takes a ``var T`` type parameter. ## The parameter function takes a ``var T`` type parameter.
##
## Example: ## Example:
## ##
## .. code-block:: nim ## .. code-block:: nim
@ -229,15 +255,16 @@ proc apply*[T](data: var seq[T], op: proc (x: var T) {.closure.})
## echo repr(a) ## echo repr(a)
## # --> ["142", "242", "342", "442"] ## # --> ["142", "242", "342", "442"]
## ##
for i in 0..data.len-1: op(data[i]) for i in 0 ..< s.len: op(s[i])
proc apply*[T](data: var seq[T], op: proc (x: T): T {.closure.}) proc apply*[T](s: var openArray[T], op: proc (x: T): T {.closure.})
{.inline.} = {.inline.} =
## Applies `op` to every item in `data` modifying it directly. ## Applies `op` to every item in `s` modifying it directly.
## ##
## Note that this requires your input and output types to ## Note that this requires your input and output types to
## be the same, since they are modified in-place. ## be the same, since they are modified in-place.
## The parameter function takes and returns a ``T`` type variable. ## The parameter function takes and returns a ``T`` type variable.
##
## Example: ## Example:
## ##
## .. code-block:: nim ## .. code-block:: nim
@ -248,11 +275,10 @@ proc apply*[T](data: var seq[T], op: proc (x: T): T {.closure.})
## echo repr(a) ## echo repr(a)
## # --> ["142", "242", "342", "442"] ## # --> ["142", "242", "342", "442"]
## ##
for i in 0..data.len-1: data[i] = op(data[i]) for i in 0 ..< s.len: s[i] = op(s[i])
iterator filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): T =
iterator filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): T = ## Iterates through a container and yields every item that fulfills the
## Iterates through a sequence and yields every item that fulfills the
## predicate. ## predicate.
## ##
## Example: ## Example:
@ -262,11 +288,11 @@ iterator filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): T =
## for n in filter(numbers, proc (x: int): bool = x mod 2 == 0): ## for n in filter(numbers, proc (x: int): bool = x mod 2 == 0):
## echo($n) ## echo($n)
## # echoes 4, 8, 4 in separate lines ## # echoes 4, 8, 4 in separate lines
for i in 0..<seq1.len: for i in 0 ..< s.len:
if pred(seq1[i]): if pred(s[i]):
yield seq1[i] yield s[i]
proc filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): seq[T] proc filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): seq[T]
{.inline.} = {.inline.} =
## Returns a new sequence with all the items that fulfilled the predicate. ## Returns a new sequence with all the items that fulfilled the predicate.
## ##
@ -280,11 +306,11 @@ proc filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): seq[T]
## assert f1 == @["red", "black"] ## assert f1 == @["red", "black"]
## assert f2 == @["yellow"] ## assert f2 == @["yellow"]
result = newSeq[T]() result = newSeq[T]()
for i in 0..<seq1.len: for i in 0 ..< s.len:
if pred(seq1[i]): if pred(s[i]):
result.add(seq1[i]) result.add(s[i])
proc keepIf*[T](seq1: var seq[T], pred: proc(item: T): bool {.closure.}) proc keepIf*[T](s: var seq[T], pred: proc(x: T): bool {.closure.})
{.inline.} = {.inline.} =
## Keeps the items in the passed sequence if they fulfilled the predicate. ## Keeps the items in the passed sequence if they fulfilled the predicate.
## Same as the ``filter`` proc, but modifies the sequence directly. ## Same as the ``filter`` proc, but modifies the sequence directly.
@ -296,12 +322,12 @@ proc keepIf*[T](seq1: var seq[T], pred: proc(item: T): bool {.closure.})
## keepIf(floats, proc(x: float): bool = x > 10) ## keepIf(floats, proc(x: float): bool = x > 10)
## assert floats == @[13.0, 12.5, 10.1] ## assert floats == @[13.0, 12.5, 10.1]
var pos = 0 var pos = 0
for i in 0 .. <len(seq1): for i in 0 ..< len(s):
if pred(seq1[i]): if pred(s[i]):
if pos != i: if pos != i:
shallowCopy(seq1[pos], seq1[i]) shallowCopy(s[pos], s[i])
inc(pos) inc(pos)
setLen(seq1, pos) setLen(s, pos)
proc delete*[T](s: var seq[T]; first, last: Natural) = proc delete*[T](s: var seq[T]; first, last: Natural) =
## Deletes in `s` the items at position `first` .. `last`. This modifies ## Deletes in `s` the items at position `first` .. `last`. This modifies
@ -354,11 +380,12 @@ proc insert*[T](dest: var seq[T], src: openArray[T], pos=0) =
inc(j) inc(j)
template filterIt*(seq1, pred: untyped): untyped = template filterIt*(s, pred: untyped): untyped =
## Returns a new sequence with all the items that fulfilled the predicate. ## Returns a new sequence with all the items that fulfilled the predicate.
## ##
## Unlike the `proc` version, the predicate needs to be an expression using ## Unlike the `proc` version, the predicate needs to be an expression using
## the ``it`` variable for testing, like: ``filterIt("abcxyz", it == 'x')``. ## the ``it`` variable for testing, like: ``filterIt("abcxyz", it == 'x')``.
##
## Example: ## Example:
## ##
## .. code-block:: ## .. code-block::
@ -368,8 +395,8 @@ template filterIt*(seq1, pred: untyped): untyped =
## notAcceptable = filterIt(temperatures, it > 50 or it < -10) ## notAcceptable = filterIt(temperatures, it > 50 or it < -10)
## assert acceptable == @[-2.0, 24.5, 44.31] ## assert acceptable == @[-2.0, 24.5, 44.31]
## assert notAcceptable == @[-272.15, 99.9, -113.44] ## assert notAcceptable == @[-272.15, 99.9, -113.44]
var result = newSeq[type(seq1[0])]() var result = newSeq[type(s[0])]()
for it {.inject.} in items(seq1): for it {.inject.} in items(s):
if pred: result.add(it) if pred: result.add(it)
result result
@ -378,6 +405,7 @@ template keepItIf*(varSeq: seq, pred: untyped) =
## ##
## Unlike the `proc` version, the predicate needs to be an expression using ## Unlike the `proc` version, the predicate needs to be an expression using
## the ``it`` variable for testing, like: ``keepItIf("abcxyz", it == 'x')``. ## the ``it`` variable for testing, like: ``keepItIf("abcxyz", it == 'x')``.
##
## Example: ## Example:
## ##
## .. code-block:: ## .. code-block::
@ -393,8 +421,8 @@ template keepItIf*(varSeq: seq, pred: untyped) =
inc(pos) inc(pos)
setLen(varSeq, pos) setLen(varSeq, pos)
proc all*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): bool = proc all*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool =
## Iterates through a sequence and checks if every item fulfills the ## Iterates through a container and checks if every item fulfills the
## predicate. ## predicate.
## ##
## Example: ## Example:
@ -403,12 +431,12 @@ proc all*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): bool =
## let numbers = @[1, 4, 5, 8, 9, 7, 4] ## let numbers = @[1, 4, 5, 8, 9, 7, 4]
## assert all(numbers, proc (x: int): bool = return x < 10) == true ## assert all(numbers, proc (x: int): bool = return x < 10) == true
## assert all(numbers, proc (x: int): bool = return x < 9) == false ## assert all(numbers, proc (x: int): bool = return x < 9) == false
for i in seq1: for i in s:
if not pred(i): if not pred(i):
return false return false
return true return true
template allIt*(seq1, pred: untyped): bool = template allIt*(s, pred: untyped): bool =
## Checks if every item fulfills the predicate. ## Checks if every item fulfills the predicate.
## ##
## Example: ## Example:
@ -418,14 +446,14 @@ template allIt*(seq1, pred: untyped): bool =
## assert allIt(numbers, it < 10) == true ## assert allIt(numbers, it < 10) == true
## assert allIt(numbers, it < 9) == false ## assert allIt(numbers, it < 9) == false
var result = true var result = true
for it {.inject.} in items(seq1): for it {.inject.} in items(s):
if not pred: if not pred:
result = false result = false
break break
result result
proc any*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): bool = proc any*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool =
## Iterates through a sequence and checks if some item fulfills the ## Iterates through a container and checks if some item fulfills the
## predicate. ## predicate.
## ##
## Example: ## Example:
@ -434,12 +462,12 @@ proc any*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): bool =
## let numbers = @[1, 4, 5, 8, 9, 7, 4] ## let numbers = @[1, 4, 5, 8, 9, 7, 4]
## assert any(numbers, proc (x: int): bool = return x > 8) == true ## assert any(numbers, proc (x: int): bool = return x > 8) == true
## assert any(numbers, proc (x: int): bool = return x > 9) == false ## assert any(numbers, proc (x: int): bool = return x > 9) == false
for i in seq1: for i in s:
if pred(i): if pred(i):
return true return true
return false return false
template anyIt*(seq1, pred: untyped): bool = template anyIt*(s, pred: untyped): bool =
## Checks if some item fulfills the predicate. ## Checks if some item fulfills the predicate.
## ##
## Example: ## Example:
@ -449,7 +477,7 @@ template anyIt*(seq1, pred: untyped): bool =
## assert anyIt(numbers, it > 8) == true ## assert anyIt(numbers, it > 8) == true
## assert anyIt(numbers, it > 9) == false ## assert anyIt(numbers, it > 9) == false
var result = false var result = false
for it {.inject.} in items(seq1): for it {.inject.} in items(s):
if pred: if pred:
result = true result = true
break break
@ -493,7 +521,9 @@ template foldl*(sequence, operation: untyped): untyped =
## variables ``a`` and ``b`` for each step of the fold. Since this is a left ## variables ``a`` and ``b`` for each step of the fold. Since this is a left
## fold, for non associative binary operations like subtraction think that ## fold, for non associative binary operations like subtraction think that
## the sequence of numbers 1, 2 and 3 will be parenthesized as (((1) - 2) - ## the sequence of numbers 1, 2 and 3 will be parenthesized as (((1) - 2) -
## 3). Example: ## 3).
##
## Example:
## ##
## .. code-block:: ## .. code-block::
## let ## let
@ -527,6 +557,7 @@ template foldl*(sequence, operation, first): untyped =
## The ``operation`` parameter should be an expression which uses the variables ## The ``operation`` parameter should be an expression which uses the variables
## ``a`` and ``b`` for each step of the fold. The ``first`` parameter is the ## ``a`` and ``b`` for each step of the fold. The ``first`` parameter is the
## start value (the first ``a``) and therefor defines the type of the result. ## start value (the first ``a``) and therefor defines the type of the result.
##
## Example: ## Example:
## ##
## .. code-block:: ## .. code-block::
@ -555,7 +586,9 @@ template foldr*(sequence, operation: untyped): untyped =
## variables ``a`` and ``b`` for each step of the fold. Since this is a right ## variables ``a`` and ``b`` for each step of the fold. Since this is a right
## fold, for non associative binary operations like subtraction think that ## fold, for non associative binary operations like subtraction think that
## the sequence of numbers 1, 2 and 3 will be parenthesized as (1 - (2 - ## the sequence of numbers 1, 2 and 3 will be parenthesized as (1 - (2 -
## (3))). Example: ## (3))).
##
## Example:
## ##
## .. code-block:: ## .. code-block::
## let ## let
@ -580,13 +613,15 @@ template foldr*(sequence, operation: untyped): untyped =
result = operation result = operation
result result
template mapIt*(seq1, typ, op: untyped): untyped = template mapIt*(s, typ, op: untyped): untyped =
## Convenience template around the ``map`` proc to reduce typing. ## Convenience template around the ``map`` proc to reduce typing.
## ##
## The template injects the ``it`` variable which you can use directly in an ## The template injects the ``it`` variable which you can use directly in an
## expression. You also need to pass as `typ` the type of the expression, ## expression. You also need to pass as `typ` the type of the expression,
## since the new returned sequence can have a different type than the ## since the new returned sequence can have a different type than the
## original. Example: ## original.
##
## Example:
## ##
## .. code-block:: ## .. code-block::
## let ## let
@ -596,16 +631,18 @@ template mapIt*(seq1, typ, op: untyped): untyped =
## **Deprecated since version 0.12.0:** Use the ``mapIt(seq1, op)`` ## **Deprecated since version 0.12.0:** Use the ``mapIt(seq1, op)``
## template instead. ## template instead.
var result: seq[typ] = @[] var result: seq[typ] = @[]
for it {.inject.} in items(seq1): for it {.inject.} in items(s):
result.add(op) result.add(op)
result result
template mapIt*(seq1, op: untyped): untyped = template mapIt*(s, op: untyped): untyped =
## Convenience template around the ``map`` proc to reduce typing. ## Convenience template around the ``map`` proc to reduce typing.
## ##
## The template injects the ``it`` variable which you can use directly in an ## The template injects the ``it`` variable which you can use directly in an
## expression. Example: ## expression.
##
## Example:
## ##
## .. code-block:: ## .. code-block::
## let ## let
@ -614,19 +651,19 @@ template mapIt*(seq1, op: untyped): untyped =
## assert strings == @["4", "8", "12", "16"] ## assert strings == @["4", "8", "12", "16"]
type outType = type(( type outType = type((
block: block:
var it{.inject.}: type(items(seq1)); var it{.inject.}: type(items(s));
op)) op))
var result: seq[outType] var result: seq[outType]
when compiles(seq1.len): when compiles(s.len):
let s = seq1 let t = s
var i = 0 var i = 0
result = newSeq[outType](s.len) result = newSeq[outType](s.len)
for it {.inject.} in s: for it {.inject.} in t:
result[i] = op result[i] = op
i += 1 i += 1
else: else:
result = @[] result = @[]
for it {.inject.} in seq1: for it {.inject.} in s:
result.add(op) result.add(op)
result result
@ -635,7 +672,9 @@ template applyIt*(varSeq, op: untyped) =
## ##
## The template injects the ``it`` variable which you can use directly in an ## The template injects the ``it`` variable which you can use directly in an
## expression. The expression has to return the same type as the sequence you ## expression. The expression has to return the same type as the sequence you
## are mutating. Example: ## are mutating.
##
## Example:
## ##
## .. code-block:: ## .. code-block::
## var nums = @[1, 2, 3, 4] ## var nums = @[1, 2, 3, 4]
@ -646,9 +685,10 @@ template applyIt*(varSeq, op: untyped) =
varSeq[i] = op varSeq[i] = op
template newSeqWith*(len: int, init: untyped): untyped = template newSeqWith*(len: int, init: untyped): untyped =
## creates a new sequence, calling `init` to initialize each value. Example: ## creates a new sequence, calling `init` to initialize each value.
##
## Example:
## ##
## .. code-block:: ## .. code-block::
## var seq2D = newSeqWith(20, newSeq[bool](10)) ## var seq2D = newSeqWith(20, newSeq[bool](10))
@ -674,45 +714,178 @@ when isMainModule:
total = concat(s1, s2, s3) total = concat(s1, s2, s3)
assert total == @[1, 2, 3, 4, 5, 6, 7] assert total == @[1, 2, 3, 4, 5, 6, 7]
block: # duplicates test block: # count test
let
s1 = @[1, 2, 3, 2]
s2 = @['a', 'b', 'x', 'a']
a1 = [1, 2, 3, 2]
a2 = ['a', 'b', 'x', 'a']
r0 = count(s1, 0)
r1 = count(s1, 1)
r2 = count(s1, 2)
r3 = count(s2, 'y')
r4 = count(s2, 'x')
r5 = count(s2, 'a')
ar0 = count(a1, 0)
ar1 = count(a1, 1)
ar2 = count(a1, 2)
ar3 = count(a2, 'y')
ar4 = count(a2, 'x')
ar5 = count(a2, 'a')
assert r0 == 0
assert r1 == 1
assert r2 == 2
assert r3 == 0
assert r4 == 1
assert r5 == 2
assert ar0 == 0
assert ar1 == 1
assert ar2 == 2
assert ar3 == 0
assert ar4 == 1
assert ar5 == 2
block: # cycle tests
let
a = @[1, 2, 3]
b: seq[int] = @[]
c = [1, 2, 3]
doAssert a.cycle(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
doAssert a.cycle(0) == @[]
#doAssert a.cycle(-1) == @[] # will not compile!
doAssert b.cycle(3) == @[]
doAssert c.cycle(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
doAssert c.cycle(0) == @[]
block: # repeat tests
assert repeat(10, 5) == @[10, 10, 10, 10, 10]
assert repeat(@[1,2,3], 2) == @[@[1,2,3], @[1,2,3]]
assert repeat([1,2,3], 2) == @[[1,2,3], [1,2,3]]
block: # deduplicates test
let let
dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4] dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4]
dup2 = @["a", "a", "c", "d", "d"] dup2 = @["a", "a", "c", "d", "d"]
dup3 = [1, 1, 3, 4, 2, 2, 8, 1, 4]
dup4 = ["a", "a", "c", "d", "d"]
unique1 = deduplicate(dup1) unique1 = deduplicate(dup1)
unique2 = deduplicate(dup2) unique2 = deduplicate(dup2)
unique3 = deduplicate(dup3)
unique4 = deduplicate(dup4)
assert unique1 == @[1, 3, 4, 2, 8] assert unique1 == @[1, 3, 4, 2, 8]
assert unique2 == @["a", "c", "d"] assert unique2 == @["a", "c", "d"]
assert unique3 == @[1, 3, 4, 2, 8]
assert unique4 == @["a", "c", "d"]
block: # zip test block: # zip test
let let
short = @[1, 2, 3] short = @[1, 2, 3]
long = @[6, 5, 4, 3, 2, 1] long = @[6, 5, 4, 3, 2, 1]
words = @["one", "two", "three"] words = @["one", "two", "three"]
ashort = [1, 2, 3]
along = [6, 5, 4, 3, 2, 1]
awords = ["one", "two", "three"]
zip1 = zip(short, long) zip1 = zip(short, long)
zip2 = zip(short, words) zip2 = zip(short, words)
zip3 = zip(ashort, along)
zip4 = zip(ashort, awords)
zip5 = zip(ashort, words)
assert zip1 == @[(1, 6), (2, 5), (3, 4)] assert zip1 == @[(1, 6), (2, 5), (3, 4)]
assert zip2 == @[(1, "one"), (2, "two"), (3, "three")] assert zip2 == @[(1, "one"), (2, "two"), (3, "three")]
assert zip3 == @[(1, 6), (2, 5), (3, 4)]
assert zip4 == @[(1, "one"), (2, "two"), (3, "three")]
assert zip5 == @[(1, "one"), (2, "two"), (3, "three")]
assert zip1[2].b == 4 assert zip1[2].b == 4
assert zip2[2].b == "three" assert zip2[2].b == "three"
assert zip3[2].b == 4
assert zip4[2].b == "three"
assert zip5[2].b == "three"
block: # distribute tests
let numbers = @[1, 2, 3, 4, 5, 6, 7]
doAssert numbers.distribute(3) == @[@[1, 2, 3], @[4, 5], @[6, 7]]
doAssert numbers.distribute(6)[0] == @[1, 2]
doAssert numbers.distribute(6)[5] == @[7]
let a = @[1, 2, 3, 4, 5, 6, 7]
doAssert a.distribute(1, true) == @[@[1, 2, 3, 4, 5, 6, 7]]
doAssert a.distribute(1, false) == @[@[1, 2, 3, 4, 5, 6, 7]]
doAssert a.distribute(2, true) == @[@[1, 2, 3, 4], @[5, 6, 7]]
doAssert a.distribute(2, false) == @[@[1, 2, 3, 4], @[5, 6, 7]]
doAssert a.distribute(3, true) == @[@[1, 2, 3], @[4, 5], @[6, 7]]
doAssert a.distribute(3, false) == @[@[1, 2, 3], @[4, 5, 6], @[7]]
doAssert a.distribute(4, true) == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
doAssert a.distribute(4, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
doAssert a.distribute(5, true) == @[@[1, 2], @[3, 4], @[5], @[6], @[7]]
doAssert a.distribute(5, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7], @[]]
doAssert a.distribute(6, true) == @[@[1, 2], @[3], @[4], @[5], @[6], @[7]]
doAssert a.distribute(6, false) == @[
@[1, 2], @[3, 4], @[5, 6], @[7], @[], @[]]
doAssert a.distribute(8, false) == a.distribute(8, true)
doAssert a.distribute(90, false) == a.distribute(90, true)
var b = @[0]
for f in 1 .. 25: b.add(f)
doAssert b.distribute(5, true)[4].len == 5
doAssert b.distribute(5, false)[4].len == 2
block: # map test
let
numbers = @[1, 4, 5, 8, 9, 7, 4]
anumbers = [1, 4, 5, 8, 9, 7, 4]
m1 = map(numbers, proc(x: int): int = 2*x)
m2 = map(anumbers, proc(x: int): int = 2*x)
assert m1 == @[2, 8, 10, 16, 18, 14, 8]
assert m2 == @[2, 8, 10, 16, 18, 14, 8]
block: # apply test
var a = @["1", "2", "3", "4"]
apply(a, proc(x: var string) = x &= "42")
assert a == @["142", "242", "342", "442"]
block: # filter proc test block: # filter proc test
let let
colors = @["red", "yellow", "black"] colors = @["red", "yellow", "black"]
acolors = ["red", "yellow", "black"]
f1 = filter(colors, proc(x: string): bool = x.len < 6) f1 = filter(colors, proc(x: string): bool = x.len < 6)
f2 = filter(colors) do (x: string) -> bool : x.len > 5 f2 = filter(colors) do (x: string) -> bool : x.len > 5
f3 = filter(acolors, proc(x: string): bool = x.len < 6)
f4 = filter(acolors) do (x: string) -> bool : x.len > 5
assert f1 == @["red", "black"] assert f1 == @["red", "black"]
assert f2 == @["yellow"] assert f2 == @["yellow"]
assert f3 == @["red", "black"]
assert f4 == @["yellow"]
block: # filter iterator test block: # filter iterator test
let numbers = @[1, 4, 5, 8, 9, 7, 4] let numbers = @[1, 4, 5, 8, 9, 7, 4]
let anumbers = [1, 4, 5, 8, 9, 7, 4]
assert toSeq(filter(numbers, proc (x: int): bool = x mod 2 == 0)) == assert toSeq(filter(numbers, proc (x: int): bool = x mod 2 == 0)) ==
@[4, 8, 4] @[4, 8, 4]
assert toSeq(filter(anumbers, proc (x: int): bool = x mod 2 == 0)) ==
@[4, 8, 4]
block: # keepIf test block: # keepIf test
var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1] var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1]
keepIf(floats, proc(x: float): bool = x > 10) keepIf(floats, proc(x: float): bool = x > 10)
assert floats == @[13.0, 12.5, 10.1] assert floats == @[13.0, 12.5, 10.1]
block: # delete tests
let outcome = @[1,1,1,1,1,1,1,1]
var dest = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1]
dest.delete(3, 8)
assert outcome == dest, """\
Deleting range 3-9 from [1,1,1,2,2,2,2,2,2,1,1,1,1,1]
is [1,1,1,1,1,1,1,1]"""
block: # insert tests
var dest = @[1,1,1,1,1,1,1,1]
let
src = @[2,2,2,2,2,2]
outcome = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1]
dest.insert(src, 3)
assert dest == outcome, """\
Inserting [2,2,2,2,2,2] into [1,1,1,1,1,1,1,1]
at 3 is [1,1,1,2,2,2,2,2,2,1,1,1,1,1]"""
block: # filterIt test block: # filterIt test
let let
temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44] temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44]
@ -726,37 +899,49 @@ when isMainModule:
keepItIf(candidates, it.len == 3 and it[0] == 'b') keepItIf(candidates, it.len == 3 and it[0] == 'b')
assert candidates == @["bar", "baz"] assert candidates == @["bar", "baz"]
block: # any
let
numbers = @[1, 4, 5, 8, 9, 7, 4]
len0seq : seq[int] = @[]
assert any(numbers, proc (x: int): bool = return x > 8) == true
assert any(numbers, proc (x: int): bool = return x > 9) == false
assert any(len0seq, proc (x: int): bool = return true) == false
block: # anyIt
let
numbers = @[1, 4, 5, 8, 9, 7, 4]
len0seq : seq[int] = @[]
assert anyIt(numbers, it > 8) == true
assert anyIt(numbers, it > 9) == false
assert anyIt(len0seq, true) == false
block: # all block: # all
let let
numbers = @[1, 4, 5, 8, 9, 7, 4] numbers = @[1, 4, 5, 8, 9, 7, 4]
anumbers = [1, 4, 5, 8, 9, 7, 4]
len0seq : seq[int] = @[] len0seq : seq[int] = @[]
assert all(numbers, proc (x: int): bool = return x < 10) == true assert all(numbers, proc (x: int): bool = return x < 10) == true
assert all(numbers, proc (x: int): bool = return x < 9) == false assert all(numbers, proc (x: int): bool = return x < 9) == false
assert all(len0seq, proc (x: int): bool = return false) == true assert all(len0seq, proc (x: int): bool = return false) == true
assert all(anumbers, proc (x: int): bool = return x < 10) == true
assert all(anumbers, proc (x: int): bool = return x < 9) == false
block: # allIt block: # allIt
let let
numbers = @[1, 4, 5, 8, 9, 7, 4] numbers = @[1, 4, 5, 8, 9, 7, 4]
anumbers = [1, 4, 5, 8, 9, 7, 4]
len0seq : seq[int] = @[] len0seq : seq[int] = @[]
assert allIt(numbers, it < 10) == true assert allIt(numbers, it < 10) == true
assert allIt(numbers, it < 9) == false assert allIt(numbers, it < 9) == false
assert allIt(len0seq, false) == true assert allIt(len0seq, false) == true
assert allIt(anumbers, it < 10) == true
assert allIt(anumbers, it < 9) == false
block: # any
let
numbers = @[1, 4, 5, 8, 9, 7, 4]
anumbers = [1, 4, 5, 8, 9, 7, 4]
len0seq : seq[int] = @[]
assert any(numbers, proc (x: int): bool = return x > 8) == true
assert any(numbers, proc (x: int): bool = return x > 9) == false
assert any(len0seq, proc (x: int): bool = return true) == false
assert any(anumbers, proc (x: int): bool = return x > 8) == true
assert any(anumbers, proc (x: int): bool = return x > 9) == false
block: # anyIt
let
numbers = @[1, 4, 5, 8, 9, 7, 4]
anumbers = [1, 4, 5, 8, 9, 7, 4]
len0seq : seq[int] = @[]
assert anyIt(numbers, it > 8) == true
assert anyIt(numbers, it > 9) == false
assert anyIt(len0seq, true) == false
assert anyIt(anumbers, it > 8) == true
assert anyIt(anumbers, it > 9) == false
block: # toSeq test block: # toSeq test
let let
@ -792,56 +977,13 @@ when isMainModule:
assert multiplication == 495, "Multiplication is (5*(9*(11)))" assert multiplication == 495, "Multiplication is (5*(9*(11)))"
assert concatenation == "nimiscool" assert concatenation == "nimiscool"
block: # delete tests
let outcome = @[1,1,1,1,1,1,1,1]
var dest = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1]
dest.delete(3, 8)
assert outcome == dest, """\
Deleting range 3-9 from [1,1,1,2,2,2,2,2,2,1,1,1,1,1]
is [1,1,1,1,1,1,1,1]"""
block: # insert tests
var dest = @[1,1,1,1,1,1,1,1]
let
src = @[2,2,2,2,2,2]
outcome = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1]
dest.insert(src, 3)
assert dest == outcome, """\
Inserting [2,2,2,2,2,2] into [1,1,1,1,1,1,1,1]
at 3 is [1,1,1,2,2,2,2,2,2,1,1,1,1,1]"""
block: # mapIt tests block: # mapIt tests
var var
nums = @[1, 2, 3, 4] nums = @[1, 2, 3, 4]
strings = nums.mapIt($(4 * it)) strings = nums.mapIt($(4 * it))
nums.applyIt(it * 3) nums.applyIt(it * 3)
assert nums[0] + nums[3] == 15 assert nums[0] + nums[3] == 15
assert strings[2] == "12"
block: # distribute tests
let numbers = @[1, 2, 3, 4, 5, 6, 7]
doAssert numbers.distribute(3) == @[@[1, 2, 3], @[4, 5], @[6, 7]]
doAssert numbers.distribute(6)[0] == @[1, 2]
doAssert numbers.distribute(6)[5] == @[7]
let a = @[1, 2, 3, 4, 5, 6, 7]
doAssert a.distribute(1, true) == @[@[1, 2, 3, 4, 5, 6, 7]]
doAssert a.distribute(1, false) == @[@[1, 2, 3, 4, 5, 6, 7]]
doAssert a.distribute(2, true) == @[@[1, 2, 3, 4], @[5, 6, 7]]
doAssert a.distribute(2, false) == @[@[1, 2, 3, 4], @[5, 6, 7]]
doAssert a.distribute(3, true) == @[@[1, 2, 3], @[4, 5], @[6, 7]]
doAssert a.distribute(3, false) == @[@[1, 2, 3], @[4, 5, 6], @[7]]
doAssert a.distribute(4, true) == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
doAssert a.distribute(4, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
doAssert a.distribute(5, true) == @[@[1, 2], @[3, 4], @[5], @[6], @[7]]
doAssert a.distribute(5, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7], @[]]
doAssert a.distribute(6, true) == @[@[1, 2], @[3], @[4], @[5], @[6], @[7]]
doAssert a.distribute(6, false) == @[
@[1, 2], @[3, 4], @[5, 6], @[7], @[], @[]]
doAssert a.distribute(8, false) == a.distribute(8, true)
doAssert a.distribute(90, false) == a.distribute(90, true)
var b = @[0]
for f in 1 .. 25: b.add(f)
doAssert b.distribute(5, true)[4].len == 5
doAssert b.distribute(5, false)[4].len == 2
block: # newSeqWith tests block: # newSeqWith tests
var seq2D = newSeqWith(4, newSeq[bool](2)) var seq2D = newSeqWith(4, newSeq[bool](2))
@ -850,19 +992,5 @@ when isMainModule:
seq2D[0][1] = true seq2D[0][1] = true
doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]] doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]]
block: # cycle tests
let
a = @[1, 2, 3]
b: seq[int] = @[]
doAssert a.cycle(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
doAssert a.cycle(0) == @[]
#doAssert a.cycle(-1) == @[] # will not compile!
doAssert b.cycle(3) == @[]
block: # repeat tests
assert repeat(10, 5) == @[10, 10, 10, 10, 10]
assert repeat(@[1,2,3], 2) == @[@[1,2,3], @[1,2,3]]
when not defined(testing): when not defined(testing):
echo "Finished doc tests" echo "Finished doc tests"

View file

@ -55,7 +55,7 @@ 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, "index out of bounds")
proc `[]`*(s: SharedString; ab: Slice[int]): SharedString = proc `[]`*(s: SharedString; ab: HSlice[int, int]): SharedString =
#incRef(src.buffer) #incRef(src.buffer)
if ab.a < s.len: if ab.a < s.len:
result.buffer = s.buffer result.buffer = s.buffer

View file

@ -45,8 +45,25 @@ proc countProcessors*(): int {.rtl, extern: "ncpi$1".} =
## returns the numer of the processors/cores the machine has. ## returns the numer of the processors/cores the machine has.
## Returns 0 if it cannot be detected. ## Returns 0 if it cannot be detected.
when defined(windows): when defined(windows):
var x = getEnv("NUMBER_OF_PROCESSORS") type
if x.len > 0: result = parseInt(x.string) SYSTEM_INFO {.final, pure.} = object
u1: int32
dwPageSize: int32
lpMinimumApplicationAddress: pointer
lpMaximumApplicationAddress: pointer
dwActiveProcessorMask: ptr int32
dwNumberOfProcessors: int32
dwProcessorType: int32
dwAllocationGranularity: int32
wProcessorLevel: int16
wProcessorRevision: int16
proc GetSystemInfo(lpSystemInfo: var SYSTEM_INFO) {.stdcall, dynlib: "kernel32", importc: "GetSystemInfo".}
var
si: SYSTEM_INFO
GetSystemInfo(si)
result = si.dwNumberOfProcessors
elif defined(macosx) or defined(bsd): elif defined(macosx) or defined(bsd):
var var
mib: array[0..3, cint] mib: array[0..3, cint]

View file

@ -448,12 +448,22 @@ proc nimSpawn3(fn: WorkerProc; data: pointer) {.compilerProc.} =
if selectWorker(readyWorker, fn, data): return if selectWorker(readyWorker, fn, data): return
for i in 0..<currentPoolSize: for i in 0..<currentPoolSize:
if selectWorker(addr(workersData[i]), fn, data): return if selectWorker(addr(workersData[i]), fn, data): return
# determine what to do, but keep in mind this is expensive too: # determine what to do, but keep in mind this is expensive too:
# state.calls < maxPoolSize: warmup phase # state.calls < maxPoolSize: warmup phase
# (state.calls and 127) == 0: periodic check # (state.calls and 127) == 0: periodic check
if state.calls < maxPoolSize or (state.calls and 127) == 0: if state.calls < maxPoolSize or (state.calls and 127) == 0:
# ensure the call to 'advice' is atomic: # ensure the call to 'advice' is atomic:
if tryAcquire(stateLock): if tryAcquire(stateLock):
if currentPoolSize < minPoolSize:
if not workersData[currentPoolSize].initialized:
activateWorkerThread(currentPoolSize)
let w = addr(workersData[currentPoolSize])
atomicInc currentPoolSize
if selectWorker(w, fn, data):
release(stateLock)
return
case advice(state) case advice(state)
of doNothing: discard of doNothing: discard
of doCreateThread: of doCreateThread:

View file

@ -883,7 +883,9 @@ proc recvFull(client: HttpClient | AsyncHttpClient, size: int, timeout: int,
let data = client.socket.recv(sizeToRecv, timeout) let data = client.socket.recv(sizeToRecv, timeout)
else: else:
let data = await client.socket.recv(sizeToRecv) let data = await client.socket.recv(sizeToRecv)
if data == "": break # We've been disconnected. if data == "":
client.close()
break # We've been disconnected.
readLen.inc(data.len) readLen.inc(data.len)
if keep: if keep:
@ -950,6 +952,7 @@ proc parseBody(client: HttpClient | AsyncHttpClient,
if length > 0: if length > 0:
let recvLen = await client.recvFull(length, client.timeout, true) let recvLen = await client.recvFull(length, client.timeout, true)
if recvLen == 0: if recvLen == 0:
client.close()
httpError("Got disconnected while trying to read body.") httpError("Got disconnected while trying to read body.")
if recvLen != length: if recvLen != length:
httpError("Received length doesn't match expected length. Wanted " & httpError("Received length doesn't match expected length. Wanted " &
@ -962,13 +965,20 @@ proc parseBody(client: HttpClient | AsyncHttpClient,
if headers.getOrDefault"Connection" == "close" or httpVersion == "1.0": if headers.getOrDefault"Connection" == "close" or httpVersion == "1.0":
while true: while true:
let recvLen = await client.recvFull(4000, client.timeout, true) let recvLen = await client.recvFull(4000, client.timeout, true)
if recvLen == 0: break if recvLen == 0:
client.close()
break
when client is AsyncHttpClient: when client is AsyncHttpClient:
client.bodyStream.complete() client.bodyStream.complete()
else: else:
client.bodyStream.setPosition(0) client.bodyStream.setPosition(0)
# If the server will close our connection, then no matter the method of
# reading the body, we need to close our socket.
if headers.getOrDefault"Connection" == "close":
client.close()
proc parseResponse(client: HttpClient | AsyncHttpClient, proc parseResponse(client: HttpClient | AsyncHttpClient,
getBody: bool): Future[Response | AsyncResponse] getBody: bool): Future[Response | AsyncResponse]
{.multisync.} = {.multisync.} =
@ -984,7 +994,10 @@ proc parseResponse(client: HttpClient | AsyncHttpClient,
line = await client.socket.recvLine(client.timeout) line = await client.socket.recvLine(client.timeout)
else: else:
line = await client.socket.recvLine() line = await client.socket.recvLine()
if line == "": break # We've been disconnected. if line == "":
# We've been disconnected.
client.close()
break
if line == "\c\L": if line == "\c\L":
fullyRead = true fullyRead = true
break break
@ -1033,7 +1046,8 @@ proc newConnection(client: HttpClient | AsyncHttpClient,
url: Uri) {.multisync.} = url: Uri) {.multisync.} =
if client.currentURL.hostname != url.hostname or if client.currentURL.hostname != url.hostname or
client.currentURL.scheme != url.scheme or client.currentURL.scheme != url.scheme or
client.currentURL.port != url.port: client.currentURL.port != url.port or
(not client.connected):
let isSsl = url.scheme.toLowerAscii() == "https" let isSsl = url.scheme.toLowerAscii() == "https"
if isSsl and not defined(ssl): if isSsl and not defined(ssl):

View file

@ -113,6 +113,9 @@ proc newHttpHeaders*(keyValuePairs:
new result new result
result.table = newTable[string, seq[string]](pairs) result.table = newTable[string, seq[string]](pairs)
proc `$`*(headers: HttpHeaders): string =
return $headers.table
proc clear*(headers: HttpHeaders) = proc clear*(headers: HttpHeaders) =
headers.table.clear() headers.table.clear()

View file

@ -94,7 +94,7 @@ proc findEnvVar(key: string): int =
if startsWith(environment[i], temp): return i if startsWith(environment[i], temp): return i
return -1 return -1
proc getEnv*(key: string): TaintedString {.tags: [ReadEnvEffect].} = proc getEnv*(key: string, default = ""): TaintedString {.tags: [ReadEnvEffect].} =
## Returns the value of the `environment variable`:idx: named `key`. ## Returns the value of the `environment variable`:idx: named `key`.
## ##
## If the variable does not exist, "" is returned. To distinguish ## If the variable does not exist, "" is returned. To distinguish
@ -108,7 +108,7 @@ proc getEnv*(key: string): TaintedString {.tags: [ReadEnvEffect].} =
return TaintedString(substr(environment[i], find(environment[i], '=')+1)) return TaintedString(substr(environment[i], find(environment[i], '=')+1))
else: else:
var env = c_getenv(key) var env = c_getenv(key)
if env == nil: return TaintedString("") if env == nil: return TaintedString(default)
result = TaintedString($env) result = TaintedString($env)
proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} = proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =

View file

@ -37,15 +37,15 @@
## Retrieving the value of a JSON node can then be achieved using one of the ## Retrieving the value of a JSON node can then be achieved using one of the
## helper procedures, which include: ## helper procedures, which include:
## ##
## * ``getNum`` ## * ``getInt``
## * ``getFNum`` ## * ``getFloat``
## * ``getStr`` ## * ``getStr``
## * ``getBVal`` ## * ``getBool``
## ##
## To retrieve the value of ``"key"`` you can do the following: ## To retrieve the value of ``"key"`` you can do the following:
## ##
## .. code-block:: Nim ## .. code-block:: Nim
## doAssert jsonNode["key"].getFNum() == 3.14 ## doAssert jsonNode["key"].getFloat() == 3.14
## ##
## The ``[]`` operator will raise an exception when the specified field does ## The ``[]`` operator will raise an exception when the specified field does
## not exist. If you wish to avoid this behaviour you can use the ``{}`` ## not exist. If you wish to avoid this behaviour you can use the ``{}``
@ -681,14 +681,25 @@ proc getStr*(n: JsonNode, default: string = ""): string =
if n.isNil or n.kind != JString: return default if n.isNil or n.kind != JString: return default
else: return n.str else: return n.str
proc getNum*(n: JsonNode, default: BiggestInt = 0): BiggestInt = proc getInt*(n: JsonNode, default: int = 0): int =
## Retrieves the int value of a `JInt JsonNode`. ## Retrieves the int value of a `JInt JsonNode`.
## ##
## Returns ``default`` if ``n`` is not a ``JInt``, or if ``n`` is nil. ## Returns ``default`` if ``n`` is not a ``JInt``, or if ``n`` is nil.
if n.isNil or n.kind != JInt: return default if n.isNil or n.kind != JInt: return default
else: return int(n.num)
proc getBiggestInt*(n: JsonNode, default: BiggestInt = 0): BiggestInt =
## Retrieves the BiggestInt value of a `JInt JsonNode`.
##
## Returns ``default`` if ``n`` is not a ``JInt``, or if ``n`` is nil.
if n.isNil or n.kind != JInt: return default
else: return n.num else: return n.num
proc getFNum*(n: JsonNode, default: float = 0.0): float = proc getNum*(n: JsonNode, default: BiggestInt = 0): BiggestInt {.deprecated.} =
## Deprecated - use getInt or getBiggestInt instead
getBiggestInt(n, default)
proc getFloat*(n: JsonNode, default: float = 0.0): float =
## Retrieves the float value of a `JFloat JsonNode`. ## Retrieves the float value of a `JFloat JsonNode`.
## ##
## Returns ``default`` if ``n`` is not a ``JFloat`` or ``JInt``, or if ``n`` is nil. ## Returns ``default`` if ``n`` is not a ``JFloat`` or ``JInt``, or if ``n`` is nil.
@ -698,13 +709,21 @@ proc getFNum*(n: JsonNode, default: float = 0.0): float =
of JInt: return float(n.num) of JInt: return float(n.num)
else: return default else: return default
proc getBVal*(n: JsonNode, default: bool = false): bool = proc getFNum*(n: JsonNode, default: float = 0.0): float {.deprecated.} =
## Deprecated - use getFloat instead
getFloat(n, default)
proc getBool*(n: JsonNode, default: bool = false): bool =
## Retrieves the bool value of a `JBool JsonNode`. ## Retrieves the bool value of a `JBool JsonNode`.
## ##
## Returns ``default`` if ``n`` is not a ``JBool``, or if ``n`` is nil. ## Returns ``default`` if ``n`` is not a ``JBool``, or if ``n`` is nil.
if n.isNil or n.kind != JBool: return default if n.isNil or n.kind != JBool: return default
else: return n.bval else: return n.bval
proc getBVal*(n: JsonNode, default: bool = false): bool {.deprecated.} =
## Deprecated - use getBVal instead
getBool(n, default)
proc getFields*(n: JsonNode, proc getFields*(n: JsonNode,
default = initOrderedTable[string, JsonNode](4)): default = initOrderedTable[string, JsonNode](4)):
OrderedTable[string, JsonNode] = OrderedTable[string, JsonNode] =
@ -1342,7 +1361,7 @@ proc getEnum(node: JsonNode, ast: string, T: typedesc): T =
# TODO: I shouldn't need this proc. # TODO: I shouldn't need this proc.
proc convert[T](x: BiggestInt): T = T(x) proc convert[T](x: BiggestInt): T = T(x)
verifyJsonKind(node, {JInt}, ast) verifyJsonKind(node, {JInt}, ast)
return convert[T](node.getNum()) return convert[T](node.getBiggestInt())
else: else:
verifyJsonKind(node, {JString}, ast) verifyJsonKind(node, {JString}, ast)
return parseEnum[T](node.getStr()) return parseEnum[T](node.getStr())

View file

@ -37,6 +37,7 @@ type
lineNumber*: int ## the current line number lineNumber*: int ## the current line number
sentinel: int sentinel: int
lineStart: int # index of last line start in buffer lineStart: int # index of last line start in buffer
offsetBase*: int # use ``offsetBase + bufpos`` to get the offset
refillChars: set[char] refillChars: set[char]
{.deprecated: [TBaseLexer: BaseLexer].} {.deprecated: [TBaseLexer: BaseLexer].}
@ -107,7 +108,8 @@ proc fillBaseLexer(L: var BaseLexer, pos: int): int =
result = pos + 1 # nothing to do result = pos + 1 # nothing to do
else: else:
fillBuffer(L) fillBuffer(L)
L.bufpos = 0 # XXX: is this really correct? L.offsetBase += pos
L.bufpos = 0
result = 0 result = 0
proc handleCR*(L: var BaseLexer, pos: int): int = proc handleCR*(L: var BaseLexer, pos: int): int =
@ -147,6 +149,7 @@ proc open*(L: var BaseLexer, input: Stream, bufLen: int = 8192;
assert(input != nil) assert(input != nil)
L.input = input L.input = input
L.bufpos = 0 L.bufpos = 0
L.offsetBase = 0
L.bufLen = bufLen L.bufLen = bufLen
L.refillChars = refillChars L.refillChars = refillChars
when defined(js): when defined(js):

View file

@ -283,7 +283,7 @@ proc to*[T](data: string): T =
loadAny(newStringStream(data), toAny(result), tab) loadAny(newStringStream(data), toAny(result), tab)
when not defined(testing) and isMainModule: when not defined(testing) and isMainModule:
template testit(x: expr) = echo($$to[type(x)]($$x)) template testit(x: untyped) = echo($$to[type(x)]($$x))
var x: array[0..4, array[0..4, string]] = [ var x: array[0..4, array[0..4, string]] = [
["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"],

View file

@ -48,7 +48,7 @@ proc validEmailAddress*(s: string): bool {.noSideEffect,
"aero", "jobs", "museum": return true "aero", "jobs", "museum": return true
else: return false else: return false
proc parseInt*(s: string, value: var int, validRange: Slice[int]) {. proc parseInt*(s: string, value: var int, validRange: HSlice[int, int]) {.
noSideEffect, rtl, extern: "nmatchParseInt".} = noSideEffect, rtl, extern: "nmatchParseInt".} =
## parses `s` into an integer in the range `validRange`. If successful, ## parses `s` into an integer in the range `validRange`. If successful,
## `value` is modified to contain the result. Otherwise no exception is ## `value` is modified to contain the result. Otherwise no exception is

View file

@ -184,6 +184,8 @@ when not defined(JS):
proc pow*(x, y: float32): float32 {.importc: "powf", header: "<math.h>".} proc pow*(x, y: float32): float32 {.importc: "powf", header: "<math.h>".}
proc pow*(x, y: float64): float64 {.importc: "pow", header: "<math.h>".} proc pow*(x, y: float64): float64 {.importc: "pow", header: "<math.h>".}
## computes x to power raised of y. ## computes x to power raised of y.
##
## To compute power between integers, use `^` e.g. 2 ^ 6
proc erf*(x: float32): float32 {.importc: "erff", header: "<math.h>".} proc erf*(x: float32): float32 {.importc: "erff", header: "<math.h>".}
proc erf*(x: float64): float64 {.importc: "erf", header: "<math.h>".} proc erf*(x: float64): float64 {.importc: "erf", header: "<math.h>".}

View file

@ -496,11 +496,12 @@ proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
addr(namelen)) == -1'i32: addr(namelen)) == -1'i32:
raiseOSError(osLastError()) raiseOSError(osLastError())
# Cannot use INET6_ADDRSTRLEN here, because it's a C define. # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
var buf: array[64, char] result[0] = newString(64)
if inet_ntop(name.sin6_family.cint, if inet_ntop(name.sin6_family.cint,
addr name.sin6_addr, buf.cstring, sizeof(buf).int32).isNil: addr name.sin6_addr, addr result[0][0], (result[0].len+1).int32).isNil:
raiseOSError(osLastError()) raiseOSError(osLastError())
result = ($buf, Port(nativesockets.ntohs(name.sin6_port))) setLen(result[0], result[0].cstring.len)
result[1] = Port(nativesockets.ntohs(name.sin6_port))
else: else:
raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr") raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")
@ -532,11 +533,12 @@ proc getPeerAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
addr(namelen)) == -1'i32: addr(namelen)) == -1'i32:
raiseOSError(osLastError()) raiseOSError(osLastError())
# Cannot use INET6_ADDRSTRLEN here, because it's a C define. # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
var buf: array[64, char] result[0] = newString(64)
if inet_ntop(name.sin6_family.cint, if inet_ntop(name.sin6_family.cint,
addr name.sin6_addr, buf.cstring, sizeof(buf).int32).isNil: addr name.sin6_addr, addr result[0][0], (result[0].len+1).int32).isNil:
raiseOSError(osLastError()) raiseOSError(osLastError())
result = ($buf, Port(nativesockets.ntohs(name.sin6_port))) setLen(result[0], result[0].cstring.len)
result[1] = Port(nativesockets.ntohs(name.sin6_port))
else: else:
raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr") raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")

View file

@ -857,13 +857,16 @@ proc close*(socket: Socket) =
# shutdown i.e not wait for the peers "close notify" alert with a second # shutdown i.e not wait for the peers "close notify" alert with a second
# call to SSLShutdown # call to SSLShutdown
let res = SSLShutdown(socket.sslHandle) let res = SSLShutdown(socket.sslHandle)
SSLFree(socket.sslHandle)
socket.sslHandle = nil
if res == 0: if res == 0:
discard discard
elif res != 1: elif res != 1:
socketError(socket, res) socketError(socket, res)
finally: finally:
when defineSsl:
if socket.isSSL and socket.sslHandle != nil:
SSLFree(socket.sslHandle)
socket.sslHandle = nil
socket.fd.close() socket.fd.close()
proc toCInt*(opt: SOBool): cint = proc toCInt*(opt: SOBool): cint =

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