Merge pull request #3289 from nanoant/patch/fix-whitespace

Patch/fix whitespace
This commit is contained in:
Andreas Rumpf 2015-09-04 23:37:59 +02:00
commit cd9d126a2f
541 changed files with 8539 additions and 8540 deletions

View file

@ -1 +1 @@
This file keeps several tools from deleting this subdirectory. This file keeps several tools from deleting this subdirectory.

View file

@ -1 +1 @@
This file keeps several tools from deleting this subdirectory. This file keeps several tools from deleting this subdirectory.

View file

@ -11,7 +11,7 @@
import import
ast, astalgo, types, trees, intsets, msgs ast, astalgo, types, trees, intsets, msgs
type type
TAnalysisResult* = enum TAnalysisResult* = enum
arNo, arMaybe, arYes arNo, arMaybe, arYes
@ -21,42 +21,42 @@ proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult
proc isPartOfAux(n: PNode, b: PType, marker: var IntSet): TAnalysisResult = proc isPartOfAux(n: PNode, b: PType, marker: var IntSet): TAnalysisResult =
result = arNo result = arNo
case n.kind case n.kind
of nkRecList: of nkRecList:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
result = isPartOfAux(n.sons[i], b, marker) result = isPartOfAux(n.sons[i], b, marker)
if result == arYes: return if result == arYes: return
of nkRecCase: of nkRecCase:
assert(n.sons[0].kind == nkSym) assert(n.sons[0].kind == nkSym)
result = isPartOfAux(n.sons[0], b, marker) result = isPartOfAux(n.sons[0], b, marker)
if result == arYes: return if result == arYes: return
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
case n.sons[i].kind case n.sons[i].kind
of nkOfBranch, nkElse: of nkOfBranch, nkElse:
result = isPartOfAux(lastSon(n.sons[i]), b, marker) result = isPartOfAux(lastSon(n.sons[i]), b, marker)
if result == arYes: return if result == arYes: return
else: internalError("isPartOfAux(record case branch)") else: internalError("isPartOfAux(record case branch)")
of nkSym: of nkSym:
result = isPartOfAux(n.sym.typ, b, marker) result = isPartOfAux(n.sym.typ, b, marker)
else: internalError(n.info, "isPartOfAux()") else: internalError(n.info, "isPartOfAux()")
proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult = proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult =
result = arNo result = arNo
if a == nil or b == nil: return if a == nil or b == nil: return
if containsOrIncl(marker, a.id): return if containsOrIncl(marker, a.id): return
if compareTypes(a, b, dcEqIgnoreDistinct): return arYes if compareTypes(a, b, dcEqIgnoreDistinct): return arYes
case a.kind case a.kind
of tyObject: of tyObject:
result = isPartOfAux(a.sons[0], b, marker) result = isPartOfAux(a.sons[0], b, marker)
if result == arNo: result = isPartOfAux(a.n, b, marker) if result == arNo: result = isPartOfAux(a.n, b, marker)
of tyGenericInst, tyDistinct: of tyGenericInst, tyDistinct:
result = isPartOfAux(lastSon(a), b, marker) result = isPartOfAux(lastSon(a), b, marker)
of tyArray, tyArrayConstr, tySet, tyTuple: of tyArray, tyArrayConstr, tySet, tyTuple:
for i in countup(0, sonsLen(a) - 1): for i in countup(0, sonsLen(a) - 1):
result = isPartOfAux(a.sons[i], b, marker) result = isPartOfAux(a.sons[i], b, marker)
if result == arYes: return if result == arYes: return
else: discard else: discard
proc isPartOf(a, b: PType): TAnalysisResult = proc isPartOf(a, b: PType): TAnalysisResult =
## checks iff 'a' can be part of 'b'. Iterates over VALUE types! ## checks iff 'a' can be part of 'b'. Iterates over VALUE types!
var marker = initIntSet() var marker = initIntSet()
# watch out: parameters reversed because I'm too lazy to change the code... # watch out: parameters reversed because I'm too lazy to change the code...
@ -70,14 +70,14 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
## type. Since however type analysis is more expensive, we perform it only ## type. Since however type analysis is more expensive, we perform it only
## if necessary. ## if necessary.
## ##
## cases: ## cases:
## ##
## YES-cases: ## YES-cases:
## x <| x # for general trees ## x <| x # for general trees
## x[] <| x ## x[] <| x
## x[i] <| x ## x[i] <| x
## x.f <| x ## x.f <| x
## ##
## NO-cases: ## NO-cases:
## x !<| y # depending on type and symbol kind ## x !<| y # depending on type and symbol kind
## x[constA] !<| x[constB] ## x[constA] !<| x[constB]
@ -88,16 +88,16 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
## ##
## x[] ?<| y[] iff compatible type ## x[] ?<| y[] iff compatible type
## ##
## ##
## x[] ?<| y depending on type ## x[] ?<| y depending on type
## ##
if a.kind == b.kind: if a.kind == b.kind:
case a.kind case a.kind
of nkSym: of nkSym:
const varKinds = {skVar, skTemp, skProc} const varKinds = {skVar, skTemp, skProc}
# same symbol: aliasing: # same symbol: aliasing:
if a.sym.id == b.sym.id: result = arYes if a.sym.id == b.sym.id: result = arYes
elif a.sym.kind in varKinds or b.sym.kind in varKinds: elif a.sym.kind in varKinds or b.sym.kind in varKinds:
# actually, a param could alias a var but we know that cannot happen # actually, a param could alias a var but we know that cannot happen
# here. XXX make this more generic # here. XXX make this more generic
result = arNo result = arNo
@ -110,11 +110,11 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
if len(a) >= 2 and len(b) >= 2: if len(a) >= 2 and len(b) >= 2:
# array accesses: # array accesses:
if result == arYes and isDeepConstExpr(a[1]) and isDeepConstExpr(b[1]): if result == arYes and isDeepConstExpr(a[1]) and isDeepConstExpr(b[1]):
# we know it's the same array and we have 2 constant indexes; # we know it's the same array and we have 2 constant indexes;
# if they are # if they are
var x = if a[1].kind == nkHiddenStdConv: a[1][1] else: a[1] var x = if a[1].kind == nkHiddenStdConv: a[1][1] else: a[1]
var y = if b[1].kind == nkHiddenStdConv: b[1][1] else: b[1] var y = if b[1].kind == nkHiddenStdConv: b[1][1] else: b[1]
if sameValue(x, y): result = arYes if sameValue(x, y): result = arYes
else: result = arNo else: result = arNo
# else: maybe and no are accurate # else: maybe and no are accurate
@ -122,7 +122,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
# pointer derefs: # pointer derefs:
if result != arYes: if result != arYes:
if isPartOf(a.typ, b.typ) != arNo: result = arMaybe if isPartOf(a.typ, b.typ) != arNo: result = arMaybe
of nkDotExpr: of nkDotExpr:
result = isPartOf(a[0], b[0]) result = isPartOf(a[0], b[0])
if result != arNo: if result != arNo:
@ -135,7 +135,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
# weaken because of indirection: # weaken because of indirection:
if result != arYes: if result != arYes:
if isPartOf(a.typ, b.typ) != arNo: result = arMaybe if isPartOf(a.typ, b.typ) != arNo: result = arMaybe
of nkHiddenStdConv, nkHiddenSubConv, nkConv: of nkHiddenStdConv, nkHiddenSubConv, nkConv:
result = isPartOf(a[1], b[1]) result = isPartOf(a[1], b[1])
of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
@ -144,7 +144,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
# Calls return a new location, so a default of ``arNo`` is fine. # Calls return a new location, so a default of ``arNo`` is fine.
else: else:
# go down recursively; this is quite demanding: # go down recursively; this is quite demanding:
const const
Ix0Kinds = {nkDotExpr, nkBracketExpr, nkObjUpConv, nkObjDownConv, Ix0Kinds = {nkDotExpr, nkBracketExpr, nkObjUpConv, nkObjDownConv,
nkCheckedFieldExpr} nkCheckedFieldExpr}
Ix1Kinds = {nkHiddenStdConv, nkHiddenSubConv, nkConv} Ix1Kinds = {nkHiddenStdConv, nkHiddenSubConv, nkConv}
@ -153,17 +153,17 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
of Ix0Kinds: of Ix0Kinds:
# a* !<| b.f iff a* !<| b # a* !<| b.f iff a* !<| b
result = isPartOf(a, b[0]) result = isPartOf(a, b[0])
of DerefKinds: of DerefKinds:
# a* !<| b[] iff # a* !<| b[] iff
if isPartOf(a.typ, b.typ) != arNo: if isPartOf(a.typ, b.typ) != arNo:
result = isPartOf(a, b[0]) result = isPartOf(a, b[0])
if result == arNo: result = arMaybe if result == arNo: result = arMaybe
of Ix1Kinds: of Ix1Kinds:
# a* !<| T(b) iff a* !<| b # a* !<| T(b) iff a* !<| b
result = isPartOf(a, b[1]) result = isPartOf(a, b[1])
of nkSym: of nkSym:
# b is an atom, so we have to check a: # b is an atom, so we have to check a:
case a.kind case a.kind
@ -172,7 +172,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
result = isPartOf(a[0], b) result = isPartOf(a[0], b)
of Ix1Kinds: of Ix1Kinds:
result = isPartOf(a[1], b) result = isPartOf(a[1], b)
of DerefKinds: of DerefKinds:
if isPartOf(a.typ, b.typ) != arNo: if isPartOf(a.typ, b.typ) != arNo:
result = isPartOf(a[0], b) result = isPartOf(a[0], b)

View file

@ -10,12 +10,12 @@
# this unit handles Nim sets; it implements bit sets # this unit handles Nim sets; it implements bit sets
# the code here should be reused in the Nim standard library # the code here should be reused in the Nim standard library
type type
TBitSet* = seq[int8] # we use byte here to avoid issues with TBitSet* = seq[int8] # we use byte here to avoid issues with
# cross-compiling; uint would be more efficient # cross-compiling; uint would be more efficient
# however # however
const const
ElemSize* = sizeof(int8) * 8 ElemSize* = sizeof(int8) * 8
proc bitSetInit*(b: var TBitSet, length: int) proc bitSetInit*(b: var TBitSet, length: int)
@ -30,42 +30,42 @@ proc bitSetEquals*(x, y: TBitSet): bool
proc bitSetContains*(x, y: TBitSet): bool proc bitSetContains*(x, y: TBitSet): bool
# implementation # implementation
proc bitSetIn(x: TBitSet, e: BiggestInt): bool = proc bitSetIn(x: TBitSet, e: BiggestInt): bool =
result = (x[int(e div ElemSize)] and toU8(int(1 shl (e mod ElemSize)))) != result = (x[int(e div ElemSize)] and toU8(int(1 shl (e mod ElemSize)))) !=
toU8(0) toU8(0)
proc bitSetIncl(x: var TBitSet, elem: BiggestInt) = proc bitSetIncl(x: var TBitSet, elem: BiggestInt) =
assert(elem >= 0) assert(elem >= 0)
x[int(elem div ElemSize)] = x[int(elem div ElemSize)] or x[int(elem div ElemSize)] = x[int(elem div ElemSize)] or
toU8(int(1 shl (elem mod ElemSize))) toU8(int(1 shl (elem mod ElemSize)))
proc bitSetExcl(x: var TBitSet, elem: BiggestInt) = proc bitSetExcl(x: var TBitSet, elem: BiggestInt) =
x[int(elem div ElemSize)] = x[int(elem div ElemSize)] and x[int(elem div ElemSize)] = x[int(elem div ElemSize)] and
not toU8(int(1 shl (elem mod ElemSize))) not toU8(int(1 shl (elem mod ElemSize)))
proc bitSetInit(b: var TBitSet, length: int) = proc bitSetInit(b: var TBitSet, length: int) =
newSeq(b, length) newSeq(b, length)
proc bitSetUnion(x: var TBitSet, y: TBitSet) = proc bitSetUnion(x: var TBitSet, y: TBitSet) =
for i in countup(0, high(x)): x[i] = x[i] or y[i] for i in countup(0, high(x)): x[i] = x[i] or y[i]
proc bitSetDiff(x: var TBitSet, y: TBitSet) = proc bitSetDiff(x: var TBitSet, y: TBitSet) =
for i in countup(0, high(x)): x[i] = x[i] and not y[i] for i in countup(0, high(x)): x[i] = x[i] and not y[i]
proc bitSetSymDiff(x: var TBitSet, y: TBitSet) = proc bitSetSymDiff(x: var TBitSet, y: TBitSet) =
for i in countup(0, high(x)): x[i] = x[i] xor y[i] for i in countup(0, high(x)): x[i] = x[i] xor y[i]
proc bitSetIntersect(x: var TBitSet, y: TBitSet) = proc bitSetIntersect(x: var TBitSet, y: TBitSet) =
for i in countup(0, high(x)): x[i] = x[i] and y[i] for i in countup(0, high(x)): x[i] = x[i] and y[i]
proc bitSetEquals(x, y: TBitSet): bool = proc bitSetEquals(x, y: TBitSet): bool =
for i in countup(0, high(x)): for i in countup(0, high(x)):
if x[i] != y[i]: if x[i] != y[i]:
return false return false
result = true result = true
proc bitSetContains(x, y: TBitSet): bool = proc bitSetContains(x, y: TBitSet): bool =
for i in countup(0, high(x)): for i in countup(0, high(x)):
if (x[i] and not y[i]) != int8(0): if (x[i] and not y[i]) != int8(0):
return false return false
result = true result = true

View file

@ -30,39 +30,39 @@ type
# #
# This is a good compromise between correctness and brevity. ;-) # This is a good compromise between correctness and brevity. ;-)
const const
cb64 = [ cb64 = [
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
"O", "P", "Q", "R", "S", "T" "U", "V", "W", "X", "Y", "Z", "O", "P", "Q", "R", "S", "T" "U", "V", "W", "X", "Y", "Z",
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
"o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"_A", "_B"] "_A", "_B"]
proc toBase64a(s: cstring, len: int): string = proc toBase64a(s: cstring, len: int): string =
## encodes `s` into base64 representation. After `lineLen` characters, a ## encodes `s` into base64 representation. After `lineLen` characters, a
## `newline` is added. ## `newline` is added.
result = newStringOfCap(((len + 2) div 3) * 4) result = newStringOfCap(((len + 2) div 3) * 4)
var i = 0 var i = 0
while i < s.len - 2: while i < s.len - 2:
let a = ord(s[i]) let a = ord(s[i])
let b = ord(s[i+1]) let b = ord(s[i+1])
let c = ord(s[i+2]) let c = ord(s[i+2])
result.add cb64[a shr 2] result.add cb64[a shr 2]
result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)] result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
result.add cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)] result.add cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
result.add cb64[c and 0x3F] result.add cb64[c and 0x3F]
inc(i, 3) inc(i, 3)
if i < s.len-1: if i < s.len-1:
let a = ord(s[i]) let a = ord(s[i])
let b = ord(s[i+1]) let b = ord(s[i+1])
result.add cb64[a shr 2] result.add cb64[a shr 2]
result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)] result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
result.add cb64[((b and 0x0F) shl 2)] result.add cb64[((b and 0x0F) shl 2)]
elif i < s.len: elif i < s.len:
let a = ord(s[i]) let a = ord(s[i])
result.add cb64[a shr 2] result.add cb64[a shr 2]
result.add cb64[(a and 3) shl 4] result.add cb64[(a and 3) shl 4]
proc toBase64a(u: TUid): string = toBase64a(cast[cstring](u), sizeof(u)) proc toBase64a(u: TUid): string = toBase64a(cast[cstring](u), sizeof(u))
@ -73,7 +73,7 @@ proc hashSym(c: var MD5Context, s: PSym) =
c &= ":anon" c &= ":anon"
else: else:
var it = s.owner var it = s.owner
while it != nil: while it != nil:
hashSym(c, it) hashSym(c, it)
c &= "." c &= "."
it = s.owner it = s.owner
@ -106,18 +106,18 @@ proc hashTree(c: var MD5Context, n: PNode) =
proc hashType(c: var MD5Context, t: PType) = proc hashType(c: var MD5Context, t: PType) =
# modelled after 'typeToString' # modelled after 'typeToString'
if t == nil: if t == nil:
c &= "\254" c &= "\254"
return return
var k = t.kind var k = t.kind
md5Update(c, cast[cstring](addr(k)), 1) md5Update(c, cast[cstring](addr(k)), 1)
if t.sym != nil and sfAnon notin t.sym.flags: if t.sym != nil and sfAnon notin t.sym.flags:
# t.n for literals, but not for e.g. objects! # t.n for literals, but not for e.g. objects!
if t.kind in {tyFloat, tyInt}: c.hashNode(t.n) if t.kind in {tyFloat, tyInt}: c.hashNode(t.n)
c.hashSym(t.sym) c.hashSym(t.sym)
case t.kind case t.kind
of tyGenericBody, tyGenericInst, tyGenericInvocation: of tyGenericBody, tyGenericInst, tyGenericInvocation:
for i in countup(0, sonsLen(t) -1 -ord(t.kind != tyGenericInvocation)): for i in countup(0, sonsLen(t) -1 -ord(t.kind != tyGenericInvocation)):
@ -135,10 +135,10 @@ proc hashType(c: var MD5Context, t: PType) =
of tyArrayConstr: of tyArrayConstr:
c.hashTree(t.sons[0].n) c.hashTree(t.sons[0].n)
c.hashType(t.sons[1]) c.hashType(t.sons[1])
of tyTuple: of tyTuple:
if t.n != nil: if t.n != nil:
assert(sonsLen(t.n) == sonsLen(t)) assert(sonsLen(t.n) == sonsLen(t))
for i in countup(0, sonsLen(t.n) - 1): for i in countup(0, sonsLen(t.n) - 1):
assert(t.n.sons[i].kind == nkSym) assert(t.n.sons[i].kind == nkSym)
c &= t.n.sons[i].sym.name.s c &= t.n.sons[i].sym.name.s
c &= ":" c &= ":"
@ -184,18 +184,18 @@ proc pushSym(w: PRodWriter, s: PSym) =
if iiTableGet(w.index.tab, s.id) == InvalidKey: if iiTableGet(w.index.tab, s.id) == InvalidKey:
w.sstack.add(s) w.sstack.add(s)
proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
result: var string) = result: var string) =
if n == nil: if n == nil:
# nil nodes have to be stored too: # nil nodes have to be stored too:
result.add("()") result.add("()")
return return
result.add('(') result.add('(')
encodeVInt(ord(n.kind), result) encodeVInt(ord(n.kind), result)
# we do not write comments for now # we do not write comments for now
# Line information takes easily 20% or more of the filesize! Therefore we # Line information takes easily 20% or more of the filesize! Therefore we
# omit line information if it is the same as the father's line information: # omit line information if it is the same as the father's line information:
if fInfo.fileIndex != n.info.fileIndex: if fInfo.fileIndex != n.info.fileIndex:
result.add('?') result.add('?')
encodeVInt(n.info.col, result) encodeVInt(n.info.col, result)
result.add(',') result.add(',')
@ -211,7 +211,7 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
result.add('?') result.add('?')
encodeVInt(n.info.col, result) encodeVInt(n.info.col, result)
var f = n.flags * PersistentNodeFlags var f = n.flags * PersistentNodeFlags
if f != {}: if f != {}:
result.add('$') result.add('$')
encodeVInt(cast[int32](f), result) encodeVInt(cast[int32](f), result)
if n.typ != nil: if n.typ != nil:
@ -219,16 +219,16 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
encodeVInt(n.typ.id, result) encodeVInt(n.typ.id, result)
pushType(w, n.typ) pushType(w, n.typ)
case n.kind case n.kind
of nkCharLit..nkInt64Lit: of nkCharLit..nkInt64Lit:
if n.intVal != 0: if n.intVal != 0:
result.add('!') result.add('!')
encodeVBiggestInt(n.intVal, result) encodeVBiggestInt(n.intVal, result)
of nkFloatLit..nkFloat64Lit: of nkFloatLit..nkFloat64Lit:
if n.floatVal != 0.0: if n.floatVal != 0.0:
result.add('!') result.add('!')
encodeStr($n.floatVal, result) encodeStr($n.floatVal, result)
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
if n.strVal != "": if n.strVal != "":
result.add('!') result.add('!')
encodeStr(n.strVal, result) encodeStr(n.strVal, result)
of nkIdent: of nkIdent:
@ -239,7 +239,7 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
encodeVInt(n.sym.id, result) encodeVInt(n.sym.id, result)
pushSym(w, n.sym) pushSym(w, n.sym)
else: else:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
encodeNode(w, n.info, n.sons[i], result) encodeNode(w, n.info, n.sons[i], result)
add(result, ')') add(result, ')')
@ -268,9 +268,9 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
setLen(result, oldLen) setLen(result, oldLen)
else: else:
add(result, '>') add(result, '>')
proc encodeType(w: PRodWriter, t: PType, result: var string) = proc encodeType(w: PRodWriter, t: PType, result: var string) =
if t == nil: if t == nil:
# nil nodes have to be stored too: # nil nodes have to be stored too:
result.add("[]") result.add("[]")
return return
@ -282,38 +282,38 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
encodeVInt(ord(t.kind), result) encodeVInt(ord(t.kind), result)
add(result, '+') add(result, '+')
encodeVInt(t.id, result) encodeVInt(t.id, result)
if t.n != nil: if t.n != nil:
encodeNode(w, unknownLineInfo(), t.n, result) encodeNode(w, unknownLineInfo(), t.n, result)
if t.flags != {}: if t.flags != {}:
add(result, '$') add(result, '$')
encodeVInt(cast[int32](t.flags), result) encodeVInt(cast[int32](t.flags), result)
if t.callConv != low(t.callConv): if t.callConv != low(t.callConv):
add(result, '?') add(result, '?')
encodeVInt(ord(t.callConv), result) encodeVInt(ord(t.callConv), result)
if t.owner != nil: if t.owner != nil:
add(result, '*') add(result, '*')
encodeVInt(t.owner.id, result) encodeVInt(t.owner.id, result)
pushSym(w, t.owner) pushSym(w, t.owner)
if t.sym != nil: if t.sym != nil:
add(result, '&') add(result, '&')
encodeVInt(t.sym.id, result) encodeVInt(t.sym.id, result)
pushSym(w, t.sym) pushSym(w, t.sym)
if t.size != - 1: if t.size != - 1:
add(result, '/') add(result, '/')
encodeVBiggestInt(t.size, result) encodeVBiggestInt(t.size, result)
if t.align != 2: if t.align != 2:
add(result, '=') add(result, '=')
encodeVInt(t.align, result) encodeVInt(t.align, result)
encodeLoc(w, t.loc, result) encodeLoc(w, t.loc, result)
for i in countup(0, sonsLen(t) - 1): for i in countup(0, sonsLen(t) - 1):
if t.sons[i] == nil: if t.sons[i] == nil:
add(result, "^()") add(result, "^()")
else: else:
add(result, '^') add(result, '^')
encodeVInt(t.sons[i].id, result) encodeVInt(t.sons[i].id, result)
pushType(w, t.sons[i]) pushType(w, t.sons[i])
proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) = proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
add(result, '|') add(result, '|')
encodeVInt(ord(lib.kind), result) encodeVInt(ord(lib.kind), result)
add(result, '|') add(result, '|')
@ -352,10 +352,10 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
if s.magic != mNone: if s.magic != mNone:
result.add('@') result.add('@')
encodeVInt(ord(s.magic), result) encodeVInt(ord(s.magic), result)
if s.options != w.options: if s.options != w.options:
result.add('!') result.add('!')
encodeVInt(cast[int32](s.options), result) encodeVInt(cast[int32](s.options), result)
if s.position != 0: if s.position != 0:
result.add('%') result.add('%')
encodeVInt(s.position, result) encodeVInt(s.position, result)
if s.offset != - 1: if s.offset != - 1:
@ -383,7 +383,7 @@ proc createDb() =
fullpath varchar(256) not null, fullpath varchar(256) not null,
interfHash varchar(256) not null, interfHash varchar(256) not null,
fullHash varchar(256) not null, fullHash varchar(256) not null,
created timestamp not null default (DATETIME('now')), created timestamp not null default (DATETIME('now')),
);""") );""")
@ -397,7 +397,7 @@ proc createDb() =
foreign key (module) references module(id) foreign key (module) references module(id)
);""") );""")
db.exec(sql""" db.exec(sql"""
create table if not exists Type( create table if not exists Type(
id integer primary key, id integer primary key,

View file

@ -9,33 +9,33 @@
## This module implements code generation for multi methods. ## This module implements code generation for multi methods.
import import
intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys, intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys,
sempass2, strutils sempass2, strutils
proc genConv(n: PNode, d: PType, downcast: bool): PNode = proc genConv(n: PNode, d: PType, downcast: bool): PNode =
var dest = skipTypes(d, abstractPtrs) var dest = skipTypes(d, abstractPtrs)
var source = skipTypes(n.typ, abstractPtrs) var source = skipTypes(n.typ, abstractPtrs)
if (source.kind == tyObject) and (dest.kind == tyObject): if (source.kind == tyObject) and (dest.kind == tyObject):
var diff = inheritanceDiff(dest, source) var diff = inheritanceDiff(dest, source)
if diff == high(int): internalError(n.info, "cgmeth.genConv") if diff == high(int): internalError(n.info, "cgmeth.genConv")
if diff < 0: if diff < 0:
result = newNodeIT(nkObjUpConv, n.info, d) result = newNodeIT(nkObjUpConv, n.info, d)
addSon(result, n) addSon(result, n)
if downcast: internalError(n.info, "cgmeth.genConv: no upcast allowed") if downcast: internalError(n.info, "cgmeth.genConv: no upcast allowed")
elif diff > 0: elif diff > 0:
result = newNodeIT(nkObjDownConv, n.info, d) result = newNodeIT(nkObjDownConv, n.info, d)
addSon(result, n) addSon(result, n)
if not downcast: if not downcast:
internalError(n.info, "cgmeth.genConv: no downcast allowed") internalError(n.info, "cgmeth.genConv: no downcast allowed")
else: else:
result = n result = n
else: else:
result = n result = n
proc methodCall*(n: PNode): PNode = proc methodCall*(n: PNode): PNode =
result = n result = n
# replace ordinary method by dispatcher method: # replace ordinary method by dispatcher method:
var disp = lastSon(result.sons[0].sym.ast).sym var disp = lastSon(result.sons[0].sym.ast).sym
assert sfDispatcher in disp.flags assert sfDispatcher in disp.flags
result.sons[0].sym = disp result.sons[0].sym = disp
@ -47,23 +47,23 @@ proc methodCall*(n: PNode): PNode =
var var
gMethods: seq[tuple[methods: TSymSeq, dispatcher: PSym]] = @[] gMethods: seq[tuple[methods: TSymSeq, dispatcher: PSym]] = @[]
proc sameMethodBucket(a, b: PSym): bool = proc sameMethodBucket(a, b: PSym): bool =
result = false result = false
if a.name.id != b.name.id: return if a.name.id != b.name.id: return
if sonsLen(a.typ) != sonsLen(b.typ): if sonsLen(a.typ) != sonsLen(b.typ):
return # check for return type: return # check for return type:
if not sameTypeOrNil(a.typ.sons[0], b.typ.sons[0]): return if not sameTypeOrNil(a.typ.sons[0], b.typ.sons[0]): return
for i in countup(1, sonsLen(a.typ) - 1): for i in countup(1, sonsLen(a.typ) - 1):
var aa = a.typ.sons[i] var aa = a.typ.sons[i]
var bb = b.typ.sons[i] var bb = b.typ.sons[i]
while true: while true:
aa = skipTypes(aa, {tyGenericInst}) aa = skipTypes(aa, {tyGenericInst})
bb = skipTypes(bb, {tyGenericInst}) bb = skipTypes(bb, {tyGenericInst})
if (aa.kind == bb.kind) and (aa.kind in {tyVar, tyPtr, tyRef}): if (aa.kind == bb.kind) and (aa.kind in {tyVar, tyPtr, tyRef}):
aa = aa.lastSon aa = aa.lastSon
bb = bb.lastSon bb = bb.lastSon
else: else:
break break
if sameType(aa, bb) or if sameType(aa, bb) or
(aa.kind == tyObject) and (bb.kind == tyObject) and (aa.kind == tyObject) and (bb.kind == tyObject) and
(inheritanceDiff(bb, aa) < 0): (inheritanceDiff(bb, aa) < 0):
@ -140,7 +140,7 @@ proc methodDef*(s: PSym, fromCache: bool) =
attachDispatcher(s, lastSon(disp.ast)) attachDispatcher(s, lastSon(disp.ast))
fixupDispatcher(s, disp) fixupDispatcher(s, disp)
when useEffectSystem: checkMethodEffects(disp, s) when useEffectSystem: checkMethodEffects(disp, s)
return return
# create a new dispatcher: # create a new dispatcher:
add(gMethods, (methods: @[s], dispatcher: createDispatcher(s))) add(gMethods, (methods: @[s], dispatcher: createDispatcher(s)))
if fromCache: if fromCache:
@ -154,35 +154,35 @@ proc relevantCol(methods: TSymSeq, col: int): bool =
let t2 = skipTypes(methods[i].typ.sons[col], skipPtrs) let t2 = skipTypes(methods[i].typ.sons[col], skipPtrs)
if not sameType(t2, t): if not sameType(t2, t):
return true return true
proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int = proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int =
for col in countup(1, sonsLen(a.typ) - 1): for col in countup(1, sonsLen(a.typ) - 1):
if contains(relevantCols, col): if contains(relevantCols, col):
var aa = skipTypes(a.typ.sons[col], skipPtrs) var aa = skipTypes(a.typ.sons[col], skipPtrs)
var bb = skipTypes(b.typ.sons[col], skipPtrs) var bb = skipTypes(b.typ.sons[col], skipPtrs)
var d = inheritanceDiff(aa, bb) var d = inheritanceDiff(aa, bb)
if (d != high(int)): if (d != high(int)):
return d return d
proc sortBucket(a: var TSymSeq, relevantCols: IntSet) = proc sortBucket(a: var TSymSeq, relevantCols: IntSet) =
# we use shellsort here; fast and simple # we use shellsort here; fast and simple
var n = len(a) var n = len(a)
var h = 1 var h = 1
while true: while true:
h = 3 * h + 1 h = 3 * h + 1
if h > n: break if h > n: break
while true: while true:
h = h div 3 h = h div 3
for i in countup(h, n - 1): for i in countup(h, n - 1):
var v = a[i] var v = a[i]
var j = i var j = i
while cmpSignatures(a[j - h], v, relevantCols) >= 0: while cmpSignatures(a[j - h], v, relevantCols) >= 0:
a[j] = a[j - h] a[j] = a[j - h]
j = j - h j = j - h
if j < h: break if j < h: break
a[j] = v a[j] = v
if h == 1: break if h == 1: break
proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym = proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
var base = lastSon(methods[0].ast).sym var base = lastSon(methods[0].ast).sym
result = base result = base
@ -199,7 +199,7 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
addSon(isn, newSymNode(iss)) addSon(isn, newSymNode(iss))
addSon(isn, newSymNode(base.typ.n.sons[col].sym)) addSon(isn, newSymNode(base.typ.n.sons[col].sym))
addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col])) addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col]))
if cond != nil: if cond != nil:
var a = newNodeIT(nkCall, base.info, getSysType(tyBool)) var a = newNodeIT(nkCall, base.info, getSysType(tyBool))
addSon(a, newSymNode(ands)) addSon(a, newSymNode(ands))
addSon(a, cond) addSon(a, cond)
@ -209,8 +209,8 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
cond = isn cond = isn
var call = newNodeI(nkCall, base.info) var call = newNodeI(nkCall, base.info)
addSon(call, newSymNode(curr)) addSon(call, newSymNode(curr))
for col in countup(1, paramLen - 1): for col in countup(1, paramLen - 1):
addSon(call, genConv(newSymNode(base.typ.n.sons[col].sym), addSon(call, genConv(newSymNode(base.typ.n.sons[col].sym),
curr.typ.sons[col], false)) curr.typ.sons[col], false))
var ret: PNode var ret: PNode
if base.typ.sons[0] != nil: if base.typ.sons[0] != nil:
@ -230,11 +230,11 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
disp = ret disp = ret
result.ast.sons[bodyPos] = disp result.ast.sons[bodyPos] = disp
proc generateMethodDispatchers*(): PNode = proc generateMethodDispatchers*(): PNode =
result = newNode(nkStmtList) result = newNode(nkStmtList)
for bucket in countup(0, len(gMethods) - 1): for bucket in countup(0, len(gMethods) - 1):
var relevantCols = initIntSet() var relevantCols = initIntSet()
for col in countup(1, sonsLen(gMethods[bucket].methods[0].typ) - 1): for col in countup(1, sonsLen(gMethods[bucket].methods[0].typ) - 1):
if relevantCol(gMethods[bucket].methods, col): incl(relevantCols, col) if relevantCol(gMethods[bucket].methods, col): incl(relevantCols, col)
sortBucket(gMethods[bucket].methods, relevantCols) sortBucket(gMethods[bucket].methods, relevantCols)
addSon(result, addSon(result,

View file

@ -10,10 +10,10 @@
# This module implements a new documentation generator that runs after # This module implements a new documentation generator that runs after
# semantic checking. # semantic checking.
import import
os, options, ast, astalgo, msgs, ropes, idents, passes, docgen os, options, ast, astalgo, msgs, ropes, idents, passes, docgen
type type
TGen = object of TPassContext TGen = object of TPassContext
doc: PDoc doc: PDoc
module: PSym module: PSym
@ -29,12 +29,12 @@ proc close(p: PPassContext, n: PNode): PNode =
except IOError: except IOError:
discard discard
proc processNode(c: PPassContext, n: PNode): PNode = proc processNode(c: PPassContext, n: PNode): PNode =
result = n result = n
var g = PGen(c) var g = PGen(c)
generateDoc(g.doc, n) generateDoc(g.doc, n)
proc myOpen(module: PSym): PPassContext = proc myOpen(module: PSym): PPassContext =
var g: PGen var g: PGen
new(g) new(g)
g.module = module g.module = module
@ -45,5 +45,5 @@ proc myOpen(module: PSym): PPassContext =
const docgen2Pass* = makePass(open = myOpen, process = processNode, close = close) const docgen2Pass* = makePass(open = myOpen, process = processNode, close = close)
proc finishDoc2Pass*(project: string) = proc finishDoc2Pass*(project: string) =
discard discard

View file

@ -31,7 +31,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
for y in items(x): result.add(y) for y in items(x): result.add(y)
else: else:
result.add copyTree(x) result.add copyTree(x)
case templ.kind case templ.kind
of nkSym: of nkSym:
var s = templ.sym var s = templ.sym
@ -81,7 +81,7 @@ proc evalTemplateArgs(n: PNode, s: PSym): PNode =
if totalParams > expectedRegularParams + genericParams: if totalParams > expectedRegularParams + genericParams:
globalError(n.info, errWrongNumberOfArguments) globalError(n.info, errWrongNumberOfArguments)
result = newNodeI(nkArgList, n.info) result = newNodeI(nkArgList, n.info)
for i in 1 .. givenRegularParams: for i in 1 .. givenRegularParams:
result.addSon n.sons[i] result.addSon n.sons[i]
@ -96,7 +96,7 @@ proc evalTemplateArgs(n: PNode, s: PSym): PNode =
addSon(result, ast.emptyNode) addSon(result, ast.emptyNode)
else: else:
addSon(result, default.copyTree) addSon(result, default.copyTree)
# add any generic paramaters # add any generic paramaters
for i in 1 .. genericParams: for i in 1 .. genericParams:
result.addSon n.sons[givenRegularParams + i] result.addSon n.sons[givenRegularParams + i]

View file

@ -9,18 +9,18 @@
# This module implements Nim's standard template filter. # This module implements Nim's standard template filter.
import 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 proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode): PLLStream
# #! template(subsChar='$', metaChar='#') | standard(version="0.7.2") # #! template(subsChar='$', metaChar='#') | standard(version="0.7.2")
# implementation # implementation
type type
TParseState = enum TParseState = enum
psDirective, psTempl psDirective, psTempl
TTmplParser{.final.} = object TTmplParser{.final.} = object
inp: PLLStream inp: PLLStream
state: TParseState state: TParseState
info: TLineInfo info: TLineInfo
@ -33,18 +33,18 @@ type
pendingExprLine: bool pendingExprLine: bool
const const
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'} PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'}
proc newLine(p: var TTmplParser) = proc newLine(p: var TTmplParser) =
llStreamWrite(p.outp, repeat(')', p.emitPar)) llStreamWrite(p.outp, repeat(')', p.emitPar))
p.emitPar = 0 p.emitPar = 0
if p.info.line > int16(1): llStreamWrite(p.outp, "\n") if p.info.line > int16(1): llStreamWrite(p.outp, "\n")
if p.pendingExprLine: if p.pendingExprLine:
llStreamWrite(p.outp, spaces(2)) llStreamWrite(p.outp, spaces(2))
p.pendingExprLine = false p.pendingExprLine = false
proc scanPar(p: var TTmplParser, d: int) = proc scanPar(p: var TTmplParser, d: int) =
var i = d var i = d
while true: while true:
case p.x[i] case p.x[i]
@ -58,44 +58,44 @@ proc scanPar(p: var TTmplParser, d: int) =
else: discard else: discard
inc(i) inc(i)
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
proc parseLine(p: var TTmplParser) = proc parseLine(p: var TTmplParser) =
var var
d, j, curly: int d, j, curly: int
keyw: string keyw: string
j = 0 j = 0
while p.x[j] == ' ': inc(j) while p.x[j] == ' ': inc(j)
if (p.x[0] == p.nimDirective) and (p.x[0 + 1] == '!'): if (p.x[0] == p.nimDirective) and (p.x[0 + 1] == '!'):
newLine(p) newLine(p)
elif (p.x[j] == p.nimDirective): elif (p.x[j] == p.nimDirective):
newLine(p) newLine(p)
inc(j) inc(j)
while p.x[j] == ' ': inc(j) while p.x[j] == ' ': inc(j)
d = j d = j
keyw = "" keyw = ""
while p.x[j] in PatternChars: while p.x[j] in PatternChars:
add(keyw, p.x[j]) add(keyw, p.x[j])
inc(j) inc(j)
scanPar(p, j) scanPar(p, j)
p.pendingExprLine = withInExpr(p) or llstream.endsWithOpr(p.x) p.pendingExprLine = withInExpr(p) or llstream.endsWithOpr(p.x)
case whichKeyword(keyw) case whichKeyword(keyw)
of wEnd: of wEnd:
if p.indent >= 2: if p.indent >= 2:
dec(p.indent, 2) dec(p.indent, 2)
else: else:
p.info.col = int16(j) p.info.col = int16(j)
localError(p.info, errXNotAllowedHere, "end") localError(p.info, errXNotAllowedHere, "end")
llStreamWrite(p.outp, spaces(p.indent)) llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, "#end") llStreamWrite(p.outp, "#end")
of wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator, of wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator,
wConverter, wMacro, wTemplate, wMethod: wConverter, wMacro, wTemplate, wMethod:
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 wElif, wOf, wElse, wExcept, wFinally: of wElif, wOf, wElse, wExcept, wFinally:
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 wLet, wVar, wConst, wType:
@ -108,7 +108,7 @@ proc parseLine(p: var TTmplParser) =
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))
p.state = psDirective p.state = psDirective
else: else:
# data line # data line
# reset counters # reset counters
p.par = 0 p.par = 0
@ -116,42 +116,42 @@ proc parseLine(p: var TTmplParser) =
p.bracket = 0 p.bracket = 0
j = 0 j = 0
case p.state case p.state
of psTempl: of psTempl:
# next line of string literal: # next line of string literal:
llStreamWrite(p.outp, p.conc) llStreamWrite(p.outp, p.conc)
llStreamWrite(p.outp, "\n") llStreamWrite(p.outp, "\n")
llStreamWrite(p.outp, spaces(p.indent + 2)) llStreamWrite(p.outp, spaces(p.indent + 2))
llStreamWrite(p.outp, "\"") llStreamWrite(p.outp, "\"")
of psDirective: of psDirective:
newLine(p) newLine(p)
llStreamWrite(p.outp, spaces(p.indent)) llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, p.emit) llStreamWrite(p.outp, p.emit)
llStreamWrite(p.outp, "(\"") llStreamWrite(p.outp, "(\"")
inc(p.emitPar) inc(p.emitPar)
p.state = psTempl p.state = psTempl
while true: while true:
case p.x[j] case p.x[j]
of '\0': of '\0':
break break
of '\x01'..'\x1F', '\x80'..'\xFF': of '\x01'..'\x1F', '\x80'..'\xFF':
llStreamWrite(p.outp, "\\x") llStreamWrite(p.outp, "\\x")
llStreamWrite(p.outp, toHex(ord(p.x[j]), 2)) llStreamWrite(p.outp, toHex(ord(p.x[j]), 2))
inc(j) inc(j)
of '\\': of '\\':
llStreamWrite(p.outp, "\\\\") llStreamWrite(p.outp, "\\\\")
inc(j) inc(j)
of '\'': of '\'':
llStreamWrite(p.outp, "\\\'") llStreamWrite(p.outp, "\\\'")
inc(j) inc(j)
of '\"': of '\"':
llStreamWrite(p.outp, "\\\"") llStreamWrite(p.outp, "\\\"")
inc(j) inc(j)
else: else:
if p.x[j] == p.subsChar: if p.x[j] == p.subsChar:
# parse Nim expression: # parse Nim expression:
inc(j) inc(j)
case p.x[j] case p.x[j]
of '{': of '{':
p.info.col = int16(j) p.info.col = int16(j)
llStreamWrite(p.outp, '\"') llStreamWrite(p.outp, '\"')
llStreamWrite(p.outp, p.conc) llStreamWrite(p.outp, p.conc)
@ -159,50 +159,50 @@ proc parseLine(p: var TTmplParser) =
llStreamWrite(p.outp, '(') llStreamWrite(p.outp, '(')
inc(j) inc(j)
curly = 0 curly = 0
while true: while true:
case p.x[j] case p.x[j]
of '\0': of '\0':
localError(p.info, errXExpected, "}") localError(p.info, errXExpected, "}")
break break
of '{': of '{':
inc(j) inc(j)
inc(curly) inc(curly)
llStreamWrite(p.outp, '{') llStreamWrite(p.outp, '{')
of '}': of '}':
inc(j) inc(j)
if curly == 0: break if curly == 0: break
if curly > 0: dec(curly) if curly > 0: dec(curly)
llStreamWrite(p.outp, '}') llStreamWrite(p.outp, '}')
else: else:
llStreamWrite(p.outp, p.x[j]) llStreamWrite(p.outp, p.x[j])
inc(j) inc(j)
llStreamWrite(p.outp, ')') llStreamWrite(p.outp, ')')
llStreamWrite(p.outp, p.conc) llStreamWrite(p.outp, p.conc)
llStreamWrite(p.outp, '\"') llStreamWrite(p.outp, '\"')
of 'a'..'z', 'A'..'Z', '\x80'..'\xFF': of 'a'..'z', 'A'..'Z', '\x80'..'\xFF':
llStreamWrite(p.outp, '\"') llStreamWrite(p.outp, '\"')
llStreamWrite(p.outp, p.conc) llStreamWrite(p.outp, p.conc)
llStreamWrite(p.outp, p.toStr) llStreamWrite(p.outp, p.toStr)
llStreamWrite(p.outp, '(') llStreamWrite(p.outp, '(')
while p.x[j] in PatternChars: while p.x[j] in PatternChars:
llStreamWrite(p.outp, p.x[j]) llStreamWrite(p.outp, p.x[j])
inc(j) inc(j)
llStreamWrite(p.outp, ')') llStreamWrite(p.outp, ')')
llStreamWrite(p.outp, p.conc) llStreamWrite(p.outp, p.conc)
llStreamWrite(p.outp, '\"') llStreamWrite(p.outp, '\"')
else: else:
if p.x[j] == p.subsChar: if p.x[j] == p.subsChar:
llStreamWrite(p.outp, p.subsChar) llStreamWrite(p.outp, p.subsChar)
inc(j) inc(j)
else: else:
p.info.col = int16(j) p.info.col = int16(j)
localError(p.info, errInvalidExpression, "$") localError(p.info, errInvalidExpression, "$")
else: else:
llStreamWrite(p.outp, p.x[j]) llStreamWrite(p.outp, p.x[j])
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

@ -10,7 +10,7 @@
# This module implements Nim's simple filters and helpers for filters. # This module implements Nim's simple filters and helpers for filters.
import 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 filterReplace*(stdin: PLLStream, filename: string, call: PNode): PLLStream
@ -21,40 +21,40 @@ proc strArg*(n: PNode, name: string, pos: int, default: string): string
proc boolArg*(n: PNode, name: string, pos: int, default: bool): bool proc boolArg*(n: PNode, name: string, pos: int, default: bool): bool
# implementation # implementation
proc invalidPragma(n: PNode) = proc invalidPragma(n: PNode) =
localError(n.info, errXNotAllowedHere, renderTree(n, {renderNoComments})) localError(n.info, errXNotAllowedHere, renderTree(n, {renderNoComments}))
proc getArg(n: PNode, name: string, pos: int): PNode = proc getArg(n: PNode, name: string, pos: int): PNode =
result = nil result = nil
if n.kind in {nkEmpty..nkNilLit}: return if n.kind in {nkEmpty..nkNilLit}: return
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
if n.sons[i].kind == nkExprEqExpr: if n.sons[i].kind == nkExprEqExpr:
if n.sons[i].sons[0].kind != nkIdent: invalidPragma(n) if n.sons[i].sons[0].kind != nkIdent: invalidPragma(n)
if identEq(n.sons[i].sons[0].ident, name): if identEq(n.sons[i].sons[0].ident, name):
return n.sons[i].sons[1] return n.sons[i].sons[1]
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 identEq(x.ident, "true"): result = true elif (x.kind == nkIdent) and identEq(x.ident, "true"): result = true
elif (x.kind == nkIdent) and identEq(x.ident, "false"): result = false elif (x.kind == nkIdent) and identEq(x.ident, "false"): 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)
@ -62,13 +62,13 @@ proc filterStrip(stdin: PLLStream, filename: string, call: PNode): PLLStream =
var line = newStringOfCap(80) var line = newStringOfCap(80)
while llStreamReadLine(stdin, line): while llStreamReadLine(stdin, line):
var stripped = strip(line, leading, trailing) var stripped = strip(line, leading, trailing)
if (len(pattern) == 0) or startsWith(stripped, pattern): if (len(pattern) == 0) or startsWith(stripped, pattern):
llStreamWriteln(result, stripped) llStreamWriteln(result, stripped)
else: else:
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

@ -14,11 +14,11 @@ import ast, astalgo
const const
someCmp = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc, someCmp = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
mEqUntracedRef, mLeI, mLeF64, mLeU, mLeU64, mLeEnum, mEqUntracedRef, mLeI, mLeF64, mLeU, mLeU64, mLeEnum,
mLeCh, mLeB, mLePtr, mLtI, mLtF64, mLtU, mLtU64, mLtEnum, mLeCh, mLeB, mLePtr, mLtI, mLtF64, mLtU, mLtU64, mLtEnum,
mLtCh, mLtB, mLtPtr} mLtCh, mLtB, mLtPtr}
proc isCounter(s: PSym): bool {.inline.} = proc isCounter(s: PSym): bool {.inline.} =
s.kind in {skResult, skVar, skLet, skTemp} and s.kind in {skResult, skVar, skLet, skTemp} and
{sfGlobal, sfAddrTaken} * s.flags == {} {sfGlobal, sfAddrTaken} * s.flags == {}
proc isCall(n: PNode): bool {.inline.} = proc isCall(n: PNode): bool {.inline.} =
@ -29,7 +29,7 @@ proc fromSystem(op: PSym): bool = sfSystemModule in getModule(op).flags
proc getCounter(lastStmt: PNode): PSym = proc getCounter(lastStmt: PNode): PSym =
if lastStmt.isCall: if lastStmt.isCall:
let op = lastStmt.sym let op = lastStmt.sym
if op.magic in {mDec, mInc} or if op.magic in {mDec, mInc} or
((op.name.s == "+=" or op.name.s == "-=") and op.fromSystem): ((op.name.s == "+=" or op.name.s == "-=") and op.fromSystem):
if op[1].kind == nkSym and isCounter(op[1].sym): if op[1].kind == nkSym and isCounter(op[1].sym):
result = op[1].sym result = op[1].sym
@ -67,7 +67,7 @@ proc extractForLoop*(loop, fullTree: PNode): ForLoop =
if not cond.isCall: return if not cond.isCall: return
if cond[0].sym.magic notin someCmp: return if cond[0].sym.magic notin someCmp: return
var lastStmt = loop[1] var lastStmt = loop[1]
while lastStmt.kind in {nkStmtList, nkStmtListExpr}: while lastStmt.kind in {nkStmtList, nkStmtListExpr}:
lastStmt = lastStmt.lastSon lastStmt = lastStmt.lastSon
@ -76,7 +76,7 @@ proc extractForLoop*(loop, fullTree: PNode): ForLoop =
if counter.isNil or counter.ast.isNil: return if counter.isNil or counter.ast.isNil: return
template `=~`(a, b): expr = a.kind == nkSym and a.sym == b template `=~`(a, b): expr = a.kind == nkSym and a.sym == b
if cond[1] =~ counter or cond[2] =~ counter: if cond[1] =~ counter or cond[2] =~ counter:
# ok, now check 'counter' is not used *after* the loop # ok, now check 'counter' is not used *after* the loop
if counterInTree(fullTree, loop, counter): return if counterInTree(fullTree, loop, counter): return

View file

@ -28,7 +28,7 @@ proc evalPattern(c: PContext, n, orig: PNode): PNode =
else: else:
result = semDirectOp(c, n, {}) result = semDirectOp(c, n, {})
if optHints in gOptions and hintPattern in gNotes: if optHints in gOptions and hintPattern in gNotes:
message(orig.info, hintPattern, rule & " --> '" & message(orig.info, hintPattern, rule & " --> '" &
renderTree(result, {renderNoComments}) & "'") renderTree(result, {renderNoComments}) & "'")
proc applyPatterns(c: PContext, n: PNode): PNode = proc applyPatterns(c: PContext, n: PNode): PNode =
@ -68,7 +68,7 @@ proc hlo(c: PContext, n: PNode): PNode =
result = n result = n
else: else:
if n.kind in {nkFastAsgn, nkAsgn, nkIdentDefs, nkVarTuple} and if n.kind in {nkFastAsgn, nkAsgn, nkIdentDefs, nkVarTuple} and
n.sons[0].kind == nkSym and n.sons[0].kind == nkSym and
{sfGlobal, sfPure} * n.sons[0].sym.flags == {sfGlobal, sfPure}: {sfGlobal, sfPure} * n.sons[0].sym.flags == {sfGlobal, sfPure}:
# do not optimize 'var g {.global} = re(...)' again! # do not optimize 'var g {.global} = re(...)' again!
return n return n

View file

@ -11,13 +11,13 @@
# An identifier is a shared immutable string that can be compared by its # An identifier is a shared immutable string that can be compared by its
# id. This module is essential for the compiler's performance. # id. This module is essential for the compiler's performance.
import import
hashes, strutils, etcpriv hashes, strutils, etcpriv
type type
TIdObj* = object of RootObj TIdObj* = object of RootObj
id*: int # unique id; use this for comparisons and not the pointers id*: int # unique id; use this for comparisons and not the pointers
PIdObj* = ref TIdObj PIdObj* = ref TIdObj
PIdent* = ref TIdent PIdent* = ref TIdent
TIdent*{.acyclic.} = object of TIdObj TIdent*{.acyclic.} = object of TIdObj
@ -45,12 +45,12 @@ proc cmpIgnoreStyle(a, b: cstring, blen: int): int =
if aa >= 'A' and aa <= 'Z': aa = chr(ord(aa) + (ord('a') - ord('A'))) if aa >= 'A' and aa <= 'Z': aa = chr(ord(aa) + (ord('a') - ord('A')))
if bb >= 'A' and bb <= 'Z': bb = chr(ord(bb) + (ord('a') - ord('A'))) if bb >= 'A' and bb <= 'Z': bb = chr(ord(bb) + (ord('a') - ord('A')))
result = ord(aa) - ord(bb) result = ord(aa) - ord(bb)
if (result != 0) or (aa == '\0'): break if (result != 0) or (aa == '\0'): break
inc(i) inc(i)
inc(j) inc(j)
if result == 0: if result == 0:
if a[i] != '\0': result = 1 if a[i] != '\0': result = 1
proc cmpExact(a, b: cstring, blen: int): int = proc cmpExact(a, b: cstring, blen: int): int =
var i = 0 var i = 0
var j = 0 var j = 0
@ -59,10 +59,10 @@ proc cmpExact(a, b: cstring, blen: int): int =
var aa = a[i] var aa = a[i]
var bb = b[j] var bb = b[j]
result = ord(aa) - ord(bb) result = ord(aa) - ord(bb)
if (result != 0) or (aa == '\0'): break if (result != 0) or (aa == '\0'): break
inc(i) inc(i)
inc(j) inc(j)
if result == 0: if result == 0:
if a[i] != '\0': result = 1 if a[i] != '\0': result = 1
var wordCounter = 1 var wordCounter = 1
@ -72,14 +72,14 @@ proc getIdent*(identifier: cstring, length: int, h: Hash): PIdent =
result = buckets[idx] result = buckets[idx]
var last: PIdent = nil var last: PIdent = nil
var id = 0 var id = 0
while result != nil: while result != nil:
if cmpExact(cstring(result.s), identifier, length) == 0: if cmpExact(cstring(result.s), identifier, length) == 0:
if last != nil: if last != nil:
# make access to last looked up identifier faster: # make access to last looked up identifier faster:
last.next = result.next last.next = result.next
result.next = buckets[idx] result.next = buckets[idx]
buckets[idx] = result buckets[idx] = result
return return
elif cmpIgnoreStyle(cstring(result.s), identifier, length) == 0: elif cmpIgnoreStyle(cstring(result.s), identifier, length) == 0:
assert((id == 0) or (id == result.id)) assert((id == 0) or (id == result.id))
id = result.id id = result.id
@ -91,20 +91,20 @@ proc getIdent*(identifier: cstring, length: int, h: Hash): PIdent =
for i in countup(0, length - 1): result.s[i] = identifier[i] for i in countup(0, length - 1): result.s[i] = identifier[i]
result.next = buckets[idx] result.next = buckets[idx]
buckets[idx] = result buckets[idx] = result
if id == 0: if id == 0:
inc(wordCounter) inc(wordCounter)
result.id = -wordCounter result.id = -wordCounter
else: else:
result.id = id result.id = id
proc getIdent*(identifier: string): PIdent = proc getIdent*(identifier: string): PIdent =
result = getIdent(cstring(identifier), len(identifier), result = getIdent(cstring(identifier), len(identifier),
hashIgnoreStyle(identifier)) hashIgnoreStyle(identifier))
proc getIdent*(identifier: string, h: Hash): PIdent = proc getIdent*(identifier: string, h: Hash): PIdent =
result = getIdent(cstring(identifier), len(identifier), h) result = getIdent(cstring(identifier), len(identifier), h)
proc identEq*(id: PIdent, name: string): bool = proc identEq*(id: PIdent, name: string): bool =
result = id.id == getIdent(name).id result = id.id == getIdent(name).id
var idAnon* = getIdent":anonymous" var idAnon* = getIdent":anonymous"

View file

@ -10,7 +10,7 @@
# This module implements a generic doubled linked list. # This module implements a generic doubled linked list.
# TODO Remove this and replace it with something sensible # TODO Remove this and replace it with something sensible
import os import os
type type
PListEntry* = ref TListEntry PListEntry* = ref TListEntry
TListEntry* = object of RootObj TListEntry* = object of RootObj
prev*, next*: PListEntry prev*, next*: PListEntry
@ -25,68 +25,68 @@ type
TCompareProc* = proc (entry: PListEntry, closure: pointer): bool {.nimcall.} TCompareProc* = proc (entry: PListEntry, closure: pointer): bool {.nimcall.}
proc initLinkedList*(list: var TLinkedList) = proc initLinkedList*(list: var TLinkedList) =
list.counter = 0 list.counter = 0
list.head = nil list.head = nil
list.tail = nil list.tail = nil
proc append*(list: var TLinkedList, entry: PListEntry) = proc append*(list: var TLinkedList, entry: PListEntry) =
inc(list.counter) inc(list.counter)
entry.next = nil entry.next = nil
entry.prev = list.tail entry.prev = list.tail
if list.tail != nil: if list.tail != nil:
assert(list.tail.next == nil) assert(list.tail.next == nil)
list.tail.next = entry list.tail.next = entry
list.tail = entry list.tail = entry
if list.head == nil: list.head = entry if list.head == nil: list.head = entry
proc contains*(list: TLinkedList, data: string): bool = proc contains*(list: TLinkedList, data: string): bool =
var it = list.head var it = list.head
while it != nil: while it != nil:
if PStrEntry(it).data == data: if PStrEntry(it).data == data:
return true return true
it = it.next it = it.next
proc newStrEntry(data: string): PStrEntry = proc newStrEntry(data: string): PStrEntry =
new(result) new(result)
result.data = data result.data = data
proc appendStr*(list: var TLinkedList, data: string) = proc appendStr*(list: var TLinkedList, data: string) =
append(list, newStrEntry(data)) append(list, newStrEntry(data))
proc includeStr*(list: var TLinkedList, data: string): bool = proc includeStr*(list: var TLinkedList, data: string): bool =
if contains(list, data): return true if contains(list, data): return true
appendStr(list, data) # else: add to list appendStr(list, data) # else: add to list
proc prepend*(list: var TLinkedList, entry: PListEntry) = proc prepend*(list: var TLinkedList, entry: PListEntry) =
inc(list.counter) inc(list.counter)
entry.prev = nil entry.prev = nil
entry.next = list.head entry.next = list.head
if list.head != nil: if list.head != nil:
assert(list.head.prev == nil) assert(list.head.prev == nil)
list.head.prev = entry list.head.prev = entry
list.head = entry list.head = entry
if list.tail == nil: list.tail = entry if list.tail == nil: list.tail = entry
proc prependStr*(list: var TLinkedList, data: string) = proc prependStr*(list: var TLinkedList, data: string) =
prepend(list, newStrEntry(data)) prepend(list, newStrEntry(data))
proc insertBefore*(list: var TLinkedList, pos, entry: PListEntry) = proc insertBefore*(list: var TLinkedList, pos, entry: PListEntry) =
assert(pos != nil) assert(pos != nil)
if pos == list.head: if pos == list.head:
prepend(list, entry) prepend(list, entry)
else: else:
inc(list.counter) inc(list.counter)
entry.next = pos entry.next = pos
entry.prev = pos.prev entry.prev = pos.prev
if pos.prev != nil: pos.prev.next = entry if pos.prev != nil: pos.prev.next = entry
pos.prev = entry pos.prev = entry
proc remove*(list: var TLinkedList, entry: PListEntry) = proc remove*(list: var TLinkedList, entry: PListEntry) =
dec(list.counter) dec(list.counter)
if entry == list.tail: if entry == list.tail:
list.tail = entry.prev list.tail = entry.prev
if entry == list.head: if entry == list.head:
list.head = entry.next list.head = entry.next
if entry.next != nil: entry.next.prev = entry.prev if entry.next != nil: entry.next.prev = entry.prev
if entry.prev != nil: entry.prev.next = entry.next if entry.prev != nil: entry.prev.next = entry.next
@ -112,8 +112,8 @@ proc excludePath*(list: var TLinkedList, data: string) =
remove(list, it) remove(list, it)
it = nxt it = nxt
proc find*(list: TLinkedList, fn: TCompareProc, closure: pointer): PListEntry = proc find*(list: TLinkedList, fn: TCompareProc, closure: pointer): PListEntry =
result = list.head result = list.head
while result != nil: while result != nil:
if fn(result, closure): return if fn(result, closure): return
result = result.next result = result.next

View file

@ -9,7 +9,7 @@
# Built-in types and compilerprocs are registered here. # Built-in types and compilerprocs are registered here.
import import
ast, astalgo, hashes, msgs, platform, nversion, times, idents, rodread ast, astalgo, hashes, msgs, platform, nversion, times, idents, rodread
var systemModule*: PSym var systemModule*: PSym
@ -29,23 +29,23 @@ var
proc nilOrSysInt*: PType = gSysTypes[tyInt] proc nilOrSysInt*: PType = gSysTypes[tyInt]
proc registerSysType(t: PType) = proc registerSysType(t: PType) =
if gSysTypes[t.kind] == nil: gSysTypes[t.kind] = t if gSysTypes[t.kind] == nil: gSysTypes[t.kind] = t
proc newSysType(kind: TTypeKind, size: int): PType = proc newSysType(kind: TTypeKind, size: int): PType =
result = newType(kind, systemModule) result = newType(kind, systemModule)
result.size = size result.size = size
result.align = size.int16 result.align = size.int16
proc getSysSym(name: string): PSym = proc getSysSym(name: string): PSym =
result = strTableGet(systemModule.tab, getIdent(name)) result = strTableGet(systemModule.tab, getIdent(name))
if result == nil: if result == nil:
rawMessage(errSystemNeeds, name) rawMessage(errSystemNeeds, name)
result = newSym(skError, getIdent(name), systemModule, systemModule.info) result = newSym(skError, getIdent(name), systemModule, systemModule.info)
result.typ = newType(tyError, systemModule) result.typ = newType(tyError, systemModule)
if result.kind == skStub: loadStub(result) if result.kind == skStub: loadStub(result)
if result.kind == skAlias: result = result.owner if result.kind == skAlias: result = result.owner
proc getSysMagic*(name: string, m: TMagic): PSym = proc getSysMagic*(name: string, m: TMagic): PSym =
var ti: TIdentIter var ti: TIdentIter
let id = getIdent(name) let id = getIdent(name)
@ -57,13 +57,13 @@ proc getSysMagic*(name: string, m: TMagic): PSym =
rawMessage(errSystemNeeds, name) rawMessage(errSystemNeeds, name)
result = newSym(skError, id, systemModule, systemModule.info) result = newSym(skError, id, systemModule, systemModule.info)
result.typ = newType(tyError, systemModule) result.typ = newType(tyError, systemModule)
proc sysTypeFromName*(name: string): PType = proc sysTypeFromName*(name: string): PType =
result = getSysSym(name).typ result = getSysSym(name).typ
proc getSysType(kind: TTypeKind): PType = proc getSysType(kind: TTypeKind): PType =
result = gSysTypes[kind] result = gSysTypes[kind]
if result == nil: if result == nil:
case kind case kind
of tyInt: result = sysTypeFromName("int") of tyInt: result = sysTypeFromName("int")
of tyInt8: result = sysTypeFromName("int8") of tyInt8: result = sysTypeFromName("int8")
@ -87,7 +87,7 @@ proc getSysType(kind: TTypeKind): PType =
of tyNil: result = newSysType(tyNil, ptrSize) of tyNil: result = newSysType(tyNil, ptrSize)
else: internalError("request for typekind: " & $kind) else: internalError("request for typekind: " & $kind)
gSysTypes[kind] = result gSysTypes[kind] = result
if result.kind != kind: if result.kind != kind:
internalError("wanted: " & $kind & " got: " & $result.kind) internalError("wanted: " & $kind & " got: " & $result.kind)
if result == nil: internalError("type not found: " & $kind) if result == nil: internalError("type not found: " & $kind)
@ -163,7 +163,7 @@ proc setIntLitType*(result: PNode) =
result.typ = getSysType(tyInt64) result.typ = getSysType(tyInt64)
else: internalError(result.info, "invalid int size") else: internalError(result.info, "invalid int size")
proc getCompilerProc(name: string): PSym = proc getCompilerProc(name: string): PSym =
var ident = getIdent(name, hashIgnoreStyle(name)) var ident = getIdent(name, hashIgnoreStyle(name))
result = strTableGet(compilerprocs, ident) result = strTableGet(compilerprocs, ident)
if result == nil: if result == nil:

View file

@ -11,8 +11,8 @@
import parseutils, strutils, strtabs, os, options, msgs, lists import parseutils, strutils, strtabs, os, options, msgs, lists
proc addPath*(path: string, info: TLineInfo) = proc addPath*(path: string, info: TLineInfo) =
if not contains(options.searchPaths, path): if not contains(options.searchPaths, path):
lists.prependStr(options.searchPaths, path) lists.prependStr(options.searchPaths, path)
proc versionSplitPos(s: string): int = proc versionSplitPos(s: string): int =
@ -23,7 +23,7 @@ proc versionSplitPos(s: string): int =
const const
latest = "head" latest = "head"
proc `<.`(a, b: string): bool = proc `<.`(a, b: string): bool =
# wether a has a smaller version than b: # wether a has a smaller version than b:
if a == latest: return false if a == latest: return false
var i = 0 var i = 0

View file

@ -10,7 +10,7 @@
## exposes the Nim VM to clients. ## exposes the Nim VM to clients.
import import
ast, modules, passes, passaux, condsyms, ast, modules, passes, passaux, condsyms,
options, nimconf, lists, sem, semdata, llstream, vm options, nimconf, lists, sem, semdata, llstream, vm
proc execute*(program: string) = proc execute*(program: string) =

View file

@ -12,10 +12,10 @@
# handling that exists! Only at line endings checks are necessary # handling that exists! Only at line endings checks are necessary
# if the buffer needs refilling. # if the buffer needs refilling.
import import
llstream, strutils llstream, strutils
const const
Lrz* = ' ' Lrz* = ' '
Apo* = '\'' Apo* = '\''
Tabulator* = '\x09' Tabulator* = '\x09'
@ -27,7 +27,7 @@ const
BACKSPACE* = '\x08' BACKSPACE* = '\x08'
VT* = '\x0B' VT* = '\x0B'
const const
EndOfFile* = '\0' # end of file marker EndOfFile* = '\0' # end of file marker
# A little picture makes everything clear :-) # A little picture makes everything clear :-)
# buf: # buf:
@ -36,7 +36,7 @@ const
# #
NewLines* = {CR, LF} NewLines* = {CR, LF}
type type
TBaseLexer* = object of RootObj TBaseLexer* = object of RootObj
bufpos*: int bufpos*: int
buf*: cstring buf*: cstring
@ -46,9 +46,9 @@ 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
proc openBaseLexer*(L: var TBaseLexer, inputstream: PLLStream,
proc openBaseLexer*(L: var TBaseLexer, inputstream: PLLStream,
bufLen: int = 8192) bufLen: int = 8192)
# 8K is a reasonable buffer size # 8K is a reasonable buffer size
proc closeBaseLexer*(L: var TBaseLexer) proc closeBaseLexer*(L: var TBaseLexer)
@ -64,15 +64,15 @@ proc handleLF*(L: var TBaseLexer, pos: int): int
# of the LF. # of the LF.
# implementation # implementation
const const
chrSize = sizeof(char) chrSize = sizeof(char)
proc closeBaseLexer(L: var TBaseLexer) = proc closeBaseLexer(L: var TBaseLexer) =
dealloc(L.buf) dealloc(L.buf)
llStreamClose(L.stream) llStreamClose(L.stream)
proc fillBuffer(L: var TBaseLexer) = proc fillBuffer(L: var TBaseLexer) =
var var
charsRead, toCopy, s: int # all are in characters, charsRead, toCopy, s: int # all are in characters,
# not bytes (in case this # not bytes (in case this
# is not the same) # is not the same)
@ -82,68 +82,68 @@ proc fillBuffer(L: var TBaseLexer) =
assert(L.sentinel < L.bufLen) assert(L.sentinel < L.bufLen)
toCopy = L.bufLen - L.sentinel - 1 toCopy = L.bufLen - L.sentinel - 1
assert(toCopy >= 0) assert(toCopy >= 0)
if toCopy > 0: if toCopy > 0:
moveMem(L.buf, addr(L.buf[L.sentinel + 1]), toCopy * chrSize) moveMem(L.buf, addr(L.buf[L.sentinel + 1]), toCopy * chrSize)
# "moveMem" handles overlapping regions # "moveMem" handles overlapping regions
charsRead = llStreamRead(L.stream, addr(L.buf[toCopy]), charsRead = llStreamRead(L.stream, addr(L.buf[toCopy]),
(L.sentinel + 1) * chrSize) div chrSize (L.sentinel + 1) * chrSize) div chrSize
s = toCopy + charsRead s = toCopy + charsRead
if charsRead < L.sentinel + 1: if charsRead < L.sentinel + 1:
L.buf[s] = EndOfFile # set end marker L.buf[s] = EndOfFile # set end marker
L.sentinel = s L.sentinel = s
else: else:
# compute sentinel: # compute sentinel:
dec(s) # BUGFIX (valgrind) dec(s) # BUGFIX (valgrind)
while true: while true:
assert(s < L.bufLen) assert(s < L.bufLen)
while (s >= 0) and not (L.buf[s] in NewLines): dec(s) while (s >= 0) and not (L.buf[s] in NewLines): dec(s)
if s >= 0: if s >= 0:
# we found an appropriate character for a sentinel: # we found an appropriate character for a sentinel:
L.sentinel = s L.sentinel = s
break break
else: else:
# rather than to give up here because the line is too long, # rather than to give up here because the line is too long,
# double the buffer's size and try again: # double the buffer's size and try again:
oldBufLen = L.bufLen oldBufLen = L.bufLen
L.bufLen = L.bufLen * 2 L.bufLen = L.bufLen * 2
L.buf = cast[cstring](realloc(L.buf, L.bufLen * chrSize)) L.buf = cast[cstring](realloc(L.buf, L.bufLen * chrSize))
assert(L.bufLen - oldBufLen == oldBufLen) assert(L.bufLen - oldBufLen == oldBufLen)
charsRead = llStreamRead(L.stream, addr(L.buf[oldBufLen]), charsRead = llStreamRead(L.stream, addr(L.buf[oldBufLen]),
oldBufLen * chrSize) div chrSize oldBufLen * chrSize) div chrSize
if charsRead < oldBufLen: if charsRead < oldBufLen:
L.buf[oldBufLen + charsRead] = EndOfFile L.buf[oldBufLen + charsRead] = EndOfFile
L.sentinel = oldBufLen + charsRead L.sentinel = oldBufLen + charsRead
break break
s = L.bufLen - 1 s = L.bufLen - 1
proc fillBaseLexer(L: var TBaseLexer, pos: int): int = proc fillBaseLexer(L: var TBaseLexer, pos: int): int =
assert(pos <= L.sentinel) assert(pos <= L.sentinel)
if pos < L.sentinel: if pos < L.sentinel:
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.bufpos = 0 # XXX: is this really correct?
result = 0 result = 0
L.lineStart = result L.lineStart = result
proc handleCR(L: var TBaseLexer, pos: int): int = proc handleCR(L: var TBaseLexer, pos: int): int =
assert(L.buf[pos] == CR) assert(L.buf[pos] == CR)
inc(L.lineNumber) inc(L.lineNumber)
result = fillBaseLexer(L, pos) result = fillBaseLexer(L, pos)
if L.buf[result] == LF: if L.buf[result] == LF:
result = fillBaseLexer(L, result) result = fillBaseLexer(L, result)
proc handleLF(L: var TBaseLexer, pos: int): int = proc handleLF(L: var TBaseLexer, pos: int): int =
assert(L.buf[pos] == LF) assert(L.buf[pos] == LF)
inc(L.lineNumber) inc(L.lineNumber)
result = fillBaseLexer(L, pos) #L.lastNL := result-1; // BUGFIX: was: result; result = fillBaseLexer(L, pos) #L.lastNL := result-1; // BUGFIX: was: result;
proc skipUTF8BOM(L: var TBaseLexer) = proc skipUTF8BOM(L: var TBaseLexer) =
if L.buf[0] == '\xEF' and L.buf[1] == '\xBB' and L.buf[2] == '\xBF': if L.buf[0] == '\xEF' and L.buf[1] == '\xBB' and L.buf[2] == '\xBF':
inc(L.bufpos, 3) inc(L.bufpos, 3)
inc(L.lineStart, 3) inc(L.lineStart, 3)
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.bufLen = bufLen L.bufLen = bufLen
@ -155,15 +155,15 @@ proc openBaseLexer(L: var TBaseLexer, inputstream: PLLStream, bufLen = 8192) =
fillBuffer(L) fillBuffer(L)
skipUTF8BOM(L) skipUTF8BOM(L)
proc getColNumber(L: TBaseLexer, pos: int): int = proc getColNumber(L: TBaseLexer, pos: int): int =
result = abs(pos - L.lineStart) result = abs(pos - L.lineStart)
proc getCurrentLine(L: TBaseLexer, marker: bool = true): string = proc getCurrentLine(L: TBaseLexer, marker: bool = true): string =
result = "" result = ""
var i = L.lineStart var i = L.lineStart
while not (L.buf[i] in {CR, LF, EndOfFile}): while not (L.buf[i] in {CR, LF, EndOfFile}):
add(result, L.buf[i]) add(result, L.buf[i])
inc(i) inc(i)
result.add("\n") result.add("\n")
if marker: if marker:
result.add(spaces(getColNumber(L, L.bufpos)) & '^' & "\n") result.add(spaces(getColNumber(L, L.bufpos)) & '^' & "\n")

View file

@ -9,7 +9,7 @@
# this unit handles Nim sets; it implements symbolic sets # this unit handles Nim sets; it implements symbolic sets
import import
ast, astalgo, trees, nversion, msgs, platform, bitsets, types, renderer ast, astalgo, trees, nversion, msgs, platform, bitsets, types, renderer
proc toBitSet*(s: PNode, b: var TBitSet) proc toBitSet*(s: PNode, b: var TBitSet)
@ -30,17 +30,17 @@ proc equalSets*(a, b: PNode): bool
proc cardSet*(s: PNode): BiggestInt proc cardSet*(s: PNode): BiggestInt
# implementation # implementation
proc inSet(s: PNode, elem: PNode): bool = proc inSet(s: PNode, elem: PNode): bool =
if s.kind != nkCurly: if s.kind != nkCurly:
internalError(s.info, "inSet") internalError(s.info, "inSet")
return false return false
for i in countup(0, sonsLen(s) - 1): for i in countup(0, sonsLen(s) - 1):
if s.sons[i].kind == nkRange: if s.sons[i].kind == nkRange:
if leValue(s.sons[i].sons[0], elem) and if leValue(s.sons[i].sons[0], elem) and
leValue(elem, s.sons[i].sons[1]): leValue(elem, s.sons[i].sons[1]):
return true return true
else: else:
if sameValue(s.sons[i], elem): if sameValue(s.sons[i], elem):
return true return true
result = false result = false
@ -58,37 +58,37 @@ proc overlap(a, b: PNode): bool =
else: else:
result = sameValue(a, b) result = sameValue(a, b)
proc someInSet(s: PNode, a, b: PNode): bool = proc someInSet(s: PNode, a, b: PNode): bool =
# checks if some element of a..b is in the set s # checks if some element of a..b is in the set s
if s.kind != nkCurly: if s.kind != nkCurly:
internalError(s.info, "SomeInSet") internalError(s.info, "SomeInSet")
return false return false
for i in countup(0, sonsLen(s) - 1): for i in countup(0, sonsLen(s) - 1):
if s.sons[i].kind == nkRange: if s.sons[i].kind == nkRange:
if leValue(s.sons[i].sons[0], b) and leValue(b, s.sons[i].sons[1]) or if leValue(s.sons[i].sons[0], b) and leValue(b, s.sons[i].sons[1]) or
leValue(s.sons[i].sons[0], a) and leValue(a, s.sons[i].sons[1]): leValue(s.sons[i].sons[0], a) and leValue(a, s.sons[i].sons[1]):
return true return true
else: else:
# a <= elem <= b # a <= elem <= b
if leValue(a, s.sons[i]) and leValue(s.sons[i], b): if leValue(a, s.sons[i]) and leValue(s.sons[i], b):
return true return true
result = false result = false
proc toBitSet(s: PNode, b: var TBitSet) = proc toBitSet(s: PNode, b: var TBitSet) =
var first, j: BiggestInt var first, j: BiggestInt
first = firstOrd(s.typ.sons[0]) first = firstOrd(s.typ.sons[0])
bitSetInit(b, int(getSize(s.typ))) bitSetInit(b, int(getSize(s.typ)))
for i in countup(0, sonsLen(s) - 1): for i in countup(0, sonsLen(s) - 1):
if s.sons[i].kind == nkRange: if s.sons[i].kind == nkRange:
j = getOrdValue(s.sons[i].sons[0]) j = getOrdValue(s.sons[i].sons[0])
while j <= getOrdValue(s.sons[i].sons[1]): while j <= getOrdValue(s.sons[i].sons[1]):
bitSetIncl(b, j - first) bitSetIncl(b, j - first)
inc(j) inc(j)
else: else:
bitSetIncl(b, getOrdValue(s.sons[i]) - first) bitSetIncl(b, getOrdValue(s.sons[i]) - first)
proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode = proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode =
var var
a, b, e, first: BiggestInt # a, b are interval borders a, b, e, first: BiggestInt # a, b are interval borders
elemType: PType elemType: PType
n: PNode n: PNode
@ -98,17 +98,17 @@ proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode =
result.typ = settype result.typ = settype
result.info = info result.info = info
e = 0 e = 0
while e < len(s) * ElemSize: while e < len(s) * ElemSize:
if bitSetIn(s, e): if bitSetIn(s, e):
a = e a = e
b = e b = e
while true: while true:
inc(b) inc(b)
if (b >= len(s) * ElemSize) or not bitSetIn(s, b): break if (b >= len(s) * ElemSize) or not bitSetIn(s, b): break
dec(b) dec(b)
if a == b: if a == b:
addSon(result, newIntTypeNode(nkIntLit, a + first, elemType)) addSon(result, newIntTypeNode(nkIntLit, a + first, elemType))
else: else:
n = newNodeI(nkRange, info) n = newNodeI(nkRange, info)
n.typ = elemType n.typ = elemType
addSon(n, newIntTypeNode(nkIntLit, a + first, elemType)) addSon(n, newIntTypeNode(nkIntLit, a + first, elemType))
@ -117,7 +117,7 @@ proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode =
e = b e = b
inc(e) inc(e)
template nodeSetOp(a, b: PNode, op: expr) {.dirty.} = template nodeSetOp(a, b: PNode, op: expr) {.dirty.} =
var x, y: TBitSet var x, y: TBitSet
toBitSet(a, x) toBitSet(a, x)
toBitSet(b, y) toBitSet(b, y)
@ -129,13 +129,13 @@ proc diffSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetDiff)
proc intersectSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetIntersect) proc intersectSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetIntersect)
proc symdiffSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetSymDiff) proc symdiffSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetSymDiff)
proc containsSets(a, b: PNode): bool = proc containsSets(a, b: PNode): bool =
var x, y: TBitSet var x, y: TBitSet
toBitSet(a, x) toBitSet(a, x)
toBitSet(b, y) toBitSet(b, y)
result = bitSetContains(x, y) result = bitSetContains(x, y)
proc equalSets(a, b: PNode): bool = proc equalSets(a, b: PNode): bool =
var x, y: TBitSet var x, y: TBitSet
toBitSet(a, x) toBitSet(a, x)
toBitSet(b, y) toBitSet(b, y)
@ -147,26 +147,26 @@ proc complement*(a: PNode): PNode =
for i in countup(0, high(x)): x[i] = not x[i] for i in countup(0, high(x)): x[i] = not x[i]
result = toTreeSet(x, a.typ, a.info) result = toTreeSet(x, a.typ, a.info)
proc cardSet(s: PNode): BiggestInt = proc cardSet(s: PNode): BiggestInt =
# here we can do better than converting it into a compact set # here we can do better than converting it into a compact set
# we just count the elements directly # we just count the elements directly
result = 0 result = 0
for i in countup(0, sonsLen(s) - 1): for i in countup(0, sonsLen(s) - 1):
if s.sons[i].kind == nkRange: if s.sons[i].kind == nkRange:
result = result + getOrdValue(s.sons[i].sons[1]) - result = result + getOrdValue(s.sons[i].sons[1]) -
getOrdValue(s.sons[i].sons[0]) + 1 getOrdValue(s.sons[i].sons[0]) + 1
else: else:
inc(result) inc(result)
proc setHasRange(s: PNode): bool = proc setHasRange(s: PNode): bool =
if s.kind != nkCurly: if s.kind != nkCurly:
internalError(s.info, "SetHasRange") internalError(s.info, "SetHasRange")
return false return false
for i in countup(0, sonsLen(s) - 1): for i in countup(0, sonsLen(s) - 1):
if s.sons[i].kind == nkRange: if s.sons[i].kind == nkRange:
return true return true
result = false result = false
proc emptyRange(a, b: PNode): bool = proc emptyRange(a, b: PNode): bool =
result = not leValue(a, b) # a > b iff not (a <= b) result = not leValue(a, b) # a > b iff not (a <= b)

View file

@ -10,7 +10,7 @@
# This module contains Nim's version. It is the only place where it needs # This module contains Nim's version. It is the only place where it needs
# to be changed. # to be changed.
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* = "1215" # modify this if the rod-format changes! RodFileVersion* = "1215" # modify this if the rod-format changes!

View file

@ -9,38 +9,38 @@
## implements some little helper passes ## implements some little helper passes
import import
strutils, ast, astalgo, passes, msgs, options, idgen strutils, ast, astalgo, passes, msgs, options, idgen
proc verboseOpen(s: PSym): PPassContext = proc verboseOpen(s: PSym): PPassContext =
#MessageOut('compiling ' + s.name.s); #MessageOut('compiling ' + s.name.s);
result = nil # we don't need a context result = nil # we don't need a context
rawMessage(hintProcessing, s.name.s) rawMessage(hintProcessing, s.name.s)
proc verboseProcess(context: PPassContext, n: PNode): PNode = proc verboseProcess(context: PPassContext, n: PNode): PNode =
result = n result = n
if context != nil: internalError("logpass: context is not nil") if context != nil: internalError("logpass: context is not nil")
if gVerbosity == 3: if gVerbosity == 3:
# system.nim deactivates all hints, for verbosity:3 we want the processing # system.nim deactivates all hints, for verbosity:3 we want the processing
# messages nonetheless, so we activate them again unconditionally: # messages nonetheless, so we activate them again unconditionally:
incl(msgs.gNotes, hintProcessing) incl(msgs.gNotes, hintProcessing)
message(n.info, hintProcessing, $idgen.gBackendId) message(n.info, hintProcessing, $idgen.gBackendId)
const verbosePass* = makePass(open = verboseOpen, process = verboseProcess) const verbosePass* = makePass(open = verboseOpen, process = verboseProcess)
proc cleanUp(c: PPassContext, n: PNode): PNode = proc cleanUp(c: PPassContext, n: PNode): PNode =
result = n result = n
# we cannot clean up if dead code elimination is activated # we cannot clean up if dead code elimination is activated
if optDeadCodeElim in gGlobalOptions or n == nil: return if optDeadCodeElim in gGlobalOptions or n == nil: return
case n.kind case n.kind
of nkStmtList: of nkStmtList:
for i in countup(0, sonsLen(n) - 1): discard cleanUp(c, n.sons[i]) for i in countup(0, sonsLen(n) - 1): discard cleanUp(c, n.sons[i])
of nkProcDef, nkMethodDef: of nkProcDef, nkMethodDef:
if n.sons[namePos].kind == nkSym: if n.sons[namePos].kind == nkSym:
var s = n.sons[namePos].sym var s = n.sons[namePos].sym
if sfDeadCodeElim notin getModule(s).flags and not astNeeded(s): if sfDeadCodeElim notin getModule(s).flags and not astNeeded(s):
s.ast.sons[bodyPos] = ast.emptyNode # free the memory s.ast.sons[bodyPos] = ast.emptyNode # free the memory
else: else:
discard discard
const cleanupPass* = makePass(process = cleanUp, close = cleanUp) const cleanupPass* = makePass(process = cleanUp, close = cleanUp)

View file

@ -68,7 +68,7 @@ proc inSymChoice(sc, x: PNode): bool =
elif sc.kind == nkOpenSymChoice: elif sc.kind == nkOpenSymChoice:
# same name suffices for open sym choices! # same name suffices for open sym choices!
result = sc.sons[0].sym.name.id == x.sym.name.id result = sc.sons[0].sym.name.id == x.sym.name.id
proc checkTypes(c: PPatternContext, p: PSym, n: PNode): bool = proc checkTypes(c: PPatternContext, p: PSym, n: PNode): bool =
# check param constraints first here as this is quite optimized: # check param constraints first here as this is quite optimized:
if p.constraint != nil: if p.constraint != nil:
@ -115,13 +115,13 @@ proc matchNested(c: PPatternContext, p, n: PNode, rpn: bool): bool =
if rpn: arglist.add(n.sons[0]) if rpn: arglist.add(n.sons[0])
elif n.kind == nkHiddenStdConv and n.sons[1].kind == nkBracket: elif n.kind == nkHiddenStdConv and n.sons[1].kind == nkBracket:
let n = n.sons[1] let n = n.sons[1]
for i in 0.. <n.len: for i in 0.. <n.len:
if not matchStarAux(c, op, n[i], arglist, rpn): return false if not matchStarAux(c, op, n[i], arglist, rpn): return false
elif checkTypes(c, p.sons[2].sym, n): elif checkTypes(c, p.sons[2].sym, n):
add(arglist, n) add(arglist, n)
else: else:
result = false result = false
if n.kind notin nkCallKinds: return false if n.kind notin nkCallKinds: return false
if matches(c, p.sons[1], n.sons[0]): if matches(c, p.sons[1], n.sons[0]):
var arglist = newNodeI(nkArgList, n.info) var arglist = newNodeI(nkArgList, n.info)
@ -151,7 +151,7 @@ proc matches(c: PPatternContext, p, n: PNode): bool =
of "**": result = matchNested(c, p, n, rpn=true) of "**": result = matchNested(c, p, n, rpn=true)
of "~": result = not matches(c, p.sons[1], n) of "~": result = not matches(c, p.sons[1], n)
else: internalError(p.info, "invalid pattern") else: internalError(p.info, "invalid pattern")
# template {add(a, `&` * b)}(a: string{noalias}, b: varargs[string]) = # template {add(a, `&` * b)}(a: string{noalias}, b: varargs[string]) =
# add(a, b) # add(a, b)
elif p.kind == nkCurlyExpr: elif p.kind == nkCurlyExpr:
if p.sons[1].kind == nkPrefix: if p.sons[1].kind == nkPrefix:
@ -212,7 +212,7 @@ proc matchStmtList(c: PPatternContext, p, n: PNode): PNode =
if not isNil(c.mapping): c.mapping = nil if not isNil(c.mapping): c.mapping = nil
return false return false
result = true result = true
if p.kind == nkStmtList and n.kind == p.kind and p.len < n.len: if p.kind == nkStmtList and n.kind == p.kind and p.len < n.len:
let n = flattenStmts(n) let n = flattenStmts(n)
# no need to flatten 'p' here as that has already been done # no need to flatten 'p' here as that has already been done

View file

@ -7,12 +7,12 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
import import
llstream, lexer, parser, idents, strutils, ast, msgs llstream, lexer, parser, idents, strutils, ast, msgs
proc parseAll*(p: var TParser): PNode = proc parseAll*(p: var TParser): PNode =
result = nil result = nil
proc parseTopLevelStmt*(p: var TParser): PNode = proc parseTopLevelStmt*(p: var TParser): PNode =
result = nil result = nil

View file

@ -87,7 +87,7 @@ proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym =
discard discard
result = nextIdentIter(it, scope.symbols) result = nextIdentIter(it, scope.symbols)
return nil return nil
proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym = proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym =
@ -99,17 +99,17 @@ proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym =
debug fn.typ debug fn.typ
debug if result != nil: result.typ else: nil debug if result != nil: result.typ else: nil
debug if old != nil: old.typ else: nil debug if old != nil: old.typ else: nil
when false: when false:
proc paramsFitBorrow(child, parent: PNode): bool = proc paramsFitBorrow(child, parent: PNode): bool =
var length = sonsLen(child) var length = sonsLen(child)
result = false result = false
if length == sonsLen(parent): if length == sonsLen(parent):
for i in countup(1, length - 1): for i in countup(1, length - 1):
var m = child.sons[i].sym var m = child.sons[i].sym
var n = parent.sons[i].sym var n = parent.sons[i].sym
assert((m.kind == skParam) and (n.kind == skParam)) assert((m.kind == skParam) and (n.kind == skParam))
if not compareTypes(m.typ, n.typ, dcEqOrDistinctOf): return if not compareTypes(m.typ, n.typ, dcEqOrDistinctOf): return
if not compareTypes(child.sons[0].typ, parent.sons[0].typ, if not compareTypes(child.sons[0].typ, parent.sons[0].typ,
dcEqOrDistinctOf): return dcEqOrDistinctOf): return
result = true result = true
@ -120,10 +120,10 @@ when false:
var it: TIdentIter var it: TIdentIter
for scope in walkScopes(startScope): for scope in walkScopes(startScope):
result = initIdentIter(it, scope.symbols, fn.Name) result = initIdentIter(it, scope.symbols, fn.Name)
while result != nil: while result != nil:
# watchout! result must not be the same as fn! # watchout! result must not be the same as fn!
if (result.Kind == fn.kind) and (result.id != fn.id): if (result.Kind == fn.kind) and (result.id != fn.id):
if equalGenericParams(result.ast.sons[genericParamsPos], if equalGenericParams(result.ast.sons[genericParamsPos],
fn.ast.sons[genericParamsPos]): fn.ast.sons[genericParamsPos]):
if paramsFitBorrow(fn.typ.n, result.typ.n): return if paramsFitBorrow(fn.typ.n, result.typ.n): return
result = NextIdentIter(it, scope.symbols) result = NextIdentIter(it, scope.symbols)

View file

@ -12,7 +12,7 @@ import strutils
proc c_sprintf(buf, frmt: cstring) {.importc: "sprintf", header: "<stdio.h>", nodecl, varargs.} proc c_sprintf(buf, frmt: cstring) {.importc: "sprintf", header: "<stdio.h>", nodecl, varargs.}
proc toStrMaxPrecision*(f: BiggestFloat): string = proc toStrMaxPrecision*(f: BiggestFloat): string =
if f != f: if f != f:
result = "NAN" result = "NAN"
elif f == 0.0: elif f == 0.0:
@ -21,17 +21,17 @@ proc toStrMaxPrecision*(f: BiggestFloat): string =
if f > 0.0: result = "INF" if f > 0.0: result = "INF"
else: result = "-INF" else: result = "-INF"
else: else:
var buf: array [0..80, char] var buf: array [0..80, char]
c_sprintf(buf, "%#.16e", f) c_sprintf(buf, "%#.16e", f)
result = $buf result = $buf
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):
case s[i] case s[i]
of 'a'..'z', 'A'..'Z', '0'..'9', '_': add(result, s[i]) of 'a'..'z', 'A'..'Z', '0'..'9', '_': add(result, s[i])
else: add(result, '\\' & toHex(ord(s[i]), 2)) else: add(result, '\\' & toHex(ord(s[i]), 2))
proc hexChar(c: char, xi: var int) = proc hexChar(c: char, xi: var int) =
case c case c
of '0'..'9': xi = (xi shl 4) or (ord(c) - ord('0')) of '0'..'9': xi = (xi shl 4) or (ord(c) - ord('0'))
of 'a'..'f': xi = (xi shl 4) or (ord(c) - ord('a') + 10) of 'a'..'f': xi = (xi shl 4) or (ord(c) - ord('a') + 10)
@ -41,18 +41,18 @@ proc hexChar(c: char, xi: var int) =
proc decodeStr*(s: cstring, pos: var int): string = proc decodeStr*(s: cstring, pos: var int): string =
var i = pos var i = pos
result = "" result = ""
while true: while true:
case s[i] case s[i]
of '\\': of '\\':
inc(i, 3) inc(i, 3)
var xi = 0 var xi = 0
hexChar(s[i-2], xi) hexChar(s[i-2], xi)
hexChar(s[i-1], xi) hexChar(s[i-1], xi)
add(result, chr(xi)) add(result, chr(xi))
of 'a'..'z', 'A'..'Z', '0'..'9', '_': of 'a'..'z', 'A'..'Z', '0'..'9', '_':
add(result, s[i]) add(result, s[i])
inc(i) inc(i)
else: break else: break
pos = i pos = i
const const
@ -68,11 +68,11 @@ template encodeIntImpl(self: expr) =
var d: char var d: char
var v = x var v = x
var rem = v mod 190 var rem = v mod 190
if rem < 0: if rem < 0:
add(result, '-') add(result, '-')
v = - (v div 190) v = - (v div 190)
rem = - rem rem = - rem
else: else:
v = v div 190 v = v div 190
var idx = int(rem) var idx = int(rem)
if idx < 62: d = chars[idx] if idx < 62: d = chars[idx]
@ -89,11 +89,11 @@ proc encodeVBiggestInt*(x: BiggestInt, result: var string) =
encodeVBiggestIntAux(x +% vintDelta, result) encodeVBiggestIntAux(x +% vintDelta, result)
# encodeIntImpl(encodeVBiggestInt) # encodeIntImpl(encodeVBiggestInt)
proc encodeVIntAux(x: int, result: var string) = proc encodeVIntAux(x: int, result: var string) =
## encode an int as a variable length base 190 int. ## encode an int as a variable length base 190 int.
encodeIntImpl(encodeVIntAux) encodeIntImpl(encodeVIntAux)
proc encodeVInt*(x: int, result: var string) = proc encodeVInt*(x: int, result: var string) =
## encode an int as a variable length base 190 int. ## encode an int as a variable length base 190 int.
encodeVIntAux(x +% vintDelta, result) encodeVIntAux(x +% vintDelta, result)
@ -101,11 +101,11 @@ template decodeIntImpl() =
var i = pos var i = pos
var sign = - 1 var sign = - 1
assert(s[i] in {'a'..'z', 'A'..'Z', '0'..'9', '-', '\x80'..'\xFF'}) assert(s[i] in {'a'..'z', 'A'..'Z', '0'..'9', '-', '\x80'..'\xFF'})
if s[i] == '-': if s[i] == '-':
inc(i) inc(i)
sign = 1 sign = 1
result = 0 result = 0
while true: while true:
case s[i] case s[i]
of '0'..'9': result = result * 190 - (ord(s[i]) - ord('0')) of '0'..'9': result = result * 190 - (ord(s[i]) - ord('0'))
of 'a'..'z': result = result * 190 - (ord(s[i]) - ord('a') + 10) of 'a'..'z': result = result * 190 - (ord(s[i]) - ord('a') + 10)
@ -116,7 +116,7 @@ template decodeIntImpl() =
result = result * sign -% vintDelta result = result * sign -% vintDelta
pos = i pos = i
proc decodeVInt*(s: cstring, pos: var int): int = proc decodeVInt*(s: cstring, pos: var int): int =
decodeIntImpl() decodeIntImpl()
proc decodeVBiggestInt*(s: cstring, pos: var int): BiggestInt = proc decodeVBiggestInt*(s: cstring, pos: var int): BiggestInt =

View file

@ -306,7 +306,7 @@ const
proc equalsFile*(r: Rope, f: File): bool = proc equalsFile*(r: Rope, f: File): bool =
## returns true if the contents of the file `f` equal `r`. ## returns true if the contents of the file `f` equal `r`.
var var
buf: array[bufSize, char] buf: array[bufSize, char]
bpos = buf.len bpos = buf.len
blen = buf.len blen = buf.len

View file

@ -72,7 +72,7 @@ proc `|*|`*(a, b: BiggestInt): BiggestInt =
# 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough" # 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd): if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd):
return result return result
if floatProd >= 0.0: if floatProd >= 0.0:
result = high(result) result = high(result)
else: else:

View file

@ -320,7 +320,7 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
x.call.add newSymNode(s, n.info) x.call.add newSymNode(s, n.info)
else: else:
internalAssert false internalAssert false
result = x.call result = x.call
instGenericConvertersSons(c, result, x) instGenericConvertersSons(c, result, x)
result.sons[0] = newSymNode(finalCallee, result.sons[0].info) result.sons[0] = newSymNode(finalCallee, result.sons[0].info)

View file

@ -76,7 +76,7 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
let L = forLoop.len let L = forLoop.len
let call = forLoop.sons[L-2] let call = forLoop.sons[L-2]
if call.len > 2: if call.len > 2:
localError(forLoop.info, errGenerated, localError(forLoop.info, errGenerated,
"parallel 'fields' iterator does not work for 'case' objects") "parallel 'fields' iterator does not work for 'case' objects")
return return
# iterate over the selector: # iterate over the selector:
@ -106,7 +106,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
# a 'while true: stmt; break' loop ... # a 'while true: stmt; break' loop ...
result = newNodeI(nkWhileStmt, n.info, 2) result = newNodeI(nkWhileStmt, n.info, 2)
var trueSymbol = strTableGet(magicsys.systemModule.tab, getIdent"true") var trueSymbol = strTableGet(magicsys.systemModule.tab, getIdent"true")
if trueSymbol == nil: if trueSymbol == nil:
localError(n.info, errSystemNeeds, "true") localError(n.info, errSystemNeeds, "true")
trueSymbol = newSym(skUnknown, getIdent"true", getCurrOwner(), n.info) trueSymbol = newSym(skUnknown, getIdent"true", getCurrOwner(), n.info)
trueSymbol.typ = getSysType(tyBool) trueSymbol.typ = getSysType(tyBool)
@ -114,13 +114,13 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
result.sons[0] = newSymNode(trueSymbol, n.info) result.sons[0] = newSymNode(trueSymbol, n.info)
var stmts = newNodeI(nkStmtList, n.info) var stmts = newNodeI(nkStmtList, n.info)
result.sons[1] = stmts result.sons[1] = stmts
var length = sonsLen(n) var length = sonsLen(n)
var call = n.sons[length-2] var call = n.sons[length-2]
if length-2 != sonsLen(call)-1 + ord(m==mFieldPairs): if length-2 != sonsLen(call)-1 + ord(m==mFieldPairs):
localError(n.info, errWrongNumberOfVariables) localError(n.info, errWrongNumberOfVariables)
return result return result
var tupleTypeA = skipTypes(call.sons[1].typ, abstractVar-{tyTypeDesc}) var tupleTypeA = skipTypes(call.sons[1].typ, abstractVar-{tyTypeDesc})
if tupleTypeA.kind notin {tyTuple, tyObject}: if tupleTypeA.kind notin {tyTuple, tyObject}:
localError(n.info, errGenerated, "no object or tuple type") localError(n.info, errGenerated, "no object or tuple type")
@ -129,7 +129,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
var tupleTypeB = skipTypes(call.sons[i].typ, abstractVar-{tyTypeDesc}) var tupleTypeB = skipTypes(call.sons[i].typ, abstractVar-{tyTypeDesc})
if not sameType(tupleTypeA, tupleTypeB): if not sameType(tupleTypeA, tupleTypeB):
typeMismatch(call.sons[i], tupleTypeA, tupleTypeB) typeMismatch(call.sons[i], tupleTypeA, tupleTypeB)
inc(c.p.nestedLoopCounter) inc(c.p.nestedLoopCounter)
if tupleTypeA.kind == tyTuple: if tupleTypeA.kind == tyTuple:
var loopBody = n.sons[length-1] var loopBody = n.sons[length-1]

View file

@ -1327,7 +1327,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
# because in that case there is no point in continuing. # because in that case there is no point in continuing.
var bothMetaCounter = 0 var bothMetaCounter = 0
var lastBindingsLength = -1 var lastBindingsLength = -1
while r == isBothMetaConvertible and while r == isBothMetaConvertible and
lastBindingsLength != m.bindings.counter and lastBindingsLength != m.bindings.counter and
bothMetaCounter < 100: bothMetaCounter < 100:
lastBindingsLength = m.bindings.counter lastBindingsLength = m.bindings.counter

View file

@ -34,7 +34,7 @@ var
template origModuleName(m: PSym): string = m.name.s template origModuleName(m: PSym): string = m.name.s
proc symToSuggest(s: PSym, isLocal: bool, section: string, li: TLineInfo): Suggest = proc symToSuggest(s: PSym, isLocal: bool, section: string, li: TLineInfo): Suggest =
result.section = parseIdeCmd(section) result.section = parseIdeCmd(section)
if optIdeTerse in gGlobalOptions: if optIdeTerse in gGlobalOptions:
result.symkind = s.kind result.symkind = s.kind
@ -52,7 +52,7 @@ proc symToSuggest(s: PSym, isLocal: bool, section: string, li: TLineInfo): Sugge
result.qualifiedPath.add(ow.origModuleName) result.qualifiedPath.add(ow.origModuleName)
result.qualifiedPath.add(s.name.s) result.qualifiedPath.add(s.name.s)
if s.typ != nil: if s.typ != nil:
result.forth = typeToString(s.typ) result.forth = typeToString(s.typ)
else: else:
result.forth = "" result.forth = ""
@ -62,7 +62,7 @@ proc symToSuggest(s: PSym, isLocal: bool, section: string, li: TLineInfo): Sugge
when not defined(noDocgen): when not defined(noDocgen):
result.doc = s.extractDocComment result.doc = s.extractDocComment
proc `$`(suggest: Suggest): string = proc `$`(suggest: Suggest): string =
result = $suggest.section result = $suggest.section
result.add(sep) result.add(sep)
result.add($suggest.symkind) result.add($suggest.symkind)
@ -80,7 +80,7 @@ proc `$`(suggest: Suggest): string =
when not defined(noDocgen): when not defined(noDocgen):
result.add(suggest.doc.escape) result.add(suggest.doc.escape)
proc symToSuggest(s: PSym, isLocal: bool, section: string): Suggest = proc symToSuggest(s: PSym, isLocal: bool, section: string): Suggest =
result = symToSuggest(s, isLocal, section, s.info) result = symToSuggest(s, isLocal, section, s.info)
proc suggestResult(s: Suggest) = proc suggestResult(s: Suggest) =
@ -104,7 +104,7 @@ proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} =
result = true result = true
break break
proc suggestField(c: PContext, s: PSym, outputs: var int) = proc suggestField(c: PContext, s: PSym, outputs: var int) =
if filterSym(s) and fieldVisible(c, s): if filterSym(s) and fieldVisible(c, s):
suggestResult(symToSuggest(s, isLocal=true, $ideSug)) suggestResult(symToSuggest(s, isLocal=true, $ideSug))
inc outputs inc outputs
@ -122,17 +122,17 @@ template wholeSymTab(cond, section: expr) {.immediate.} =
suggestResult(symToSuggest(it, isLocal = isLocal, section)) suggestResult(symToSuggest(it, isLocal = isLocal, section))
inc outputs inc outputs
proc suggestSymList(c: PContext, list: PNode, outputs: var int) = proc suggestSymList(c: PContext, list: PNode, outputs: var int) =
for i in countup(0, sonsLen(list) - 1): for i in countup(0, sonsLen(list) - 1):
if list.sons[i].kind == nkSym: if list.sons[i].kind == nkSym:
suggestField(c, list.sons[i].sym, outputs) suggestField(c, list.sons[i].sym, outputs)
#else: InternalError(list.info, "getSymFromList") #else: InternalError(list.info, "getSymFromList")
proc suggestObject(c: PContext, n: PNode, outputs: var int) = proc suggestObject(c: PContext, n: PNode, outputs: var int) =
case n.kind case n.kind
of nkRecList: of nkRecList:
for i in countup(0, sonsLen(n)-1): suggestObject(c, n.sons[i], outputs) for i in countup(0, sonsLen(n)-1): suggestObject(c, n.sons[i], outputs)
of nkRecCase: of nkRecCase:
var L = sonsLen(n) var L = sonsLen(n)
if L > 0: if L > 0:
suggestObject(c, n.sons[0], outputs) suggestObject(c, n.sons[0], outputs)
@ -140,7 +140,7 @@ proc suggestObject(c: PContext, n: PNode, outputs: var int) =
of nkSym: suggestField(c, n.sym, outputs) of nkSym: suggestField(c, n.sym, outputs)
else: discard else: discard
proc nameFits(c: PContext, s: PSym, n: PNode): bool = proc nameFits(c: PContext, s: PSym, n: PNode): bool =
var op = n.sons[0] var op = n.sons[0]
if op.kind in {nkOpenSymChoice, nkClosedSymChoice}: op = op.sons[0] if op.kind in {nkOpenSymChoice, nkClosedSymChoice}: op = op.sons[0]
var opr: PIdent var opr: PIdent
@ -150,8 +150,8 @@ proc nameFits(c: PContext, s: PSym, n: PNode): bool =
else: return false else: return false
result = opr.id == s.name.id result = opr.id == s.name.id
proc argsFit(c: PContext, candidate: PSym, n, nOrig: PNode): bool = proc argsFit(c: PContext, candidate: PSym, n, nOrig: PNode): bool =
case candidate.kind case candidate.kind
of OverloadableSyms: of OverloadableSyms:
var m: TCandidate var m: TCandidate
initCandidate(c, m, candidate, nil) initCandidate(c, m, candidate, nil)
@ -160,11 +160,11 @@ proc argsFit(c: PContext, candidate: PSym, n, nOrig: PNode): bool =
else: else:
result = false result = false
proc suggestCall(c: PContext, n, nOrig: PNode, outputs: var int) = proc suggestCall(c: PContext, n, nOrig: PNode, outputs: var int) =
wholeSymTab(filterSym(it) and nameFits(c, it, n) and argsFit(c, it, n, nOrig), wholeSymTab(filterSym(it) and nameFits(c, it, n) and argsFit(c, it, n, nOrig),
$ideCon) $ideCon)
proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} = proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} =
if s.typ != nil and sonsLen(s.typ) > 1 and s.typ.sons[1] != nil: if s.typ != nil and sonsLen(s.typ) > 1 and s.typ.sons[1] != nil:
# special rule: if system and some weird generic match via 'tyExpr' # special rule: if system and some weird generic match via 'tyExpr'
# or 'tyGenericParam' we won't list it either to reduce the noise (nobody # or 'tyGenericParam' we won't list it either to reduce the noise (nobody
@ -198,48 +198,48 @@ proc suggestFieldAccess(c: PContext, n: PNode, outputs: var int) =
var typ = n.typ var typ = n.typ
if typ == nil: if typ == nil:
# a module symbol has no type for example: # a module symbol has no type for example:
if n.kind == nkSym and n.sym.kind == skModule: if n.kind == nkSym and n.sym.kind == skModule:
if n.sym == c.module: if n.sym == c.module:
# all symbols accessible, because we are in the current module: # all symbols accessible, because we are in the current module:
for it in items(c.topLevelScope.symbols): for it in items(c.topLevelScope.symbols):
if filterSym(it): if filterSym(it):
suggestResult(symToSuggest(it, isLocal=false, $ideSug)) suggestResult(symToSuggest(it, isLocal=false, $ideSug))
inc outputs inc outputs
else: else:
for it in items(n.sym.tab): for it in items(n.sym.tab):
if filterSym(it): if filterSym(it):
suggestResult(symToSuggest(it, isLocal=false, $ideSug)) suggestResult(symToSuggest(it, isLocal=false, $ideSug))
inc outputs inc outputs
else: else:
# fallback: # fallback:
suggestEverything(c, n, outputs) suggestEverything(c, n, outputs)
elif typ.kind == tyEnum and n.kind == nkSym and n.sym.kind == skType: elif typ.kind == tyEnum and n.kind == nkSym and n.sym.kind == skType:
# look up if the identifier belongs to the enum: # look up if the identifier belongs to the enum:
var t = typ var t = typ
while t != nil: while t != nil:
suggestSymList(c, t.n, outputs) suggestSymList(c, t.n, outputs)
t = t.sons[0] t = t.sons[0]
suggestOperations(c, n, typ, outputs) suggestOperations(c, n, typ, outputs)
else: else:
typ = skipTypes(typ, {tyGenericInst, tyVar, tyPtr, tyRef}) typ = skipTypes(typ, {tyGenericInst, tyVar, tyPtr, tyRef})
if typ.kind == tyObject: if typ.kind == tyObject:
var t = typ var t = typ
while true: while true:
suggestObject(c, t.n, outputs) suggestObject(c, t.n, outputs)
if t.sons[0] == nil: break if t.sons[0] == nil: break
t = skipTypes(t.sons[0], {tyGenericInst}) t = skipTypes(t.sons[0], {tyGenericInst})
suggestOperations(c, n, typ, outputs) suggestOperations(c, n, typ, outputs)
elif typ.kind == tyTuple and typ.n != nil: elif typ.kind == tyTuple and typ.n != nil:
suggestSymList(c, typ.n, outputs) suggestSymList(c, typ.n, outputs)
suggestOperations(c, n, typ, outputs) suggestOperations(c, n, typ, outputs)
else: else:
suggestOperations(c, n, typ, outputs) suggestOperations(c, n, typ, outputs)
type type
TCheckPointResult = enum TCheckPointResult = enum
cpNone, cpFuzzy, cpExact cpNone, cpFuzzy, cpExact
proc inCheckpoint(current: TLineInfo): TCheckPointResult = proc inCheckpoint(current: TLineInfo): TCheckPointResult =
if current.fileIndex == gTrackPos.fileIndex: if current.fileIndex == gTrackPos.fileIndex:
if current.line == gTrackPos.line and if current.line == gTrackPos.line and
abs(current.col-gTrackPos.col) < 4: abs(current.col-gTrackPos.col) < 4:
@ -255,8 +255,8 @@ proc findClosestDot(n: PNode): PNode =
result = findClosestDot(n.sons[i]) result = findClosestDot(n.sons[i])
if result != nil: return if result != nil: return
proc findClosestCall(n: PNode): PNode = proc findClosestCall(n: PNode): PNode =
if n.kind in nkCallKinds and inCheckpoint(n.info) == cpExact: if n.kind in nkCallKinds and inCheckpoint(n.info) == cpExact:
result = n result = n
else: else:
for i in 0.. <safeLen(n): for i in 0.. <safeLen(n):
@ -270,8 +270,8 @@ proc isTracked(current: TLineInfo, tokenLen: int): bool =
if col >= current.col and col <= current.col+tokenLen-1: if col >= current.col and col <= current.col+tokenLen-1:
return true return true
proc findClosestSym(n: PNode): PNode = proc findClosestSym(n: PNode): PNode =
if n.kind == nkSym and inCheckpoint(n.info) == cpExact: if n.kind == nkSym and inCheckpoint(n.info) == cpExact:
result = n result = n
elif n.kind notin {nkNone..nkNilLit}: elif n.kind notin {nkNone..nkNilLit}:
for i in 0.. <sonsLen(n): for i in 0.. <sonsLen(n):
@ -328,7 +328,7 @@ proc safeSemExpr*(c: PContext, n: PNode): PNode =
except ERecoverableError: except ERecoverableError:
result = ast.emptyNode result = ast.emptyNode
proc suggestExpr*(c: PContext, node: PNode) = proc suggestExpr*(c: PContext, node: PNode) =
if nfIsCursor notin node.flags: if nfIsCursor notin node.flags:
if gTrackPos.line < 0: return if gTrackPos.line < 0: return
var cp = inCheckpoint(node.info) var cp = inCheckpoint(node.info)
@ -349,7 +349,7 @@ proc suggestExpr*(c: PContext, node: PNode) =
#writeStackTrace() #writeStackTrace()
else: else:
suggestEverything(c, n, outputs) suggestEverything(c, n, outputs)
elif gIdeCmd == ideCon: elif gIdeCmd == ideCon:
var n = if nfIsCursor in node.flags: node else: findClosestCall(node) var n = if nfIsCursor in node.flags: node else: findClosestCall(node)
if n == nil: n = node if n == nil: n = node
@ -364,9 +364,9 @@ proc suggestExpr*(c: PContext, node: PNode) =
if x.kind == nkEmpty or x.typ == nil: break if x.kind == nkEmpty or x.typ == nil: break
addSon(a, x) addSon(a, x)
suggestCall(c, a, n, outputs) suggestCall(c, a, n, outputs)
dec(c.inCompilesContext) dec(c.inCompilesContext)
if outputs > 0 and gIdeCmd != ideUse: suggestQuit() if outputs > 0 and gIdeCmd != ideUse: suggestQuit()
proc suggestStmt*(c: PContext, n: PNode) = proc suggestStmt*(c: PContext, n: PNode) =
suggestExpr(c, n) suggestExpr(c, n)

View file

@ -9,24 +9,24 @@
## Implements the dispatcher for the different parsers. ## Implements the dispatcher for the different parsers.
import import
strutils, llstream, ast, astalgo, idents, lexer, options, msgs, parser, strutils, llstream, ast, astalgo, idents, lexer, options, msgs, parser,
pbraces, filters, filter_tmpl, renderer pbraces, filters, filter_tmpl, renderer
type type
TFilterKind* = enum TFilterKind* = enum
filtNone, filtTemplate, filtReplace, filtStrip filtNone, filtTemplate, filtReplace, filtStrip
TParserKind* = enum TParserKind* = enum
skinStandard, skinStrongSpaces, skinBraces, skinEndX skinStandard, skinStrongSpaces, skinBraces, skinEndX
const const
parserNames*: array[TParserKind, string] = ["standard", "strongspaces", parserNames*: array[TParserKind, string] = ["standard", "strongspaces",
"braces", "endx"] "braces", "endx"]
filterNames*: array[TFilterKind, string] = ["none", "stdtmpl", "replace", filterNames*: array[TFilterKind, string] = ["none", "stdtmpl", "replace",
"strip"] "strip"]
type type
TParsers*{.final.} = object TParsers*{.final.} = object
skin*: TParserKind skin*: TParserKind
parser*: TParser parser*: TParser
@ -42,53 +42,53 @@ proc parseTopLevelStmt*(p: var TParsers): PNode
# implementation # implementation
proc parseFile(fileIdx: int32): PNode = proc parseFile(fileIdx: int32): PNode =
var var
p: TParsers p: TParsers
f: File f: File
let filename = fileIdx.toFullPathConsiderDirty let filename = fileIdx.toFullPathConsiderDirty
if not open(f, filename): if not open(f, filename):
rawMessage(errCannotOpenFile, filename) rawMessage(errCannotOpenFile, filename)
return return
openParsers(p, fileIdx, llStreamOpen(f)) openParsers(p, fileIdx, llStreamOpen(f))
result = parseAll(p) result = parseAll(p)
closeParsers(p) closeParsers(p)
proc parseAll(p: var TParsers): PNode = proc parseAll(p: var TParsers): PNode =
case p.skin case p.skin
of skinStandard, skinStrongSpaces: of skinStandard, skinStrongSpaces:
result = parser.parseAll(p.parser) result = parser.parseAll(p.parser)
of skinBraces: of skinBraces:
result = pbraces.parseAll(p.parser) result = pbraces.parseAll(p.parser)
of skinEndX: of skinEndX:
internalError("parser to implement") internalError("parser to implement")
result = ast.emptyNode result = ast.emptyNode
proc parseTopLevelStmt(p: var TParsers): PNode = proc parseTopLevelStmt(p: var TParsers): PNode =
case p.skin case p.skin
of skinStandard, skinStrongSpaces: of skinStandard, skinStrongSpaces:
result = parser.parseTopLevelStmt(p.parser) result = parser.parseTopLevelStmt(p.parser)
of skinBraces: of skinBraces:
result = pbraces.parseTopLevelStmt(p.parser) result = pbraces.parseTopLevelStmt(p.parser)
of skinEndX: of skinEndX:
internalError("parser to implement") internalError("parser to implement")
result = ast.emptyNode result = ast.emptyNode
proc utf8Bom(s: string): int = proc utf8Bom(s: string): int =
if (s[0] == '\xEF') and (s[1] == '\xBB') and (s[2] == '\xBF'): if (s[0] == '\xEF') and (s[1] == '\xBB') and (s[2] == '\xBF'):
result = 3 result = 3
else: else:
result = 0 result = 0
proc containsShebang(s: string, i: int): bool = proc containsShebang(s: string, i: int): bool =
if (s[i] == '#') and (s[i + 1] == '!'): if (s[i] == '#') and (s[i + 1] == '!'):
var j = i + 2 var j = i + 2
while s[j] in Whitespace: inc(j) while s[j] in Whitespace: inc(j)
result = s[j] == '/' result = s[j] == '/'
proc parsePipe(filename: string, inputStream: PLLStream): PNode = proc parsePipe(filename: string, inputStream: PLLStream): PNode =
result = ast.emptyNode result = ast.emptyNode
var s = llStreamOpen(filename, fmRead) var s = llStreamOpen(filename, fmRead)
if s != nil: if s != nil:
var line = newStringOfCap(80) var line = newStringOfCap(80)
discard llStreamReadLine(s, line) discard llStreamReadLine(s, line)
var i = utf8Bom(line) var i = utf8Bom(line)
@ -104,50 +104,50 @@ proc parsePipe(filename: string, inputStream: PLLStream): PNode =
closeParser(q) closeParser(q)
llStreamClose(s) llStreamClose(s)
proc getFilter(ident: PIdent): TFilterKind = proc getFilter(ident: PIdent): TFilterKind =
for i in countup(low(TFilterKind), high(TFilterKind)): for i in countup(low(TFilterKind), high(TFilterKind)):
if identEq(ident, filterNames[i]): if identEq(ident, filterNames[i]):
return i return i
result = filtNone result = filtNone
proc getParser(ident: PIdent): TParserKind = proc getParser(ident: PIdent): TParserKind =
for i in countup(low(TParserKind), high(TParserKind)): for i in countup(low(TParserKind), high(TParserKind)):
if identEq(ident, parserNames[i]): if identEq(ident, parserNames[i]):
return i return i
rawMessage(errInvalidDirectiveX, ident.s) rawMessage(errInvalidDirectiveX, ident.s)
proc getCallee(n: PNode): PIdent = proc getCallee(n: PNode): PIdent =
if n.kind in nkCallKinds and n.sons[0].kind == nkIdent: if n.kind in nkCallKinds and n.sons[0].kind == nkIdent:
result = n.sons[0].ident result = n.sons[0].ident
elif n.kind == nkIdent: elif n.kind == nkIdent:
result = n.ident result = n.ident
else: else:
rawMessage(errXNotAllowedHere, renderTree(n)) rawMessage(errXNotAllowedHere, renderTree(n))
proc applyFilter(p: var TParsers, n: PNode, filename: string, proc applyFilter(p: var TParsers, n: PNode, filename: string,
stdin: PLLStream): PLLStream = stdin: PLLStream): PLLStream =
var ident = getCallee(n) var ident = getCallee(n)
var f = getFilter(ident) var f = getFilter(ident)
case f case f
of filtNone: of filtNone:
p.skin = getParser(ident) p.skin = getParser(ident)
result = stdin result = stdin
of filtTemplate: of filtTemplate:
result = filterTmpl(stdin, filename, n) result = filterTmpl(stdin, filename, n)
of filtStrip: of filtStrip:
result = filterStrip(stdin, filename, n) result = filterStrip(stdin, filename, n)
of filtReplace: of filtReplace:
result = filterReplace(stdin, filename, n) result = filterReplace(stdin, filename, n)
if f != filtNone: if f != filtNone:
if hintCodeBegin in gNotes: if hintCodeBegin in gNotes:
rawMessage(hintCodeBegin, []) rawMessage(hintCodeBegin, [])
msgWriteln(result.s) msgWriteln(result.s)
rawMessage(hintCodeEnd, []) rawMessage(hintCodeEnd, [])
proc evalPipe(p: var TParsers, n: PNode, filename: string, proc evalPipe(p: var TParsers, n: PNode, filename: string,
start: PLLStream): PLLStream = start: PLLStream): PLLStream =
result = start result = start
if n.kind == nkEmpty: return if n.kind == nkEmpty: return
if n.kind == nkInfix and n.sons[0].kind == nkIdent and if n.kind == nkInfix and n.sons[0].kind == nkIdent and
identEq(n.sons[0].ident, "|"): identEq(n.sons[0].ident, "|"):
for i in countup(1, 2): for i in countup(1, 2):
@ -159,8 +159,8 @@ proc evalPipe(p: var TParsers, n: PNode, filename: string,
result = evalPipe(p, n.sons[0], filename, result) result = evalPipe(p, n.sons[0], filename, result)
else: else:
result = applyFilter(p, n, filename, result) result = applyFilter(p, n, filename, result)
proc openParsers(p: var TParsers, fileIdx: int32, inputstream: PLLStream) = proc openParsers(p: var TParsers, fileIdx: int32, inputstream: PLLStream) =
var s: PLLStream var s: PLLStream
p.skin = skinStandard p.skin = skinStandard
let filename = fileIdx.toFullPathConsiderDirty let filename = fileIdx.toFullPathConsiderDirty
@ -172,6 +172,6 @@ proc openParsers(p: var TParsers, fileIdx: int32, inputstream: PLLStream) =
parser.openParser(p.parser, fileIdx, s, false) parser.openParser(p.parser, fileIdx, s, false)
of skinStrongSpaces: of skinStrongSpaces:
parser.openParser(p.parser, fileIdx, s, true) parser.openParser(p.parser, fileIdx, s, true)
proc closeParsers(p: var TParsers) = proc closeParsers(p: var TParsers) =
parser.closeParser(p.parser) parser.closeParser(p.parser)

View file

@ -12,10 +12,10 @@ import
{.compile: "../tinyc/libtcc.c".} {.compile: "../tinyc/libtcc.c".}
proc tinyCErrorHandler(closure: pointer, msg: cstring) {.cdecl.} = proc tinyCErrorHandler(closure: pointer, msg: cstring) {.cdecl.} =
rawMessage(errGenerated, $msg) rawMessage(errGenerated, $msg)
proc initTinyCState: PccState = proc initTinyCState: PccState =
result = openCCState() result = openCCState()
setErrorFunc(result, nil, tinyCErrorHandler) setErrorFunc(result, nil, tinyCErrorHandler)
@ -23,25 +23,25 @@ var
gTinyC = initTinyCState() gTinyC = initTinyCState()
libIncluded = false libIncluded = false
proc addFile(filename: string) = proc addFile(filename: string) =
if addFile(gTinyC, filename) != 0'i32: if addFile(gTinyC, filename) != 0'i32:
rawMessage(errCannotOpenFile, filename) rawMessage(errCannotOpenFile, filename)
proc setupEnvironment = proc setupEnvironment =
when defined(amd64): when defined(amd64):
defineSymbol(gTinyC, "__x86_64__", nil) defineSymbol(gTinyC, "__x86_64__", nil)
elif defined(i386): elif defined(i386):
defineSymbol(gTinyC, "__i386__", nil) defineSymbol(gTinyC, "__i386__", nil)
when defined(linux): when defined(linux):
defineSymbol(gTinyC, "__linux__", nil) defineSymbol(gTinyC, "__linux__", nil)
defineSymbol(gTinyC, "__linux", nil) defineSymbol(gTinyC, "__linux", nil)
var nimrodDir = getPrefixDir() var nimrodDir = getPrefixDir()
addIncludePath(gTinyC, libpath) addIncludePath(gTinyC, libpath)
when defined(windows): when defined(windows):
addSysincludePath(gTinyC, nimrodDir / "tinyc/win32/include") addSysincludePath(gTinyC, nimrodDir / "tinyc/win32/include")
addSysincludePath(gTinyC, nimrodDir / "tinyc/include") addSysincludePath(gTinyC, nimrodDir / "tinyc/include")
when defined(windows): when defined(windows):
defineSymbol(gTinyC, "_WIN32", nil) defineSymbol(gTinyC, "_WIN32", nil)
# we need Mingw's headers too: # we need Mingw's headers too:
var gccbin = getConfigVar("gcc.path") % ["nimrod", nimrodDir] var gccbin = getConfigVar("gcc.path") % ["nimrod", nimrodDir]
@ -54,7 +54,7 @@ proc setupEnvironment =
#addFile(nimrodDir / r"tinyc\win32\dllcrt1.o") #addFile(nimrodDir / r"tinyc\win32\dllcrt1.o")
#addFile(nimrodDir / r"tinyc\win32\dllmain.o") #addFile(nimrodDir / r"tinyc\win32\dllmain.o")
addFile(nimrodDir / r"tinyc\win32\libtcc1.o") addFile(nimrodDir / r"tinyc\win32\libtcc1.o")
#addFile(nimrodDir / r"tinyc\win32\lib\crt1.c") #addFile(nimrodDir / r"tinyc\win32\lib\crt1.c")
#addFile(nimrodDir / r"tinyc\lib\libtcc1.c") #addFile(nimrodDir / r"tinyc\lib\libtcc1.c")
else: else:
@ -62,12 +62,12 @@ proc setupEnvironment =
when defined(amd64): when defined(amd64):
addSysincludePath(gTinyC, "/usr/include/x86_64-linux-gnu") addSysincludePath(gTinyC, "/usr/include/x86_64-linux-gnu")
proc compileCCode*(ccode: string) = proc compileCCode*(ccode: string) =
if not libIncluded: if not libIncluded:
libIncluded = true libIncluded = true
setupEnvironment() setupEnvironment()
discard compileString(gTinyC, ccode) discard compileString(gTinyC, ccode)
proc run*(args: string) = proc run*(args: string) =
var s = @[cstring(gProjectName)] & map(split(args), proc(x: string): cstring = cstring(x)) var s = @[cstring(gProjectName)] & map(split(args), proc(x: string): cstring = cstring(x))
var err = tinyc.run(gTinyC, cint(len(s)), cast[cstringArray](addr(s[0]))) != 0'i32 var err = tinyc.run(gTinyC, cint(len(s)), cast[cstringArray](addr(s[0]))) != 0'i32

View file

@ -9,36 +9,36 @@
# Implements a table from trees to trees. Does structural equivalence checking. # Implements a table from trees to trees. Does structural equivalence checking.
import import
hashes, ast, astalgo, types hashes, ast, astalgo, types
proc hashTree(n: PNode): Hash = proc hashTree(n: PNode): Hash =
if n == nil: return if n == nil: return
result = ord(n.kind) result = ord(n.kind)
case n.kind case n.kind
of nkEmpty, nkNilLit, nkType: of nkEmpty, nkNilLit, nkType:
discard discard
of nkIdent: of nkIdent:
result = result !& n.ident.h result = result !& n.ident.h
of nkSym: of nkSym:
result = result !& n.sym.name.h result = result !& n.sym.name.h
of nkCharLit..nkUInt64Lit: of nkCharLit..nkUInt64Lit:
if (n.intVal >= low(int)) and (n.intVal <= high(int)): if (n.intVal >= low(int)) and (n.intVal <= high(int)):
result = result !& int(n.intVal) result = result !& int(n.intVal)
of nkFloatLit..nkFloat64Lit: of nkFloatLit..nkFloat64Lit:
if (n.floatVal >= - 1000000.0) and (n.floatVal <= 1000000.0): if (n.floatVal >= - 1000000.0) and (n.floatVal <= 1000000.0):
result = result !& toInt(n.floatVal) result = result !& toInt(n.floatVal)
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
if not n.strVal.isNil: if not n.strVal.isNil:
result = result !& hash(n.strVal) result = result !& hash(n.strVal)
else: else:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
result = result !& hashTree(n.sons[i]) result = result !& hashTree(n.sons[i])
proc treesEquivalent(a, b: PNode): bool = proc treesEquivalent(a, b: PNode): bool =
if a == b: if a == b:
result = true result = true
elif (a != nil) and (b != nil) and (a.kind == b.kind): elif (a != nil) and (b != nil) and (a.kind == b.kind):
case a.kind case a.kind
of nkEmpty, nkNilLit, nkType: result = true of nkEmpty, nkNilLit, nkType: result = true
of nkSym: result = a.sym.id == b.sym.id of nkSym: result = a.sym.id == b.sym.id
@ -46,28 +46,28 @@ proc treesEquivalent(a, b: PNode): bool =
of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal
of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
else: else:
if sonsLen(a) == sonsLen(b): if sonsLen(a) == sonsLen(b):
for i in countup(0, sonsLen(a) - 1): for i in countup(0, sonsLen(a) - 1):
if not treesEquivalent(a.sons[i], b.sons[i]): return if not treesEquivalent(a.sons[i], b.sons[i]): return
result = true result = true
if result: result = sameTypeOrNil(a.typ, b.typ) if result: result = sameTypeOrNil(a.typ, b.typ)
proc nodeTableRawGet(t: TNodeTable, k: Hash, key: PNode): int = proc nodeTableRawGet(t: TNodeTable, k: Hash, key: PNode): int =
var h: Hash = k and high(t.data) var h: Hash = k and high(t.data)
while t.data[h].key != nil: while t.data[h].key != nil:
if (t.data[h].h == k) and treesEquivalent(t.data[h].key, key): if (t.data[h].h == k) and treesEquivalent(t.data[h].key, key):
return h return h
h = nextTry(h, high(t.data)) h = nextTry(h, high(t.data))
result = -1 result = -1
proc nodeTableGet*(t: TNodeTable, key: PNode): int = proc nodeTableGet*(t: TNodeTable, key: PNode): int =
var index = nodeTableRawGet(t, hashTree(key), key) var index = nodeTableRawGet(t, hashTree(key), key)
if index >= 0: result = t.data[index].val if index >= 0: result = t.data[index].val
else: result = low(int) else: result = low(int)
proc nodeTableRawInsert(data: var TNodePairSeq, k: Hash, key: PNode, proc nodeTableRawInsert(data: var TNodePairSeq, k: Hash, key: PNode,
val: int) = val: int) =
var h: Hash = k and high(data) var h: Hash = k and high(data)
while data[h].key != nil: h = nextTry(h, high(data)) while data[h].key != nil: h = nextTry(h, high(data))
assert(data[h].key == nil) assert(data[h].key == nil)
@ -75,35 +75,35 @@ proc nodeTableRawInsert(data: var TNodePairSeq, k: Hash, key: PNode,
data[h].key = key data[h].key = key
data[h].val = val data[h].val = val
proc nodeTablePut*(t: var TNodeTable, key: PNode, val: int) = proc nodeTablePut*(t: var TNodeTable, key: PNode, val: int) =
var n: TNodePairSeq var n: TNodePairSeq
var k: Hash = hashTree(key) var k: Hash = hashTree(key)
var index = nodeTableRawGet(t, k, key) var index = nodeTableRawGet(t, k, key)
if index >= 0: if index >= 0:
assert(t.data[index].key != nil) assert(t.data[index].key != nil)
t.data[index].val = val t.data[index].val = val
else: else:
if mustRehash(len(t.data), t.counter): if mustRehash(len(t.data), t.counter):
newSeq(n, len(t.data) * GrowthFactor) newSeq(n, len(t.data) * GrowthFactor)
for i in countup(0, high(t.data)): for i in countup(0, high(t.data)):
if t.data[i].key != nil: if t.data[i].key != nil:
nodeTableRawInsert(n, t.data[i].h, t.data[i].key, t.data[i].val) nodeTableRawInsert(n, t.data[i].h, t.data[i].key, t.data[i].val)
swap(t.data, n) swap(t.data, n)
nodeTableRawInsert(t.data, k, key, val) nodeTableRawInsert(t.data, k, key, val)
inc(t.counter) inc(t.counter)
proc nodeTableTestOrSet*(t: var TNodeTable, key: PNode, val: int): int = proc nodeTableTestOrSet*(t: var TNodeTable, key: PNode, val: int): int =
var n: TNodePairSeq var n: TNodePairSeq
var k: Hash = hashTree(key) var k: Hash = hashTree(key)
var index = nodeTableRawGet(t, k, key) var index = nodeTableRawGet(t, k, key)
if index >= 0: if index >= 0:
assert(t.data[index].key != nil) assert(t.data[index].key != nil)
result = t.data[index].val result = t.data[index].val
else: else:
if mustRehash(len(t.data), t.counter): if mustRehash(len(t.data), t.counter):
newSeq(n, len(t.data) * GrowthFactor) newSeq(n, len(t.data) * GrowthFactor)
for i in countup(0, high(t.data)): for i in countup(0, high(t.data)):
if t.data[i].key != nil: if t.data[i].key != nil:
nodeTableRawInsert(n, t.data[i].h, t.data[i].key, t.data[i].val) nodeTableRawInsert(n, t.data[i].h, t.data[i].key, t.data[i].val)
swap(t.data, n) swap(t.data, n)
nodeTableRawInsert(t.data, k, key, val) nodeTableRawInsert(t.data, k, key, val)

View file

@ -9,5 +9,4 @@ Mario Ray Mahardhika
Alex Mitchell Alex Mitchell
Dominik Picheta Dominik Picheta
Jonathan Plona Jonathan Plona
Alexander Rødseth Alexander Rødseth

View file

@ -2,17 +2,17 @@
Nim -- a Compiler for Nim. http://nim-lang.org/ Nim -- a Compiler for Nim. http://nim-lang.org/
Copyright (C) 2006-2015 Andreas Rumpf. All rights reserved. Copyright (C) 2006-2015 Andreas Rumpf. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software. all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@ -21,4 +21,4 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
[ MIT license: http://www.opensource.org/licenses/mit-license.php ] [ MIT license: http://www.opensource.org/licenses/mit-license.php ]

View file

@ -20,7 +20,7 @@ English word To use Notes
------------------- ------------ -------------------------------------- ------------------- ------------ --------------------------------------
initialize initT ``init`` is used to create a initialize initT ``init`` is used to create a
value type ``T`` value type ``T``
new newP ``new`` is used to create a new newP ``new`` is used to create a
reference type ``P`` reference type ``P``
find find should return the position where find find should return the position where
something was found; for a bool result something was found; for a bool result

View file

@ -28,7 +28,7 @@ The documentation consists of several documents:
builtin templating system. builtin templating system.
- | `Term rewriting macros <trmacros.html>`_ - | `Term rewriting macros <trmacros.html>`_
| Term rewriting macros enhance the compilation process with user defined | Term rewriting macros enhance the compilation process with user defined
optimizations. optimizations.
- | `Internal documentation <intern.html>`_ - | `Internal documentation <intern.html>`_

View file

@ -1,203 +1,203 @@
============================================== ==============================================
Embedded Nim Debugger (ENDB) User Guide Embedded Nim Debugger (ENDB) User Guide
============================================== ==============================================
:Author: Andreas Rumpf :Author: Andreas Rumpf
:Version: |nimversion| :Version: |nimversion|
.. contents:: .. contents::
**WARNING**: ENDB is not maintained anymore! Please help if you're interested **WARNING**: ENDB is not maintained anymore! Please help if you're interested
in this tool. in this tool.
Nim comes with a platform independent debugger - Nim comes with a platform independent debugger -
the Embedded Nim Debugger (ENDB). The debugger is the Embedded Nim Debugger (ENDB). The debugger is
*embedded* into your executable if it has been *embedded* into your executable if it has been
compiled with the ``--debugger:on`` command line option. compiled with the ``--debugger:on`` command line option.
This also defines the conditional symbol ``ENDB`` for you. This also defines the conditional symbol ``ENDB`` for you.
Note: You must not compile your program with the ``--app:gui`` Note: You must not compile your program with the ``--app:gui``
command line option because then there would be no console command line option because then there would be no console
available for the debugger. available for the debugger.
If you start your program the debugger will immediately show If you start your program the debugger will immediately show
a prompt on the console. You can now enter a command. The next sections a prompt on the console. You can now enter a command. The next sections
deal with the possible commands. As usual in Nim in all commands deal with the possible commands. As usual in Nim in all commands
underscores and case do not matter. Optional components of a command underscores and case do not matter. Optional components of a command
are listed in brackets ``[...]`` here. are listed in brackets ``[...]`` here.
General Commands General Commands
================ ================
``h``, ``help`` ``h``, ``help``
Display a quick reference of the possible commands. Display a quick reference of the possible commands.
``q``, ``quit`` ``q``, ``quit``
Quit the debugger and the program. Quit the debugger and the program.
<ENTER> <ENTER>
(Without any typed command) repeat the previous debugger command. (Without any typed command) repeat the previous debugger command.
If there is no previous command, ``step_into`` is assumed. If there is no previous command, ``step_into`` is assumed.
Executing Commands Executing Commands
================== ==================
``s``, ``step_into`` ``s``, ``step_into``
Single step, stepping into routine calls. Single step, stepping into routine calls.
``n``, ``step_over`` ``n``, ``step_over``
Single step, without stepping into routine calls. Single step, without stepping into routine calls.
``f``, ``skip_current`` ``f``, ``skip_current``
Continue execution until the current routine finishes. Continue execution until the current routine finishes.
``c``, ``continue`` ``c``, ``continue``
Continue execution until the next breakpoint. Continue execution until the next breakpoint.
``i``, ``ignore`` ``i``, ``ignore``
Continue execution, ignore all breakpoints. This effectively quits Continue execution, ignore all breakpoints. This effectively quits
the debugger and runs the program until it finishes. the debugger and runs the program until it finishes.
Breakpoint Commands Breakpoint Commands
=================== ===================
``b``, ``setbreak`` [fromline [toline]] [file] ``b``, ``setbreak`` [fromline [toline]] [file]
Set a new breakpoint for the given file Set a new breakpoint for the given file
and line numbers. If no file is given, the current execution point's and line numbers. If no file is given, the current execution point's
filename is used. If the filename has no extension, ``.nim`` is filename is used. If the filename has no extension, ``.nim`` is
appended for your convenience. appended for your convenience.
If no line numbers are given, the current execution point's If no line numbers are given, the current execution point's
line is used. If both ``fromline`` and ``toline`` are given the line is used. If both ``fromline`` and ``toline`` are given the
breakpoint contains a line number range. Some examples if it is still breakpoint contains a line number range. Some examples if it is still
unclear: unclear:
* ``b 12 15 thallo`` creates a breakpoint that * ``b 12 15 thallo`` creates a breakpoint that
will be triggered if the instruction pointer reaches one of the will be triggered if the instruction pointer reaches one of the
lines 12-15 in the file ``thallo.nim``. lines 12-15 in the file ``thallo.nim``.
* ``b 12 thallo`` creates a breakpoint that * ``b 12 thallo`` creates a breakpoint that
will be triggered if the instruction pointer reaches the will be triggered if the instruction pointer reaches the
line 12 in the file ``thallo.nim``. line 12 in the file ``thallo.nim``.
* ``b 12`` creates a breakpoint that * ``b 12`` creates a breakpoint that
will be triggered if the instruction pointer reaches the will be triggered if the instruction pointer reaches the
line 12 in the current file. line 12 in the current file.
* ``b`` creates a breakpoint that * ``b`` creates a breakpoint that
will be triggered if the instruction pointer reaches the will be triggered if the instruction pointer reaches the
current line in the current file again. current line in the current file again.
``breakpoints`` ``breakpoints``
Display the entire breakpoint list. Display the entire breakpoint list.
``disable`` <identifier> ``disable`` <identifier>
Disable a breakpoint. It remains disabled until you turn it on again Disable a breakpoint. It remains disabled until you turn it on again
with the ``enable`` command. with the ``enable`` command.
``enable`` <identifier> ``enable`` <identifier>
Enable a breakpoint. Enable a breakpoint.
Often it happens when debugging that you keep retyping the breakpoints again Often it happens when debugging that you keep retyping the breakpoints again
and again because they are lost when you restart your program. This is not and again because they are lost when you restart your program. This is not
necessary: A special pragma has been defined for this: necessary: A special pragma has been defined for this:
The ``breakpoint`` pragma The ``breakpoint`` pragma
------------------------- -------------------------
The ``breakpoint`` pragma is syntactically a statement. It can be used The ``breakpoint`` pragma is syntactically a statement. It can be used
to mark the *following line* as a breakpoint: to mark the *following line* as a breakpoint:
.. code-block:: Nim .. code-block:: Nim
write("1") write("1")
{.breakpoint: "before_write_2".} {.breakpoint: "before_write_2".}
write("2") write("2")
The name of the breakpoint here is ``before_write_2``. Of course the The name of the breakpoint here is ``before_write_2``. Of course the
breakpoint's name is optional - the compiler will generate one for you breakpoint's name is optional - the compiler will generate one for you
if you leave it out. if you leave it out.
Code for the ``breakpoint`` pragma is only generated if the debugger Code for the ``breakpoint`` pragma is only generated if the debugger
is turned on, so you don't need to remove it from your source code after is turned on, so you don't need to remove it from your source code after
debugging. debugging.
The ``watchpoint`` pragma The ``watchpoint`` pragma
------------------------- -------------------------
The ``watchpoint`` pragma is syntactically a statement. It can be used The ``watchpoint`` pragma is syntactically a statement. It can be used
to mark a location as a watchpoint: to mark a location as a watchpoint:
.. code-block:: Nim .. code-block:: Nim
var a: array [0..20, int] var a: array [0..20, int]
{.watchpoint: a[3].} {.watchpoint: a[3].}
for i in 0 .. 20: a[i] = i for i in 0 .. 20: a[i] = i
ENDB then writes a stack trace whenever the content of the location ``a[3]`` ENDB then writes a stack trace whenever the content of the location ``a[3]``
changes. The current implementation only tracks a hash value of the location's changes. The current implementation only tracks a hash value of the location's
contents and so locations that are not word sized may encounter false contents and so locations that are not word sized may encounter false
negatives in very rare cases. negatives in very rare cases.
Code for the ``watchpoint`` pragma is only generated if the debugger Code for the ``watchpoint`` pragma is only generated if the debugger
is turned on, so you don't need to remove it from your source code after is turned on, so you don't need to remove it from your source code after
debugging. debugging.
Due to the primitive implementation watchpoints are even slower than Due to the primitive implementation watchpoints are even slower than
breakpoints: After *every* executed Nim code line it is checked whether the breakpoints: After *every* executed Nim code line it is checked whether the
location changed. location changed.
Data Display Commands Data Display Commands
===================== =====================
``e``, ``eval`` <exp> ``e``, ``eval`` <exp>
Evaluate the expression <exp>. Note that ENDB has no full-blown expression Evaluate the expression <exp>. Note that ENDB has no full-blown expression
evaluator built-in. So expressions are limited: evaluator built-in. So expressions are limited:
* To display global variables prefix their names with their * To display global variables prefix their names with their
owning module: ``nim1.globalVar`` owning module: ``nim1.globalVar``
* To display local variables or parameters just type in * To display local variables or parameters just type in
their name: ``localVar``. If you want to inspect variables that are not their name: ``localVar``. If you want to inspect variables that are not
in the current stack frame, use the ``up`` or ``down`` command. in the current stack frame, use the ``up`` or ``down`` command.
Unfortunately, only inspecting variables is possible at the moment. Maybe Unfortunately, only inspecting variables is possible at the moment. Maybe
a future version will implement a full-blown Nim expression evaluator, a future version will implement a full-blown Nim expression evaluator,
but this is not easy to do and would bloat the debugger's code. but this is not easy to do and would bloat the debugger's code.
Since displaying the whole data structures is often not needed and Since displaying the whole data structures is often not needed and
painfully slow, the debugger uses a *maximal display depth* concept for painfully slow, the debugger uses a *maximal display depth* concept for
displaying. displaying.
You can alter the maximal display depth with the ``maxdisplay`` You can alter the maximal display depth with the ``maxdisplay``
command. command.
``maxdisplay`` <natural> ``maxdisplay`` <natural>
Sets the maximal display depth to the given integer value. A value of 0 Sets the maximal display depth to the given integer value. A value of 0
means there is no maximal display depth. Default is 3. means there is no maximal display depth. Default is 3.
``o``, ``out`` <filename> <exp> ``o``, ``out`` <filename> <exp>
Evaluate the expression <exp> and store its string representation into a Evaluate the expression <exp> and store its string representation into a
file named <filename>. If the file does not exist, it will be created, file named <filename>. If the file does not exist, it will be created,
otherwise it will be opened for appending. otherwise it will be opened for appending.
``w``, ``where`` ``w``, ``where``
Display the current execution point. Display the current execution point.
``u``, ``up`` ``u``, ``up``
Go up in the call stack. Go up in the call stack.
``d``, ``down`` ``d``, ``down``
Go down in the call stack. Go down in the call stack.
``stackframe`` [file] ``stackframe`` [file]
Displays the content of the current stack frame in ``stdout`` or Displays the content of the current stack frame in ``stdout`` or
appends it to the file, depending on whether a file is given. appends it to the file, depending on whether a file is given.
``callstack`` ``callstack``
Display the entire call stack (but not its content). Display the entire call stack (but not its content).
``l``, ``locals`` ``l``, ``locals``
Display the available local variables in the current stack frame. Display the available local variables in the current stack frame.
``g``, ``globals`` ``g``, ``globals``
Display all the global variables that are available for inspection. Display all the global variables that are available for inspection.

View file

@ -1,30 +1,30 @@
=================================================== ===================================================
Embedded Stack Trace Profiler (ESTP) User Guide Embedded Stack Trace Profiler (ESTP) User Guide
=================================================== ===================================================
:Author: Andreas Rumpf
:Version: |nimversion|
Nim comes with a platform independent profiler -
the Embedded Stack Trace Profiler (ESTP). The profiler
is *embedded* into your executable. To activate the profiler you need to do:
* compile your program with the ``--profiler:on --stackTrace:on`` command :Author: Andreas Rumpf
:Version: |nimversion|
Nim comes with a platform independent profiler -
the Embedded Stack Trace Profiler (ESTP). The profiler
is *embedded* into your executable. To activate the profiler you need to do:
* compile your program with the ``--profiler:on --stackTrace:on`` command
line options line options
* import the ``nimprof`` module * import the ``nimprof`` module
* run your program as usual. * run your program as usual.
You can in fact look at ``nimprof``'s source code to see how to implement You can in fact look at ``nimprof``'s source code to see how to implement
your own profiler. your own profiler.
The setting ``--profiler:on`` defines the conditional symbol ``profiler``. The setting ``--profiler:on`` defines the conditional symbol ``profiler``.
After your program has finished the profiler will create a After your program has finished the profiler will create a
file ``profile_results.txt`` containing the profiling results. file ``profile_results.txt`` containing the profiling results.
Since the profiler works by examining stack traces, it's essential that Since the profiler works by examining stack traces, it's essential that
the option ``--stackTrace:on`` is active! Unfortunately this means that a the option ``--stackTrace:on`` is active! Unfortunately this means that a
profiling build is much slower than a release build. profiling build is much slower than a release build.
@ -32,7 +32,7 @@ Memory profiler
=============== ===============
You can also use ESTP as a memory profiler to see which stack traces allocate You can also use ESTP as a memory profiler to see which stack traces allocate
the most memory and thus create the most GC pressure. It may also help to the most memory and thus create the most GC pressure. It may also help to
find memory leaks. To activate the memory profiler you need to do: find memory leaks. To activate the memory profiler you need to do:
* compile your program with the ``--profiler:off --stackTrace:on -d:memProfiler`` * compile your program with the ``--profiler:off --stackTrace:on -d:memProfiler``
@ -40,22 +40,22 @@ find memory leaks. To activate the memory profiler you need to do:
* import the ``nimprof`` module * import the ``nimprof`` module
* run your program as usual. * run your program as usual.
Define the symbol ``ignoreAllocationSize`` so that only the number of Define the symbol ``ignoreAllocationSize`` so that only the number of
allocations is counted and the sizes of the memory allocations do not matter. allocations is counted and the sizes of the memory allocations do not matter.
Example results file Example results file
==================== ====================
The results file lists stack traces ordered by significance. The results file lists stack traces ordered by significance.
The following example file has been generated by profiling the Nim compiler The following example file has been generated by profiling the Nim compiler
itself: It shows that in total 5.4% of the runtime has been spent itself: It shows that in total 5.4% of the runtime has been spent
in ``crcFromRope`` or its children. in ``crcFromRope`` or its children.
In general the stack traces show you immediately where the problem is because In general the stack traces show you immediately where the problem is because
the trace acts like an explanation; in traditional profilers you can only find the trace acts like an explanation; in traditional profilers you can only find
expensive leaf functions easily but the *reason* why they are invoked expensive leaf functions easily but the *reason* why they are invoked
often remains mysterious. often remains mysterious.
:: ::

View file

@ -4,8 +4,8 @@ Source Code Filters
.. contents:: .. contents::
A `Source Code Filter` transforms the input character stream to an in-memory A `Source Code Filter` transforms the input character stream to an in-memory
output stream before parsing. A filter can be used to provide templating output stream before parsing. A filter can be used to provide templating
systems or preprocessors. systems or preprocessors.
To use a filter for a source file the *shebang* notation is used:: To use a filter for a source file the *shebang* notation is used::
@ -52,7 +52,7 @@ Parameters and their defaults:
``sub: string = ""`` ``sub: string = ""``
the substring that is searched for the substring that is searched for
``by: string = ""`` ``by: string = ""``
the string the substring is replaced with the string the substring is replaced with
@ -71,7 +71,7 @@ Parameters and their defaults:
``leading: bool = true`` ``leading: bool = true``
strip leading whitespace strip leading whitespace
``trailing: bool = true`` ``trailing: bool = true``
strip trailing whitespace strip trailing whitespace
@ -89,16 +89,16 @@ Parameters and their defaults:
``metaChar: char = '#'`` ``metaChar: char = '#'``
prefix for a line that contains Nim code prefix for a line that contains Nim code
``subsChar: char = '$'`` ``subsChar: char = '$'``
prefix for a Nim expression within a template line prefix for a Nim expression within a template line
``conc: string = " & "`` ``conc: string = " & "``
the operation for concatenation the operation for concatenation
``emit: string = "result.add"`` ``emit: string = "result.add"``
the operation to emit a string literal the operation to emit a string literal
``toString: string = "$"`` ``toString: string = "$"``
the operation that is applied to each expression the operation that is applied to each expression
@ -106,7 +106,7 @@ Example::
#! stdtmpl | standard #! stdtmpl | standard
#proc generateHTMLPage(title, currentTab, content: string, #proc generateHTMLPage(title, currentTab, content: string,
# tabs: openArray[string]): string = # tabs: openArray[string]): string =
# result = "" # result = ""
<head><title>$title</title></head> <head><title>$title</title></head>
<body> <body>
@ -114,7 +114,7 @@ Example::
<ul> <ul>
#for tab in items(tabs): #for tab in items(tabs):
#if currentTab == tab: #if currentTab == tab:
<li><a id="selected" <li><a id="selected"
#else: #else:
<li><a <li><a
#end if #end if
@ -132,11 +132,11 @@ The filter transforms this into:
.. code-block:: nim .. code-block:: nim
proc generateHTMLPage(title, currentTab, content: string, proc generateHTMLPage(title, currentTab, content: string,
tabs: openArray[string]): string = tabs: openArray[string]): string =
result = "" result = ""
result.add("<head><title>" & $(title) & "</title></head>\n" & result.add("<head><title>" & $(title) & "</title></head>\n" &
"<body>\n" & "<body>\n" &
" <div id=\"menu\">\n" & " <div id=\"menu\">\n" &
" <ul>\n") " <ul>\n")
for tab in items(tabs): for tab in items(tabs):
if currentTab == tab: if currentTab == tab:
@ -146,17 +146,17 @@ The filter transforms this into:
#end #end
result.add(" href=\"" & $(tab) & ".html\">" & $(tab) & "</a></li>\n") result.add(" href=\"" & $(tab) & ".html\">" & $(tab) & "</a></li>\n")
#end #end
result.add(" </ul>\n" & result.add(" </ul>\n" &
" </div>\n" & " </div>\n" &
" <div id=\"content\">\n" & " <div id=\"content\">\n" &
" " & $(content) & "\n" & " " & $(content) & "\n" &
" A dollar: $.\n" & " A dollar: $.\n" &
" </div>\n" & " </div>\n" &
"</body>\n") "</body>\n")
Each line that does not start with the meta character (ignoring leading Each line that does not start with the meta character (ignoring leading
whitespace) is converted to a string literal that is added to ``result``. whitespace) is converted to a string literal that is added to ``result``.
The substitution character introduces a Nim expression *e* within the The substitution character introduces a Nim expression *e* within the
string literal. *e* is converted to a string with the *toString* operation string literal. *e* is converted to a string with the *toString* operation
@ -174,14 +174,14 @@ writes the template code directly to a file::
#! stdtmpl(emit="f.write") | standard #! stdtmpl(emit="f.write") | standard
#proc writeHTMLPage(f: File, title, currentTab, content: string, #proc writeHTMLPage(f: File, title, currentTab, content: string,
# tabs: openArray[string]) = # tabs: openArray[string]) =
<head><title>$title</title></head> <head><title>$title</title></head>
<body> <body>
<div id="menu"> <div id="menu">
<ul> <ul>
#for tab in items(tabs): #for tab in items(tabs):
#if currentTab == tab: #if currentTab == tab:
<li><a id="selected" <li><a id="selected"
#else: #else:
<li><a <li><a
#end if #end if

View file

@ -37,13 +37,13 @@ The cycle collector can be en-/disabled independently from the other parts of
the GC with ``GC_enableMarkAndSweep`` and ``GC_disableMarkAndSweep``. The the GC with ``GC_enableMarkAndSweep`` and ``GC_disableMarkAndSweep``. The
compiler analyses the types for their possibility to build cycles, but often compiler analyses the types for their possibility to build cycles, but often
it is necessary to help this analysis with the ``acyclic`` pragma (see it is necessary to help this analysis with the ``acyclic`` pragma (see
`acyclic <manual.html#acyclic-pragma>`_ for further information). `acyclic <manual.html#acyclic-pragma>`_ for further information).
You can also use the ``acyclic`` pragma for data that is cyclic in reality and You can also use the ``acyclic`` pragma for data that is cyclic in reality and
then break up the cycles explicitly with ``GC_addCycleRoot``. This can be a then break up the cycles explicitly with ``GC_addCycleRoot``. This can be a
very valuable optimization; the Nim compiler itself relies on this very valuable optimization; the Nim compiler itself relies on this
optimization trick to improve performance. Note that ``GC_addCycleRoot`` is optimization trick to improve performance. Note that ``GC_addCycleRoot`` is
a quick operation; the root is only registered for the next run of the a quick operation; the root is only registered for the next run of the
cycle collector. cycle collector.
@ -51,7 +51,7 @@ Realtime support
================ ================
To enable realtime support, the symbol `useRealtimeGC`:idx: needs to be To enable realtime support, the symbol `useRealtimeGC`:idx: needs to be
defined via ``--define:useRealtimeGC`` (you can put this into your config defined via ``--define:useRealtimeGC`` (you can put this into your config
file as well). With this switch the GC supports the following operations: file as well). With this switch the GC supports the following operations:
.. code-block:: nim .. code-block:: nim
@ -60,21 +60,21 @@ file as well). With this switch the GC supports the following operations:
The unit of the parameters ``MaxPauseInUs`` and ``us`` is microseconds. The unit of the parameters ``MaxPauseInUs`` and ``us`` is microseconds.
These two procs are the two modus operandi of the realtime GC: These two procs are the two modus operandi of the realtime GC:
(1) GC_SetMaxPause Mode (1) GC_SetMaxPause Mode
You can call ``GC_SetMaxPause`` at program startup and then each triggered You can call ``GC_SetMaxPause`` at program startup and then each triggered
GC run tries to not take longer than ``MaxPause`` time. However, it is GC run tries to not take longer than ``MaxPause`` time. However, it is
possible (and common) that the work is nevertheless not evenly distributed possible (and common) that the work is nevertheless not evenly distributed
as each call to ``new`` can trigger the GC and thus take ``MaxPause`` as each call to ``new`` can trigger the GC and thus take ``MaxPause``
time. time.
(2) GC_step Mode (2) GC_step Mode
This allows the GC to perform some work for up to ``us`` time. This is This allows the GC to perform some work for up to ``us`` time. This is
useful to call in a main loop to ensure the GC can do its work. To useful to call in a main loop to ensure the GC can do its work. To
bind all GC activity to a ``GC_step`` call, deactivate the GC with bind all GC activity to a ``GC_step`` call, deactivate the GC with
``GC_disable`` at program startup. ``GC_disable`` at program startup.
These procs provide a "best effort" realtime guarantee; in particular the These procs provide a "best effort" realtime guarantee; in particular the
@ -87,7 +87,7 @@ is triggered.
Time measurement Time measurement
---------------- ----------------
The GC's way of measuring time uses (see ``lib/system/timers.nim`` for the The GC's way of measuring time uses (see ``lib/system/timers.nim`` for the
implementation): implementation):
1) ``QueryPerformanceCounter`` and ``QueryPerformanceFrequency`` on Windows. 1) ``QueryPerformanceCounter`` and ``QueryPerformanceFrequency`` on Windows.
@ -95,7 +95,7 @@ implementation):
3) ``gettimeofday`` on Posix systems. 3) ``gettimeofday`` on Posix systems.
As such it supports a resolution of nanoseconds internally; however the API As such it supports a resolution of nanoseconds internally; however the API
uses microseconds for convenience. uses microseconds for convenience.
Define the symbol ``reportMissedDeadlines`` to make the GC output whenever it Define the symbol ``reportMissedDeadlines`` to make the GC output whenever it
@ -106,7 +106,7 @@ later versions of the collector.
Tweaking the GC Tweaking the GC
--------------- ---------------
The collector checks whether there is still time left for its work after The collector checks whether there is still time left for its work after
every ``workPackage``'th iteration. This is currently set to 100 which means every ``workPackage``'th iteration. This is currently set to 100 which means
that up to 100 objects are traversed and freed before it checks again. Thus that up to 100 objects are traversed and freed before it checks again. Thus
``workPackage`` affects the timing granularity and may need to be tweaked in ``workPackage`` affects the timing granularity and may need to be tweaked in

View file

@ -22,8 +22,8 @@ Path Purpose
``bin`` generated binary files ``bin`` generated binary files
``build`` generated C code for the installation ``build`` generated C code for the installation
``compiler`` the Nim compiler itself; note that this ``compiler`` the Nim compiler itself; note that this
code has been translated from a bootstrapping code has been translated from a bootstrapping
version written in Pascal, so the code is **not** version written in Pascal, so the code is **not**
a poster child of good Nim code a poster child of good Nim code
``config`` configuration files for Nim ``config`` configuration files for Nim
``dist`` additional packages for the distribution ``dist`` additional packages for the distribution
@ -38,8 +38,8 @@ Path Purpose
Bootstrapping the compiler Bootstrapping the compiler
========================== ==========================
As of version 0.8.5 the compiler is maintained in Nim. (The first versions As of version 0.8.5 the compiler is maintained in Nim. (The first versions
have been implemented in Object Pascal.) The Python-based build system has have been implemented in Object Pascal.) The Python-based build system has
been rewritten in Nim too. been rewritten in Nim too.
Compiling the compiler is a simple matter of running:: Compiling the compiler is a simple matter of running::
@ -121,7 +121,7 @@ Look at the file ``lib/system/hti.nim`` for more information.
Debugging the compiler Debugging the compiler
====================== ======================
You can of course use GDB or Visual Studio to debug the You can of course use GDB or Visual Studio to debug the
compiler (via ``--debuginfo --lineDir:on``). However, there compiler (via ``--debuginfo --lineDir:on``). However, there
are also lots of procs that aid in debugging: are also lots of procs that aid in debugging:
@ -180,7 +180,7 @@ children. Types and symbols are represented by other nodes, because they
may contain cycles. The AST changes its shape after semantic checking. This may contain cycles. The AST changes its shape after semantic checking. This
is needed to make life easier for the code generators. See the "ast" module is needed to make life easier for the code generators. See the "ast" module
for the type definitions. The `macros <macros.html>`_ module contains many for the type definitions. The `macros <macros.html>`_ module contains many
examples how the AST represents each syntactic structure. examples how the AST represents each syntactic structure.
How the RTL is compiled How the RTL is compiled
@ -202,7 +202,7 @@ Compilation cache
================= =================
The implementation of the compilation cache is tricky: There are lots The implementation of the compilation cache is tricky: There are lots
of issues to be solved for the front- and backend. In the following of issues to be solved for the front- and backend. In the following
sections *global* means *shared between modules* or *property of the whole sections *global* means *shared between modules* or *property of the whole
program*. program*.
@ -214,31 +214,31 @@ Methods and type converters
~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
Nim contains language features that are *global*. The best example for that Nim contains language features that are *global*. The best example for that
are multi methods: Introducing a new method with the same name and some are multi methods: Introducing a new method with the same name and some
compatible object parameter means that the method's dispatcher needs to take compatible object parameter means that the method's dispatcher needs to take
the new method into account. So the dispatching logic is only completely known the new method into account. So the dispatching logic is only completely known
after the whole program has been translated! after the whole program has been translated!
Other features that are *implicitly* triggered cause problems for modularity Other features that are *implicitly* triggered cause problems for modularity
too. Type converters fall into this category: too. Type converters fall into this category:
.. code-block:: nim .. code-block:: nim
# module A # module A
converter toBool(x: int): bool = converter toBool(x: int): bool =
result = x != 0 result = x != 0
.. code-block:: nim .. code-block:: nim
# module B # module B
import A import A
if 1: if 1:
echo "ugly, but should work" echo "ugly, but should work"
If in the above example module ``B`` is re-compiled, but ``A`` is not then If in the above example module ``B`` is re-compiled, but ``A`` is not then
``B`` needs to be aware of ``toBool`` even though ``toBool`` is not referenced ``B`` needs to be aware of ``toBool`` even though ``toBool`` is not referenced
in ``B`` *explicitly*. in ``B`` *explicitly*.
Both the multi method and the type converter problems are solved by storing Both the multi method and the type converter problems are solved by storing
them in special sections in the ROD file that are loaded *unconditionally* them in special sections in the ROD file that are loaded *unconditionally*
when the ROD file is read. when the ROD file is read.
@ -266,7 +266,7 @@ Backend issues
- Emulated thread vars are global. - Emulated thread vars are global.
However the biggest problem is that dead code elimination breaks modularity! However the biggest problem is that dead code elimination breaks modularity!
To see why, consider this scenario: The module ``G`` (for example the huge To see why, consider this scenario: The module ``G`` (for example the huge
Gtk2 module...) is compiled with dead code elimination turned on. So none Gtk2 module...) is compiled with dead code elimination turned on. So none
of ``G``'s procs is generated at all. of ``G``'s procs is generated at all.
@ -274,13 +274,13 @@ of ``G``'s procs is generated at all.
Then module ``B`` is compiled that requires ``G.P1``. Ok, no problem, Then module ``B`` is compiled that requires ``G.P1``. Ok, no problem,
``G.P1`` is loaded from the symbol file and ``G.c`` now contains ``G.P1``. ``G.P1`` is loaded from the symbol file and ``G.c`` now contains ``G.P1``.
Then module ``A`` (that depends onto ``B`` and ``G``) is compiled and ``B`` Then module ``A`` (that depends onto ``B`` and ``G``) is compiled and ``B``
and ``G`` are left unchanged. ``A`` requires ``G.P2``. and ``G`` are left unchanged. ``A`` requires ``G.P2``.
So now ``G.c`` MUST contain both ``P1`` and ``P2``, but we haven't even So now ``G.c`` MUST contain both ``P1`` and ``P2``, but we haven't even
loaded ``P1`` from the symbol file, nor do we want to because we then quickly loaded ``P1`` from the symbol file, nor do we want to because we then quickly
would restore large parts of the whole program. But we also don't want to would restore large parts of the whole program. But we also don't want to
store ``P1`` in ``B.c`` because that would mean to store every symbol where store ``P1`` in ``B.c`` because that would mean to store every symbol where
it is referred from which ultimately means the main module and putting it is referred from which ultimately means the main module and putting
everything in a single C file. everything in a single C file.
@ -290,7 +290,7 @@ that is implemented in the C code generator (have a look at the ``ccgmerge``
module). The merging may lead to *cruft* (aka dead code) in generated C code module). The merging may lead to *cruft* (aka dead code) in generated C code
which can only be removed by recompiling a project with the compilation cache which can only be removed by recompiling a project with the compilation cache
turned off. Nevertheless the merge solution is way superior to the turned off. Nevertheless the merge solution is way superior to the
cheap solution "turn off dead code elimination if the compilation cache is cheap solution "turn off dead code elimination if the compilation cache is
turned on". turned on".
@ -394,7 +394,7 @@ Consider this example:
# r is on the stack # r is on the stack
setRef(r.left) # here we should update the refcounts! setRef(r.left) # here we should update the refcounts!
We have to decide at runtime whether the reference is on the stack or not. We have to decide at runtime whether the reference is on the stack or not.
The generated code looks roughly like this: The generated code looks roughly like this:
.. code-block:: C .. code-block:: C
@ -422,7 +422,7 @@ Design
A ``closure`` proc var can call ordinary procs of the default Nim calling A ``closure`` proc var can call ordinary procs of the default Nim calling
convention. But not the other way round! A closure is implemented as a convention. But not the other way round! A closure is implemented as a
``tuple[prc, env]``. ``env`` can be nil implying a call without a closure. ``tuple[prc, env]``. ``env`` can be nil implying a call without a closure.
This means that a call through a closure generates an ``if`` but the This means that a call through a closure generates an ``if`` but the
interoperability is worth the cost of the ``if``. Thunk generation would be interoperability is worth the cost of the ``if``. Thunk generation would be
possible too, but it's slightly more effort to implement. possible too, but it's slightly more effort to implement.
@ -430,7 +430,7 @@ Tests with GCC on Amd64 showed that it's really beneficical if the
'environment' pointer is passed as the last argument, not as the first argument. 'environment' pointer is passed as the last argument, not as the first argument.
Proper thunk generation is harder because the proc that is to wrap Proper thunk generation is harder because the proc that is to wrap
could stem from a complex expression: could stem from a complex expression:
.. code-block:: nim .. code-block:: nim
receivesClosure(returnsDefaultCC[i]) receivesClosure(returnsDefaultCC[i])
@ -438,15 +438,15 @@ could stem from a complex expression:
A thunk would need to call 'returnsDefaultCC[i]' somehow and that would require A thunk would need to call 'returnsDefaultCC[i]' somehow and that would require
an *additional* closure generation... Ok, not really, but it requires to pass an *additional* closure generation... Ok, not really, but it requires to pass
the function to call. So we'd end up with 2 indirect calls instead of one. the function to call. So we'd end up with 2 indirect calls instead of one.
Another much more severe problem which this solution is that it's not GC-safe Another much more severe problem which this solution is that it's not GC-safe
to pass a proc pointer around via a generic ``ref`` type. to pass a proc pointer around via a generic ``ref`` type.
Example code: Example code:
.. code-block:: nim .. code-block:: nim
proc add(x: int): proc (y: int): int {.closure.} = proc add(x: int): proc (y: int): int {.closure.} =
return proc (y: int): int = return proc (y: int): int =
return x + y return x + y
var add2 = add(2) var add2 = add(2)
@ -458,16 +458,16 @@ This should produce roughly this code:
type type
PEnv = ref object PEnv = ref object
x: int # data x: int # data
proc anon(y: int, c: PClosure): int = proc anon(y: int, c: PClosure): int =
return y + c.x return y + c.x
proc add(x: int): tuple[prc, data] = proc add(x: int): tuple[prc, data] =
var env: PEnv var env: PEnv
new env new env
env.x = x env.x = x
result = (anon, env) result = (anon, env)
var add2 = add(2) var add2 = add(2)
let tmp = if add2.data == nil: add2.prc(5) else: add2.prc(5, add2.data) let tmp = if add2.data == nil: add2.prc(5) else: add2.prc(5, add2.data)
echo tmp echo tmp
@ -476,9 +476,9 @@ This should produce roughly this code:
Beware of nesting: Beware of nesting:
.. code-block:: nim .. code-block:: nim
proc add(x: int): proc (y: int): proc (z: int): int {.closure.} {.closure.} = proc add(x: int): proc (y: int): proc (z: int): int {.closure.} {.closure.} =
return lamba (y: int): proc (z: int): int {.closure.} = return lamba (y: int): proc (z: int): int {.closure.} =
return lambda (z: int): int = return lambda (z: int): int =
return x + y + z return x + y + z
var add24 = add(2)(4) var add24 = add(2)(4)
@ -490,26 +490,26 @@ This should produce roughly this code:
type type
PEnvX = ref object PEnvX = ref object
x: int # data x: int # data
PEnvY = ref object PEnvY = ref object
y: int y: int
ex: PEnvX ex: PEnvX
proc lambdaZ(z: int, ey: PEnvY): int = proc lambdaZ(z: int, ey: PEnvY): int =
return ey.ex.x + ey.y + z return ey.ex.x + ey.y + z
proc lambdaY(y: int, ex: PEnvX): tuple[prc, data: PEnvY] = proc lambdaY(y: int, ex: PEnvX): tuple[prc, data: PEnvY] =
var ey: PEnvY var ey: PEnvY
new ey new ey
ey.y = y ey.y = y
ey.ex = ex ey.ex = ex
result = (lambdaZ, ey) result = (lambdaZ, ey)
proc add(x: int): tuple[prc, data: PEnvX] = proc add(x: int): tuple[prc, data: PEnvX] =
var ex: PEnvX var ex: PEnvX
ex.x = x ex.x = x
result = (labmdaY, ex) result = (labmdaY, ex)
var tmp = add(2) var tmp = add(2)
var tmp2 = tmp.fn(4, tmp.data) var tmp2 = tmp.fn(4, tmp.data)
var add24 = tmp2.fn(4, tmp2.data) var add24 = tmp2.fn(4, tmp2.data)
@ -517,14 +517,14 @@ This should produce roughly this code:
We could get rid of nesting environments by always inlining inner anon procs. We could get rid of nesting environments by always inlining inner anon procs.
More useful is escape analysis and stack allocation of the environment, More useful is escape analysis and stack allocation of the environment,
however. however.
Alternative Alternative
----------- -----------
Process the closure of all inner procs in one pass and accumulate the Process the closure of all inner procs in one pass and accumulate the
environments. This is however not always possible. environments. This is however not always possible.
@ -532,21 +532,21 @@ Accumulator
----------- -----------
.. code-block:: nim .. code-block:: nim
proc getAccumulator(start: int): proc (): int {.closure} = proc getAccumulator(start: int): proc (): int {.closure} =
var i = start var i = start
return lambda: int = return lambda: int =
inc i inc i
return i return i
proc p = proc p =
var delta = 7 var delta = 7
proc accumulator(start: int): proc(): int = proc accumulator(start: int): proc(): int =
var x = start-1 var x = start-1
result = proc (): int = result = proc (): int =
x = x + delta x = x + delta
inc delta inc delta
return x return x
var a = accumulator(3) var a = accumulator(3)
var b = accumulator(4) var b = accumulator(4)
echo a() + b() echo a() + b()
@ -560,7 +560,7 @@ pass generates code to setup the environment and to pass it around. However,
this pass does not change the types! So we have some kind of mismatch here; on this pass does not change the types! So we have some kind of mismatch here; on
the one hand the proc expression becomes an explicit tuple, on the other hand the one hand the proc expression becomes an explicit tuple, on the other hand
the tyProc(ccClosure) type is not changed. For C code generation it's also the tyProc(ccClosure) type is not changed. For C code generation it's also
important the hidden formal param is ``void*`` and not something more important the hidden formal param is ``void*`` and not something more
specialized. However the more specialized env type needs to passed to the specialized. However the more specialized env type needs to passed to the
backend somehow. We deal with this by modifying ``s.ast[paramPos]`` to contain backend somehow. We deal with this by modifying ``s.ast[paramPos]`` to contain
the formal hidden parameter, but not ``s.typ``! the formal hidden parameter, but not ``s.typ``!

View file

@ -14,20 +14,20 @@ optional *a*. Parentheses may be used to group elements.
``&`` is the lookahead operator; ``&a`` means that an ``a`` is expected but ``&`` is the lookahead operator; ``&a`` means that an ``a`` is expected but
not consumed. It will be consumed in the following rule. not consumed. It will be consumed in the following rule.
The ``|``, ``/`` symbols are used to mark alternatives and have the lowest The ``|``, ``/`` symbols are used to mark alternatives and have the lowest
precedence. ``/`` is the ordered choice that requires the parser to try the precedence. ``/`` is the ordered choice that requires the parser to try the
alternatives in the given order. ``/`` is often used to ensure the grammar alternatives in the given order. ``/`` is often used to ensure the grammar
is not ambiguous. is not ambiguous.
Non-terminals start with a lowercase letter, abstract terminal symbols are in Non-terminals start with a lowercase letter, abstract terminal symbols are in
UPPERCASE. Verbatim terminal symbols (including keywords) are quoted UPPERCASE. Verbatim terminal symbols (including keywords) are quoted
with ``'``. An example:: with ``'``. An example::
ifStmt = 'if' expr ':' stmts ('elif' expr ':' stmts)* ('else' stmts)? ifStmt = 'if' expr ':' stmts ('elif' expr ':' stmts)* ('else' stmts)?
The binary ``^*`` operator is used as a shorthand for 0 or more occurrences The binary ``^*`` operator is used as a shorthand for 0 or more occurrences
separated by its second argument; likewise ``^+`` means 1 or more separated by its second argument; likewise ``^+`` means 1 or more
occurrences: ``a ^+ b`` is short for ``a (b a)*`` occurrences: ``a ^+ b`` is short for ``a (b a)*``
and ``a ^* b`` is short for ``(a (b a)*)?``. Example:: and ``a ^* b`` is short for ``(a (b a)*)?``. Example::
arrayConstructor = '[' expr ^* ',' ']' arrayConstructor = '[' expr ^* ',' ']'

View file

@ -26,9 +26,9 @@ program execution. Unless explicitly classified, an error is a static error.
A `checked runtime error`:idx: is an error that the implementation detects A `checked runtime error`:idx: is an error that the implementation detects
and reports at runtime. The method for reporting such errors is via *raising and reports at runtime. The method for reporting such errors is via *raising
exceptions* or *dying with a fatal error*. However, the implementation exceptions* or *dying with a fatal error*. However, the implementation
provides a means to disable these runtime checks. See the section pragmas_ provides a means to disable these runtime checks. See the section pragmas_
for details. for details.
Whether a checked runtime error results in an exception or in a fatal error at Whether a checked runtime error results in an exception or in a fatal error at
runtime is implementation specific. Thus the following program is always runtime is implementation specific. Thus the following program is always

View file

@ -5,7 +5,7 @@ Exception tracking
------------------ ------------------
Nim supports exception tracking. The `raises`:idx: pragma can be used Nim supports exception tracking. The `raises`:idx: pragma can be used
to explicitly define which exceptions a proc/iterator/method/converter is to explicitly define which exceptions a proc/iterator/method/converter is
allowed to raise. The compiler verifies this: allowed to raise. The compiler verifies this:
.. code-block:: nim .. code-block:: nim
@ -24,7 +24,7 @@ An empty ``raises`` list (``raises: []``) means that no exception may be raised:
result = false result = false
A ``raises`` list can also be attached to a proc type. This affects type A ``raises`` list can also be attached to a proc type. This affects type
compatibility: compatibility:
.. code-block:: nim .. code-block:: nim
@ -35,7 +35,7 @@ compatibility:
proc p(x: string) = proc p(x: string) =
raise newException(OSError, "OS") raise newException(OSError, "OS")
c = p # type error c = p # type error
@ -46,30 +46,30 @@ possibly raised exceptions; the algorithm operates on ``p``'s call graph:
raise ``system.Exception`` (the base type of the exception hierarchy) and raise ``system.Exception`` (the base type of the exception hierarchy) and
thus any exception unless ``T`` has an explicit ``raises`` list. thus any exception unless ``T`` has an explicit ``raises`` list.
However if the call is of the form ``f(...)`` where ``f`` is a parameter However if the call is of the form ``f(...)`` where ``f`` is a parameter
of the currently analysed routine it is ignored. The call is optimistically of the currently analysed routine it is ignored. The call is optimistically
assumed to have no effect. Rule 2 compensates for this case. assumed to have no effect. Rule 2 compensates for this case.
2. Every expression of some proc type within a call that is not a call 2. Every expression of some proc type within a call that is not a call
itself (and not nil) is assumed to be called indirectly somehow and thus itself (and not nil) is assumed to be called indirectly somehow and thus
its raises list is added to ``p``'s raises list. its raises list is added to ``p``'s raises list.
3. Every call to a proc ``q`` which has an unknown body (due to a forward 3. Every call to a proc ``q`` which has an unknown body (due to a forward
declaration or an ``importc`` pragma) is assumed to declaration or an ``importc`` pragma) is assumed to
raise ``system.Exception`` unless ``q`` has an explicit ``raises`` list. raise ``system.Exception`` unless ``q`` has an explicit ``raises`` list.
4. Every call to a method ``m`` is assumed to 4. Every call to a method ``m`` is assumed to
raise ``system.Exception`` unless ``m`` has an explicit ``raises`` list. raise ``system.Exception`` unless ``m`` has an explicit ``raises`` list.
5. For every other call the analysis can determine an exact ``raises`` list. 5. For every other call the analysis can determine an exact ``raises`` list.
6. For determining a ``raises`` list, the ``raise`` and ``try`` statements 6. For determining a ``raises`` list, the ``raise`` and ``try`` statements
of ``p`` are taken into consideration. of ``p`` are taken into consideration.
Rules 1-2 ensure the following works: Rules 1-2 ensure the following works:
.. code-block:: nim .. code-block:: nim
proc noRaise(x: proc()) {.raises: [].} = proc noRaise(x: proc()) {.raises: [].} =
# unknown call that might raise anything, but valid: # unknown call that might raise anything, but valid:
x() x()
proc doRaise() {.raises: [IOError].} = proc doRaise() {.raises: [IOError].} =
raise newException(IOError, "IO") raise newException(IOError, "IO")
proc use() {.raises: [].} = proc use() {.raises: [].} =
# doesn't compile! Can raise IOError! # doesn't compile! Can raise IOError!
noRaise(doRaise) noRaise(doRaise)
@ -82,21 +82,21 @@ Tag tracking
------------ ------------
The exception tracking is part of Nim's `effect system`:idx:. Raising an The exception tracking is part of Nim's `effect system`:idx:. Raising an
exception is an *effect*. Other effects can also be defined. A user defined exception is an *effect*. Other effects can also be defined. A user defined
effect is a means to *tag* a routine and to perform checks against this tag: effect is a means to *tag* a routine and to perform checks against this tag:
.. code-block:: nim .. code-block:: nim
type IO = object ## input/output effect type IO = object ## input/output effect
proc readLine(): string {.tags: [IO].} proc readLine(): string {.tags: [IO].}
proc no_IO_please() {.tags: [].} = proc no_IO_please() {.tags: [].} =
# the compiler prevents this: # the compiler prevents this:
let x = readLine() let x = readLine()
A tag has to be a type name. A ``tags`` list - like a ``raises`` list - can A tag has to be a type name. A ``tags`` list - like a ``raises`` list - can
also be attached to a proc type. This affects type compatibility. also be attached to a proc type. This affects type compatibility.
The inference for tag tracking is analogous to the inference for The inference for tag tracking is analogous to the inference for
exception tracking. exception tracking.
@ -105,7 +105,7 @@ Read/Write tracking
**Note**: Read/write tracking is not yet implemented! **Note**: Read/write tracking is not yet implemented!
The inference for read/write tracking is analogous to the inference for The inference for read/write tracking is analogous to the inference for
exception tracking. exception tracking.

View file

@ -164,7 +164,7 @@ Alternatively, the ``distinct`` type modifier can be applied to the type class
to allow each param matching the type class to bind to a different type. to allow each param matching the type class to bind to a different type.
If a proc param doesn't have a type specified, Nim will use the If a proc param doesn't have a type specified, Nim will use the
``distinct auto`` type class (also known as ``any``). Note this behavior is ``distinct auto`` type class (also known as ``any``). Note this behavior is
deprecated for procs; templates, however, support them: deprecated for procs; templates, however, support them:
.. code-block:: nim .. code-block:: nim

View file

@ -36,7 +36,7 @@ With this notation we can now easily define the core of the grammar: A block of
statements (simplified example):: statements (simplified example)::
ifStmt = 'if' expr ':' stmt ifStmt = 'if' expr ':' stmt
(IND{=} 'elif' expr ':' stmt)* (IND{=} 'elif' expr ':' stmt)*
(IND{=} 'else' ':' stmt)? (IND{=} 'else' ':' stmt)?
simpleStmt = ifStmt / ... simpleStmt = ifStmt / ...
@ -156,7 +156,7 @@ contain the following `escape sequences`:idx:\ :
================== =================================================== ================== ===================================================
Strings in Nim may contain any 8-bit value, even embedded zeros. However Strings in Nim may contain any 8-bit value, even embedded zeros. However
some operations may interpret the first binary zero as a terminator. some operations may interpret the first binary zero as a terminator.
@ -174,7 +174,7 @@ be whitespace between the opening ``"""`` and the newline),
the newline (and the preceding whitespace) is not included in the string. The the newline (and the preceding whitespace) is not included in the string. The
ending of the string literal is defined by the pattern ``"""[^"]``, so this: ending of the string literal is defined by the pattern ``"""[^"]``, so this:
.. code-block:: nim .. code-block:: nim
""""long string within quotes"""" """"long string within quotes""""
Produces:: Produces::
@ -187,9 +187,9 @@ Raw string literals
Terminal symbol in the grammar: ``RSTR_LIT``. Terminal symbol in the grammar: ``RSTR_LIT``.
There are also raw string literals that are preceded with the There are also raw string literals that are preceded with the
letter ``r`` (or ``R``) and are delimited by matching double quotes (just letter ``r`` (or ``R``) and are delimited by matching double quotes (just
like ordinary string literals) and do not interpret the escape sequences. like ordinary string literals) and do not interpret the escape sequences.
This is especially convenient for regular expressions or Windows paths: This is especially convenient for regular expressions or Windows paths:
.. code-block:: nim .. code-block:: nim
@ -201,21 +201,21 @@ To produce a single ``"`` within a raw string literal, it has to be doubled:
.. code-block:: nim .. code-block:: nim
r"a""b" r"a""b"
Produces:: Produces::
a"b a"b
``r""""`` is not possible with this notation, because the three leading ``r""""`` is not possible with this notation, because the three leading
quotes introduce a triple quoted string literal. ``r"""`` is the same quotes introduce a triple quoted string literal. ``r"""`` is the same
as ``"""`` since triple quoted string literals do not interpret escape as ``"""`` since triple quoted string literals do not interpret escape
sequences either. sequences either.
Generalized raw string literals Generalized raw string literals
------------------------------- -------------------------------
Terminal symbols in the grammar: ``GENERALIZED_STR_LIT``, Terminal symbols in the grammar: ``GENERALIZED_STR_LIT``,
``GENERALIZED_TRIPLESTR_LIT``. ``GENERALIZED_TRIPLESTR_LIT``.
The construct ``identifier"string literal"`` (without whitespace between the The construct ``identifier"string literal"`` (without whitespace between the
@ -281,7 +281,7 @@ Numerical constants are of a single type and have the form::
DEC_LIT = digit ( ['_'] digit )* DEC_LIT = digit ( ['_'] digit )*
OCT_LIT = '0' ('o' | 'c' | 'C') octdigit ( ['_'] octdigit )* OCT_LIT = '0' ('o' | 'c' | 'C') octdigit ( ['_'] octdigit )*
BIN_LIT = '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )* BIN_LIT = '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )*
INT_LIT = HEX_LIT INT_LIT = HEX_LIT
| DEC_LIT | DEC_LIT
| OCT_LIT | OCT_LIT
@ -383,7 +383,7 @@ The following strings denote other tokens::
` ( ) { } [ ] , ; [. .] {. .} (. .) ` ( ) { } [ ] , ; [. .] {. .} (. .)
The `slice`:idx: operator `..`:tok: takes precedence over other tokens that The `slice`:idx: operator `..`:tok: takes precedence over other tokens that
contain a dot: `{..}`:tok: are the three tokens `{`:tok:, `..`:tok:, `}`:tok: contain a dot: `{..}`:tok: are the three tokens `{`:tok:, `..`:tok:, `}`:tok:
and not the two tokens `{.`:tok:, `.}`:tok:. and not the two tokens `{.`:tok:, `.}`:tok:.

View file

@ -63,7 +63,7 @@ model low level lockfree mechanisms:
.. code-block:: nim .. code-block:: nim
var dummyLock {.compileTime.}: int var dummyLock {.compileTime.}: int
var atomicCounter {.guard: dummyLock.}: int var atomicCounter {.guard: dummyLock.}: int
template atomicRead(x): expr = template atomicRead(x): expr =
{.locks: [dummyLock].}: {.locks: [dummyLock].}:
memoryReadBarrier() memoryReadBarrier()
@ -135,30 +135,30 @@ Lock levels are used to enforce a global locking order in order to prevent
deadlocks at compile-time. A lock level is an constant integer in the range deadlocks at compile-time. A lock level is an constant integer in the range
0..1_000. Lock level 0 means that no lock is acquired at all. 0..1_000. Lock level 0 means that no lock is acquired at all.
If a section of code holds a lock of level ``M`` than it can also acquire any If a section of code holds a lock of level ``M`` than it can also acquire any
lock of level ``N < M``. Another lock of level ``M`` cannot be acquired. Locks lock of level ``N < M``. Another lock of level ``M`` cannot be acquired. Locks
of the same level can only be acquired *at the same time* within a of the same level can only be acquired *at the same time* within a
single ``locks`` section: single ``locks`` section:
.. code-block:: nim .. code-block:: nim
var a, b: TLock[2] var a, b: TLock[2]
var x: TLock[1] var x: TLock[1]
# invalid locking order: TLock[1] cannot be acquired before TLock[2]: # invalid locking order: TLock[1] cannot be acquired before TLock[2]:
{.locks: [x].}: {.locks: [x].}:
{.locks: [a].}: {.locks: [a].}:
... ...
# valid locking order: TLock[2] acquired before TLock[1]: # valid locking order: TLock[2] acquired before TLock[1]:
{.locks: [a].}: {.locks: [a].}:
{.locks: [x].}: {.locks: [x].}:
... ...
# invalid locking order: TLock[2] acquired before TLock[2]: # invalid locking order: TLock[2] acquired before TLock[2]:
{.locks: [a].}: {.locks: [a].}:
{.locks: [b].}: {.locks: [b].}:
... ...
# valid locking order, locks of the same level acquired at the same time: # valid locking order, locks of the same level acquired at the same time:
{.locks: [a, b].}: {.locks: [a, b].}:
... ...
@ -187,7 +187,7 @@ level. This then means that the routine may acquire locks of up to this level.
This is essential so that procs can be called within a ``locks`` section: This is essential so that procs can be called within a ``locks`` section:
.. code-block:: nim .. code-block:: nim
proc p() {.locks: 3.} = discard proc p() {.locks: 3.} = discard
var a: TLock[4] var a: TLock[4]
{.locks: [a].}: {.locks: [a].}:
@ -195,6 +195,6 @@ This is essential so that procs can be called within a ``locks`` section:
p() p()
As usual ``locks`` is an inferred effect and there is a subtype As usual ``locks`` is an inferred effect and there is a subtype
relation: ``proc () {.locks: N.}`` is a subtype of ``proc () {.locks: M.}`` relation: ``proc () {.locks: N.}`` is a subtype of ``proc () {.locks: M.}``
iff (M <= N). iff (M <= N).

View file

@ -81,8 +81,8 @@ A module alias can be introduced via the ``as`` keyword:
echo su.format("$1", "lalelu") echo su.format("$1", "lalelu")
The original module name is then not accessible. The The original module name is then not accessible. The
notations ``path/to/module`` or ``path.to.module`` or ``"path/to/module"`` notations ``path/to/module`` or ``path.to.module`` or ``"path/to/module"``
can be used to refer to a module in subdirectories: can be used to refer to a module in subdirectories:
.. code-block:: nim .. code-block:: nim
@ -104,7 +104,7 @@ Likewise the following does not make sense as the name is ``strutils`` already:
From import statement 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 explict
full qualification: full qualification:
@ -115,8 +115,8 @@ full qualification:
# always possible: full qualification: # always possible: full qualification:
echo strutils.replace("abc", "a", "z") echo strutils.replace("abc", "a", "z")
It's also possible to use ``from module import nil`` if one wants to import It's also possible to use ``from module import nil`` if one wants to import
the module but wants to enforce fully qualified access to every symbol the module but wants to enforce fully qualified access to every symbol
in ``module``. in ``module``.
@ -134,15 +134,15 @@ modules don't need to import a module's dependencies:
# module A # module A
import B import B
export B.MyObject export B.MyObject
proc `$`*(x: MyObject): string = "my object" proc `$`*(x: MyObject): string = "my object"
.. code-block:: nim .. code-block:: nim
# module C # module C
import A import A
# B.MyObject has been imported implicitly here: # B.MyObject has been imported implicitly here:
var x: MyObject var x: MyObject
echo($x) echo($x)
@ -185,7 +185,7 @@ following places:
Module scope Module scope
~~~~~~~~~~~~ ~~~~~~~~~~~~
All identifiers of a module are valid from the point of declaration until All identifiers of a module are valid from the point of declaration until
the end of the module. Identifiers from indirectly dependent modules are *not* the end of the module. Identifiers from indirectly dependent modules are *not*
available. The `system`:idx: module is automatically imported in every module. available. The `system`:idx: module is automatically imported in every module.
If a module imports an identifier by two different modules, each occurrence of If a module imports an identifier by two different modules, each occurrence of

View file

@ -120,7 +120,7 @@ current module:
Method call syntax Method call syntax
------------------ ------------------
For object oriented programming, the syntax ``obj.method(args)`` can be used For object oriented programming, the syntax ``obj.method(args)`` can be used
instead of ``method(obj, args)``. The parentheses can be omitted if there are no instead of ``method(obj, args)``. The parentheses can be omitted if there are no
remaining arguments: ``obj.len`` (instead of ``len(obj)``). remaining arguments: ``obj.len`` (instead of ``len(obj)``).
@ -128,7 +128,7 @@ This method call syntax is not restricted to objects, it can be used
to supply any type of first argument for procedures: to supply any type of first argument for procedures:
.. code-block:: nim .. code-block:: nim
echo("abc".len) # is the same as echo(len("abc")) echo("abc".len) # is the same as echo(len("abc"))
echo("abc".toUpper()) echo("abc".toUpper())
echo({'a', 'b', 'c'}.card) echo({'a', 'b', 'c'}.card)
@ -143,11 +143,11 @@ See also: `Limitations of the method call syntax`_.
Properties Properties
---------- ----------
Nim has no need for *get-properties*: Ordinary get-procedures that are called Nim has no need for *get-properties*: Ordinary get-procedures that are called
with the *method call syntax* achieve the same. But setting a value is with the *method call syntax* achieve the same. But setting a value is
different; for this a special setter syntax is needed: different; for this a special setter syntax is needed:
.. code-block:: nim .. code-block:: nim
type type
Socket* = ref object of RootObj Socket* = ref object of RootObj
FHost: int # cannot be accessed from the outside of the module FHost: int # cannot be accessed from the outside of the module
@ -157,7 +157,7 @@ different; for this a special setter syntax is needed:
proc `host=`*(s: var Socket, value: int) {.inline.} = proc `host=`*(s: var Socket, value: int) {.inline.} =
## setter of hostAddr ## setter of hostAddr
s.FHost = value s.FHost = value
proc host*(s: Socket): int {.inline.} = proc host*(s: Socket): int {.inline.} =
## getter of hostAddr ## getter of hostAddr
s.FHost s.FHost
@ -180,18 +180,18 @@ more argument in this case:
.. code-block:: nim .. code-block:: nim
proc optarg(x: int, y: int = 0): int = x + y proc optarg(x: int, y: int = 0): int = x + y
proc singlearg(x: int): int = 20*x proc singlearg(x: int): int = 20*x
echo optarg 1, " ", singlearg 2 # prints "1 40" echo optarg 1, " ", singlearg 2 # prints "1 40"
let fail = optarg 1, optarg 8 # Wrong. Too many arguments for a command call let fail = optarg 1, optarg 8 # Wrong. Too many arguments for a command call
let x = optarg(1, optarg 8) # traditional procedure call with 2 arguments let x = optarg(1, optarg 8) # traditional procedure call with 2 arguments
let y = 1.optarg optarg 8 # same thing as above, w/o the parenthesis let y = 1.optarg optarg 8 # same thing as above, w/o the parenthesis
assert x == y assert x == y
The command invocation syntax also can't have complex expressions as arguments. The command invocation syntax also can't have complex expressions as arguments.
For example: (`anonymous procs`_), ``if``, ``case`` or ``try``. The (`do For example: (`anonymous procs`_), ``if``, ``case`` or ``try``. The (`do
notation`_) is limited, but usable for a single proc (see the example in the notation`_) is limited, but usable for a single proc (see the example in the
corresponding section). Function calls with no arguments still needs () to corresponding section). Function calls with no arguments still needs () to
distinguish between a call and the function itself as a first class value. distinguish between a call and the function itself as a first class value.
@ -221,7 +221,7 @@ the proc's name.
cmp(x.len, y.len)) cmp(x.len, y.len))
Procs as expressions can appear both as nested procs and inside top level Procs as expressions can appear both as nested procs and inside top level
executable code. executable code.
@ -237,10 +237,10 @@ calls can use the ``do`` keyword:
sort(cities) do (x,y: string) -> int: sort(cities) do (x,y: string) -> int:
cmp(x.len, y.len) cmp(x.len, y.len)
# Less parenthesis using the method plus command syntax: # Less parenthesis using the method plus command syntax:
cities = cities.map do (x:string) -> string: cities = cities.map do (x:string) -> string:
"City of " & x "City of " & x
``do`` is written after the parentheses enclosing the regular proc params. ``do`` is written after the parentheses enclosing the regular proc params.
The proc expression represented by the do block is appended to them. The proc expression represented by the do block is appended to them.
More than one ``do`` block can appear in a single call: More than one ``do`` block can appear in a single call:
@ -261,11 +261,11 @@ Nonoverloadable builtins
The following builtin procs cannot be overloaded for reasons of implementation The following builtin procs cannot be overloaded for reasons of implementation
simplicity (they require specialized semantic checking):: simplicity (they require specialized semantic checking)::
declared, defined, definedInScope, compiles, low, high, sizeOf, declared, defined, definedInScope, compiles, low, high, sizeOf,
is, of, shallowCopy, getAst, astToStr, spawn, procCall is, of, shallowCopy, getAst, astToStr, spawn, procCall
Thus they act more like keywords than like ordinary identifiers; unlike a Thus they act more like keywords than like ordinary identifiers; unlike a
keyword however, a redefinition may `shadow`:idx: the definition in keyword however, a redefinition may `shadow`:idx: the definition in
the ``system`` module. From this list the following should not be written in dot the ``system`` module. From this list the following should not be written in dot
notation ``x.f`` since ``x`` cannot be type checked before it gets passed notation ``x.f`` since ``x`` cannot be type checked before it gets passed
to ``f``:: to ``f``::
@ -342,11 +342,11 @@ returned value is an l-value and can be modified by the caller:
proc WriteAccessToG(): var int = proc WriteAccessToG(): var int =
result = g result = g
WriteAccessToG() = 6 WriteAccessToG() = 6
assert g == 6 assert g == 6
It is a compile time error if the implicitly introduced pointer could be It is a compile time error if the implicitly introduced pointer could be
used to access a location beyond its lifetime: used to access a location beyond its lifetime:
.. code-block:: nim .. code-block:: nim
@ -354,7 +354,7 @@ used to access a location beyond its lifetime:
var g = 0 var g = 0
result = g # Error! result = g # Error!
For iterators, a component of a tuple return type can have a ``var`` type too: For iterators, a component of a tuple return type can have a ``var`` type too:
.. code-block:: nim .. code-block:: nim
iterator mpairs(a: var seq[string]): tuple[key: int, val: var string] = iterator mpairs(a: var seq[string]): tuple[key: int, val: var string] =
@ -384,28 +384,28 @@ dispatch.
x: int x: int
PlusExpr = ref object of Expression PlusExpr = ref object of Expression
a, b: Expression a, b: Expression
method eval(e: Expression): int = method eval(e: Expression): int =
# override this base method # override this base method
quit "to override!" quit "to override!"
method eval(e: Literal): int = return e.x method eval(e: Literal): int = return e.x
method eval(e: PlusExpr): int = method eval(e: PlusExpr): int =
# watch out: relies on dynamic binding # watch out: relies on dynamic binding
result = eval(e.a) + eval(e.b) result = eval(e.a) + eval(e.b)
proc newLit(x: int): Literal = proc newLit(x: int): Literal =
new(result) new(result)
result.x = x result.x = x
proc newPlus(a, b: Expression): PlusExpr = proc newPlus(a, b: Expression): PlusExpr =
new(result) new(result)
result.a = a result.a = a
result.b = b result.b = b
echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4))) echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
In the example the constructors ``newLit`` and ``newPlus`` are procs In the example the constructors ``newLit`` and ``newPlus`` are procs
because they should use static binding, but ``eval`` is a method because it because they should use static binding, but ``eval`` is a method because it
requires dynamic binding. requires dynamic binding.
@ -418,24 +418,24 @@ dispatching:
Thing = ref object of RootObj Thing = ref object of RootObj
Unit = ref object of Thing Unit = ref object of Thing
x: int x: int
method collide(a, b: Thing) {.inline.} = method collide(a, b: Thing) {.inline.} =
quit "to override!" quit "to override!"
method collide(a: Thing, b: Unit) {.inline.} = method collide(a: Thing, b: Unit) {.inline.} =
echo "1" echo "1"
method collide(a: Unit, b: Thing) {.inline.} = method collide(a: Unit, b: Thing) {.inline.} =
echo "2" echo "2"
var a, b: Unit var a, b: Unit
new a new a
new b new b
collide(a, b) # output: 2 collide(a, b) # output: 2
Invocation of a multi-method cannot be ambiguous: collide 2 is preferred over Invocation of a multi-method cannot be ambiguous: collide 2 is preferred over
collide 1 because the resolution works from left to right. collide 1 because the resolution works from left to right.
In the example ``Unit, Thing`` is preferred over ``Thing, Unit``. In the example ``Unit, Thing`` is preferred over ``Thing, Unit``.
**Performance note**: Nim does not produce a virtual method table, but **Performance note**: Nim does not produce a virtual method table, but
@ -450,7 +450,7 @@ Iterators and the for statement
The `for`:idx: statement is an abstract mechanism to iterate over the elements The `for`:idx: statement is an abstract mechanism to iterate over the elements
of a container. It relies on an `iterator`:idx: to do so. Like ``while`` of a container. It relies on an `iterator`:idx: to do so. Like ``while``
statements, ``for`` statements open an `implicit block`:idx:, so that they statements, ``for`` statements open an `implicit block`:idx:, so that they
can be left with a ``break`` statement. can be left with a ``break`` statement.
The ``for`` loop declares iteration variables - their scope reaches until the The ``for`` loop declares iteration variables - their scope reaches until the
end of the loop body. The iteration variables' types are inferred by the end of the loop body. The iteration variables' types are inferred by the
@ -486,7 +486,7 @@ The compiler generates code as if the programmer would have written this:
If the iterator yields a tuple, there can be as many iteration variables If the iterator yields a tuple, there can be as many iteration variables
as there are components in the tuple. The i'th iteration variable's type is as there are components in the tuple. The i'th iteration variable's type is
the type of the i'th component. In other words, implicit tuple unpacking in a the type of the i'th component. In other words, implicit tuple unpacking in a
for loop context is supported. for loop context is supported.
Implict items/pairs invocations Implict items/pairs invocations
@ -498,11 +498,11 @@ ie. an ``items`` iterator is implicitly invoked:
.. code-block:: nim .. code-block:: nim
for x in [1,2,3]: echo x for x in [1,2,3]: echo x
If the for loop has exactly 2 variables, a ``pairs`` iterator is implicitly If the for loop has exactly 2 variables, a ``pairs`` iterator is implicitly
invoked. invoked.
Symbol lookup of the identifiers ``items``/``pairs`` is performed after Symbol lookup of the identifiers ``items``/``pairs`` is performed after
the rewriting step, so that all overloads of ``items``/``pairs`` are taken the rewriting step, so that all overloads of ``items``/``pairs`` are taken
into account. into account.
@ -511,7 +511,7 @@ First class iterators
--------------------- ---------------------
There are 2 kinds of iterators in Nim: *inline* and *closure* iterators. There are 2 kinds of iterators in Nim: *inline* and *closure* iterators.
An `inline iterator`:idx: is an iterator that's always inlined by the compiler An `inline iterator`:idx: is an iterator that's always inlined by the compiler
leading to zero overhead for the abstraction, but may result in a heavy leading to zero overhead for the abstraction, but may result in a heavy
increase in code size. Inline iterators are second class citizens; increase in code size. Inline iterators are second class citizens;
They can be passed as parameters only to other inlining code facilities like They can be passed as parameters only to other inlining code facilities like
@ -522,7 +522,7 @@ In contrast to that, a `closure iterator`:idx: can be passed around more freely:
.. code-block:: nim .. code-block:: nim
iterator count0(): int {.closure.} = iterator count0(): int {.closure.} =
yield 0 yield 0
iterator count2(): int {.closure.} = iterator count2(): int {.closure.} =
var x = 1 var x = 1
yield x yield x
@ -539,7 +539,7 @@ Closure iterators have other restrictions than inline iterators:
1. ``yield`` in a closure iterator can not occur in a ``try`` statement. 1. ``yield`` in a closure iterator can not occur in a ``try`` statement.
2. For now, a closure iterator cannot be evaluated at compile time. 2. For now, a closure iterator cannot be evaluated at compile time.
3. ``return`` is allowed in a closure iterator (but rarely useful) and ends 3. ``return`` is allowed in a closure iterator (but rarely useful) and ends
iteration. iteration.
4. Neither inline nor closure iterators can be recursive. 4. Neither inline nor closure iterators can be recursive.
@ -547,7 +547,7 @@ Iterators that are neither marked ``{.closure.}`` nor ``{.inline.}`` explicitly
default to being inline, but this may change in future versions of the default to being inline, but this may change in future versions of the
implementation. implementation.
The ``iterator`` type is always of the calling convention ``closure`` The ``iterator`` type is always of the calling convention ``closure``
implicitly; the following example shows how to use iterators to implement implicitly; the following example shows how to use iterators to implement
a `collaborative tasking`:idx: system: a `collaborative tasking`:idx: system:
@ -650,7 +650,7 @@ parameters of an outer factory proc:
Converters Converters
========== ==========
A converter is like an ordinary proc except that it enhances A converter is like an ordinary proc except that it enhances
the "implicitly convertible" type relation (see `Convertible relation`_): the "implicitly convertible" type relation (see `Convertible relation`_):
.. code-block:: nim .. code-block:: nim

View file

@ -34,7 +34,7 @@ templates and macros), depending on the desired effect:
The following dot operators are available: The following dot operators are available:
operator `.` operator `.`
------------ ------------
This operator will be matched against both field accesses and method calls. This operator will be matched against both field accesses and method calls.
operator `.()` operator `.()`

View file

@ -176,8 +176,8 @@ The rules for compile-time computability are:
1. Literals are compile-time computable. 1. Literals are compile-time computable.
2. Type conversions are compile-time computable. 2. Type conversions are compile-time computable.
3. Procedure calls of the form ``p(X)`` are compile-time computable if 3. Procedure calls of the form ``p(X)`` are compile-time computable if
``p`` is a proc without side-effects (see the `noSideEffect pragma ``p`` is a proc without side-effects (see the `noSideEffect pragma
<#pragmas-nosideeffect-pragma>`_ for details) and if ``X`` is a <#pragmas-nosideeffect-pragma>`_ for details) and if ``X`` is a
(possibly empty) list of compile-time computable arguments. (possibly empty) list of compile-time computable arguments.

View file

@ -1,11 +1,11 @@
Taint mode Taint mode
========== ==========
The Nim compiler and most parts of the standard library support The Nim compiler and most parts of the standard library support
a taint mode. Input strings are declared with the `TaintedString`:idx: a taint mode. Input strings are declared with the `TaintedString`:idx:
string type declared in the ``system`` module. string type declared in the ``system`` module.
If the taint mode is turned on (via the ``--taintMode:on`` command line If the taint mode is turned on (via the ``--taintMode:on`` command line
option) it is a distinct string type which helps to detect input option) it is a distinct string type which helps to detect input
validation errors: validation errors:
@ -13,7 +13,7 @@ validation errors:
echo "your name: " echo "your name: "
var name: TaintedString = stdin.readline var name: TaintedString = stdin.readline
# it is safe here to output the name without any input validation, so # it is safe here to output the name without any input validation, so
# we simply convert `name` to string to make the compiler happy: # we simply convert `name` to string to make the compiler happy:
echo "hi, ", name.string echo "hi, ", name.string
If the taint mode is turned off, ``TaintedString`` is simply an alias for If the taint mode is turned off, ``TaintedString`` is simply an alias for

View file

@ -16,17 +16,17 @@ Example:
assert(5 != 6) # the compiler rewrites that to: assert(not (5 == 6)) assert(5 != 6) # the compiler rewrites that to: assert(not (5 == 6))
The ``!=``, ``>``, ``>=``, ``in``, ``notin``, ``isnot`` operators are in fact The ``!=``, ``>``, ``>=``, ``in``, ``notin``, ``isnot`` operators are in fact
templates: templates:
| ``a > b`` is transformed into ``b < a``. | ``a > b`` is transformed into ``b < a``.
| ``a in b`` is transformed into ``contains(b, a)``. | ``a in b`` is transformed into ``contains(b, a)``.
| ``notin`` and ``isnot`` have the obvious meanings. | ``notin`` and ``isnot`` have the obvious meanings.
The "types" of templates can be the symbols ``expr`` (stands for *expression*), The "types" of templates can be the symbols ``expr`` (stands for *expression*),
``stmt`` (stands for *statement*) or ``typedesc`` (stands for *type ``stmt`` (stands for *statement*) or ``typedesc`` (stands for *type
description*). These are "meta types", they can only be used in certain description*). These are "meta types", they can only be used in certain
contexts. Real types can be used too; this implies that expressions are contexts. Real types can be used too; this implies that expressions are
expected. expected.
@ -40,7 +40,7 @@ So ordinary templates cannot receive undeclared identifiers:
.. code-block:: nim .. code-block:: nim
template declareInt(x: expr) = template declareInt(x: expr) =
var x: int var x: int
declareInt(x) # error: unknown identifier: 'x' declareInt(x) # error: unknown identifier: 'x'
@ -51,7 +51,7 @@ receive undeclared identifiers:
.. code-block:: nim .. code-block:: nim
template declareInt(x: expr) {.immediate.} = template declareInt(x: expr) {.immediate.} =
var x: int var x: int
declareInt(x) # valid declareInt(x) # valid
@ -75,11 +75,11 @@ special ``:`` syntax:
close(f) close(f)
else: else:
quit("cannot open: " & fn) quit("cannot open: " & fn)
withFile(txt, "ttempl3.txt", fmWrite): withFile(txt, "ttempl3.txt", fmWrite):
txt.writeLine("line 1") txt.writeLine("line 1")
txt.writeLine("line 2") txt.writeLine("line 2")
In the example the two ``writeLine`` statements are bound to the ``actions`` In the example the two ``writeLine`` statements are bound to the ``actions``
parameter. parameter.
@ -92,9 +92,9 @@ bound from the definition scope of the template:
.. code-block:: nim .. code-block:: nim
# Module A # Module A
var var
lastId = 0 lastId = 0
template genId*: expr = template genId*: expr =
inc(lastId) inc(lastId)
lastId lastId
@ -102,10 +102,10 @@ bound from the definition scope of the template:
.. code-block:: nim .. code-block:: nim
# Module B # Module B
import A import A
echo genId() # Works as 'lastId' has been bound in 'genId's defining scope echo genId() # Works as 'lastId' has been bound in 'genId's defining scope
As in generics symbol binding can be influenced via ``mixin`` or ``bind`` As in generics symbol binding can be influenced via ``mixin`` or ``bind``
statements. statements.
@ -117,11 +117,11 @@ In templates identifiers can be constructed with the backticks notation:
.. code-block:: nim .. code-block:: nim
template typedef(name: expr, typ: typedesc) {.immediate.} = template typedef(name: expr, typ: typedesc) {.immediate.} =
type type
`T name`* {.inject.} = typ `T name`* {.inject.} = typ
`P name`* {.inject.} = ref `T name` `P name`* {.inject.} = ref `T name`
typedef(myint, int) typedef(myint, int)
var x: PMyInt var x: PMyInt
@ -150,7 +150,7 @@ shadowed by the same argument name even when fully qualified:
tstLev(levA) tstLev(levA)
# produces: 'levA levA' # produces: 'levA levA'
But the global symbol can properly be captured by a ``bind`` statement: But the global symbol can properly be captured by a ``bind`` statement:
.. code-block:: nim .. code-block:: nim
@ -177,14 +177,14 @@ Per default templates are `hygienic`:idx:\: Local identifiers declared in a
template cannot be accessed in the instantiation context: template cannot be accessed in the instantiation context:
.. code-block:: nim .. code-block:: nim
template newException*(exceptn: typedesc, message: string): expr = template newException*(exceptn: typedesc, message: string): expr =
var var
e: ref exceptn # e is implicitly gensym'ed here e: ref exceptn # e is implicitly gensym'ed here
new(e) new(e)
e.msg = message e.msg = message
e e
# so this works: # so this works:
let e = "message" let e = "message"
raise newException(EIO, e) raise newException(EIO, e)
@ -196,7 +196,7 @@ symbols are not exposed but inject'ed are.
The default for symbols of entity ``type``, ``var``, ``let`` and ``const`` The default for symbols of entity ``type``, ``var``, ``let`` and ``const``
is ``gensym`` and for ``proc``, ``iterator``, ``converter``, ``template``, is ``gensym`` and for ``proc``, ``iterator``, ``converter``, ``template``,
``macro`` is ``inject``. However, if the name of the entity is passed as a ``macro`` is ``inject``. However, if the name of the entity is passed as a
template parameter, it is an inject'ed symbol: template parameter, it is an inject'ed symbol:
.. code-block:: nim .. code-block:: nim
@ -204,7 +204,7 @@ template parameter, it is an inject'ed symbol:
block: block:
var f: File # since 'f' is a template param, it's injected implicitly var f: File # since 'f' is a template param, it's injected implicitly
... ...
withFile(txt, "ttempl3.txt", fmWrite): withFile(txt, "ttempl3.txt", fmWrite):
txt.writeLine("line 1") txt.writeLine("line 1")
txt.writeLine("line 2") txt.writeLine("line 2")
@ -215,7 +215,7 @@ no semantics outside of a template definition and cannot be abstracted over:
.. code-block:: nim .. code-block:: nim
{.pragma myInject: inject.} {.pragma myInject: inject.}
template t() = template t() =
var x {.myInject.}: int # does NOT work var x {.myInject.}: int # does NOT work
@ -334,9 +334,9 @@ BindSym
------- -------
The above ``debug`` macro relies on the fact that ``write``, ``writeLine`` and The above ``debug`` macro relies on the fact that ``write``, ``writeLine`` and
``stdout`` are declared in the system module and thus visible in the ``stdout`` are declared in the system module and thus visible in the
instantiating context. There is a way to use bound identifiers instantiating context. There is a way to use bound identifiers
(aka `symbols`:idx:) instead of using unbound identifiers. The ``bindSym`` (aka `symbols`:idx:) instead of using unbound identifiers. The ``bindSym``
builtin can be used for that: builtin can be used for that:
.. code-block:: nim .. code-block:: nim
@ -418,8 +418,8 @@ powerful programming construct that still suffices. So the "check list" is:
Macros as pragmas Macros as pragmas
----------------- -----------------
Whole routines (procs, iterators etc.) can also be passed to a template or Whole routines (procs, iterators etc.) can also be passed to a template or
a macro via the pragma notation: a macro via the pragma notation:
.. code-block:: nim .. code-block:: nim
template m(s: stmt) = discard template m(s: stmt) = discard

View file

@ -3,32 +3,32 @@ Term rewriting macros
Term rewriting macros are macros or templates that have not only Term rewriting macros are macros or templates that have not only
a *name* but also a *pattern* that is searched for after the semantic checking a *name* but also a *pattern* that is searched for after the semantic checking
phase of the compiler: This means they provide an easy way to enhance the phase of the compiler: This means they provide an easy way to enhance the
compilation pipeline with user defined optimizations: compilation pipeline with user defined optimizations:
.. code-block:: nim .. code-block:: nim
template optMul{`*`(a, 2)}(a: int): int = a+a template optMul{`*`(a, 2)}(a: int): int = a+a
let x = 3 let x = 3
echo x * 2 echo x * 2
The compiler now rewrites ``x * 2`` as ``x + x``. The code inside the The compiler now rewrites ``x * 2`` as ``x + x``. The code inside the
curlies is the pattern to match against. The operators ``*``, ``**``, curlies is the pattern to match against. The operators ``*``, ``**``,
``|``, ``~`` have a special meaning in patterns if they are written in infix ``|``, ``~`` have a special meaning in patterns if they are written in infix
notation, so to match verbatim against ``*`` the ordinary function call syntax notation, so to match verbatim against ``*`` the ordinary function call syntax
needs to be used. needs to be used.
Unfortunately optimizations are hard to get right and even the tiny example Unfortunately optimizations are hard to get right and even the tiny example
is **wrong**: is **wrong**:
.. code-block:: nim .. code-block:: nim
template optMul{`*`(a, 2)}(a: int): int = a+a template optMul{`*`(a, 2)}(a: int): int = a+a
proc f(): int = proc f(): int =
echo "side effect!" echo "side effect!"
result = 55 result = 55
echo f() * 2 echo f() * 2
We cannot duplicate 'a' if it denotes an expression that has a side effect! We cannot duplicate 'a' if it denotes an expression that has a side effect!
@ -36,11 +36,11 @@ Fortunately Nim supports side effect analysis:
.. code-block:: nim .. code-block:: nim
template optMul{`*`(a, 2)}(a: int{noSideEffect}): int = a+a template optMul{`*`(a, 2)}(a: int{noSideEffect}): int = a+a
proc f(): int = proc f(): int =
echo "side effect!" echo "side effect!"
result = 55 result = 55
echo f() * 2 # not optimized ;-) echo f() * 2 # not optimized ;-)
You can make one overload matching with a constraint and one without, and the You can make one overload matching with a constraint and one without, and the
@ -53,13 +53,13 @@ blindly:
.. code-block:: nim .. code-block:: nim
template mulIsCommutative{`*`(a, b)}(a, b: int): int = b*a template mulIsCommutative{`*`(a, b)}(a, b: int): int = b*a
What optimizers really need to do is a *canonicalization*: What optimizers really need to do is a *canonicalization*:
.. code-block:: nim .. code-block:: nim
template canonMul{`*`(a, b)}(a: int{lit}, b: int): int = b*a template canonMul{`*`(a, b)}(a: int{lit}, b: int): int = b*a
The ``int{lit}`` parameter pattern matches against an expression of The ``int{lit}`` parameter pattern matches against an expression of
type ``int``, but only if it's a literal. type ``int``, but only if it's a literal.
@ -67,7 +67,7 @@ type ``int``, but only if it's a literal.
Parameter constraints Parameter constraints
--------------------- ---------------------
The `parameter constraint`:idx: expression can use the operators ``|`` (or), The `parameter constraint`:idx: expression can use the operators ``|`` (or),
``&`` (and) and ``~`` (not) and the following predicates: ``&`` (and) and ``~`` (not) and the following predicates:
=================== ===================================================== =================== =====================================================
@ -75,7 +75,7 @@ Predicate Meaning
=================== ===================================================== =================== =====================================================
``atom`` The matching node has no children. ``atom`` The matching node has no children.
``lit`` The matching node is a literal like "abc", 12. ``lit`` The matching node is a literal like "abc", 12.
``sym`` The matching node must be a symbol (a bound ``sym`` The matching node must be a symbol (a bound
identifier). identifier).
``ident`` The matching node must be an identifier (an unbound ``ident`` The matching node must be an identifier (an unbound
identifier). identifier).
@ -101,15 +101,15 @@ Predicate Meaning
``enumfield`` A symbol which is a field in an enumeration. ``enumfield`` A symbol which is a field in an enumeration.
``forvar`` A for loop variable. ``forvar`` A for loop variable.
``label`` A label (used in ``block`` statements). ``label`` A label (used in ``block`` statements).
``nk*`` The matching AST must have the specified kind. ``nk*`` The matching AST must have the specified kind.
(Example: ``nkIfStmt`` denotes an ``if`` statement.) (Example: ``nkIfStmt`` denotes an ``if`` statement.)
``alias`` States that the marked parameter needs to alias ``alias`` States that the marked parameter needs to alias
with *some* other parameter. with *some* other parameter.
``noalias`` States that *every* other parameter must not alias ``noalias`` States that *every* other parameter must not alias
with the marked parameter. with the marked parameter.
=================== ===================================================== =================== =====================================================
Predicates that share their name with a keyword have to be escaped with Predicates that share their name with a keyword have to be escaped with
backticks: `` `const` ``. backticks: `` `const` ``.
The ``alias`` and ``noalias`` predicates refer not only to the matching AST, The ``alias`` and ``noalias`` predicates refer not only to the matching AST,
but also to every other bound parameter; syntactically they need to occur after but also to every other bound parameter; syntactically they need to occur after
@ -151,14 +151,14 @@ constant folding, so the following does not work:
The reason is that the compiler already transformed the 1 into "1" for The reason is that the compiler already transformed the 1 into "1" for
the ``echo`` statement. However, a term rewriting macro should not change the the ``echo`` statement. However, a term rewriting macro should not change the
semantics anyway. In fact they can be deactivated with the ``--patterns:off`` semantics anyway. In fact they can be deactivated with the ``--patterns:off``
command line option or temporarily with the ``patterns`` pragma. command line option or temporarily with the ``patterns`` pragma.
The ``{}`` operator The ``{}`` operator
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
A pattern expression can be bound to a pattern parameter via the ``expr{param}`` A pattern expression can be bound to a pattern parameter via the ``expr{param}``
notation: notation:
.. code-block:: nim .. code-block:: nim
template t{(0|1|2){x}}(x: expr): expr = x+1 template t{(0|1|2){x}}(x: expr): expr = x+1
@ -176,7 +176,7 @@ The ``~`` operator is the **not** operator in patterns:
template t{x = (~x){y} and (~x){z}}(x, y, z: bool): stmt = template t{x = (~x){y} and (~x){z}}(x, y, z: bool): stmt =
x = y x = y
if x: x = z if x: x = z
var var
a = false a = false
b = true b = true
@ -189,12 +189,12 @@ The ``*`` operator
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~
The ``*`` operator can *flatten* a nested binary expression like ``a & b & c`` The ``*`` operator can *flatten* a nested binary expression like ``a & b & c``
to ``&(a, b, c)``: to ``&(a, b, c)``:
.. code-block:: nim .. code-block:: nim
var var
calls = 0 calls = 0
proc `&&`(s: varargs[string]): string = proc `&&`(s: varargs[string]): string =
result = s[0] result = s[0]
for i in 1..len(s)-1: result.add s[i] for i in 1..len(s)-1: result.add s[i]
@ -211,8 +211,8 @@ to ``&(a, b, c)``:
The second operator of `*` must be a parameter; it is used to gather all the The second operator of `*` must be a parameter; it is used to gather all the
arguments. The expression ``"my" && (space & "awe" && "some " ) && "concat"`` arguments. The expression ``"my" && (space & "awe" && "some " ) && "concat"``
is passed to ``optConc`` in ``a`` as a special list (of kind ``nkArgList``) is passed to ``optConc`` in ``a`` as a special list (of kind ``nkArgList``)
which is flattened into a call expression; thus the invocation of ``optConc`` which is flattened into a call expression; thus the invocation of ``optConc``
produces: produces:
.. code-block:: nim .. code-block:: nim
@ -245,7 +245,7 @@ all the arguments, but also the matched operators in reverse polish notation:
var x, y, z: Matrix var x, y, z: Matrix
echo x + y * z - x echo x + y * z - x
This passes the expression ``x + y * z - x`` to the ``optM`` macro as This passes the expression ``x + y * z - x`` to the ``optM`` macro as
an ``nnkArgList`` node containing:: an ``nnkArgList`` node containing::
@ -265,7 +265,7 @@ an ``nnkArgList`` node containing::
Parameters Parameters
---------- ----------
Parameters in a pattern are type checked in the matching process. If a Parameters in a pattern are type checked in the matching process. If a
parameter is of the type ``varargs`` it is treated specially and it can match parameter is of the type ``varargs`` it is treated specially and it can match
0 or more arguments in the AST to be matched against: 0 or more arguments in the AST to be matched against:
@ -275,7 +275,7 @@ parameter is of the type ``varargs`` it is treated specially and it can match
((write|writeLine){w})(f, y) ((write|writeLine){w})(f, y)
}(x, y: varargs[expr], f: File, w: expr) = }(x, y: varargs[expr], f: File, w: expr) =
w(f, x, y) w(f, x, y)
Example: Partial evaluation Example: Partial evaluation
@ -310,7 +310,7 @@ The following example shows how some form of hoisting can be implemented:
The ``optPeg`` template optimizes the case of a peg constructor with a string The ``optPeg`` template optimizes the case of a peg constructor with a string
literal, so that the pattern will only be parsed once at program startup and literal, so that the pattern will only be parsed once at program startup and
stored in a global ``gl`` which is then re-used. This optimization is called stored in a global ``gl`` which is then re-used. This optimization is called
hoisting because it is comparable to classical loop hoisting. hoisting because it is comparable to classical loop hoisting.
@ -343,7 +343,7 @@ ordinary routines.
Move optimization Move optimization
----------------- -----------------
The ``call`` constraint is particularly useful to implement a move The ``call`` constraint is particularly useful to implement a move
optimization for types that have copying semantics: optimization for types that have copying semantics:
.. code-block:: nim .. code-block:: nim

View file

@ -301,7 +301,7 @@ matches better than just ``T`` then.
proc sayHi(x: var int): string = proc sayHi(x: var int): string =
# matches a var int # matches a var int
result = $(x + 10) result = $(x + 10)
proc sayHello(x: int) = proc sayHello(x: int) =
var m = x # a mutable version of x var m = x # a mutable version of x
echo sayHi(x) # matches the non-var version of sayHi echo sayHi(x) # matches the non-var version of sayHi

View file

@ -18,6 +18,6 @@ Example:
A type section begins with the ``type`` keyword. It contains multiple A type section begins with the ``type`` keyword. It contains multiple
type definitions. A type definition binds a type to a name. Type definitions type definitions. A type definition binds a type to a name. Type definitions
can be recursive or even mutually recursive. Mutually recursive types are only can be recursive or even mutually recursive. Mutually recursive types are only
possible within a single ``type`` section. Nominal types like ``objects`` possible within a single ``type`` section. Nominal types like ``objects``
or ``enums`` can only be defined in a ``type`` section. or ``enums`` can only be defined in a ``type`` section.

View file

@ -19,7 +19,7 @@ Note that there can be exceptions to these rules. Nim being as flexible as it
is, there will be parts of this style guide that don't make sense in certain is, there will be parts of this style guide that don't make sense in certain
contexts. Furthermore, just as contexts. Furthermore, just as
`Python's style guide<http://legacy.python.org/dev/peps/pep-0008/>`_ changes `Python's style guide<http://legacy.python.org/dev/peps/pep-0008/>`_ changes
over time, this style guide will too. over time, this style guide will too.
These rules will only be enforced for contributions to the Nim These rules will only be enforced for contributions to the Nim
codebase and official projects, such as the Nim compiler, the standard library, codebase and official projects, such as the Nim compiler, the standard library,

View file

@ -1,12 +1,12 @@
===================== =====================
Nimfix User Guide Nimfix User Guide
===================== =====================
:Author: Andreas Rumpf :Author: Andreas Rumpf
:Version: |nimversion| :Version: |nimversion|
**WARNING**: Nimfix is currently beta-quality. **WARNING**: Nimfix is currently beta-quality.
Nimfix is a tool to help you upgrade from Nimrod (<= version 0.9.6) to Nimfix is a tool to help you upgrade from Nimrod (<= version 0.9.6) to
Nim (=> version 0.10.0). Nim (=> version 0.10.0).
@ -48,7 +48,7 @@ Options:
--wholeProject overwrite every processed file. --wholeProject overwrite every processed file.
--checkExtern:on|off style check also extern names --checkExtern:on|off style check also extern names
--styleCheck:on|off|auto performs style checking for identifiers --styleCheck:on|off|auto performs style checking for identifiers
and suggests an alternative spelling; and suggests an alternative spelling;
'auto' corrects the spelling. 'auto' corrects the spelling.
In addition, all command line options of Nim are supported. In addition, all command line options of Nim are supported.

View file

@ -7,11 +7,11 @@
Nimgrep is a command line tool for search&replace tasks. It can search for Nimgrep is a command line tool for search&replace tasks. It can search for
regex or peg patterns and can search whole directories at once. User regex or peg patterns and can search whole directories at once. User
confirmation for every single replace operation can be requested. confirmation for every single replace operation can be requested.
Nimgrep has particularly good support for Nim's Nimgrep has particularly good support for Nim's
eccentric *style insensitivity*. Apart from that it is a generic text eccentric *style insensitivity*. Apart from that it is a generic text
manipulation tool. manipulation tool.
@ -34,10 +34,10 @@ Options:
--find, -f find the pattern (default) --find, -f find the pattern (default)
--replace, -r replace the pattern --replace, -r replace the pattern
--peg pattern is a peg --peg pattern is a peg
--re pattern is a regular expression (default); extended --re pattern is a regular expression (default); extended
syntax for the regular expression is always turned on syntax for the regular expression is always turned on
--recursive process directories recursively --recursive process directories recursively
--confirm confirm each occurrence/replacement; there is a chance --confirm confirm each occurrence/replacement; there is a chance
to abort any time without touching the file to abort any time without touching the file
--stdin read pattern from stdin (to avoid the shell's confusing --stdin read pattern from stdin (to avoid the shell's confusing
quoting rules) quoting rules)

View file

@ -11,12 +11,12 @@ Introduction
============ ============
niminst is a tool to generate an installer for a Nim program. Currently niminst is a tool to generate an installer for a Nim program. Currently
it can create an installer for Windows it can create an installer for Windows
via `Inno Setup <http://www.jrsoftware.org/isinfo.php>`_ as well as via `Inno Setup <http://www.jrsoftware.org/isinfo.php>`_ as well as
installation/deinstallation scripts for UNIX. Later versions will support installation/deinstallation scripts for UNIX. Later versions will support
Linux' package management systems. Linux' package management systems.
niminst works by reading a configuration file that contains all the niminst works by reading a configuration file that contains all the
information that it needs to generate an installer for the different operating information that it needs to generate an installer for the different operating
systems. systems.
@ -24,7 +24,7 @@ systems.
Configuration file Configuration file
================== ==================
niminst uses the Nim `parsecfg <parsecfg.html>`_ module to parse the niminst uses the Nim `parsecfg <parsecfg.html>`_ module to parse the
configuration file. Here's an example of how the syntax looks like: configuration file. Here's an example of how the syntax looks like:
.. include:: doc/mytest.cfg .. include:: doc/mytest.cfg
@ -54,10 +54,10 @@ Key description
``OS`` the OSes to generate C code for; for example: ``OS`` the OSes to generate C code for; for example:
``"windows;linux;macosx"`` ``"windows;linux;macosx"``
``CPU`` the CPUs to generate C code for; for example: ``CPU`` the CPUs to generate C code for; for example:
``"i386;amd64;powerpc"`` ``"i386;amd64;powerpc"``
``Authors`` the project's authors ``Authors`` the project's authors
``Description`` the project's description ``Description`` the project's description
``App`` the application's type: "Console" or "GUI". If ``App`` the application's type: "Console" or "GUI". If
"Console", niminst generates a special batch file "Console", niminst generates a special batch file
for Windows to open up the command line shell. for Windows to open up the command line shell.
``License`` the filename of the application's license ``License`` the filename of the application's license
@ -69,7 +69,7 @@ Key description
Many sections support the ``files`` key. Listed filenames Many sections support the ``files`` key. Listed filenames
can be separated by semicolon or the ``files`` key can be repeated. Wildcards can be separated by semicolon or the ``files`` key can be repeated. Wildcards
in filenames are supported. If it is a directory name, all files in the in filenames are supported. If it is a directory name, all files in the
directory are used:: directory are used::
[Config] [Config]
@ -87,35 +87,35 @@ will be installed into the OS's configuration directory.
Documentation section Documentation section
--------------------- ---------------------
The ``documentation`` section supports the ``files`` key. The ``documentation`` section supports the ``files`` key.
Listed files will be installed into the OS's native documentation directory Listed files will be installed into the OS's native documentation directory
(which might be ``$appdir/doc``). (which might be ``$appdir/doc``).
There is a ``start`` key which determines whether the Windows installer There is a ``start`` key which determines whether the Windows installer
generates a link to e.g. the ``index.html`` of your documentation. generates a link to e.g. the ``index.html`` of your documentation.
Other section Other section
------------- -------------
The ``other`` section currently only supports the ``files`` key. The ``other`` section currently only supports the ``files`` key.
Listed files will be installed into the application installation directory Listed files will be installed into the application installation directory
(``$appdir``). (``$appdir``).
Lib section Lib section
----------- -----------
The ``lib`` section currently only supports the ``files`` key. The ``lib`` section currently only supports the ``files`` key.
Listed files will be installed into the OS's native library directory Listed files will be installed into the OS's native library directory
(which might be ``$appdir/lib``). (which might be ``$appdir/lib``).
Windows section Windows section
--------------- ---------------
The ``windows`` section supports the ``files`` key for Windows specific files. The ``windows`` section supports the ``files`` key for Windows specific files.
Listed files will be installed into the application installation directory Listed files will be installed into the application installation directory
(``$appdir``). (``$appdir``).
Other possible options are: Other possible options are:
@ -133,8 +133,8 @@ Key description
UnixBin section UnixBin section
--------------- ---------------
The ``UnixBin`` section currently only supports the ``files`` key. The ``UnixBin`` section currently only supports the ``files`` key.
Listed files will be installed into the OS's native bin directory Listed files will be installed into the OS's native bin directory
(e.g. ``/usr/local/bin``). The exact location depends on the (e.g. ``/usr/local/bin``). The exact location depends on the
installation path the user specifies when running the ``install.sh`` script. installation path the user specifies when running the ``install.sh`` script.
@ -150,7 +150,7 @@ Key description
``InstallScript`` boolean flag whether an installation shell script ``InstallScript`` boolean flag whether an installation shell script
should be generated. Example: ``InstallScript: "Yes"`` should be generated. Example: ``InstallScript: "Yes"``
``UninstallScript`` boolean flag whether a deinstallation shell script ``UninstallScript`` boolean flag whether a deinstallation shell script
should be generated. should be generated.
Example: ``UninstallScript: "Yes"`` Example: ``UninstallScript: "Yes"``
==================== ======================================================= ==================== =======================================================
@ -163,7 +163,7 @@ Possible options are:
==================== ======================================================= ==================== =======================================================
Key description Key description
==================== ======================================================= ==================== =======================================================
``path`` Path to Inno Setup. ``path`` Path to Inno Setup.
Example: ``path = r"c:\inno setup 5\iscc.exe"`` Example: ``path = r"c:\inno setup 5\iscc.exe"``
``flags`` Flags to pass to Inno Setup. ``flags`` Flags to pass to Inno Setup.
Example: ``flags = "/Q"`` Example: ``flags = "/Q"``
@ -178,7 +178,7 @@ Possible options are:
==================== ======================================================= ==================== =======================================================
Key description Key description
==================== ======================================================= ==================== =======================================================
``path`` Path to the C compiler. ``path`` Path to the C compiler.
``flags`` Flags to pass to the C Compiler. ``flags`` Flags to pass to the C Compiler.
Example: ``flags = "-w"`` Example: ``flags = "-w"``
==================== ======================================================= ==================== =======================================================

View file

@ -14,7 +14,7 @@ notation meaning
order, to the text ahead, until one of them succeeds and order, to the text ahead, until one of them succeeds and
possibly consumes some text. Indicate success if one of possibly consumes some text. Indicate success if one of
expressions succeeded. Otherwise do not consume any text expressions succeeded. Otherwise do not consume any text
and indicate failure. and indicate failure.
``A ... Z`` Sequence: Apply expressions `A`, ..., `Z`, in this order, ``A ... Z`` Sequence: Apply expressions `A`, ..., `Z`, in this order,
to consume consecutive portions of the text ahead, as long to consume consecutive portions of the text ahead, as long
as they succeed. Indicate success if all succeeded. as they succeed. Indicate success if all succeeded.
@ -27,11 +27,11 @@ notation meaning
``{E}`` Capture: Apply expression `E` and store the substring ``{E}`` Capture: Apply expression `E` and store the substring
that matched `E` into a *capture* that can be accessed that matched `E` into a *capture* that can be accessed
after the matching process. after the matching process.
``$i`` Back reference to the ``i``th capture. ``i`` counts from 1. ``$i`` Back reference to the ``i``th capture. ``i`` counts from 1.
``$`` Anchor: Matches at the end of the input. No character ``$`` Anchor: Matches at the end of the input. No character
is consumed. Same as ``!.``. is consumed. Same as ``!.``.
``^`` Anchor: Matches at the start of the input. No character ``^`` Anchor: Matches at the start of the input. No character
is consumed. is consumed.
``&E`` And predicate: Indicate success if expression `E` matches ``&E`` And predicate: Indicate success if expression `E` matches
the text ahead; otherwise indicate failure. Do not consume the text ahead; otherwise indicate failure. Do not consume
any text. any text.
@ -41,15 +41,15 @@ notation meaning
``E+`` One or more: Apply expression `E` repeatedly to match ``E+`` One or more: Apply expression `E` repeatedly to match
the text ahead, as long as it succeeds. Consume the matched the text ahead, as long as it succeeds. Consume the matched
text (if any) and indicate success if there was at least text (if any) and indicate success if there was at least
one match. Otherwise indicate failure. one match. Otherwise indicate failure.
``E*`` Zero or more: Apply expression `E` repeatedly to match ``E*`` Zero or more: Apply expression `E` repeatedly to match
the text ahead, as long as it succeeds. Consume the matched the text ahead, as long as it succeeds. Consume the matched
text (if any). Always indicate success. text (if any). Always indicate success.
``E?`` Zero or one: If expression `E` matches the text ahead, ``E?`` Zero or one: If expression `E` matches the text ahead,
consume it. Always indicate success. consume it. Always indicate success.
``[s]`` Character class: If the character ahead appears in the ``[s]`` Character class: If the character ahead appears in the
string `s`, consume it and indicate success. Otherwise string `s`, consume it and indicate success. Otherwise
indicate failure. indicate failure.
``[a-b]`` Character range: If the character ahead is one from the ``[a-b]`` Character range: If the character ahead is one from the
range `a` through `b`, consume it and indicate success. range `a` through `b`, consume it and indicate success.
Otherwise indicate failure. Otherwise indicate failure.
@ -70,7 +70,7 @@ notation meaning
failure. failure.
``@E`` Search: Shorthand for ``(!E .)* E``. (Search loop for the ``@E`` Search: Shorthand for ``(!E .)* E``. (Search loop for the
pattern `E`.) pattern `E`.)
``{@} E`` Captured Search: Shorthand for ``{(!E .)*} E``. (Search ``{@} E`` Captured Search: Shorthand for ``{(!E .)*} E``. (Search
loop for the pattern `E`.) Everything until and exluding loop for the pattern `E`.) Everything until and exluding
`E` is captured. `E` is captured.
``@@ E`` Same as ``{@} E``. ``@@ E`` Same as ``{@} E``.
@ -79,7 +79,7 @@ notation meaning
matching engine.** matching engine.**
``\identifier`` Built-in macro for a longer expression. ``\identifier`` Built-in macro for a longer expression.
``\ddd`` Character with decimal code *ddd*. ``\ddd`` Character with decimal code *ddd*.
``\"``, etc Literal ``"``, etc. ``\"``, etc Literal ``"``, etc.
=============== ============================================================ =============== ============================================================
@ -101,9 +101,9 @@ macro meaning
``\n`` any newline combination: ``\10 / \13\10 / \13`` ``\n`` any newline combination: ``\10 / \13\10 / \13``
``\i`` ignore case for matching; use this at the start of the PEG ``\i`` ignore case for matching; use this at the start of the PEG
``\y`` ignore style for matching; use this at the start of the PEG ``\y`` ignore style for matching; use this at the start of the PEG
``\skip`` pat skip pattern *pat* before trying to match other tokens; ``\skip`` pat skip pattern *pat* before trying to match other tokens;
this is useful for whitespace skipping, for example: this is useful for whitespace skipping, for example:
``\skip(\s*) {\ident} ':' {\ident}`` matches key value ``\skip(\s*) {\ident} ':' {\ident}`` matches key value
pairs ignoring whitespace around the ``':'``. pairs ignoring whitespace around the ``':'``.
``\ident`` a standard ASCII identifier: ``[a-zA-Z_][a-zA-Z_0-9]*`` ``\ident`` a standard ASCII identifier: ``[a-zA-Z_][a-zA-Z_0-9]*``
``\letter`` any Unicode letter ``\letter`` any Unicode letter
@ -133,42 +133,42 @@ The PEG parser implements this grammar (written in PEG syntax)::
# Example grammar of PEG in PEG syntax. # Example grammar of PEG in PEG syntax.
# Comments start with '#'. # Comments start with '#'.
# First symbol is the start symbol. # First symbol is the start symbol.
grammar <- rule* / expr grammar <- rule* / expr
identifier <- [A-Za-z][A-Za-z0-9_]* identifier <- [A-Za-z][A-Za-z0-9_]*
charsetchar <- "\\" . / [^\]] charsetchar <- "\\" . / [^\]]
charset <- "[" "^"? (charsetchar ("-" charsetchar)?)+ "]" charset <- "[" "^"? (charsetchar ("-" charsetchar)?)+ "]"
stringlit <- identifier? ("\"" ("\\" . / [^"])* "\"" / stringlit <- identifier? ("\"" ("\\" . / [^"])* "\"" /
"'" ("\\" . / [^'])* "'") "'" ("\\" . / [^'])* "'")
builtin <- "\\" identifier / [^\13\10] builtin <- "\\" identifier / [^\13\10]
comment <- '#' @ \n comment <- '#' @ \n
ig <- (\s / comment)* # things to ignore ig <- (\s / comment)* # things to ignore
rule <- identifier \s* "<-" expr ig rule <- identifier \s* "<-" expr ig
identNoArrow <- identifier !(\s* "<-") identNoArrow <- identifier !(\s* "<-")
prefixOpr <- ig '&' / ig '!' / ig '@' / ig '{@}' / ig '@@' prefixOpr <- ig '&' / ig '!' / ig '@' / ig '{@}' / ig '@@'
literal <- ig identifier? '$' [0-9]+ / '$' / '^' / literal <- ig identifier? '$' [0-9]+ / '$' / '^' /
ig identNoArrow / ig identNoArrow /
ig charset / ig charset /
ig stringlit / ig stringlit /
ig builtin / ig builtin /
ig '.' / ig '.' /
ig '_' / ig '_' /
(ig "(" expr ig ")") (ig "(" expr ig ")")
postfixOpr <- ig '?' / ig '*' / ig '+' postfixOpr <- ig '?' / ig '*' / ig '+'
primary <- prefixOpr* (literal postfixOpr*) primary <- prefixOpr* (literal postfixOpr*)
# Concatenation has higher priority than choice: # Concatenation has higher priority than choice:
# ``a b / c`` means ``(a b) / c`` # ``a b / c`` means ``(a b) / c``
seqExpr <- primary+ seqExpr <- primary+
expr <- seqExpr (ig "/" expr)* expr <- seqExpr (ig "/" expr)*
**Note**: As a special syntactic extension if the whole PEG is only a single **Note**: As a special syntactic extension if the whole PEG is only a single
expression, identifiers are not interpreted as non-terminals, but are expression, identifiers are not interpreted as non-terminals, but are
interpreted as verbatim string: interpreted as verbatim string:
.. code-block:: nim .. code-block:: nim
@ -185,7 +185,7 @@ Check if `s` matches Nim's "while" keyword:
.. code-block:: nim .. code-block:: nim
s =~ peg" y'while'" s =~ peg" y'while'"
Exchange (key, val)-pairs: Exchange (key, val)-pairs:
.. code-block:: nim .. code-block:: nim
"key: val; key2: val2".replacef(peg"{\ident} \s* ':' \s* {\ident}", "$2: $1") "key: val; key2: val2".replacef(peg"{\ident} \s* ':' \s* {\ident}", "$2: $1")

View file

@ -1,7 +1,7 @@
============================ ============================
Nim's documenation system Nim's documenation system
============================ ============================
This folder contains Nim's documentation. The documentation This folder contains Nim's documentation. The documentation
is written in a format called *reStructuredText*, a markup language that reads is written in a format called *reStructuredText*, a markup language that reads
like ASCII and can be converted to HTML automatically! like ASCII and can be converted to HTML automatically!

View file

@ -175,17 +175,17 @@ for themselves. For example:
example meaning example meaning
============== ============================================================ ============== ============================================================
``\040`` is another way of writing a space ``\040`` is another way of writing a space
``\40`` is the same, provided there are fewer than 40 previous ``\40`` is the same, provided there are fewer than 40 previous
capturing subpatterns capturing subpatterns
``\7`` is always a back reference ``\7`` is always a back reference
``\11`` might be a back reference, or another way of writing a tab ``\11`` might be a back reference, or another way of writing a tab
``\011`` is always a tab ``\011`` is always a tab
``\0113`` is a tab followed by the character "3" ``\0113`` is a tab followed by the character "3"
``\113`` might be a back reference, otherwise the character with ``\113`` might be a back reference, otherwise the character with
octal code 113 octal code 113
``\377`` might be a back reference, otherwise the byte consisting ``\377`` might be a back reference, otherwise the byte consisting
entirely of 1 bits entirely of 1 bits
``\81`` is either a back reference, or a binary zero followed by ``\81`` is either a back reference, or a binary zero followed by
the two characters "8" and "1" the two characters "8" and "1"
============== ============================================================ ============== ============================================================
@ -240,7 +240,7 @@ even when Unicode character property support is available.
Simple assertions Simple assertions
----------------- -----------------
The fourth use of backslash is for certain `simple assertions`:idx:. An The fourth use of backslash is for certain `simple assertions`:idx:. An
assertion specifies a condition that has to be met at a particular point in assertion specifies a condition that has to be met at a particular point in
a match, without consuming any characters from the subject string. The use of a match, without consuming any characters from the subject string. The use of
subpatterns for more complicated assertions is described below. The subpatterns for more complicated assertions is described below. The

View file

@ -4,7 +4,7 @@ basetype can only be an ordinal type of a certain size, namely:
* ``uint8``/``byte``-``uint16`` * ``uint8``/``byte``-``uint16``
* ``char`` * ``char``
* ``enum`` * ``enum``
or equivalent. The reason is that sets are implemented as high or equivalent. The reason is that sets are implemented as high
performance bit vectors. Attempting to declare a set with a larger type will performance bit vectors. Attempting to declare a set with a larger type will
result in an error: result in an error:

View file

@ -2,7 +2,7 @@
Parallel & Spawn Parallel & Spawn
========================================================== ==========================================================
Nim has two flavors of parallelism: Nim has two flavors of parallelism:
1) `Structured`:idx parallelism via the ``parallel`` statement. 1) `Structured`:idx parallelism via the ``parallel`` statement.
2) `Unstructured`:idx: parallelism via the standalone ``spawn`` statement. 2) `Unstructured`:idx: parallelism via the standalone ``spawn`` statement.
@ -30,7 +30,7 @@ variables at the same time:
.. code-block:: nim .. code-block:: nim
import threadpool, ... import threadpool, ...
# wait until 2 out of 3 servers received the update: # wait until 2 out of 3 servers received the update:
proc main = proc main =
var responses = newSeq[FlowVarBase](3) var responses = newSeq[FlowVarBase](3)
@ -89,7 +89,7 @@ restrictions / changes:
the ``parallel`` section. This is called the *immutability check*. Currently the ``parallel`` section. This is called the *immutability check*. Currently
it is not specified what exactly "complex location" means. We need to make it is not specified what exactly "complex location" means. We need to make
this an optimization! this an optimization!
* Every array access has to be provably within bounds. This is called * Every array access has to be provably within bounds. This is called
the *bounds check*. the *bounds check*.
* Slices are optimized so that no copy is performed. This optimization is not * Slices are optimized so that no copy is performed. This optimization is not
yet performed for ordinary slices outside of a ``parallel`` section. Slices yet performed for ordinary slices outside of a ``parallel`` section. Slices

View file

@ -1,11 +1,11 @@
Substitution Expressions (subex) Substitution Expressions (subex)
================================ ================================
A *subex* (*Substitution Expression*) represents an advanted string A *subex* (*Substitution Expression*) represents an advanted string
substitution. In contrast to a `regex`:idx: which deals with string analysis, a substitution. In contrast to a `regex`:idx: which deals with string analysis, a
*subex* deals with string synthesis. *subex* deals with string synthesis.
Thanks to its conditional construct ``$[0|1|2|else]`` it supports Thanks to its conditional construct ``$[0|1|2|else]`` it supports
`internationalization`:idx: of format string literals quite well. `internationalization`:idx: of format string literals quite well.
@ -37,7 +37,7 @@ Notation meaning
``$[zero|one|def]1`` use ``X = parseInt(arg[1])`` to determine which ``$[zero|one|def]1`` use ``X = parseInt(arg[1])`` to determine which
branch to use. If ``X == 0`` the 'zero' branch is branch to use. If ``X == 0`` the 'zero' branch is
selected, if ``X == 1`` the 'one' branch is selected, if ``X == 1`` the 'one' branch is
selected, etc. Otherwise the 'def' branch is selected, etc. Otherwise the 'def' branch is
selected. ``$x`` is interpreted in branches too. selected. ``$x`` is interpreted in branches too.
If a branch needs to contain ``|``, ``]`` put If a branch needs to contain ``|``, ``]`` put
them in single quotes. To produce a verbatim single them in single quotes. To produce a verbatim single
@ -50,12 +50,12 @@ Examples
.. code-block:: nim .. code-block:: nim
subex"$1($', '{2..})" % ["f", "a", "b", "c"] == "f(a, b, c)" subex"$1($', '{2..})" % ["f", "a", "b", "c"] == "f(a, b, c)"
subex"$1 $[files|file|files]{1} copied" % ["1"] == "1 file copied" subex"$1 $[files|file|files]{1} copied" % ["1"] == "1 file copied"
subex"$['''|'|''''|']']#" % "0" == "'|" subex"$['''|'|''''|']']#" % "0" == "'|"
subex("type\n TEnum = enum\n $', '40c'\n '{..}") % [ subex("type\n TEnum = enum\n $', '40c'\n '{..}") % [
"fieldNameA", "fieldNameB", "fieldNameC", "fieldNameD"] "fieldNameA", "fieldNameB", "fieldNameC", "fieldNameD"]

View file

@ -1280,19 +1280,19 @@ with a compatible base type can be passed to an openarray parameter, the index
type does not matter. type does not matter.
.. code-block:: nim .. code-block:: nim
var var
fruits: seq[string] # reference to a sequence of strings that is initialized with 'nil' fruits: seq[string] # reference to a sequence of strings that is initialized with 'nil'
capitals: array[3, string] # array of strings with a fixed size capitals: array[3, string] # array of strings with a fixed size
fruits = @[] # creates an empty sequence on the heap that will be referenced by 'fruits' fruits = @[] # creates an empty sequence on the heap that will be referenced by 'fruits'
capitals = ["New York", "London", "Berlin"] # array 'capitals' allows only assignment of three elements capitals = ["New York", "London", "Berlin"] # array 'capitals' allows only assignment of three elements
fruits.add("Banana") # sequence 'fruits' is dynamically expandable during runtime fruits.add("Banana") # sequence 'fruits' is dynamically expandable during runtime
fruits.add("Mango") fruits.add("Mango")
proc openArraySize(oa: openArray[string]): int = proc openArraySize(oa: openArray[string]): int =
oa.len oa.len
assert openArraySize(fruits) == 2 # procedure accepts a sequence as parameter assert openArraySize(fruits) == 2 # procedure accepts a sequence as parameter
assert openArraySize(capitals) == 3 # but also an array type assert openArraySize(capitals) == 3 # but also an array type

View file

@ -18,9 +18,9 @@ template any(container, cond: expr): expr {.immediate.} =
break break
result result
if all("mystring", {'a'..'z'}.contains) and any("myohmy", 'y'.`==`): if all("mystring", {'a'..'z'}.contains) and any("myohmy", 'y'.`==`):
echo "works" echo "works"
else: else:
echo "does not work" echo "does not work"

View file

@ -18,13 +18,13 @@ type
TDimension2d {.final, header: irr, importc: "dimension2d".} = object TDimension2d {.final, header: irr, importc: "dimension2d".} = object
Tvector3df {.final, header: irr, importc: "vector3df".} = object Tvector3df {.final, header: irr, importc: "vector3df".} = object
TColor {.final, header: irr, importc: "SColor".} = object TColor {.final, header: irr, importc: "SColor".} = object
TIrrlichtDevice {.final, header: irr, importc: "IrrlichtDevice".} = object TIrrlichtDevice {.final, header: irr, importc: "IrrlichtDevice".} = object
TIVideoDriver {.final, header: irr, importc: "IVideoDriver".} = object TIVideoDriver {.final, header: irr, importc: "IVideoDriver".} = object
TISceneManager {.final, header: irr, importc: "ISceneManager".} = object TISceneManager {.final, header: irr, importc: "ISceneManager".} = object
TIGUIEnvironment {.final, header: irr, importc: "IGUIEnvironment".} = object TIGUIEnvironment {.final, header: irr, importc: "IGUIEnvironment".} = object
TIAnimatedMesh {.final, header: irr, importc: "IAnimatedMesh".} = object TIAnimatedMesh {.final, header: irr, importc: "IAnimatedMesh".} = object
TIAnimatedMeshSceneNode {.final, header: irr, TIAnimatedMeshSceneNode {.final, header: irr,
importc: "IAnimatedMeshSceneNode".} = object importc: "IAnimatedMeshSceneNode".} = object
TITexture {.final, header: irr, importc: "ITexture".} = object TITexture {.final, header: irr, importc: "ITexture".} = object
@ -100,7 +100,7 @@ var node = smgr.addAnimatedMeshSceneNode(mesh)
if node != nil: if node != nil:
#node->setMaterialFlag(EMF_LIGHTING, false) #node->setMaterialFlag(EMF_LIGHTING, false)
#node->setMD2Animation(scene::EMAT_STAND) #node->setMD2Animation(scene::EMAT_STAND)
node.setMaterialTexture(0, node.setMaterialTexture(0,
driver.getTexture( driver.getTexture(
"/home/andreas/download/irrlicht-1.7.2/media/media/sydney.bmp")) "/home/andreas/download/irrlicht-1.7.2/media/media/sydney.bmp"))

View file

@ -1,8 +1,8 @@
This example demonstrates how to use Nim with Lazarus. The GUI is generated This example demonstrates how to use Nim with Lazarus. The GUI is generated
with Lazarus, while the "backend" is written in Nim. To compile the example, with Lazarus, while the "backend" is written in Nim. To compile the example,
use this command: use this command:
nim c --app:gui --no_main --no_linking backend.nim nim c --app:gui --no_main --no_linking backend.nim
Open the ``nimlaz.lpi`` file in Lazarus and run the program. Open the ``nimlaz.lpi`` file in Lazarus and run the program.

View file

@ -1,5 +1,5 @@
# Backend for the different user interfaces. # Backend for the different user interfaces.
proc myAdd*(x, y: int): int {.cdecl, exportc.} = proc myAdd*(x, y: int): int {.cdecl, exportc.} =
result = x + y result = x + y

View file

@ -1,13 +1,13 @@
The cross platform calculator illustrates how to use Nim to create a backend The cross platform calculator illustrates how to use Nim to create a backend
called by different native user interfaces. called by different native user interfaces.
Since the purpose of the example is to show how the cross platform code Since the purpose of the example is to show how the cross platform code
interacts with Nimrod the actual backend code is just a simple addition proc. interacts with Nimrod the actual backend code is just a simple addition proc.
By keeping your program logic in Nim you can easily reuse it in different By keeping your program logic in Nim you can easily reuse it in different
platforms. platforms.
To avoid duplication of code, the backend code lies in a separate directory and To avoid duplication of code, the backend code lies in a separate directory and
each platform compiles it with a different custom build process, usually each platform compiles it with a different custom build process, usually
generating C code in a temporary build directory. generating C code in a temporary build directory.
For a more elaborate and useful example see the cross_todo example. For a more elaborate and useful example see the cross_todo example.

View file

@ -1,14 +1,14 @@
This directory contains the nim backend code for the todo cross platform This directory contains the nim backend code for the todo cross platform
example. example.
Unlike the cross platform calculator example, this backend features more code, Unlike the cross platform calculator example, this backend features more code,
using an sqlite database for storage. Also a basic test module is provided, not using an sqlite database for storage. Also a basic test module is provided, not
to be included with the final program but to test the exported functionality. to be included with the final program but to test the exported functionality.
The test is not embedded directly in the backend.nim file to avoid being able The test is not embedded directly in the backend.nim file to avoid being able
to access internal data types and procs not exported and replicate the to access internal data types and procs not exported and replicate the
environment of client code. environment of client code.
In a bigger project with several people you could run `nim doc backend.nim` In a bigger project with several people you could run `nim doc backend.nim`
(or use the doc2 command for a whole project) and provide the generated html (or use the doc2 command for a whole project) and provide the generated html
documentation to another programer for her to implement an interface without documentation to another programer for her to implement an interface without
having to look at the source code. having to look at the source code.

View file

@ -1,5 +1,5 @@
The cross platform todo illustrates how to use Nim to create a backend The cross platform todo illustrates how to use Nim to create a backend
called by different native user interfaces. called by different native user interfaces.
This example builds on the knowledge learned from the cross_calculator example. This example builds on the knowledge learned from the cross_calculator example.
Check it out first to learn how to set up Nim on different platforms. Check it out first to learn how to set up Nim on different platforms.

View file

@ -1,8 +1,8 @@
import import
libcurl libcurl
var hCurl = easy_init() var hCurl = easy_init()
if hCurl != nil: if hCurl != nil:
discard easy_setopt(hCurl, OPT_VERBOSE, true) discard easy_setopt(hCurl, OPT_VERBOSE, true)
discard easy_setopt(hCurl, OPT_URL, "http://nim-lang.org/") discard easy_setopt(hCurl, OPT_URL, "http://nim-lang.org/")
discard easy_perform(hCurl) discard easy_perform(hCurl)

View file

@ -1,6 +1,6 @@
#! stdtmpl | standard #! stdtmpl | standard
#proc generateHTMLPage(title, currentTab, content: string, #proc generateHTMLPage(title, currentTab, content: string,
# tabs: openArray[string]): string = # tabs: openArray[string]): string =
# result = "" # result = ""
<head><title>$title</title></head> <head><title>$title</title></head>
<body> <body>
@ -8,7 +8,7 @@
<ul> <ul>
#for tab in items(tabs): #for tab in items(tabs):
#if currentTab == tab: #if currentTab == tab:
<li><a id="selected" <li><a id="selected"
#else: #else:
<li><a <li><a
#end if #end if

View file

@ -4,11 +4,11 @@
import os, streams, parsexml, strutils import os, streams, parsexml, strutils
proc `=?=` (a, b: string): bool = proc `=?=` (a, b: string): bool =
# little trick: define our own comparator that ignores case # little trick: define our own comparator that ignores case
return cmpIgnoreCase(a, b) == 0 return cmpIgnoreCase(a, b) == 0
if paramCount() < 1: if paramCount() < 1:
quit("Usage: htmlrefs filename[.html]") quit("Usage: htmlrefs filename[.html]")
var links = 0 # count the number of links var links = 0 # count the number of links
@ -21,17 +21,17 @@ next(x) # get first event
block mainLoop: block mainLoop:
while true: while true:
case x.kind case x.kind
of xmlElementOpen: of xmlElementOpen:
# the <a href = "xyz"> tag we are interested in always has an attribute, # the <a href = "xyz"> tag we are interested in always has an attribute,
# thus we search for ``xmlElementOpen`` and not for ``xmlElementStart`` # thus we search for ``xmlElementOpen`` and not for ``xmlElementStart``
if x.elementName =?= "a": if x.elementName =?= "a":
x.next() x.next()
if x.kind == xmlAttribute: if x.kind == xmlAttribute:
if x.attrKey =?= "href": if x.attrKey =?= "href":
var link = x.attrValue var link = x.attrValue
inc(links) inc(links)
# skip until we have an ``xmlElementClose`` event # skip until we have an ``xmlElementClose`` event
while true: while true:
x.next() x.next()
case x.kind case x.kind
of xmlEof: break mainLoop of xmlEof: break mainLoop
@ -40,14 +40,14 @@ block mainLoop:
x.next() # skip ``xmlElementClose`` x.next() # skip ``xmlElementClose``
# now we have the description for the ``a`` element # now we have the description for the ``a`` element
var desc = "" var desc = ""
while x.kind == xmlCharData: while x.kind == xmlCharData:
desc.add(x.charData) desc.add(x.charData)
x.next() x.next()
echo(desc & ": " & link) echo(desc & ": " & link)
else: else:
x.next() x.next()
of xmlEof: break # end of file reached of xmlEof: break # end of file reached
of xmlError: of xmlError:
echo(errorMsg(x)) echo(errorMsg(x))
x.next() x.next()
else: x.next() # skip other events else: x.next() # skip other events

View file

@ -15,11 +15,11 @@ open(x, s, filename)
while true: while true:
x.next() x.next()
case x.kind case x.kind
of xmlElementStart: of xmlElementStart:
if cmpIgnoreCase(x.elementName, "title") == 0: if cmpIgnoreCase(x.elementName, "title") == 0:
var title = "" var title = ""
x.next() # skip "<title>" x.next() # skip "<title>"
while x.kind == xmlCharData: while x.kind == xmlCharData:
title.add(x.charData) title.add(x.charData)
x.next() x.next()
if x.kind == xmlElementEnd and cmpIgnoreCase(x.elementName, "title") == 0: if x.kind == xmlElementEnd and cmpIgnoreCase(x.elementName, "title") == 0:
@ -27,7 +27,7 @@ while true:
quit(0) # Success! quit(0) # Success!
else: else:
echo(x.errorMsgExpected("/title")) echo(x.errorMsgExpected("/title"))
of xmlEof: break # end of file reached of xmlEof: break # end of file reached
else: discard # ignore other events else: discard # ignore other events

View file

@ -90,7 +90,7 @@ proc serveFile(client: Socket, filename: string) =
# ------------------ CGI execution ------------------------------------------- # ------------------ CGI execution -------------------------------------------
proc executeCgi(server: var TServer, client: Socket, path, query: string, proc executeCgi(server: var TServer, client: Socket, path, query: string,
meth: TRequestMethod) = meth: TRequestMethod) =
var env = newStringTable(modeCaseInsensitive) var env = newStringTable(modeCaseInsensitive)
var contentLength = -1 var contentLength = -1
@ -123,12 +123,12 @@ proc executeCgi(server: var TServer, client: Socket, path, query: string,
send(client, "HTTP/1.0 200 OK" & wwwNL) send(client, "HTTP/1.0 200 OK" & wwwNL)
var process = startProcess(command=path, env=env) var process = startProcess(command=path, env=env)
var job: TJob var job: TJob
job.process = process job.process = process
job.client = client job.client = client
server.job.add(job) server.job.add(job)
if meth == reqPost: if meth == reqPost:
# get from client and post to CGI program: # get from client and post to CGI program:
var buf = alloc(contentLength) var buf = alloc(contentLength)

View file

@ -1,6 +1,6 @@
# Test high level features # Test high level features
import strutils import strutils
echo "Give a list of numbers (separated by spaces): " echo "Give a list of numbers (separated by spaces): "
stdin.readLine.split.map(parseInt).max.`$`.echo(" is the maximum!") stdin.readLine.split.map(parseInt).max.`$`.echo(" is the maximum!")

View file

@ -1,7 +1,7 @@
import import
os, parsecfg, strutils, streams os, parsecfg, strutils, streams
var f = newFileStream(paramStr(1), fmRead) var f = newFileStream(paramStr(1), fmRead)
if f != nil: if f != nil:
var p: CfgParser var p: CfgParser
@ -9,7 +9,7 @@ if f != nil:
while true: while true:
var e = next(p) var e = next(p)
case e.kind case e.kind
of cfgEof: of cfgEof:
echo("EOF!") echo("EOF!")
break break
of cfgSectionStart: ## a ``[section]`` has been parsed of cfgSectionStart: ## a ``[section]`` has been parsed

View file

@ -1,5 +1,5 @@
In this directory you will find several examples for how to use the Nimrod In this directory you will find several examples for how to use the Nimrod
library. library.
Copyright (c) 2004-2012 Andreas Rumpf. Copyright (c) 2004-2012 Andreas Rumpf.
All rights reserved. All rights reserved.

View file

@ -1,52 +1,52 @@
# Test the SDL interface: # Test the SDL interface:
import import
sdl, sdl_image, colors sdl, sdl_image, colors
var var
screen, greeting: PSurface screen, greeting: PSurface
r: TRect r: TRect
event: TEvent event: TEvent
bgColor = colChocolate.int32 bgColor = colChocolate.int32
if init(INIT_VIDEO) != 0: if init(INIT_VIDEO) != 0:
quit "SDL failed to initialize!" quit "SDL failed to initialize!"
screen = setVideoMode(640, 480, 16, SWSURFACE or ANYFORMAT) screen = setVideoMode(640, 480, 16, SWSURFACE or ANYFORMAT)
if screen.isNil: if screen.isNil:
quit($sdl.getError()) quit($sdl.getError())
greeting = imgLoad("tux.png") greeting = imgLoad("tux.png")
if greeting.isNil: if greeting.isNil:
echo "Failed to load tux.png" echo "Failed to load tux.png"
else: else:
## convert the image to alpha and free the old one ## convert the image to alpha and free the old one
var s = greeting.displayFormatAlpha() var s = greeting.displayFormatAlpha()
swap(greeting, s) swap(greeting, s)
s.freeSurface() s.freeSurface()
r.x = 0 r.x = 0
r.y = 0 r.y = 0
block game_loop: block game_loop:
while true: while true:
while pollEvent(addr event) > 0: while pollEvent(addr event) > 0:
case event.kind case event.kind
of QUITEV: of QUITEV:
break game_loop break game_loop
of KEYDOWN: of KEYDOWN:
if evKeyboard(addr event).keysym.sym == K_ESCAPE: if evKeyboard(addr event).keysym.sym == K_ESCAPE:
break game_loop break game_loop
else: else:
discard discard
discard fillRect(screen, nil, bgColor) discard fillRect(screen, nil, bgColor)
discard blitSurface(greeting, nil, screen, addr r) discard blitSurface(greeting, nil, screen, addr r)
discard flip(screen) discard flip(screen)
greeting.freeSurface() greeting.freeSurface()
screen.freeSurface() screen.freeSurface()
sdl.quit() sdl.quit()
## fowl wuz here 10/2012 ## fowl wuz here 10/2012

View file

@ -28,7 +28,7 @@ while readRow(x):
for i in 0..x.row.len-1: header[i] = "Col " & $(i+1) for i in 0..x.row.len-1: header[i] = "Col " & $(i+1)
else: else:
# data line: # data line:
for i in 0..x.row.len-1: for i in 0..x.row.len-1:
push(res[i], parseFloat(x.row[i])) push(res[i], parseFloat(x.row[i]))
x.close() x.close()

View file

@ -1,7 +1,7 @@
template htmlTag(tag: expr) {.immediate.} = template htmlTag(tag: expr) {.immediate.} =
proc tag(): string = "<" & astToStr(tag) & ">" proc tag(): string = "<" & astToStr(tag) & ">"
htmlTag(br) htmlTag(br)
htmlTag(html) htmlTag(html)

View file

@ -1,47 +1,47 @@
import import
unittest, macros unittest, macros
var var
a = 1 a = 1
b = 22 b = 22
c = 1 c = 1
d = 3 d = 3
suite "my suite": suite "my suite":
setup: setup:
echo "suite setup" echo "suite setup"
var testVar = "from setup" var testVar = "from setup"
teardown: teardown:
echo "suite teardown" echo "suite teardown"
test "first suite test": test "first suite test":
testVar = "modified" testVar = "modified"
echo "test var: " & testVar echo "test var: " & testVar
check a > b check a > b
test "second suite test": test "second suite test":
echo "test var: " & testVar echo "test var: " & testVar
proc foo: bool = proc foo: bool =
echo "running foo" echo "running foo"
return true return true
proc err = proc err =
raise newException(EArithmetic, "some exception") raise newException(EArithmetic, "some exception")
test "final test": test "final test":
echo "inside suite-less test" echo "inside suite-less test"
check: check:
a == c a == c
foo() foo()
d > 10 d > 10
test "arithmetic failure": test "arithmetic failure":
expect(EArithmetic): expect(EArithmetic):
err() err()
expect(EArithmetic, ESystem): expect(EArithmetic, ESystem):
discard foo() discard foo()

View file

@ -7,7 +7,7 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
## A higher level `mySQL`:idx: database wrapper. The same interface is ## A higher level `mySQL`:idx: database wrapper. The same interface is
## implemented for other databases too. ## implemented for other databases too.
import strutils, mysql import strutils, mysql
@ -29,22 +29,22 @@ type
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].} {.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} = proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
## constructs a SqlQuery from the string `query`. This is supposed to be ## constructs a SqlQuery from the string `query`. This is supposed to be
## used as a raw-string-literal modifier: ## used as a raw-string-literal modifier:
## ``sql"update user set counter = counter + 1"`` ## ``sql"update user set counter = counter + 1"``
## ##
## If assertions are turned off, it does nothing. If assertions are turned ## If assertions are turned off, it does nothing. If assertions are turned
## on, later versions will check the string for valid syntax. ## on, later versions will check the string for valid syntax.
result = SqlQuery(query) result = SqlQuery(query)
proc dbError(db: DbConn) {.noreturn.} = proc dbError(db: DbConn) {.noreturn.} =
## raises an EDb exception. ## raises an EDb exception.
var e: ref EDb var e: ref EDb
new(e) new(e)
e.msg = $mysql.error(db) e.msg = $mysql.error(db)
raise e raise e
proc dbError*(msg: string) {.noreturn.} = proc dbError*(msg: string) {.noreturn.} =
## raises an EDb exception with message `msg`. ## raises an EDb exception with message `msg`.
var e: ref EDb var e: ref EDb
new(e) new(e)
@ -55,9 +55,9 @@ when false:
proc dbQueryOpt*(db: DbConn, query: string, args: varargs[string, `$`]) = proc dbQueryOpt*(db: DbConn, query: string, args: varargs[string, `$`]) =
var stmt = mysql_stmt_init(db) var stmt = mysql_stmt_init(db)
if stmt == nil: dbError(db) if stmt == nil: dbError(db)
if mysql_stmt_prepare(stmt, query, len(query)) != 0: if mysql_stmt_prepare(stmt, query, len(query)) != 0:
dbError(db) dbError(db)
var var
binding: seq[MYSQL_BIND] binding: seq[MYSQL_BIND]
discard mysql_stmt_close(stmt) discard mysql_stmt_close(stmt)
@ -79,9 +79,9 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
else: else:
add(result, dbQuote(args[a])) add(result, dbQuote(args[a]))
inc(a) inc(a)
else: else:
add(result, c) add(result, c)
proc tryExec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {. proc tryExec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {.
tags: [FReadDB, FWriteDb].} = tags: [FReadDB, FWriteDb].} =
## tries to execute the query and returns true if successful, false otherwise. ## tries to execute the query and returns true if successful, false otherwise.
@ -97,19 +97,19 @@ proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
## executes the query and raises EDB if not successful. ## executes the query and raises EDB if not successful.
var q = dbFormat(query, args) var q = dbFormat(query, args)
if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db) if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db)
proc newRow(L: int): Row = proc newRow(L: int): Row =
newSeq(result, L) newSeq(result, L)
for i in 0..L-1: result[i] = "" for i in 0..L-1: result[i] = ""
proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) = proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) =
if row != nil: if row != nil:
while mysql.fetchRow(sqlres) != nil: discard while mysql.fetchRow(sqlres) != nil: discard
mysql.freeResult(sqlres) mysql.freeResult(sqlres)
iterator fastRows*(db: DbConn, query: SqlQuery, iterator fastRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [FReadDB].} = args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
## executes the query and iterates over the result dataset. This is very ## executes the query and iterates over the result dataset. This is very
## fast, but potenially dangerous: If the for-loop-body executes another ## fast, but potenially dangerous: If the for-loop-body executes another
## query, the results can be undefined. For MySQL this is the case!. ## query, the results can be undefined. For MySQL this is the case!.
rawExec(db, query, args) rawExec(db, query, args)
@ -121,7 +121,7 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
while true: while true:
row = mysql.fetchRow(sqlres) row = mysql.fetchRow(sqlres)
if row == nil: break if row == nil: break
for i in 0..L-1: for i in 0..L-1:
setLen(result[i], 0) setLen(result[i], 0)
if row[i] == nil: if row[i] == nil:
result[i] = nil result[i] = nil
@ -164,8 +164,8 @@ proc getRow*(db: DbConn, query: SqlQuery,
var L = int(mysql.numFields(sqlres)) var L = int(mysql.numFields(sqlres))
result = newRow(L) result = newRow(L)
var row = mysql.fetchRow(sqlres) var row = mysql.fetchRow(sqlres)
if row != nil: if row != nil:
for i in 0..L-1: for i in 0..L-1:
setLen(result[i], 0) setLen(result[i], 0)
if row[i] == nil: if row[i] == nil:
result[i] = nil result[i] = nil
@ -173,7 +173,7 @@ proc getRow*(db: DbConn, query: SqlQuery,
add(result[i], row[i]) add(result[i], row[i])
properFreeResult(sqlres, row) properFreeResult(sqlres, row)
proc getAllRows*(db: DbConn, query: SqlQuery, proc getAllRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): seq[Row] {.tags: [FReadDB].} = args: varargs[string, `$`]): seq[Row] {.tags: [FReadDB].} =
## executes the query and returns the whole result dataset. ## executes the query and returns the whole result dataset.
result = @[] result = @[]
@ -196,44 +196,44 @@ proc getAllRows*(db: DbConn, query: SqlQuery,
inc(j) inc(j)
mysql.freeResult(sqlres) mysql.freeResult(sqlres)
iterator rows*(db: DbConn, query: SqlQuery, iterator rows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [FReadDB].} = args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
## same as `fastRows`, but slower and safe. ## same as `fastRows`, but slower and safe.
for r in items(getAllRows(db, query, args)): yield r for r in items(getAllRows(db, query, args)): yield r
proc getValue*(db: DbConn, query: SqlQuery, proc getValue*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): string {.tags: [FReadDB].} = args: varargs[string, `$`]): string {.tags: [FReadDB].} =
## executes the query and returns the first column of the first row of the ## executes the query and returns the first column of the first row of the
## result dataset. Returns "" if the dataset contains no rows or the database ## result dataset. Returns "" if the dataset contains no rows or the database
## value is NULL. ## value is NULL.
result = getRow(db, query, args)[0] result = getRow(db, query, args)[0]
proc tryInsertId*(db: DbConn, query: SqlQuery, proc tryInsertId*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} = args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
## executes the query (typically "INSERT") and returns the ## executes the query (typically "INSERT") and returns the
## generated ID for the row or -1 in case of an error. ## generated ID for the row or -1 in case of an error.
var q = dbFormat(query, args) var q = dbFormat(query, args)
if mysql.realQuery(db, q, q.len) != 0'i32: if mysql.realQuery(db, q, q.len) != 0'i32:
result = -1'i64 result = -1'i64
else: else:
result = mysql.insertId(db) result = mysql.insertId(db)
proc insertId*(db: DbConn, query: SqlQuery, proc insertId*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} = args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
## executes the query (typically "INSERT") and returns the ## executes the query (typically "INSERT") and returns the
## generated ID for the row. ## generated ID for the row.
result = tryInsertID(db, query, args) result = tryInsertID(db, query, args)
if result < 0: dbError(db) if result < 0: dbError(db)
proc execAffectedRows*(db: DbConn, query: SqlQuery, proc execAffectedRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {. args: varargs[string, `$`]): int64 {.
tags: [FReadDB, FWriteDb].} = tags: [FReadDB, FWriteDb].} =
## runs the query (typically "UPDATE") and returns the ## runs the query (typically "UPDATE") and returns the
## number of affected rows ## number of affected rows
rawExec(db, query, args) rawExec(db, query, args)
result = mysql.affectedRows(db) result = mysql.affectedRows(db)
proc close*(db: DbConn) {.tags: [FDb].} = proc close*(db: DbConn) {.tags: [FDb].} =
## closes the database connection. ## closes the database connection.
if db != nil: mysql.close(db) if db != nil: mysql.close(db)
@ -242,14 +242,14 @@ proc open*(connection, user, password, database: string): DbConn {.
## opens a database connection. Raises `EDb` if the connection could not ## opens a database connection. Raises `EDb` if the connection could not
## be established. ## be established.
result = mysql.init(nil) result = mysql.init(nil)
if result == nil: dbError("could not open database connection") if result == nil: dbError("could not open database connection")
let let
colonPos = connection.find(':') colonPos = connection.find(':')
host = if colonPos < 0: connection host = if colonPos < 0: connection
else: substr(connection, 0, colonPos-1) else: substr(connection, 0, colonPos-1)
port: int32 = if colonPos < 0: 0'i32 port: int32 = if colonPos < 0: 0'i32
else: substr(connection, colonPos+1).parseInt.int32 else: substr(connection, colonPos+1).parseInt.int32
if mysql.realConnect(result, host, user, password, database, if mysql.realConnect(result, host, user, password, database,
port, nil, 0) == nil: port, nil, 0) == nil:
var errmsg = $mysql.error(result) var errmsg = $mysql.error(result)
db_mysql.close(result) db_mysql.close(result)
@ -257,6 +257,6 @@ proc open*(connection, user, password, database: string): DbConn {.
proc setEncoding*(connection: DbConn, encoding: string): bool {. proc setEncoding*(connection: DbConn, encoding: string): bool {.
tags: [FDb].} = tags: [FDb].} =
## sets the encoding of a database connection, returns true for ## sets the encoding of a database connection, returns true for
## success, false for failure. ## success, false for failure.
result = mysql.set_character_set(connection, encoding) == 0 result = mysql.set_character_set(connection, encoding) == 0

View file

@ -7,7 +7,7 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
## A higher level `PostgreSQL`:idx: database wrapper. This interface ## A higher level `PostgreSQL`:idx: database wrapper. This interface
## is implemented for other databases too. ## is implemented for other databases too.
import strutils, postgres import strutils, postgres
@ -20,7 +20,7 @@ type
## used to get a row's ## used to get a row's
## column text on demand ## column text on demand
EDb* = object of IOError ## exception that is raised if a database error occurs EDb* = object of IOError ## exception that is raised if a database error occurs
SqlQuery* = distinct string ## an SQL query string SqlQuery* = distinct string ## an SQL query string
SqlPrepared* = distinct string ## a identifier for the prepared queries SqlPrepared* = distinct string ## a identifier for the prepared queries
@ -30,15 +30,15 @@ type
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn, {.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn,
TSqlPrepared: SqlPrepared].} TSqlPrepared: SqlPrepared].}
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} = proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
## constructs a SqlQuery from the string `query`. This is supposed to be ## constructs a SqlQuery from the string `query`. This is supposed to be
## used as a raw-string-literal modifier: ## used as a raw-string-literal modifier:
## ``sql"update user set counter = counter + 1"`` ## ``sql"update user set counter = counter + 1"``
## ##
## If assertions are turned off, it does nothing. If assertions are turned ## If assertions are turned off, it does nothing. If assertions are turned
## on, later versions will check the string for valid syntax. ## on, later versions will check the string for valid syntax.
result = SqlQuery(query) result = SqlQuery(query)
proc dbError*(db: DbConn) {.noreturn.} = proc dbError*(db: DbConn) {.noreturn.} =
## raises an EDb exception. ## raises an EDb exception.
var e: ref EDb var e: ref EDb
@ -73,7 +73,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
inc(a) inc(a)
else: else:
add(result, c) add(result, c)
proc tryExec*(db: DbConn, query: SqlQuery, proc tryExec*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): bool {.tags: [FReadDB, FWriteDb].} = args: varargs[string, `$`]): bool {.tags: [FReadDB, FWriteDb].} =
## tries to execute the query and returns true if successful, false otherwise. ## tries to execute the query and returns true if successful, false otherwise.
@ -106,7 +106,7 @@ proc exec*(db: DbConn, stmtName: SqlPrepared,
proc newRow(L: int): Row = proc newRow(L: int): Row =
newSeq(result, L) newSeq(result, L)
for i in 0..L-1: result[i] = "" for i in 0..L-1: result[i] = ""
proc setupQuery(db: DbConn, query: SqlQuery, proc setupQuery(db: DbConn, query: SqlQuery,
args: varargs[string]): PPGresult = args: varargs[string]): PPGresult =
var arr = allocCStringArray(args) var arr = allocCStringArray(args)
@ -128,7 +128,7 @@ proc prepare*(db: DbConn; stmtName: string, query: SqlQuery;
var res = pqprepare(db, stmtName, query.string, int32(nParams), nil) var res = pqprepare(db, stmtName, query.string, int32(nParams), nil)
if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db) if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db)
return SqlPrepared(stmtName) return SqlPrepared(stmtName)
proc setRow(res: PPGresult, r: var Row, line, cols: int32) = proc setRow(res: PPGresult, r: var Row, line, cols: int32) =
for col in 0..cols-1: for col in 0..cols-1:
setLen(r[col], 0) setLen(r[col], 0)
@ -140,7 +140,7 @@ proc setRow(res: PPGresult, r: var Row, line, cols: int32) =
iterator fastRows*(db: DbConn, query: SqlQuery, iterator fastRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [FReadDB].} = args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
## executes the query and iterates over the result dataset. This is very ## executes the query and iterates over the result dataset. This is very
## fast, but potenially dangerous: If the for-loop-body executes another ## fast, but potenially dangerous: If the for-loop-body executes another
## query, the results can be undefined. For Postgres it is safe though. ## query, the results can be undefined. For Postgres it is safe though.
var res = setupQuery(db, query, args) var res = setupQuery(db, query, args)
@ -224,14 +224,14 @@ proc getValue*(db: DbConn, query: SqlQuery,
## value is NULL. ## value is NULL.
var x = pqgetvalue(setupQuery(db, query, args), 0, 0) var x = pqgetvalue(setupQuery(db, query, args), 0, 0)
result = if isNil(x): "" else: $x result = if isNil(x): "" else: $x
proc tryInsertID*(db: DbConn, query: SqlQuery, proc tryInsertID*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].}= args: varargs[string, `$`]): int64 {.tags: [FWriteDb].}=
## executes the query (typically "INSERT") and returns the ## executes the query (typically "INSERT") and returns the
## generated ID for the row or -1 in case of an error. For Postgre this adds ## generated ID for the row or -1 in case of an error. For Postgre this adds
## ``RETURNING id`` to the query, so it only works if your primary key is ## ``RETURNING id`` to the query, so it only works if your primary key is
## named ``id``. ## named ``id``.
var x = pqgetvalue(setupQuery(db, SqlQuery(string(query) & " RETURNING id"), var x = pqgetvalue(setupQuery(db, SqlQuery(string(query) & " RETURNING id"),
args), 0, 0) args), 0, 0)
if not isNil(x): if not isNil(x):
result = parseBiggestInt($x) result = parseBiggestInt($x)
@ -240,13 +240,13 @@ proc tryInsertID*(db: DbConn, query: SqlQuery,
proc insertID*(db: DbConn, query: SqlQuery, proc insertID*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} = args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
## executes the query (typically "INSERT") and returns the ## executes the query (typically "INSERT") and returns the
## generated ID for the row. For Postgre this adds ## generated ID for the row. For Postgre this adds
## ``RETURNING id`` to the query, so it only works if your primary key is ## ``RETURNING id`` to the query, so it only works if your primary key is
## named ``id``. ## named ``id``.
result = tryInsertID(db, query, args) result = tryInsertID(db, query, args)
if result < 0: dbError(db) if result < 0: dbError(db)
proc execAffectedRows*(db: DbConn, query: SqlQuery, proc execAffectedRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [ args: varargs[string, `$`]): int64 {.tags: [
FReadDB, FWriteDb].} = FReadDB, FWriteDb].} =
@ -286,6 +286,6 @@ proc open*(connection, user, password, database: string): DbConn {.
proc setEncoding*(connection: DbConn, encoding: string): bool {. proc setEncoding*(connection: DbConn, encoding: string): bool {.
tags: [FDb].} = tags: [FDb].} =
## sets the encoding of a database connection, returns true for ## sets the encoding of a database connection, returns true for
## success, false for failure. ## success, false for failure.
return pqsetClientEncoding(connection, encoding) == 0 return pqsetClientEncoding(connection, encoding) == 0

View file

@ -7,7 +7,7 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
## A higher level `SQLite`:idx: database wrapper. This interface ## A higher level `SQLite`:idx: database wrapper. This interface
## is implemented for other databases too. ## is implemented for other databases too.
import strutils, sqlite3 import strutils, sqlite3
@ -19,31 +19,31 @@ type
InstantRow* = Pstmt ## a handle that can be used to get a row's column InstantRow* = Pstmt ## a handle that can be used to get a row's column
## text on demand ## text on demand
EDb* = object of IOError ## exception that is raised if a database error occurs EDb* = object of IOError ## exception that is raised if a database error occurs
SqlQuery* = distinct string ## an SQL query string SqlQuery* = distinct string ## an SQL query string
FDb* = object of IOEffect ## effect that denotes a database operation FDb* = object of IOEffect ## effect that denotes a database operation
FReadDb* = object of FDb ## effect that denotes a read operation FReadDb* = object of FDb ## effect that denotes a read operation
FWriteDb* = object of FDb ## effect that denotes a write operation FWriteDb* = object of FDb ## effect that denotes a write operation
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].} {.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} = proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
## constructs a SqlQuery from the string `query`. This is supposed to be ## constructs a SqlQuery from the string `query`. This is supposed to be
## used as a raw-string-literal modifier: ## used as a raw-string-literal modifier:
## ``sql"update user set counter = counter + 1"`` ## ``sql"update user set counter = counter + 1"``
## ##
## If assertions are turned off, it does nothing. If assertions are turned ## If assertions are turned off, it does nothing. If assertions are turned
## on, later versions will check the string for valid syntax. ## on, later versions will check the string for valid syntax.
result = SqlQuery(query) result = SqlQuery(query)
proc dbError(db: DbConn) {.noreturn.} = proc dbError(db: DbConn) {.noreturn.} =
## raises an EDb exception. ## raises an EDb exception.
var e: ref EDb var e: ref EDb
new(e) new(e)
e.msg = $sqlite3.errmsg(db) e.msg = $sqlite3.errmsg(db)
raise e raise e
proc dbError*(msg: string) {.noreturn.} = proc dbError*(msg: string) {.noreturn.} =
## raises an EDb exception with message `msg`. ## raises an EDb exception with message `msg`.
var e: ref EDb var e: ref EDb
new(e) new(e)
@ -67,8 +67,8 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
inc(a) inc(a)
else: else:
add(result, c) add(result, c)
proc tryExec*(db: DbConn, query: SqlQuery, proc tryExec*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): bool {.tags: [FReadDb, FWriteDb].} = args: varargs[string, `$`]): bool {.tags: [FReadDb, FWriteDb].} =
## tries to execute the query and returns true if successful, false otherwise. ## tries to execute the query and returns true if successful, false otherwise.
var q = dbFormat(query, args) var q = dbFormat(query, args)
@ -81,32 +81,32 @@ proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
tags: [FReadDb, FWriteDb].} = tags: [FReadDb, FWriteDb].} =
## executes the query and raises EDB if not successful. ## executes the query and raises EDB if not successful.
if not tryExec(db, query, args): dbError(db) if not tryExec(db, query, args): dbError(db)
proc newRow(L: int): Row = proc newRow(L: int): Row =
newSeq(result, L) newSeq(result, L)
for i in 0..L-1: result[i] = "" for i in 0..L-1: result[i] = ""
proc setupQuery(db: DbConn, query: SqlQuery, proc setupQuery(db: DbConn, query: SqlQuery,
args: varargs[string]): Pstmt = args: varargs[string]): Pstmt =
var q = dbFormat(query, args) var q = dbFormat(query, args)
if prepare_v2(db, q, q.len.cint, result, nil) != SQLITE_OK: dbError(db) if prepare_v2(db, q, q.len.cint, result, nil) != SQLITE_OK: dbError(db)
proc setRow(stmt: Pstmt, r: var Row, cols: cint) = proc setRow(stmt: Pstmt, r: var Row, cols: cint) =
for col in 0..cols-1: for col in 0..cols-1:
setLen(r[col], column_bytes(stmt, col)) # set capacity setLen(r[col], column_bytes(stmt, col)) # set capacity
setLen(r[col], 0) setLen(r[col], 0)
let x = column_text(stmt, col) let x = column_text(stmt, col)
if not isNil(x): add(r[col], x) if not isNil(x): add(r[col], x)
iterator fastRows*(db: DbConn, query: SqlQuery, iterator fastRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [FReadDb].} = args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
## executes the query and iterates over the result dataset. This is very ## executes the query and iterates over the result dataset. This is very
## fast, but potenially dangerous: If the for-loop-body executes another ## fast, but potenially dangerous: If the for-loop-body executes another
## query, the results can be undefined. For Sqlite it is safe though. ## query, the results can be undefined. For Sqlite it is safe though.
var stmt = setupQuery(db, query, args) var stmt = setupQuery(db, query, args)
var L = (column_count(stmt)) var L = (column_count(stmt))
var result = newRow(L) var result = newRow(L)
while step(stmt) == SQLITE_ROW: while step(stmt) == SQLITE_ROW:
setRow(stmt, result, L) setRow(stmt, result, L)
yield result yield result
if finalize(stmt) != SQLITE_OK: dbError(db) if finalize(stmt) != SQLITE_OK: dbError(db)
@ -136,31 +136,31 @@ proc getRow*(db: DbConn, query: SqlQuery,
var stmt = setupQuery(db, query, args) var stmt = setupQuery(db, query, args)
var L = (column_count(stmt)) var L = (column_count(stmt))
result = newRow(L) result = newRow(L)
if step(stmt) == SQLITE_ROW: if step(stmt) == SQLITE_ROW:
setRow(stmt, result, L) setRow(stmt, result, L)
if finalize(stmt) != SQLITE_OK: dbError(db) if finalize(stmt) != SQLITE_OK: dbError(db)
proc getAllRows*(db: DbConn, query: SqlQuery, proc getAllRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): seq[Row] {.tags: [FReadDb].} = args: varargs[string, `$`]): seq[Row] {.tags: [FReadDb].} =
## executes the query and returns the whole result dataset. ## executes the query and returns the whole result dataset.
result = @[] result = @[]
for r in fastRows(db, query, args): for r in fastRows(db, query, args):
result.add(r) result.add(r)
iterator rows*(db: DbConn, query: SqlQuery, iterator rows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [FReadDb].} = args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
## same as `FastRows`, but slower and safe. ## same as `FastRows`, but slower and safe.
for r in fastRows(db, query, args): yield r for r in fastRows(db, query, args): yield r
proc getValue*(db: DbConn, query: SqlQuery, proc getValue*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): string {.tags: [FReadDb].} = args: varargs[string, `$`]): string {.tags: [FReadDb].} =
## executes the query and returns the first column of the first row of the ## executes the query and returns the first column of the first row of the
## result dataset. Returns "" if the dataset contains no rows or the database ## result dataset. Returns "" if the dataset contains no rows or the database
## value is NULL. ## value is NULL.
var stmt = setupQuery(db, query, args) var stmt = setupQuery(db, query, args)
if step(stmt) == SQLITE_ROW: if step(stmt) == SQLITE_ROW:
let cb = column_bytes(stmt, 0) let cb = column_bytes(stmt, 0)
if cb == 0: if cb == 0:
result = "" result = ""
else: else:
result = newStringOfCap(cb) result = newStringOfCap(cb)
@ -168,12 +168,12 @@ proc getValue*(db: DbConn, query: SqlQuery,
else: else:
result = "" result = ""
if finalize(stmt) != SQLITE_OK: dbError(db) if finalize(stmt) != SQLITE_OK: dbError(db)
proc tryInsertID*(db: DbConn, query: SqlQuery, proc tryInsertID*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 args: varargs[string, `$`]): int64
{.tags: [FWriteDb], raises: [].} = {.tags: [FWriteDb], raises: [].} =
## executes the query (typically "INSERT") and returns the ## executes the query (typically "INSERT") and returns the
## generated ID for the row or -1 in case of an error. ## generated ID for the row or -1 in case of an error.
var q = dbFormat(query, args) var q = dbFormat(query, args)
var stmt: sqlite3.Pstmt var stmt: sqlite3.Pstmt
result = -1 result = -1
@ -183,27 +183,27 @@ proc tryInsertID*(db: DbConn, query: SqlQuery,
if finalize(stmt) != SQLITE_OK: if finalize(stmt) != SQLITE_OK:
result = -1 result = -1
proc insertID*(db: DbConn, query: SqlQuery, proc insertID*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} = args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
## executes the query (typically "INSERT") and returns the ## executes the query (typically "INSERT") and returns the
## generated ID for the row. For Postgre this adds ## generated ID for the row. For Postgre this adds
## ``RETURNING id`` to the query, so it only works if your primary key is ## ``RETURNING id`` to the query, so it only works if your primary key is
## named ``id``. ## named ``id``.
result = tryInsertID(db, query, args) result = tryInsertID(db, query, args)
if result < 0: dbError(db) if result < 0: dbError(db)
proc execAffectedRows*(db: DbConn, query: SqlQuery, proc execAffectedRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {. args: varargs[string, `$`]): int64 {.
tags: [FReadDb, FWriteDb].} = tags: [FReadDb, FWriteDb].} =
## executes the query (typically "UPDATE") and returns the ## executes the query (typically "UPDATE") and returns the
## number of affected rows. ## number of affected rows.
exec(db, query, args) exec(db, query, args)
result = changes(db) result = changes(db)
proc close*(db: DbConn) {.tags: [FDb].} = proc close*(db: DbConn) {.tags: [FDb].} =
## closes the database connection. ## closes the database connection.
if sqlite3.close(db) != SQLITE_OK: dbError(db) if sqlite3.close(db) != SQLITE_OK: dbError(db)
proc open*(connection, user, password, database: string): DbConn {. proc open*(connection, user, password, database: string): DbConn {.
tags: [FDb].} = tags: [FDb].} =
## opens a database connection. Raises `EDb` if the connection could not ## opens a database connection. Raises `EDb` if the connection could not
@ -216,12 +216,12 @@ proc open*(connection, user, password, database: string): DbConn {.
proc setEncoding*(connection: DbConn, encoding: string): bool {. proc setEncoding*(connection: DbConn, encoding: string): bool {.
tags: [FDb].} = tags: [FDb].} =
## sets the encoding of a database connection, returns true for ## sets the encoding of a database connection, returns true for
## success, false for failure. ## success, false for failure.
## ##
## Note that the encoding cannot be changed once it's been set. ## Note that the encoding cannot be changed once it's been set.
## According to SQLite3 documentation, any attempt to change ## According to SQLite3 documentation, any attempt to change
## the encoding after the database is created will be silently ## the encoding after the database is created will be silently
## ignored. ## ignored.
exec(connection, sql"PRAGMA encoding = ?", [encoding]) exec(connection, sql"PRAGMA encoding = ?", [encoding])
result = connection.getValue(sql"PRAGMA encoding") == encoding result = connection.getValue(sql"PRAGMA encoding") == encoding

View file

@ -9,7 +9,7 @@
## This module implements graphical output for Nim; the current ## This module implements graphical output for Nim; the current
## implementation uses SDL but the interface is meant to support multiple ## implementation uses SDL but the interface is meant to support multiple
## backends some day. There is no need to init SDL as this module does that ## backends some day. There is no need to init SDL as this module does that
## implicitly. ## implicitly.
import colors, math import colors, math
@ -24,7 +24,7 @@ type
Surface* {.pure, final.} = object Surface* {.pure, final.} = object
w*, h*: Natural w*, h*: Natural
s*: sdl.PSurface s*: sdl.PSurface
EGraphics* = object of IOError EGraphics* = object of IOError
Font {.pure, final.} = object Font {.pure, final.} = object
@ -35,7 +35,7 @@ type
proc toSdlColor*(c: Color): sdl.Color = proc toSdlColor*(c: Color): sdl.Color =
## Convert colors.Color to sdl.Color ## Convert colors.Color to sdl.Color
var x = c.extractRGB var x = c.extractRGB
result.r = x.r and 0xff result.r = x.r and 0xff
result.g = x.g and 0xff result.g = x.g and 0xff
result.b = x.b and 0xff result.b = x.b and 0xff
@ -43,7 +43,7 @@ proc toSdlColor*(c: Color): sdl.Color =
proc createSdlColor*(sur: PSurface, c: Color, alpha: int = 0): int32 = proc createSdlColor*(sur: PSurface, c: Color, alpha: int = 0): int32 =
## Creates a color using ``sdl.MapRGBA``. ## Creates a color using ``sdl.MapRGBA``.
var x = c.extractRGB var x = c.extractRGB
return sdl.mapRGBA(sur.s.format, x.r and 0xff, x.g and 0xff, return sdl.mapRGBA(sur.s.format, x.r and 0xff, x.g and 0xff,
x.b and 0xff, alpha and 0xff) x.b and 0xff, alpha and 0xff)
proc toSdlRect*(r: Rect): sdl.Rect = proc toSdlRect*(r: Rect): sdl.Rect =
@ -53,26 +53,26 @@ proc toSdlRect*(r: Rect): sdl.Rect =
result.w = uint16(r.width) result.w = uint16(r.width)
result.h = uint16(r.height) result.h = uint16(r.height)
proc raiseEGraphics = proc raiseEGraphics =
raise newException(EGraphics, $sdl.getError()) raise newException(EGraphics, $sdl.getError())
proc surfaceFinalizer(s: PSurface) = sdl.freeSurface(s.s) proc surfaceFinalizer(s: PSurface) = sdl.freeSurface(s.s)
proc newSurface*(width, height: int): PSurface = proc newSurface*(width, height: int): PSurface =
## creates a new surface. ## creates a new surface.
new(result, surfaceFinalizer) new(result, surfaceFinalizer)
result.w = width result.w = width
result.h = height result.h = height
result.s = sdl.createRGBSurface(sdl.SWSURFACE, width, height, result.s = sdl.createRGBSurface(sdl.SWSURFACE, width, height,
32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0) 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0)
if result.s == nil: if result.s == nil:
raiseEGraphics() raiseEGraphics()
assert(not sdl.mustLock(result.s)) assert(not sdl.mustLock(result.s))
proc fontFinalizer(f: PFont) = closeFont(f.f) proc fontFinalizer(f: PFont) = closeFont(f.f)
proc newFont*(name = "VeraMono.ttf", size = 9, color = colBlack): PFont = proc newFont*(name = "VeraMono.ttf", size = 9, color = colBlack): PFont =
## Creates a new font object. Raises ``EIO`` if the font cannot be loaded. ## Creates a new font object. Raises ``EIO`` if the font cannot be loaded.
new(result, fontFinalizer) new(result, fontFinalizer)
result.f = openFont(name, size.cint) result.f = openFont(name, size.cint)
@ -84,7 +84,7 @@ var
defaultFont*: PFont ## default font that is used; this needs to initialized defaultFont*: PFont ## default font that is used; this needs to initialized
## by the client! ## by the client!
proc initDefaultFont*(name = "VeraMono.ttf", size = 9, color = colBlack) = proc initDefaultFont*(name = "VeraMono.ttf", size = 9, color = colBlack) =
## initializes the `defaultFont` var. ## initializes the `defaultFont` var.
defaultFont = newFont(name, size, color) defaultFont = newFont(name, size, color)
@ -98,7 +98,7 @@ proc newScreenSurface*(width, height: int): PSurface =
raiseEGraphics() raiseEGraphics()
proc writeToBMP*(sur: PSurface, filename: string) = proc writeToBMP*(sur: PSurface, filename: string) =
## Saves the contents of the surface `sur` to the file `filename` as a ## Saves the contents of the surface `sur` to the file `filename` as a
## BMP file. ## BMP file.
if sdl.saveBMP(sur.s, filename) != 0: if sdl.saveBMP(sur.s, filename) != 0:
raise newException(IOError, "cannot write: " & filename) raise newException(IOError, "cannot write: " & filename)
@ -111,7 +111,7 @@ type
template setPix(video, pitch, x, y, col: expr): stmt = template setPix(video, pitch, x, y, col: expr): stmt =
video[y * pitch + x] = int32(col) video[y * pitch + x] = int32(col)
template getPix(video, pitch, x, y: expr): expr = template getPix(video, pitch, x, y: expr): expr =
colors.Color(video[y * pitch + x]) colors.Color(video[y * pitch + x])
const const
@ -120,7 +120,7 @@ const
proc getPixel(sur: PSurface, x, y: Natural): colors.Color {.inline.} = proc getPixel(sur: PSurface, x, y: Natural): colors.Color {.inline.} =
assert x <% sur.w assert x <% sur.w
assert y <% sur.h assert y <% sur.h
result = getPix(cast[PPixels](sur.s.pixels), sur.s.pitch.int div ColSize, result = getPix(cast[PPixels](sur.s.pixels), sur.s.pitch.int div ColSize,
x, y) x, y)
proc setPixel(sur: PSurface, x, y: Natural, col: colors.Color) {.inline.} = proc setPixel(sur: PSurface, x, y: Natural, col: colors.Color) {.inline.} =
@ -146,7 +146,7 @@ proc `[]=`*(sur: PSurface, x, y: int, col: Color) =
## set the pixel at position ``(x, y)``. No range checking is done! ## set the pixel at position ``(x, y)``. No range checking is done!
setPixel(sur, x, y, col) setPixel(sur, x, y, col)
proc blit*(destSurf: PSurface, destRect: Rect, srcSurf: PSurface, proc blit*(destSurf: PSurface, destRect: Rect, srcSurf: PSurface,
srcRect: Rect) = srcRect: Rect) =
## Copies ``srcSurf`` into ``destSurf`` ## Copies ``srcSurf`` into ``destSurf``
var destTRect, srcTRect: sdl.Rect var destTRect, srcTRect: sdl.Rect
@ -175,7 +175,7 @@ proc drawText*(sur: PSurface, p: Point, text: string, font = defaultFont) =
## font. ## font.
var textSur: PSurface # This surface will have the text drawn on it var textSur: PSurface # This surface will have the text drawn on it
new(textSur, surfaceFinalizer) new(textSur, surfaceFinalizer)
# Render the text # Render the text
textSur.s = sdl_ttf.renderTextBlended(font.f, text, font.color) textSur.s = sdl_ttf.renderTextBlended(font.f, text, font.color)
# Merge the text surface with sur # Merge the text surface with sur
@ -183,14 +183,14 @@ proc drawText*(sur: PSurface, p: Point, text: string, font = defaultFont) =
proc drawText*(sur: PSurface, p: Point, text: string, proc drawText*(sur: PSurface, p: Point, text: string,
bg: Color, font = defaultFont) = bg: Color, font = defaultFont) =
## Draws text, at location ``p`` with font ``font``. ``bg`` ## Draws text, at location ``p`` with font ``font``. ``bg``
## is the background color. ## is the background color.
var textSur: PSurface # This surface will have the text drawn on it var textSur: PSurface # This surface will have the text drawn on it
new(textSur, surfaceFinalizer) new(textSur, surfaceFinalizer)
textSur.s = sdl_ttf.renderTextShaded(font.f, text, font.color, toSdlColor(bg)) textSur.s = sdl_ttf.renderTextShaded(font.f, text, font.color, toSdlColor(bg))
# Merge the text surface with sur # Merge the text surface with sur
sur.blit((p.x, p.y, sur.w, sur.h), textSur, (0, 0, sur.w, sur.h)) sur.blit((p.x, p.y, sur.w, sur.h), textSur, (0, 0, sur.w, sur.h))
proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) = proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) =
## draws a circle with center `p` and radius `r` with the given color ## draws a circle with center `p` and radius `r` with the given color
## onto the surface `sur`. ## onto the surface `sur`.
@ -205,7 +205,7 @@ proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) =
if x+px <% sur.w: if x+px <% sur.w:
if y+py <% sur.h: setPix(video, pitch, x+px, y+py, color) if y+py <% sur.h: setPix(video, pitch, x+px, y+py, color)
if y-py <% sur.h: setPix(video, pitch, x+px, y-py, color) if y-py <% sur.h: setPix(video, pitch, x+px, y-py, color)
if x-px <% sur.w: if x-px <% sur.w:
if y+py <% sur.h: setPix(video, pitch, x-px, y+py, color) if y+py <% sur.h: setPix(video, pitch, x-px, y+py, color)
if y-py <% sur.h: setPix(video, pitch, x-px, y-py, color) if y-py <% sur.h: setPix(video, pitch, x-px, y-py, color)
@ -213,7 +213,7 @@ proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) =
if x+py <% sur.w: if x+py <% sur.w:
if y+px <% sur.h: setPix(video, pitch, x+py, y+px, color) if y+px <% sur.h: setPix(video, pitch, x+py, y+px, color)
if y-px <% sur.h: setPix(video, pitch, x+py, y-px, color) if y-px <% sur.h: setPix(video, pitch, x+py, y-px, color)
if x-py <% sur.w: if x-py <% sur.w:
if y+px <% sur.h: setPix(video, pitch, x-py, y+px, color) if y+px <% sur.h: setPix(video, pitch, x-py, y+px, color)
if y-px <% sur.h: setPix(video, pitch, x-py, y-px, color) if y-px <% sur.h: setPix(video, pitch, x-py, y-px, color)
@ -225,10 +225,10 @@ proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) =
py = py - 1 py = py - 1
px = px + 1 px = px + 1
proc `>-<`(val: int, s: PSurface): int {.inline.} = proc `>-<`(val: int, s: PSurface): int {.inline.} =
return if val < 0: 0 elif val >= s.w: s.w-1 else: val return if val < 0: 0 elif val >= s.w: s.w-1 else: val
proc `>|<`(val: int, s: PSurface): int {.inline.} = proc `>|<`(val: int, s: PSurface): int {.inline.} =
return if val < 0: 0 elif val >= s.h: s.h-1 else: val return if val < 0: 0 elif val >= s.h: s.h-1 else: val
proc drawLine*(sur: PSurface, p1, p2: Point, color: Color) = proc drawLine*(sur: PSurface, p1, p2: Point, color: Color) =
@ -242,7 +242,7 @@ proc drawLine*(sur: PSurface, p1, p2: Point, color: Color) =
var dy = y1 - y0 var dy = y1 - y0
var dx = x1 - x0 var dx = x1 - x0
if dy < 0: if dy < 0:
dy = -dy dy = -dy
stepy = -1 stepy = -1
else: else:
stepy = 1 stepy = 1
@ -251,7 +251,7 @@ proc drawLine*(sur: PSurface, p1, p2: Point, color: Color) =
stepx = -1 stepx = -1
else: else:
stepx = 1 stepx = 1
dy = dy * 2 dy = dy * 2
dx = dx * 2 dx = dx * 2
var video = cast[PPixels](sur.s.pixels) var video = cast[PPixels](sur.s.pixels)
var pitch = sur.s.pitch.int div ColSize var pitch = sur.s.pitch.int div ColSize
@ -328,17 +328,17 @@ proc drawRect*(sur: PSurface, r: Rect, color: Color) =
if (r.x >= 0 and r.x <= sur.s.w) and (r.y >= 0 and r.y <= sur.s.h): if (r.x >= 0 and r.x <= sur.s.w) and (r.y >= 0 and r.y <= sur.s.h):
var minW = min(sur.s.w - r.x, r.width) var minW = min(sur.s.w - r.x, r.width)
var minH = min(sur.s.h - r.y, r.height) var minH = min(sur.s.h - r.y, r.height)
# Draw Top # Draw Top
for i in 0 .. minW - 1: for i in 0 .. minW - 1:
setPix(video, pitch, r.x + i, r.y, color) setPix(video, pitch, r.x + i, r.y, color)
setPix(video, pitch, r.x + i, r.y + minH - 1, color) # Draw bottom setPix(video, pitch, r.x + i, r.y + minH - 1, color) # Draw bottom
# Draw left side # Draw left side
for i in 0 .. minH - 1: for i in 0 .. minH - 1:
setPix(video, pitch, r.x, r.y + i, color) setPix(video, pitch, r.x, r.y + i, color)
setPix(video, pitch, r.x + minW - 1, r.y + i, color) # Draw right side setPix(video, pitch, r.x + minW - 1, r.y + i, color) # Draw right side
proc fillRect*(sur: PSurface, r: Rect, col: Color) = proc fillRect*(sur: PSurface, r: Rect, col: Color) =
## Fills a rectangle using sdl's ``FillRect`` function. ## Fills a rectangle using sdl's ``FillRect`` function.
var rect = toSdlRect(r) var rect = toSdlRect(r)
@ -350,23 +350,23 @@ proc plot4EllipsePoints(sur: PSurface, cx, cy, x, y: Natural, col: Color) =
var pitch = sur.s.pitch.int div ColSize var pitch = sur.s.pitch.int div ColSize
if cx+x <= sur.s.w-1: if cx+x <= sur.s.w-1:
if cy+y <= sur.s.h-1: setPix(video, pitch, cx+x, cy+y, col) if cy+y <= sur.s.h-1: setPix(video, pitch, cx+x, cy+y, col)
if cy-y <= sur.s.h-1: setPix(video, pitch, cx+x, cy-y, col) if cy-y <= sur.s.h-1: setPix(video, pitch, cx+x, cy-y, col)
if cx-x <= sur.s.w-1: if cx-x <= sur.s.w-1:
if cy+y <= sur.s.h-1: setPix(video, pitch, cx-x, cy+y, col) if cy+y <= sur.s.h-1: setPix(video, pitch, cx-x, cy+y, col)
if cy-y <= sur.s.h-1: setPix(video, pitch, cx-x, cy-y, col) if cy-y <= sur.s.h-1: setPix(video, pitch, cx-x, cy-y, col)
proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural, proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural,
col: Color) = col: Color) =
## Draws an ellipse, ``CX`` and ``CY`` specify the center X and Y of the ## Draws an ellipse, ``CX`` and ``CY`` specify the center X and Y of the
## ellipse, ``XRadius`` and ``YRadius`` specify half the width and height ## ellipse, ``XRadius`` and ``YRadius`` specify half the width and height
## of the ellipse. ## of the ellipse.
var var
x, y: Natural x, y: Natural
xChange, yChange: int xChange, yChange: int
ellipseError: Natural ellipseError: Natural
twoASquare, twoBSquare: Natural twoASquare, twoBSquare: Natural
stoppingX, stoppingY: Natural stoppingX, stoppingY: Natural
twoASquare = 2 * xRadius * xRadius twoASquare = 2 * xRadius * xRadius
twoBSquare = 2 * yRadius * yRadius twoBSquare = 2 * yRadius * yRadius
x = xRadius x = xRadius
@ -376,7 +376,7 @@ proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural,
ellipseError = 0 ellipseError = 0
stoppingX = twoBSquare * xRadius stoppingX = twoBSquare * xRadius
stoppingY = 0 stoppingY = 0
while stoppingX >= stoppingY: # 1st set of points, y` > - 1 while stoppingX >= stoppingY: # 1st set of points, y` > - 1
sur.plot4EllipsePoints(cx, cy, x, y, col) sur.plot4EllipsePoints(cx, cy, x, y, col)
inc(y) inc(y)
@ -388,7 +388,7 @@ proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural,
dec(stoppingX, twoBSquare) dec(stoppingX, twoBSquare)
inc(ellipseError, xChange) inc(ellipseError, xChange)
inc(xChange, twoBSquare) inc(xChange, twoBSquare)
# 1st point set is done; start the 2nd set of points # 1st point set is done; start the 2nd set of points
x = 0 x = 0
y = yRadius y = yRadius
@ -408,7 +408,7 @@ proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural,
dec(stoppingY, twoASquare) dec(stoppingY, twoASquare)
inc(ellipseError, yChange) inc(ellipseError, yChange)
inc(yChange,twoASquare) inc(yChange,twoASquare)
proc plotAA(sur: PSurface, x, y: int, c: float, color: Color) = proc plotAA(sur: PSurface, x, y: int, c: float, color: Color) =
if (x > 0 and x < sur.s.w) and (y > 0 and y < sur.s.h): if (x > 0 and x < sur.s.w) and (y > 0 and y < sur.s.h):
@ -419,43 +419,43 @@ proc plotAA(sur: PSurface, x, y: int, c: float, color: Color) =
setPix(video, pitch, x, y, setPix(video, pitch, x, y,
pixColor.intensity(1.0 - c) + color.intensity(c)) pixColor.intensity(1.0 - c) + color.intensity(c))
template ipart(x: expr): expr = floor(x)
template ipart(x: expr): expr = floor(x)
template cround(x: expr): expr = ipart(x + 0.5) template cround(x: expr): expr = ipart(x + 0.5)
template fpart(x: expr): expr = x - ipart(x) template fpart(x: expr): expr = x - ipart(x)
template rfpart(x: expr): expr = 1.0 - fpart(x) template rfpart(x: expr): expr = 1.0 - fpart(x)
proc drawLineAA*(sur: PSurface, p1, p2: Point, color: Color) = proc drawLineAA*(sur: PSurface, p1, p2: Point, color: Color) =
## Draws a anti-aliased line from ``p1`` to ``p2``, using Xiaolin Wu's ## Draws a anti-aliased line from ``p1`` to ``p2``, using Xiaolin Wu's
## line algorithm ## line algorithm
var (x1, x2, y1, y2) = (p1.x.toFloat(), p2.x.toFloat(), var (x1, x2, y1, y2) = (p1.x.toFloat(), p2.x.toFloat(),
p1.y.toFloat(), p2.y.toFloat()) p1.y.toFloat(), p2.y.toFloat())
var dx = x2 - x1 var dx = x2 - x1
var dy = y2 - y1 var dy = y2 - y1
var ax = dx var ax = dx
if ax < 0'f64: if ax < 0'f64:
ax = 0'f64 - ax ax = 0'f64 - ax
var ay = dy var ay = dy
if ay < 0'f64: if ay < 0'f64:
ay = 0'f64 - ay ay = 0'f64 - ay
if ax < ay: if ax < ay:
swap(x1, y1) swap(x1, y1)
swap(x2, y2) swap(x2, y2)
swap(dx, dy) swap(dx, dy)
template doPlot(x, y: int, c: float, color: Color): stmt = template doPlot(x, y: int, c: float, color: Color): stmt =
if ax < ay: if ax < ay:
sur.plotAA(y, x, c, color) sur.plotAA(y, x, c, color)
else: else:
sur.plotAA(x, y, c, color) sur.plotAA(x, y, c, color)
if x2 < x1: if x2 < x1:
swap(x1, x2) swap(x1, x2)
swap(y1, y2) swap(y1, y2)
var gradient = dy / dx var gradient = dy / dx
# handle first endpoint # handle first endpoint
var xend = cround(x1) var xend = cround(x1)
@ -509,19 +509,19 @@ when not defined(testing) and isMainModule:
# Draw the shapes # Draw the shapes
surf.drawLineAA((150, 170), (400, 471), colTan) surf.drawLineAA((150, 170), (400, 471), colTan)
surf.drawLine((100, 170), (400, 471), colRed) surf.drawLine((100, 170), (400, 471), colRed)
surf.drawEllipse(200, 300, 200, 30, colSeaGreen) surf.drawEllipse(200, 300, 200, 30, colSeaGreen)
surf.drawHorLine(1, 300, 400, colViolet) surf.drawHorLine(1, 300, 400, colViolet)
# Check if the ellipse is the size it's suppose to be. # Check if the ellipse is the size it's suppose to be.
surf.drawVerLine(200, 300 - 30 + 1, 60, colViolet) # ^^ | i suppose it is surf.drawVerLine(200, 300 - 30 + 1, 60, colViolet) # ^^ | i suppose it is
surf.drawEllipse(400, 300, 300, 300, colOrange) surf.drawEllipse(400, 300, 300, 300, colOrange)
surf.drawEllipse(5, 5, 5, 5, colGreen) surf.drawEllipse(5, 5, 5, 5, colGreen)
surf.drawHorLine(5, 5, 900, colRed) surf.drawHorLine(5, 5, 900, colRed)
surf.drawVerLine(5, 60, 800, colRed) surf.drawVerLine(5, 60, 800, colRed)
surf.drawCircle((600, 500), 60, colRed) surf.drawCircle((600, 500), 60, colRed)
surf.fillRect((50, 50, 100, 100), colFuchsia) surf.fillRect((50, 50, 100, 100), colFuchsia)
surf.fillRect((150, 50, 100, 100), colGreen) surf.fillRect((150, 50, 100, 100), colGreen)
surf.drawRect((50, 150, 100, 100), colGreen) surf.drawRect((50, 150, 100, 100), colGreen)
@ -530,12 +530,12 @@ when not defined(testing) and isMainModule:
surf.drawHorLine(250, 150, 100, colRed) surf.drawHorLine(250, 150, 100, colRed)
surf.drawLineAA((592, 160), (592, 280), colPurple) surf.drawLineAA((592, 160), (592, 280), colPurple)
#surf.drawText((300, 300), "TEST", colMidnightBlue) #surf.drawText((300, 300), "TEST", colMidnightBlue)
#var textSize = textBounds("TEST") #var textSize = textBounds("TEST")
#surf.drawText((300, 300 + textSize.height), $textSize.width & ", " & #surf.drawText((300, 300 + textSize.height), $textSize.width & ", " &
# $textSize.height, colDarkGreen) # $textSize.height, colDarkGreen)
var mouseStartX = -1 var mouseStartX = -1
var mouseStartY = -1 var mouseStartY = -1
withEvents(surf, event): withEvents(surf, event):
@ -561,17 +561,17 @@ when not defined(testing) and isMainModule:
surf.drawLineAA((mouseStartX, mouseStartY), (int(mbd.x), int(mbd.y)), colPurple) surf.drawLineAA((mouseStartX, mouseStartY), (int(mbd.x), int(mbd.y)), colPurple)
mouseStartX = -1 mouseStartX = -1
mouseStartY = -1 mouseStartY = -1
of sdl.MOUSEMOTION: of sdl.MOUSEMOTION:
var mm = sdl.evMouseMotion(eventp) var mm = sdl.evMouseMotion(eventp)
if mouseStartX != -1 and mouseStartY != -1: if mouseStartX != -1 and mouseStartY != -1:
surf.drawLineAA((mouseStartX, mouseStartY), (int(mm.x), int(mm.y)), colPurple) surf.drawLineAA((mouseStartX, mouseStartY), (int(mm.x), int(mm.y)), colPurple)
#echo(mm.x, " ", mm.y, " ", mm.yrel) #echo(mm.x, " ", mm.y, " ", mm.yrel)
else: else:
discard "echo(event.kind)" discard "echo(event.kind)"
sdl.updateRect(surf.s, 0, 0, 800, 600) sdl.updateRect(surf.s, 0, 0, 800, 600)
surf.writeToBMP("test.bmp") surf.writeToBMP("test.bmp")
sdl.quit() sdl.quit()

View file

@ -41,11 +41,11 @@ type
reExtended = 3, ## ignore whitespace and ``#`` comments reExtended = 3, ## ignore whitespace and ``#`` comments
reStudy = 4 ## study the expression (may be omitted if the reStudy = 4 ## study the expression (may be omitted if the
## expression will be used only once) ## expression will be used only once)
RegexDesc = object RegexDesc = object
h: ptr Pcre h: ptr Pcre
e: ptr ExtraData e: ptr ExtraData
Regex* {.deprecated.} = ref RegexDesc ## a compiled regular expression Regex* {.deprecated.} = ref RegexDesc ## a compiled regular expression
RegexError* = object of ValueError RegexError* = object of ValueError

View file

@ -7,7 +7,7 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
## This module provides an easy to use sockets-style ## This module provides an easy to use sockets-style
## nim interface to the OpenSSL library. ## nim interface to the OpenSSL library.
{.deprecated.} {.deprecated.}
@ -20,37 +20,37 @@ type
bio: BIO bio: BIO
{.deprecated: [TSecureSocket: SecureSocket].} {.deprecated: [TSecureSocket: SecureSocket].}
proc connect*(sock: var SecureSocket, address: string, proc connect*(sock: var SecureSocket, address: string,
port: int): int = port: int): int =
## Connects to the specified `address` on the specified `port`. ## Connects to the specified `address` on the specified `port`.
## Returns the result of the certificate validation. ## Returns the result of the certificate validation.
SslLoadErrorStrings() SslLoadErrorStrings()
ERR_load_BIO_strings() ERR_load_BIO_strings()
if SSL_library_init() != 1: if SSL_library_init() != 1:
raiseOSError(osLastError()) raiseOSError(osLastError())
var ctx = SSL_CTX_new(SSLv23_client_method()) var ctx = SSL_CTX_new(SSLv23_client_method())
if ctx == nil: if ctx == nil:
ERR_print_errors_fp(stderr) ERR_print_errors_fp(stderr)
raiseOSError(osLastError()) raiseOSError(osLastError())
#if SSL_CTX_load_verify_locations(ctx, #if SSL_CTX_load_verify_locations(ctx,
# "/tmp/openssl-0.9.8e/certs/vsign1.pem", NIL) == 0: # "/tmp/openssl-0.9.8e/certs/vsign1.pem", NIL) == 0:
# echo("Failed load verify locations") # echo("Failed load verify locations")
# ERR_print_errors_fp(stderr) # ERR_print_errors_fp(stderr)
sock.bio = BIO_new_ssl_connect(ctx) sock.bio = BIO_new_ssl_connect(ctx)
if BIO_get_ssl(sock.bio, addr(sock.ssl)) == 0: if BIO_get_ssl(sock.bio, addr(sock.ssl)) == 0:
raiseOSError(osLastError()) raiseOSError(osLastError())
if BIO_set_conn_hostname(sock.bio, address & ":" & $port) != 1: if BIO_set_conn_hostname(sock.bio, address & ":" & $port) != 1:
raiseOSError(osLastError()) raiseOSError(osLastError())
if BIO_do_connect(sock.bio) <= 0: if BIO_do_connect(sock.bio) <= 0:
ERR_print_errors_fp(stderr) ERR_print_errors_fp(stderr)
raiseOSError(osLastError()) raiseOSError(osLastError())
result = SSL_get_verify_result(sock.ssl) result = SSL_get_verify_result(sock.ssl)
proc recvLine*(sock: SecureSocket, line: var TaintedString): bool = proc recvLine*(sock: SecureSocket, line: var TaintedString): bool =
@ -86,12 +86,12 @@ proc close*(sock: SecureSocket) =
when not defined(testing) and isMainModule: when not defined(testing) and isMainModule:
var s: SecureSocket var s: SecureSocket
echo connect(s, "smtp.gmail.com", 465) echo connect(s, "smtp.gmail.com", 465)
#var buffer: array[0..255, char] #var buffer: array[0..255, char]
#echo BIO_read(bio, buffer, buffer.len) #echo BIO_read(bio, buffer, buffer.len)
var buffer: string = "" var buffer: string = ""
echo s.recvLine(buffer) echo s.recvLine(buffer)
echo buffer echo buffer
echo buffer.len echo buffer.len

View file

@ -7,8 +7,8 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
## Main file to generate a DLL from the standard library. ## Main file to generate a DLL from the standard library.
## The default Nimrtl does not only contain the ``system`` module, but these ## The default Nimrtl does not only contain the ``system`` module, but these
## too: ## too:
## ##
## * parseutils ## * parseutils
@ -22,12 +22,12 @@
## * unicode ## * unicode
## * pegs ## * pegs
## * ropes ## * ropes
## ##
when system.appType != "lib": when system.appType != "lib":
{.error: "This file has to be compiled as a library!".} {.error: "This file has to be compiled as a library!".}
when not defined(createNimRtl): when not defined(createNimRtl):
{.error: "This file has to be compiled with '-d:createNimRtl'".} {.error: "This file has to be compiled with '-d:createNimRtl'".}
import import

View file

@ -29,34 +29,34 @@ const
# Valid opcodes ( "op" parameter ) to issue to epoll_ctl(). # Valid opcodes ( "op" parameter ) to issue to epoll_ctl().
const const
EPOLL_CTL_ADD* = 1 # Add a file descriptor to the interface. EPOLL_CTL_ADD* = 1 # Add a file descriptor to the interface.
EPOLL_CTL_DEL* = 2 # Remove a file descriptor from the interface. EPOLL_CTL_DEL* = 2 # Remove a file descriptor from the interface.
EPOLL_CTL_MOD* = 3 # Change file descriptor epoll_event structure. EPOLL_CTL_MOD* = 3 # Change file descriptor epoll_event structure.
type type
epoll_data* {.importc: "union epoll_data", epoll_data* {.importc: "union epoll_data",
header: "<sys/epoll.h>", pure, final.} = object # TODO: This is actually a union. header: "<sys/epoll.h>", pure, final.} = object # TODO: This is actually a union.
#thePtr* {.importc: "ptr".}: pointer #thePtr* {.importc: "ptr".}: pointer
fd* {.importc: "fd".}: cint # \ fd* {.importc: "fd".}: cint # \
#u32*: uint32 #u32*: uint32
#u64*: uint64 #u64*: uint64
epoll_event* {.importc: "struct epoll_event", header: "<sys/epoll.h>", pure, final.} = object epoll_event* {.importc: "struct epoll_event", header: "<sys/epoll.h>", pure, final.} = object
events*: uint32 # Epoll events events*: uint32 # Epoll events
data*: epoll_data # User data variable data*: epoll_data # User data variable
proc epoll_create*(size: cint): cint {.importc: "epoll_create", proc epoll_create*(size: cint): cint {.importc: "epoll_create",
header: "<sys/epoll.h>".} header: "<sys/epoll.h>".}
## Creates an epoll instance. Returns an fd for the new instance. ## Creates an epoll instance. Returns an fd for the new instance.
## The "size" parameter is a hint specifying the number of file ## The "size" parameter is a hint specifying the number of file
## descriptors to be associated with the new instance. The fd ## descriptors to be associated with the new instance. The fd
## returned by epoll_create() should be closed with close(). ## returned by epoll_create() should be closed with close().
proc epoll_create1*(flags: cint): cint {.importc: "epoll_create1", proc epoll_create1*(flags: cint): cint {.importc: "epoll_create1",
header: "<sys/epoll.h>".} header: "<sys/epoll.h>".}
## Same as epoll_create but with an FLAGS parameter. The unused SIZE ## Same as epoll_create but with an FLAGS parameter. The unused SIZE
## parameter has been dropped. ## parameter has been dropped.
proc epoll_ctl*(epfd: cint; op: cint; fd: cint | SocketHandle; event: ptr epoll_event): cint {. proc epoll_ctl*(epfd: cint; op: cint; fd: cint | SocketHandle; event: ptr epoll_event): cint {.
importc: "epoll_ctl", header: "<sys/epoll.h>".} importc: "epoll_ctl", header: "<sys/epoll.h>".}
@ -65,10 +65,10 @@ proc epoll_ctl*(epfd: cint; op: cint; fd: cint | SocketHandle; event: ptr epoll_
## specific error code ) The "op" parameter is one of the EPOLL_CTL_* ## specific error code ) The "op" parameter is one of the EPOLL_CTL_*
## constants defined above. The "fd" parameter is the target of the ## constants defined above. The "fd" parameter is the target of the
## operation. The "event" parameter describes which events the caller ## operation. The "event" parameter describes which events the caller
## is interested in and any associated user data. ## is interested in and any associated user data.
proc epoll_wait*(epfd: cint; events: ptr epoll_event; maxevents: cint; proc epoll_wait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
timeout: cint): cint {.importc: "epoll_wait", timeout: cint): cint {.importc: "epoll_wait",
header: "<sys/epoll.h>".} header: "<sys/epoll.h>".}
## Wait for events on an epoll instance "epfd". Returns the number of ## Wait for events on an epoll instance "epfd". Returns the number of
## triggered events returned in "events" buffer. Or -1 in case of ## triggered events returned in "events" buffer. Or -1 in case of
@ -82,11 +82,11 @@ proc epoll_wait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
## __THROW. ## __THROW.
#proc epoll_pwait*(epfd: cint; events: ptr epoll_event; maxevents: cint; #proc epoll_pwait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
# timeout: cint; ss: ptr sigset_t): cint {. # timeout: cint; ss: ptr sigset_t): cint {.
# importc: "epoll_pwait", header: "<sys/epoll.h>".} # importc: "epoll_pwait", header: "<sys/epoll.h>".}
# Same as epoll_wait, but the thread's signal mask is temporarily # Same as epoll_wait, but the thread's signal mask is temporarily
# and atomically replaced with the one provided as parameter. # and atomically replaced with the one provided as parameter.
# #
# This function is a cancellation point and therefore not marked with # This function is a cancellation point and therefore not marked with
# __THROW. # __THROW.

View file

@ -9,65 +9,65 @@
{.deadCodeElim:on.} {.deadCodeElim:on.}
# Get the platform-dependent flags. # Get the platform-dependent flags.
# Structure describing an inotify event. # Structure describing an inotify event.
type type
InotifyEvent*{.pure, final, importc: "struct inotify_event", InotifyEvent*{.pure, final, importc: "struct inotify_event",
header: "<sys/inotify.h>".} = object header: "<sys/inotify.h>".} = object
wd*{.importc: "wd".}: cint # Watch descriptor. wd*{.importc: "wd".}: cint # Watch descriptor.
mask*{.importc: "mask".}: uint32 # Watch mask. mask*{.importc: "mask".}: uint32 # Watch mask.
cookie*{.importc: "cookie".}: uint32 # Cookie to synchronize two events. cookie*{.importc: "cookie".}: uint32 # Cookie to synchronize two events.
len*{.importc: "len".}: uint32 # Length (including NULs) of name. len*{.importc: "len".}: uint32 # Length (including NULs) of name.
name*{.importc: "name".}: char # Name. name*{.importc: "name".}: char # Name.
{.deprecated: [Tinotify_event: InotifyEvent].} {.deprecated: [Tinotify_event: InotifyEvent].}
# Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH. # Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH.
const const
IN_ACCESS* = 0x00000001 # File was accessed. IN_ACCESS* = 0x00000001 # File was accessed.
IN_MODIFY* = 0x00000002 # File was modified. IN_MODIFY* = 0x00000002 # File was modified.
IN_ATTRIB* = 0x00000004 # Metadata changed. IN_ATTRIB* = 0x00000004 # Metadata changed.
IN_CLOSE_WRITE* = 0x00000008 # Writtable file was closed. IN_CLOSE_WRITE* = 0x00000008 # Writtable file was closed.
IN_CLOSE_NOWRITE* = 0x00000010 # Unwrittable file closed. IN_CLOSE_NOWRITE* = 0x00000010 # Unwrittable file closed.
IN_CLOSE* = (IN_CLOSE_WRITE or IN_CLOSE_NOWRITE) # Close. IN_CLOSE* = (IN_CLOSE_WRITE or IN_CLOSE_NOWRITE) # Close.
IN_OPEN* = 0x00000020 # File was opened. IN_OPEN* = 0x00000020 # File was opened.
IN_MOVED_FROM* = 0x00000040 # File was moved from X. IN_MOVED_FROM* = 0x00000040 # File was moved from X.
IN_MOVED_TO* = 0x00000080 # File was moved to Y. IN_MOVED_TO* = 0x00000080 # File was moved to Y.
IN_MOVE* = (IN_MOVED_FROM or IN_MOVED_TO) # Moves. IN_MOVE* = (IN_MOVED_FROM or IN_MOVED_TO) # Moves.
IN_CREATE* = 0x00000100 # Subfile was created. IN_CREATE* = 0x00000100 # Subfile was created.
IN_DELETE* = 0x00000200 # Subfile was deleted. IN_DELETE* = 0x00000200 # Subfile was deleted.
IN_DELETE_SELF* = 0x00000400 # Self was deleted. IN_DELETE_SELF* = 0x00000400 # Self was deleted.
IN_MOVE_SELF* = 0x00000800 # Self was moved. IN_MOVE_SELF* = 0x00000800 # Self was moved.
# Events sent by the kernel. # Events sent by the kernel.
const const
IN_UNMOUNT* = 0x00002000 # Backing fs was unmounted. IN_UNMOUNT* = 0x00002000 # Backing fs was unmounted.
IN_Q_OVERFLOW* = 0x00004000 # Event queued overflowed. IN_Q_OVERFLOW* = 0x00004000 # Event queued overflowed.
IN_IGNORED* = 0x00008000 # File was ignored. IN_IGNORED* = 0x00008000 # File was ignored.
# Special flags. # Special flags.
const const
IN_ONLYDIR* = 0x01000000 # Only watch the path if it is a IN_ONLYDIR* = 0x01000000 # Only watch the path if it is a
# directory. # directory.
IN_DONT_FOLLOW* = 0x02000000 # Do not follow a sym link. IN_DONT_FOLLOW* = 0x02000000 # Do not follow a sym link.
IN_EXCL_UNLINK* = 0x04000000 # Exclude events on unlinked IN_EXCL_UNLINK* = 0x04000000 # Exclude events on unlinked
# objects. # objects.
IN_MASK_ADD* = 0x20000000 # Add to the mask of an already IN_MASK_ADD* = 0x20000000 # Add to the mask of an already
# existing watch. # existing watch.
IN_ISDIR* = 0x40000000 # Event occurred against dir. IN_ISDIR* = 0x40000000 # Event occurred against dir.
IN_ONESHOT* = 0x80000000 # Only send event once. IN_ONESHOT* = 0x80000000 # Only send event once.
# All events which a program can wait on. # All events which a program can wait on.
const const
IN_ALL_EVENTS* = (IN_ACCESS or IN_MODIFY or IN_ATTRIB or IN_CLOSE_WRITE or IN_ALL_EVENTS* = (IN_ACCESS or IN_MODIFY or IN_ATTRIB or IN_CLOSE_WRITE or
IN_CLOSE_NOWRITE or IN_OPEN or IN_MOVED_FROM or IN_MOVED_TO or IN_CLOSE_NOWRITE or IN_OPEN or IN_MOVED_FROM or IN_MOVED_TO or
IN_CREATE or IN_DELETE or IN_DELETE_SELF or IN_MOVE_SELF) IN_CREATE or IN_DELETE or IN_DELETE_SELF or IN_MOVE_SELF)
# Create and initialize inotify instance. # Create and initialize inotify instance.
proc inotify_init*(): cint{.cdecl, importc: "inotify_init", proc inotify_init*(): cint{.cdecl, importc: "inotify_init",
header: "<sys/inotify.h>".} header: "<sys/inotify.h>".}
# Create and initialize inotify instance. # Create and initialize inotify instance.
proc inotify_init1*(flags: cint): cint{.cdecl, importc: "inotify_init1", proc inotify_init1*(flags: cint): cint{.cdecl, importc: "inotify_init1",
header: "<sys/inotify.h>".} header: "<sys/inotify.h>".}
# Add watch of object NAME to inotify instance FD. Notify about # Add watch of object NAME to inotify instance FD. Notify about
# events specified by MASK. # events specified by MASK.
proc inotify_add_watch*(fd: cint; name: cstring; mask: uint32): cint{. proc inotify_add_watch*(fd: cint; name: cstring; mask: uint32): cint{.
cdecl, importc: "inotify_add_watch", header: "<sys/inotify.h>".} cdecl, importc: "inotify_add_watch", header: "<sys/inotify.h>".}
# Remove the watch specified by WD from the inotify instance FD. # Remove the watch specified by WD from the inotify instance FD.
proc inotify_rm_watch*(fd: cint; wd: cint): cint{.cdecl, proc inotify_rm_watch*(fd: cint; wd: cint): cint{.cdecl,
importc: "inotify_rm_watch", header: "<sys/inotify.h>".} importc: "inotify_rm_watch", header: "<sys/inotify.h>".}

View file

@ -24,5 +24,5 @@ const
# fn should be of type proc (a2: pointer): void {.cdecl.} # fn should be of type proc (a2: pointer): void {.cdecl.}
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>".}

View file

@ -41,7 +41,7 @@ type
Actor[In, Out] = object{.pure, final.} Actor[In, Out] = object{.pure, final.}
i: Channel[Task[In, Out]] i: Channel[Task[In, Out]]
t: TThread[ptr Actor[In, Out]] t: TThread[ptr Actor[In, Out]]
PActor*[In, Out] = ptr Actor[In, Out] ## an actor PActor*[In, Out] = ptr Actor[In, Out] ## an actor
{.deprecated: [TTask: Task, TActor: Actor].} {.deprecated: [TTask: Task, TActor: Actor].}
@ -83,7 +83,7 @@ proc send*[In, Out, X, Y](receiver: PActor[In, Out], msg: In,
shallowCopy(t.data, msg) shallowCopy(t.data, msg)
send(receiver.i, t) send(receiver.i, t)
proc send*[In, Out](receiver: PActor[In, Out], msg: In, proc send*[In, Out](receiver: PActor[In, Out], msg: In,
sender: ptr Channel[Out] = nil) = sender: ptr Channel[Out] = nil) =
## sends a message to `receiver`'s inbox. ## sends a message to `receiver`'s inbox.
var t: Task[In, Out] var t: Task[In, Out]
@ -138,7 +138,7 @@ proc createActorPool*[In, Out](a: var ActorPool[In, Out], poolSize = 4) =
proc sync*[In, Out](a: var ActorPool[In, Out], polling=50) = proc sync*[In, Out](a: var ActorPool[In, Out], polling=50) =
## waits for every actor of `a` to finish with its work. Currently this is ## waits for every actor of `a` to finish with its work. Currently this is
## implemented as polling every `polling` ms and has a slight chance ## implemented as polling every `polling` ms and has a slight chance
## of failing since we check for every actor to be in `ready` state and not ## of failing since we check for every actor to be in `ready` state and not
## for messages still in ether. This will change in a later ## for messages still in ether. This will change in a later
## version, however. ## version, however.
@ -146,7 +146,7 @@ proc sync*[In, Out](a: var ActorPool[In, Out], polling=50) =
while true: while true:
var wait = false var wait = false
for i in 0..high(a.actors): for i in 0..high(a.actors):
if not a.actors[i].i.ready: if not a.actors[i].i.ready:
wait = true wait = true
allReadyCount = 0 allReadyCount = 0
break break
@ -222,7 +222,7 @@ proc spawn*[In](p: var ActorPool[In, void], input: In,
var t: Task[In, void] var t: Task[In, void]
setupTask() setupTask()
schedule() schedule()
when not defined(testing) and isMainModule: when not defined(testing) and isMainModule:
var var
a: ActorPool[int, void] a: ActorPool[int, void]

View file

@ -195,10 +195,10 @@ proc read*(f: AsyncFile, size: int): Future[string] =
readBuffer.setLen(res) readBuffer.setLen(res)
f.offset.inc(res) f.offset.inc(res)
retFuture.complete(readBuffer) retFuture.complete(readBuffer)
if not cb(f.fd): if not cb(f.fd):
addRead(f.fd, cb) addRead(f.fd, cb)
return retFuture return retFuture
proc readLine*(f: AsyncFile): Future[string] {.async.} = proc readLine*(f: AsyncFile): Future[string] {.async.} =
@ -222,7 +222,7 @@ proc getFilePos*(f: AsyncFile): int64 =
proc setFilePos*(f: AsyncFile, pos: int64) = proc setFilePos*(f: AsyncFile, pos: int64) =
## Sets the position of the file pointer that is used for read/write ## Sets the position of the file pointer that is used for read/write
## operations. The file's first byte has the index zero. ## operations. The file's first byte has the index zero.
f.offset = pos f.offset = pos
when not defined(windows) and not defined(nimdoc): when not defined(windows) and not defined(nimdoc):
let ret = lseek(f.fd.cint, pos, SEEK_SET) let ret = lseek(f.fd.cint, pos, SEEK_SET)
@ -291,7 +291,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
retFuture.complete() retFuture.complete()
else: else:
var written = 0 var written = 0
proc cb(fd: AsyncFD): bool = proc cb(fd: AsyncFD): bool =
result = true result = true
let remainderSize = data.len-written let remainderSize = data.len-written
@ -309,7 +309,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
result = false # We still have data to write. result = false # We still have data to write.
else: else:
retFuture.complete() retFuture.complete()
if not cb(f.fd): if not cb(f.fd):
addWrite(f.fd, cb) addWrite(f.fd, cb)
return retFuture return retFuture

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