mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-09 05:14:20 +00:00
Merge pull request #3289 from nanoant/patch/fix-whitespace
Patch/fix whitespace
This commit is contained in:
@@ -1 +1 @@
|
||||
This file keeps several tools from deleting this subdirectory.
|
||||
This file keeps several tools from deleting this subdirectory.
|
||||
|
||||
@@ -1 +1 @@
|
||||
This file keeps several tools from deleting this subdirectory.
|
||||
This file keeps several tools from deleting this subdirectory.
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import
|
||||
ast, astalgo, types, trees, intsets, msgs
|
||||
|
||||
|
||||
type
|
||||
TAnalysisResult* = enum
|
||||
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 =
|
||||
result = arNo
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
of nkRecList:
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
result = isPartOfAux(n.sons[i], b, marker)
|
||||
if result == arYes: return
|
||||
of nkRecCase:
|
||||
assert(n.sons[0].kind == nkSym)
|
||||
result = isPartOfAux(n.sons[0], b, marker)
|
||||
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
|
||||
of nkOfBranch, nkElse:
|
||||
of nkOfBranch, nkElse:
|
||||
result = isPartOfAux(lastSon(n.sons[i]), b, marker)
|
||||
if result == arYes: return
|
||||
else: internalError("isPartOfAux(record case branch)")
|
||||
of nkSym:
|
||||
result = isPartOfAux(n.sym.typ, b, marker)
|
||||
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
|
||||
if a == nil or b == nil: return
|
||||
if containsOrIncl(marker, a.id): return
|
||||
if a == nil or b == nil: return
|
||||
if containsOrIncl(marker, a.id): return
|
||||
if compareTypes(a, b, dcEqIgnoreDistinct): return arYes
|
||||
case a.kind
|
||||
of tyObject:
|
||||
of tyObject:
|
||||
result = isPartOfAux(a.sons[0], b, marker)
|
||||
if result == arNo: result = isPartOfAux(a.n, b, marker)
|
||||
of tyGenericInst, tyDistinct:
|
||||
result = isPartOfAux(lastSon(a), b, marker)
|
||||
of tyArray, tyArrayConstr, tySet, tyTuple:
|
||||
for i in countup(0, sonsLen(a) - 1):
|
||||
of tyArray, tyArrayConstr, tySet, tyTuple:
|
||||
for i in countup(0, sonsLen(a) - 1):
|
||||
result = isPartOfAux(a.sons[i], b, marker)
|
||||
if result == arYes: return
|
||||
if result == arYes: return
|
||||
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!
|
||||
var marker = initIntSet()
|
||||
# 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
|
||||
## if necessary.
|
||||
##
|
||||
## cases:
|
||||
## cases:
|
||||
##
|
||||
## YES-cases:
|
||||
## x <| x # for general trees
|
||||
## x[] <| x
|
||||
## x[i] <| x
|
||||
## x.f <| x
|
||||
##
|
||||
##
|
||||
## NO-cases:
|
||||
## x !<| y # depending on type and symbol kind
|
||||
## x[constA] !<| x[constB]
|
||||
@@ -88,16 +88,16 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||
##
|
||||
## x[] ?<| y[] iff compatible type
|
||||
##
|
||||
##
|
||||
##
|
||||
## x[] ?<| y depending on type
|
||||
##
|
||||
##
|
||||
if a.kind == b.kind:
|
||||
case a.kind
|
||||
of nkSym:
|
||||
const varKinds = {skVar, skTemp, skProc}
|
||||
# same symbol: aliasing:
|
||||
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
|
||||
# here. XXX make this more generic
|
||||
result = arNo
|
||||
@@ -110,11 +110,11 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||
if len(a) >= 2 and len(b) >= 2:
|
||||
# array accesses:
|
||||
if result == arYes and isDeepConstExpr(a[1]) and isDeepConstExpr(b[1]):
|
||||
# we know it's the same array and we have 2 constant indexes;
|
||||
# if they are
|
||||
# we know it's the same array and we have 2 constant indexes;
|
||||
# if they are
|
||||
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]
|
||||
|
||||
|
||||
if sameValue(x, y): result = arYes
|
||||
else: result = arNo
|
||||
# else: maybe and no are accurate
|
||||
@@ -122,7 +122,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||
# pointer derefs:
|
||||
if result != arYes:
|
||||
if isPartOf(a.typ, b.typ) != arNo: result = arMaybe
|
||||
|
||||
|
||||
of nkDotExpr:
|
||||
result = isPartOf(a[0], b[0])
|
||||
if result != arNo:
|
||||
@@ -135,7 +135,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||
# weaken because of indirection:
|
||||
if result != arYes:
|
||||
if isPartOf(a.typ, b.typ) != arNo: result = arMaybe
|
||||
|
||||
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
result = isPartOf(a[1], b[1])
|
||||
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.
|
||||
else:
|
||||
# go down recursively; this is quite demanding:
|
||||
const
|
||||
const
|
||||
Ix0Kinds = {nkDotExpr, nkBracketExpr, nkObjUpConv, nkObjDownConv,
|
||||
nkCheckedFieldExpr}
|
||||
Ix1Kinds = {nkHiddenStdConv, nkHiddenSubConv, nkConv}
|
||||
@@ -153,17 +153,17 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||
of Ix0Kinds:
|
||||
# a* !<| b.f iff a* !<| b
|
||||
result = isPartOf(a, b[0])
|
||||
|
||||
|
||||
of DerefKinds:
|
||||
# a* !<| b[] iff
|
||||
# a* !<| b[] iff
|
||||
if isPartOf(a.typ, b.typ) != arNo:
|
||||
result = isPartOf(a, b[0])
|
||||
if result == arNo: result = arMaybe
|
||||
|
||||
|
||||
of Ix1Kinds:
|
||||
# a* !<| T(b) iff a* !<| b
|
||||
result = isPartOf(a, b[1])
|
||||
|
||||
|
||||
of nkSym:
|
||||
# b is an atom, so we have to check a:
|
||||
case a.kind
|
||||
@@ -172,7 +172,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||
result = isPartOf(a[0], b)
|
||||
of Ix1Kinds:
|
||||
result = isPartOf(a[1], b)
|
||||
|
||||
|
||||
of DerefKinds:
|
||||
if isPartOf(a.typ, b.typ) != arNo:
|
||||
result = isPartOf(a[0], b)
|
||||
|
||||
@@ -10,12 +10,12 @@
|
||||
# this unit handles Nim sets; it implements bit sets
|
||||
# the code here should be reused in the Nim standard library
|
||||
|
||||
type
|
||||
type
|
||||
TBitSet* = seq[int8] # we use byte here to avoid issues with
|
||||
# cross-compiling; uint would be more efficient
|
||||
# however
|
||||
|
||||
const
|
||||
const
|
||||
ElemSize* = sizeof(int8) * 8
|
||||
|
||||
proc bitSetInit*(b: var TBitSet, length: int)
|
||||
@@ -30,42 +30,42 @@ proc bitSetEquals*(x, y: TBitSet): bool
|
||||
proc bitSetContains*(x, y: TBitSet): bool
|
||||
# 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)))) !=
|
||||
toU8(0)
|
||||
|
||||
proc bitSetIncl(x: var TBitSet, elem: BiggestInt) =
|
||||
proc bitSetIncl(x: var TBitSet, elem: BiggestInt) =
|
||||
assert(elem >= 0)
|
||||
x[int(elem div ElemSize)] = x[int(elem div ElemSize)] or
|
||||
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
|
||||
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)
|
||||
|
||||
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]
|
||||
|
||||
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]
|
||||
|
||||
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]
|
||||
|
||||
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]
|
||||
|
||||
proc bitSetEquals(x, y: TBitSet): bool =
|
||||
for i in countup(0, high(x)):
|
||||
if x[i] != y[i]:
|
||||
|
||||
proc bitSetEquals(x, y: TBitSet): bool =
|
||||
for i in countup(0, high(x)):
|
||||
if x[i] != y[i]:
|
||||
return false
|
||||
result = true
|
||||
|
||||
proc bitSetContains(x, y: TBitSet): bool =
|
||||
for i in countup(0, high(x)):
|
||||
if (x[i] and not y[i]) != int8(0):
|
||||
proc bitSetContains(x, y: TBitSet): bool =
|
||||
for i in countup(0, high(x)):
|
||||
if (x[i] and not y[i]) != int8(0):
|
||||
return false
|
||||
result = true
|
||||
|
||||
@@ -30,39 +30,39 @@ type
|
||||
#
|
||||
# This is a good compromise between correctness and brevity. ;-)
|
||||
|
||||
const
|
||||
const
|
||||
cb64 = [
|
||||
"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",
|
||||
"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",
|
||||
"o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
|
||||
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||
"_A", "_B"]
|
||||
|
||||
proc toBase64a(s: cstring, len: int): string =
|
||||
## encodes `s` into base64 representation. After `lineLen` characters, a
|
||||
## `newline` is added.
|
||||
result = newStringOfCap(((len + 2) div 3) * 4)
|
||||
var i = 0
|
||||
while i < s.len - 2:
|
||||
let a = ord(s[i])
|
||||
let b = ord(s[i+1])
|
||||
let c = ord(s[i+2])
|
||||
result.add cb64[a shr 2]
|
||||
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[c and 0x3F]
|
||||
inc(i, 3)
|
||||
if i < s.len-1:
|
||||
let a = ord(s[i])
|
||||
let b = ord(s[i+1])
|
||||
result.add cb64[a shr 2]
|
||||
result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||
result.add cb64[((b and 0x0F) shl 2)]
|
||||
elif i < s.len:
|
||||
let a = ord(s[i])
|
||||
result.add cb64[a shr 2]
|
||||
result.add cb64[(a and 3) shl 4]
|
||||
"_A", "_B"]
|
||||
|
||||
proc toBase64a(s: cstring, len: int): string =
|
||||
## encodes `s` into base64 representation. After `lineLen` characters, a
|
||||
## `newline` is added.
|
||||
result = newStringOfCap(((len + 2) div 3) * 4)
|
||||
var i = 0
|
||||
while i < s.len - 2:
|
||||
let a = ord(s[i])
|
||||
let b = ord(s[i+1])
|
||||
let c = ord(s[i+2])
|
||||
result.add cb64[a shr 2]
|
||||
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[c and 0x3F]
|
||||
inc(i, 3)
|
||||
if i < s.len-1:
|
||||
let a = ord(s[i])
|
||||
let b = ord(s[i+1])
|
||||
result.add cb64[a shr 2]
|
||||
result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||
result.add cb64[((b and 0x0F) shl 2)]
|
||||
elif i < s.len:
|
||||
let a = ord(s[i])
|
||||
result.add cb64[a shr 2]
|
||||
result.add cb64[(a and 3) shl 4]
|
||||
|
||||
proc toBase64a(u: TUid): string = toBase64a(cast[cstring](u), sizeof(u))
|
||||
|
||||
@@ -73,7 +73,7 @@ proc hashSym(c: var MD5Context, s: PSym) =
|
||||
c &= ":anon"
|
||||
else:
|
||||
var it = s.owner
|
||||
while it != nil:
|
||||
while it != nil:
|
||||
hashSym(c, it)
|
||||
c &= "."
|
||||
it = s.owner
|
||||
@@ -106,18 +106,18 @@ proc hashTree(c: var MD5Context, n: PNode) =
|
||||
|
||||
proc hashType(c: var MD5Context, t: PType) =
|
||||
# modelled after 'typeToString'
|
||||
if t == nil:
|
||||
if t == nil:
|
||||
c &= "\254"
|
||||
return
|
||||
|
||||
var k = t.kind
|
||||
md5Update(c, cast[cstring](addr(k)), 1)
|
||||
|
||||
|
||||
if t.sym != nil and sfAnon notin t.sym.flags:
|
||||
# t.n for literals, but not for e.g. objects!
|
||||
if t.kind in {tyFloat, tyInt}: c.hashNode(t.n)
|
||||
c.hashSym(t.sym)
|
||||
|
||||
|
||||
case t.kind
|
||||
of tyGenericBody, tyGenericInst, 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:
|
||||
c.hashTree(t.sons[0].n)
|
||||
c.hashType(t.sons[1])
|
||||
of tyTuple:
|
||||
of tyTuple:
|
||||
if t.n != nil:
|
||||
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)
|
||||
c &= t.n.sons[i].sym.name.s
|
||||
c &= ":"
|
||||
@@ -184,18 +184,18 @@ proc pushSym(w: PRodWriter, s: PSym) =
|
||||
if iiTableGet(w.index.tab, s.id) == InvalidKey:
|
||||
w.sstack.add(s)
|
||||
|
||||
proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
|
||||
result: var string) =
|
||||
if n == nil:
|
||||
proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
|
||||
result: var string) =
|
||||
if n == nil:
|
||||
# nil nodes have to be stored too:
|
||||
result.add("()")
|
||||
return
|
||||
result.add('(')
|
||||
encodeVInt(ord(n.kind), result)
|
||||
encodeVInt(ord(n.kind), result)
|
||||
# we do not write comments for now
|
||||
# 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:
|
||||
if fInfo.fileIndex != n.info.fileIndex:
|
||||
if fInfo.fileIndex != n.info.fileIndex:
|
||||
result.add('?')
|
||||
encodeVInt(n.info.col, result)
|
||||
result.add(',')
|
||||
@@ -211,7 +211,7 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
|
||||
result.add('?')
|
||||
encodeVInt(n.info.col, result)
|
||||
var f = n.flags * PersistentNodeFlags
|
||||
if f != {}:
|
||||
if f != {}:
|
||||
result.add('$')
|
||||
encodeVInt(cast[int32](f), result)
|
||||
if n.typ != nil:
|
||||
@@ -219,16 +219,16 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
|
||||
encodeVInt(n.typ.id, result)
|
||||
pushType(w, n.typ)
|
||||
case n.kind
|
||||
of nkCharLit..nkInt64Lit:
|
||||
of nkCharLit..nkInt64Lit:
|
||||
if n.intVal != 0:
|
||||
result.add('!')
|
||||
encodeVBiggestInt(n.intVal, result)
|
||||
of nkFloatLit..nkFloat64Lit:
|
||||
if n.floatVal != 0.0:
|
||||
of nkFloatLit..nkFloat64Lit:
|
||||
if n.floatVal != 0.0:
|
||||
result.add('!')
|
||||
encodeStr($n.floatVal, result)
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
if n.strVal != "":
|
||||
if n.strVal != "":
|
||||
result.add('!')
|
||||
encodeStr(n.strVal, result)
|
||||
of nkIdent:
|
||||
@@ -239,7 +239,7 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
|
||||
encodeVInt(n.sym.id, result)
|
||||
pushSym(w, n.sym)
|
||||
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)
|
||||
add(result, ')')
|
||||
|
||||
@@ -268,9 +268,9 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
|
||||
setLen(result, oldLen)
|
||||
else:
|
||||
add(result, '>')
|
||||
|
||||
proc encodeType(w: PRodWriter, t: PType, result: var string) =
|
||||
if t == nil:
|
||||
|
||||
proc encodeType(w: PRodWriter, t: PType, result: var string) =
|
||||
if t == nil:
|
||||
# nil nodes have to be stored too:
|
||||
result.add("[]")
|
||||
return
|
||||
@@ -282,38 +282,38 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
|
||||
encodeVInt(ord(t.kind), result)
|
||||
add(result, '+')
|
||||
encodeVInt(t.id, result)
|
||||
if t.n != nil:
|
||||
if t.n != nil:
|
||||
encodeNode(w, unknownLineInfo(), t.n, result)
|
||||
if t.flags != {}:
|
||||
if t.flags != {}:
|
||||
add(result, '$')
|
||||
encodeVInt(cast[int32](t.flags), result)
|
||||
if t.callConv != low(t.callConv):
|
||||
if t.callConv != low(t.callConv):
|
||||
add(result, '?')
|
||||
encodeVInt(ord(t.callConv), result)
|
||||
if t.owner != nil:
|
||||
if t.owner != nil:
|
||||
add(result, '*')
|
||||
encodeVInt(t.owner.id, result)
|
||||
pushSym(w, t.owner)
|
||||
if t.sym != nil:
|
||||
if t.sym != nil:
|
||||
add(result, '&')
|
||||
encodeVInt(t.sym.id, result)
|
||||
pushSym(w, t.sym)
|
||||
if t.size != - 1:
|
||||
if t.size != - 1:
|
||||
add(result, '/')
|
||||
encodeVBiggestInt(t.size, result)
|
||||
if t.align != 2:
|
||||
if t.align != 2:
|
||||
add(result, '=')
|
||||
encodeVInt(t.align, result)
|
||||
encodeLoc(w, t.loc, result)
|
||||
for i in countup(0, sonsLen(t) - 1):
|
||||
if t.sons[i] == nil:
|
||||
for i in countup(0, sonsLen(t) - 1):
|
||||
if t.sons[i] == nil:
|
||||
add(result, "^()")
|
||||
else:
|
||||
add(result, '^')
|
||||
else:
|
||||
add(result, '^')
|
||||
encodeVInt(t.sons[i].id, result)
|
||||
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, '|')
|
||||
encodeVInt(ord(lib.kind), result)
|
||||
add(result, '|')
|
||||
@@ -352,10 +352,10 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
|
||||
if s.magic != mNone:
|
||||
result.add('@')
|
||||
encodeVInt(ord(s.magic), result)
|
||||
if s.options != w.options:
|
||||
if s.options != w.options:
|
||||
result.add('!')
|
||||
encodeVInt(cast[int32](s.options), result)
|
||||
if s.position != 0:
|
||||
if s.position != 0:
|
||||
result.add('%')
|
||||
encodeVInt(s.position, result)
|
||||
if s.offset != - 1:
|
||||
@@ -383,7 +383,7 @@ proc createDb() =
|
||||
fullpath varchar(256) not null,
|
||||
interfHash varchar(256) not null,
|
||||
fullHash varchar(256) not null,
|
||||
|
||||
|
||||
created timestamp not null default (DATETIME('now')),
|
||||
);""")
|
||||
|
||||
@@ -397,7 +397,7 @@ proc createDb() =
|
||||
|
||||
foreign key (module) references module(id)
|
||||
);""")
|
||||
|
||||
|
||||
db.exec(sql"""
|
||||
create table if not exists Type(
|
||||
id integer primary key,
|
||||
|
||||
@@ -9,33 +9,33 @@
|
||||
|
||||
## This module implements code generation for multi methods.
|
||||
|
||||
import
|
||||
import
|
||||
intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys,
|
||||
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 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)
|
||||
if diff == high(int): internalError(n.info, "cgmeth.genConv")
|
||||
if diff < 0:
|
||||
if diff < 0:
|
||||
result = newNodeIT(nkObjUpConv, n.info, d)
|
||||
addSon(result, n)
|
||||
if downcast: internalError(n.info, "cgmeth.genConv: no upcast allowed")
|
||||
elif diff > 0:
|
||||
elif diff > 0:
|
||||
result = newNodeIT(nkObjDownConv, n.info, d)
|
||||
addSon(result, n)
|
||||
if not downcast:
|
||||
if not downcast:
|
||||
internalError(n.info, "cgmeth.genConv: no downcast allowed")
|
||||
else:
|
||||
else:
|
||||
result = n
|
||||
else:
|
||||
else:
|
||||
result = n
|
||||
|
||||
proc methodCall*(n: PNode): PNode =
|
||||
|
||||
proc methodCall*(n: PNode): PNode =
|
||||
result = n
|
||||
# replace ordinary method by dispatcher method:
|
||||
# replace ordinary method by dispatcher method:
|
||||
var disp = lastSon(result.sons[0].sym.ast).sym
|
||||
assert sfDispatcher in disp.flags
|
||||
result.sons[0].sym = disp
|
||||
@@ -47,23 +47,23 @@ proc methodCall*(n: PNode): PNode =
|
||||
var
|
||||
gMethods: seq[tuple[methods: TSymSeq, dispatcher: PSym]] = @[]
|
||||
|
||||
proc sameMethodBucket(a, b: PSym): bool =
|
||||
proc sameMethodBucket(a, b: PSym): bool =
|
||||
result = false
|
||||
if a.name.id != b.name.id: return
|
||||
if sonsLen(a.typ) != sonsLen(b.typ):
|
||||
if a.name.id != b.name.id: return
|
||||
if sonsLen(a.typ) != sonsLen(b.typ):
|
||||
return # check for return type:
|
||||
if not sameTypeOrNil(a.typ.sons[0], b.typ.sons[0]): return
|
||||
for i in countup(1, sonsLen(a.typ) - 1):
|
||||
if not sameTypeOrNil(a.typ.sons[0], b.typ.sons[0]): return
|
||||
for i in countup(1, sonsLen(a.typ) - 1):
|
||||
var aa = a.typ.sons[i]
|
||||
var bb = b.typ.sons[i]
|
||||
while true:
|
||||
while true:
|
||||
aa = skipTypes(aa, {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
|
||||
bb = bb.lastSon
|
||||
else:
|
||||
break
|
||||
else:
|
||||
break
|
||||
if sameType(aa, bb) or
|
||||
(aa.kind == tyObject) and (bb.kind == tyObject) and
|
||||
(inheritanceDiff(bb, aa) < 0):
|
||||
@@ -140,7 +140,7 @@ proc methodDef*(s: PSym, fromCache: bool) =
|
||||
attachDispatcher(s, lastSon(disp.ast))
|
||||
fixupDispatcher(s, disp)
|
||||
when useEffectSystem: checkMethodEffects(disp, s)
|
||||
return
|
||||
return
|
||||
# create a new dispatcher:
|
||||
add(gMethods, (methods: @[s], dispatcher: createDispatcher(s)))
|
||||
if fromCache:
|
||||
@@ -154,35 +154,35 @@ proc relevantCol(methods: TSymSeq, col: int): bool =
|
||||
let t2 = skipTypes(methods[i].typ.sons[col], skipPtrs)
|
||||
if not sameType(t2, t):
|
||||
return true
|
||||
|
||||
proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int =
|
||||
for col in countup(1, sonsLen(a.typ) - 1):
|
||||
if contains(relevantCols, col):
|
||||
|
||||
proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int =
|
||||
for col in countup(1, sonsLen(a.typ) - 1):
|
||||
if contains(relevantCols, col):
|
||||
var aa = skipTypes(a.typ.sons[col], skipPtrs)
|
||||
var bb = skipTypes(b.typ.sons[col], skipPtrs)
|
||||
var d = inheritanceDiff(aa, bb)
|
||||
if (d != high(int)):
|
||||
if (d != high(int)):
|
||||
return d
|
||||
|
||||
proc sortBucket(a: var TSymSeq, relevantCols: IntSet) =
|
||||
|
||||
proc sortBucket(a: var TSymSeq, relevantCols: IntSet) =
|
||||
# we use shellsort here; fast and simple
|
||||
var n = len(a)
|
||||
var h = 1
|
||||
while true:
|
||||
while true:
|
||||
h = 3 * h + 1
|
||||
if h > n: break
|
||||
while true:
|
||||
if h > n: break
|
||||
while true:
|
||||
h = h div 3
|
||||
for i in countup(h, n - 1):
|
||||
for i in countup(h, n - 1):
|
||||
var v = a[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]
|
||||
j = j - h
|
||||
if j < h: break
|
||||
if j < h: break
|
||||
a[j] = v
|
||||
if h == 1: break
|
||||
|
||||
if h == 1: break
|
||||
|
||||
proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
|
||||
var base = lastSon(methods[0].ast).sym
|
||||
result = base
|
||||
@@ -199,7 +199,7 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
|
||||
addSon(isn, newSymNode(iss))
|
||||
addSon(isn, newSymNode(base.typ.n.sons[col].sym))
|
||||
addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col]))
|
||||
if cond != nil:
|
||||
if cond != nil:
|
||||
var a = newNodeIT(nkCall, base.info, getSysType(tyBool))
|
||||
addSon(a, newSymNode(ands))
|
||||
addSon(a, cond)
|
||||
@@ -209,8 +209,8 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
|
||||
cond = isn
|
||||
var call = newNodeI(nkCall, base.info)
|
||||
addSon(call, newSymNode(curr))
|
||||
for col in countup(1, paramLen - 1):
|
||||
addSon(call, genConv(newSymNode(base.typ.n.sons[col].sym),
|
||||
for col in countup(1, paramLen - 1):
|
||||
addSon(call, genConv(newSymNode(base.typ.n.sons[col].sym),
|
||||
curr.typ.sons[col], false))
|
||||
var ret: PNode
|
||||
if base.typ.sons[0] != nil:
|
||||
@@ -230,11 +230,11 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
|
||||
disp = ret
|
||||
result.ast.sons[bodyPos] = disp
|
||||
|
||||
proc generateMethodDispatchers*(): PNode =
|
||||
proc generateMethodDispatchers*(): PNode =
|
||||
result = newNode(nkStmtList)
|
||||
for bucket in countup(0, len(gMethods) - 1):
|
||||
for bucket in countup(0, len(gMethods) - 1):
|
||||
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)
|
||||
sortBucket(gMethods[bucket].methods, relevantCols)
|
||||
addSon(result,
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
# This module implements a new documentation generator that runs after
|
||||
# semantic checking.
|
||||
|
||||
import
|
||||
import
|
||||
os, options, ast, astalgo, msgs, ropes, idents, passes, docgen
|
||||
|
||||
type
|
||||
type
|
||||
TGen = object of TPassContext
|
||||
doc: PDoc
|
||||
module: PSym
|
||||
@@ -29,12 +29,12 @@ proc close(p: PPassContext, n: PNode): PNode =
|
||||
except IOError:
|
||||
discard
|
||||
|
||||
proc processNode(c: PPassContext, n: PNode): PNode =
|
||||
proc processNode(c: PPassContext, n: PNode): PNode =
|
||||
result = n
|
||||
var g = PGen(c)
|
||||
generateDoc(g.doc, n)
|
||||
|
||||
proc myOpen(module: PSym): PPassContext =
|
||||
proc myOpen(module: PSym): PPassContext =
|
||||
var g: PGen
|
||||
new(g)
|
||||
g.module = module
|
||||
@@ -45,5 +45,5 @@ proc myOpen(module: PSym): PPassContext =
|
||||
|
||||
const docgen2Pass* = makePass(open = myOpen, process = processNode, close = close)
|
||||
|
||||
proc finishDoc2Pass*(project: string) =
|
||||
proc finishDoc2Pass*(project: string) =
|
||||
discard
|
||||
|
||||
@@ -31,7 +31,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
||||
for y in items(x): result.add(y)
|
||||
else:
|
||||
result.add copyTree(x)
|
||||
|
||||
|
||||
case templ.kind
|
||||
of nkSym:
|
||||
var s = templ.sym
|
||||
@@ -81,7 +81,7 @@ proc evalTemplateArgs(n: PNode, s: PSym): PNode =
|
||||
|
||||
if totalParams > expectedRegularParams + genericParams:
|
||||
globalError(n.info, errWrongNumberOfArguments)
|
||||
|
||||
|
||||
result = newNodeI(nkArgList, n.info)
|
||||
for i in 1 .. givenRegularParams:
|
||||
result.addSon n.sons[i]
|
||||
@@ -96,7 +96,7 @@ proc evalTemplateArgs(n: PNode, s: PSym): PNode =
|
||||
addSon(result, ast.emptyNode)
|
||||
else:
|
||||
addSon(result, default.copyTree)
|
||||
|
||||
|
||||
# add any generic paramaters
|
||||
for i in 1 .. genericParams:
|
||||
result.addSon n.sons[givenRegularParams + i]
|
||||
|
||||
@@ -9,18 +9,18 @@
|
||||
|
||||
# This module implements Nim's standard template filter.
|
||||
|
||||
import
|
||||
llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
|
||||
import
|
||||
llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
|
||||
renderer, filters
|
||||
|
||||
proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode): PLLStream
|
||||
# #! template(subsChar='$', metaChar='#') | standard(version="0.7.2")
|
||||
# implementation
|
||||
|
||||
type
|
||||
TParseState = enum
|
||||
type
|
||||
TParseState = enum
|
||||
psDirective, psTempl
|
||||
TTmplParser{.final.} = object
|
||||
TTmplParser{.final.} = object
|
||||
inp: PLLStream
|
||||
state: TParseState
|
||||
info: TLineInfo
|
||||
@@ -33,18 +33,18 @@ type
|
||||
pendingExprLine: bool
|
||||
|
||||
|
||||
const
|
||||
const
|
||||
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))
|
||||
p.emitPar = 0
|
||||
if p.info.line > int16(1): llStreamWrite(p.outp, "\n")
|
||||
if p.pendingExprLine:
|
||||
llStreamWrite(p.outp, spaces(2))
|
||||
p.pendingExprLine = false
|
||||
|
||||
proc scanPar(p: var TTmplParser, d: int) =
|
||||
|
||||
proc scanPar(p: var TTmplParser, d: int) =
|
||||
var i = d
|
||||
while true:
|
||||
case p.x[i]
|
||||
@@ -58,44 +58,44 @@ proc scanPar(p: var TTmplParser, d: int) =
|
||||
else: discard
|
||||
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
|
||||
|
||||
proc parseLine(p: var TTmplParser) =
|
||||
var
|
||||
|
||||
proc parseLine(p: var TTmplParser) =
|
||||
var
|
||||
d, j, curly: int
|
||||
keyw: string
|
||||
j = 0
|
||||
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)
|
||||
elif (p.x[j] == p.nimDirective):
|
||||
elif (p.x[j] == p.nimDirective):
|
||||
newLine(p)
|
||||
inc(j)
|
||||
while p.x[j] == ' ': inc(j)
|
||||
d = j
|
||||
keyw = ""
|
||||
while p.x[j] in PatternChars:
|
||||
while p.x[j] in PatternChars:
|
||||
add(keyw, p.x[j])
|
||||
inc(j)
|
||||
|
||||
|
||||
scanPar(p, j)
|
||||
p.pendingExprLine = withInExpr(p) or llstream.endsWithOpr(p.x)
|
||||
case whichKeyword(keyw)
|
||||
of wEnd:
|
||||
if p.indent >= 2:
|
||||
of wEnd:
|
||||
if p.indent >= 2:
|
||||
dec(p.indent, 2)
|
||||
else:
|
||||
else:
|
||||
p.info.col = int16(j)
|
||||
localError(p.info, errXNotAllowedHere, "end")
|
||||
llStreamWrite(p.outp, spaces(p.indent))
|
||||
llStreamWrite(p.outp, "#end")
|
||||
of wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator,
|
||||
wConverter, wMacro, wTemplate, wMethod:
|
||||
of wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator,
|
||||
wConverter, wMacro, wTemplate, wMethod:
|
||||
llStreamWrite(p.outp, spaces(p.indent))
|
||||
llStreamWrite(p.outp, substr(p.x, d))
|
||||
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, substr(p.x, d))
|
||||
of wLet, wVar, wConst, wType:
|
||||
@@ -108,7 +108,7 @@ proc parseLine(p: var TTmplParser) =
|
||||
llStreamWrite(p.outp, spaces(p.indent))
|
||||
llStreamWrite(p.outp, substr(p.x, d))
|
||||
p.state = psDirective
|
||||
else:
|
||||
else:
|
||||
# data line
|
||||
# reset counters
|
||||
p.par = 0
|
||||
@@ -116,42 +116,42 @@ proc parseLine(p: var TTmplParser) =
|
||||
p.bracket = 0
|
||||
j = 0
|
||||
case p.state
|
||||
of psTempl:
|
||||
of psTempl:
|
||||
# next line of string literal:
|
||||
llStreamWrite(p.outp, p.conc)
|
||||
llStreamWrite(p.outp, "\n")
|
||||
llStreamWrite(p.outp, spaces(p.indent + 2))
|
||||
llStreamWrite(p.outp, "\"")
|
||||
of psDirective:
|
||||
of psDirective:
|
||||
newLine(p)
|
||||
llStreamWrite(p.outp, spaces(p.indent))
|
||||
llStreamWrite(p.outp, p.emit)
|
||||
llStreamWrite(p.outp, "(\"")
|
||||
inc(p.emitPar)
|
||||
p.state = psTempl
|
||||
while true:
|
||||
while true:
|
||||
case p.x[j]
|
||||
of '\0':
|
||||
break
|
||||
of '\x01'..'\x1F', '\x80'..'\xFF':
|
||||
of '\0':
|
||||
break
|
||||
of '\x01'..'\x1F', '\x80'..'\xFF':
|
||||
llStreamWrite(p.outp, "\\x")
|
||||
llStreamWrite(p.outp, toHex(ord(p.x[j]), 2))
|
||||
inc(j)
|
||||
of '\\':
|
||||
of '\\':
|
||||
llStreamWrite(p.outp, "\\\\")
|
||||
inc(j)
|
||||
of '\'':
|
||||
of '\'':
|
||||
llStreamWrite(p.outp, "\\\'")
|
||||
inc(j)
|
||||
of '\"':
|
||||
of '\"':
|
||||
llStreamWrite(p.outp, "\\\"")
|
||||
inc(j)
|
||||
else:
|
||||
if p.x[j] == p.subsChar:
|
||||
else:
|
||||
if p.x[j] == p.subsChar:
|
||||
# parse Nim expression:
|
||||
inc(j)
|
||||
case p.x[j]
|
||||
of '{':
|
||||
of '{':
|
||||
p.info.col = int16(j)
|
||||
llStreamWrite(p.outp, '\"')
|
||||
llStreamWrite(p.outp, p.conc)
|
||||
@@ -159,50 +159,50 @@ proc parseLine(p: var TTmplParser) =
|
||||
llStreamWrite(p.outp, '(')
|
||||
inc(j)
|
||||
curly = 0
|
||||
while true:
|
||||
while true:
|
||||
case p.x[j]
|
||||
of '\0':
|
||||
of '\0':
|
||||
localError(p.info, errXExpected, "}")
|
||||
break
|
||||
of '{':
|
||||
of '{':
|
||||
inc(j)
|
||||
inc(curly)
|
||||
llStreamWrite(p.outp, '{')
|
||||
of '}':
|
||||
of '}':
|
||||
inc(j)
|
||||
if curly == 0: break
|
||||
if curly == 0: break
|
||||
if curly > 0: dec(curly)
|
||||
llStreamWrite(p.outp, '}')
|
||||
else:
|
||||
else:
|
||||
llStreamWrite(p.outp, p.x[j])
|
||||
inc(j)
|
||||
llStreamWrite(p.outp, ')')
|
||||
llStreamWrite(p.outp, p.conc)
|
||||
llStreamWrite(p.outp, '\"')
|
||||
of 'a'..'z', 'A'..'Z', '\x80'..'\xFF':
|
||||
of 'a'..'z', 'A'..'Z', '\x80'..'\xFF':
|
||||
llStreamWrite(p.outp, '\"')
|
||||
llStreamWrite(p.outp, p.conc)
|
||||
llStreamWrite(p.outp, p.toStr)
|
||||
llStreamWrite(p.outp, '(')
|
||||
while p.x[j] in PatternChars:
|
||||
while p.x[j] in PatternChars:
|
||||
llStreamWrite(p.outp, p.x[j])
|
||||
inc(j)
|
||||
llStreamWrite(p.outp, ')')
|
||||
llStreamWrite(p.outp, p.conc)
|
||||
llStreamWrite(p.outp, '\"')
|
||||
else:
|
||||
if p.x[j] == p.subsChar:
|
||||
else:
|
||||
if p.x[j] == p.subsChar:
|
||||
llStreamWrite(p.outp, p.subsChar)
|
||||
inc(j)
|
||||
else:
|
||||
else:
|
||||
p.info.col = int16(j)
|
||||
localError(p.info, errInvalidExpression, "$")
|
||||
else:
|
||||
else:
|
||||
llStreamWrite(p.outp, p.x[j])
|
||||
inc(j)
|
||||
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
|
||||
p.info = newLineInfo(filename, 0, 0)
|
||||
p.outp = llStreamOpen("")
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
# This module implements Nim's simple filters and helpers for filters.
|
||||
|
||||
import
|
||||
llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
|
||||
llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
|
||||
renderer
|
||||
|
||||
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
|
||||
# implementation
|
||||
|
||||
proc invalidPragma(n: PNode) =
|
||||
proc invalidPragma(n: PNode) =
|
||||
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
|
||||
if n.kind in {nkEmpty..nkNilLit}: return
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
if n.sons[i].kind == nkExprEqExpr:
|
||||
if n.kind in {nkEmpty..nkNilLit}: return
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
if n.sons[i].kind == nkExprEqExpr:
|
||||
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]
|
||||
elif i == pos:
|
||||
elif i == pos:
|
||||
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)
|
||||
if x == nil: result = default
|
||||
elif x.kind == nkCharLit: result = chr(int(x.intVal))
|
||||
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)
|
||||
if x == nil: result = default
|
||||
elif x.kind in {nkStrLit..nkTripleStrLit}: result = x.strVal
|
||||
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)
|
||||
if x == nil: result = default
|
||||
elif (x.kind == nkIdent) and identEq(x.ident, "true"): result = true
|
||||
elif (x.kind == nkIdent) and identEq(x.ident, "false"): result = false
|
||||
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 leading = boolArg(call, "leading", 2, 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)
|
||||
while llStreamReadLine(stdin, line):
|
||||
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)
|
||||
else:
|
||||
else:
|
||||
llStreamWriteln(result, line)
|
||||
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, "")
|
||||
if len(sub) == 0: invalidPragma(call)
|
||||
var by = strArg(call, "by", 2, "")
|
||||
|
||||
@@ -14,11 +14,11 @@ import ast, astalgo
|
||||
const
|
||||
someCmp = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
|
||||
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}
|
||||
|
||||
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 == {}
|
||||
|
||||
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 =
|
||||
if lastStmt.isCall:
|
||||
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):
|
||||
if op[1].kind == nkSym and isCounter(op[1].sym):
|
||||
result = op[1].sym
|
||||
@@ -67,7 +67,7 @@ proc extractForLoop*(loop, fullTree: PNode): ForLoop =
|
||||
|
||||
if not cond.isCall: return
|
||||
if cond[0].sym.magic notin someCmp: return
|
||||
|
||||
|
||||
var lastStmt = loop[1]
|
||||
while lastStmt.kind in {nkStmtList, nkStmtListExpr}:
|
||||
lastStmt = lastStmt.lastSon
|
||||
@@ -76,7 +76,7 @@ proc extractForLoop*(loop, fullTree: PNode): ForLoop =
|
||||
if counter.isNil or counter.ast.isNil: return
|
||||
|
||||
template `=~`(a, b): expr = a.kind == nkSym and a.sym == b
|
||||
|
||||
|
||||
if cond[1] =~ counter or cond[2] =~ counter:
|
||||
# ok, now check 'counter' is not used *after* the loop
|
||||
if counterInTree(fullTree, loop, counter): return
|
||||
|
||||
@@ -28,7 +28,7 @@ proc evalPattern(c: PContext, n, orig: PNode): PNode =
|
||||
else:
|
||||
result = semDirectOp(c, n, {})
|
||||
if optHints in gOptions and hintPattern in gNotes:
|
||||
message(orig.info, hintPattern, rule & " --> '" &
|
||||
message(orig.info, hintPattern, rule & " --> '" &
|
||||
renderTree(result, {renderNoComments}) & "'")
|
||||
|
||||
proc applyPatterns(c: PContext, n: PNode): PNode =
|
||||
@@ -68,7 +68,7 @@ proc hlo(c: PContext, n: PNode): PNode =
|
||||
result = n
|
||||
else:
|
||||
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}:
|
||||
# do not optimize 'var g {.global} = re(...)' again!
|
||||
return n
|
||||
|
||||
@@ -11,13 +11,13 @@
|
||||
# An identifier is a shared immutable string that can be compared by its
|
||||
# id. This module is essential for the compiler's performance.
|
||||
|
||||
import
|
||||
import
|
||||
hashes, strutils, etcpriv
|
||||
|
||||
type
|
||||
type
|
||||
TIdObj* = object of RootObj
|
||||
id*: int # unique id; use this for comparisons and not the pointers
|
||||
|
||||
|
||||
PIdObj* = ref TIdObj
|
||||
PIdent* = ref TIdent
|
||||
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 bb >= 'A' and bb <= 'Z': bb = chr(ord(bb) + (ord('a') - ord('A')))
|
||||
result = ord(aa) - ord(bb)
|
||||
if (result != 0) or (aa == '\0'): break
|
||||
if (result != 0) or (aa == '\0'): break
|
||||
inc(i)
|
||||
inc(j)
|
||||
if result == 0:
|
||||
if a[i] != '\0': result = 1
|
||||
|
||||
|
||||
proc cmpExact(a, b: cstring, blen: int): int =
|
||||
var i = 0
|
||||
var j = 0
|
||||
@@ -59,10 +59,10 @@ proc cmpExact(a, b: cstring, blen: int): int =
|
||||
var aa = a[i]
|
||||
var bb = b[j]
|
||||
result = ord(aa) - ord(bb)
|
||||
if (result != 0) or (aa == '\0'): break
|
||||
if (result != 0) or (aa == '\0'): break
|
||||
inc(i)
|
||||
inc(j)
|
||||
if result == 0:
|
||||
if result == 0:
|
||||
if a[i] != '\0': result = 1
|
||||
|
||||
var wordCounter = 1
|
||||
@@ -72,14 +72,14 @@ proc getIdent*(identifier: cstring, length: int, h: Hash): PIdent =
|
||||
result = buckets[idx]
|
||||
var last: PIdent = nil
|
||||
var id = 0
|
||||
while result != nil:
|
||||
if cmpExact(cstring(result.s), identifier, length) == 0:
|
||||
if last != nil:
|
||||
while result != nil:
|
||||
if cmpExact(cstring(result.s), identifier, length) == 0:
|
||||
if last != nil:
|
||||
# make access to last looked up identifier faster:
|
||||
last.next = result.next
|
||||
result.next = buckets[idx]
|
||||
buckets[idx] = result
|
||||
return
|
||||
return
|
||||
elif cmpIgnoreStyle(cstring(result.s), identifier, length) == 0:
|
||||
assert((id == 0) or (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]
|
||||
result.next = buckets[idx]
|
||||
buckets[idx] = result
|
||||
if id == 0:
|
||||
if id == 0:
|
||||
inc(wordCounter)
|
||||
result.id = -wordCounter
|
||||
else:
|
||||
else:
|
||||
result.id = id
|
||||
|
||||
proc getIdent*(identifier: string): PIdent =
|
||||
result = getIdent(cstring(identifier), len(identifier),
|
||||
proc getIdent*(identifier: string): PIdent =
|
||||
result = getIdent(cstring(identifier), len(identifier),
|
||||
hashIgnoreStyle(identifier))
|
||||
|
||||
proc getIdent*(identifier: string, h: Hash): PIdent =
|
||||
proc getIdent*(identifier: string, h: Hash): PIdent =
|
||||
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
|
||||
|
||||
var idAnon* = getIdent":anonymous"
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
# This module implements a generic doubled linked list.
|
||||
# TODO Remove this and replace it with something sensible
|
||||
import os
|
||||
type
|
||||
type
|
||||
PListEntry* = ref TListEntry
|
||||
TListEntry* = object of RootObj
|
||||
prev*, next*: PListEntry
|
||||
@@ -25,68 +25,68 @@ type
|
||||
|
||||
TCompareProc* = proc (entry: PListEntry, closure: pointer): bool {.nimcall.}
|
||||
|
||||
proc initLinkedList*(list: var TLinkedList) =
|
||||
proc initLinkedList*(list: var TLinkedList) =
|
||||
list.counter = 0
|
||||
list.head = nil
|
||||
list.tail = nil
|
||||
|
||||
proc append*(list: var TLinkedList, entry: PListEntry) =
|
||||
proc append*(list: var TLinkedList, entry: PListEntry) =
|
||||
inc(list.counter)
|
||||
entry.next = nil
|
||||
entry.prev = list.tail
|
||||
if list.tail != nil:
|
||||
if list.tail != nil:
|
||||
assert(list.tail.next == nil)
|
||||
list.tail.next = entry
|
||||
list.tail = 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
|
||||
while it != nil:
|
||||
if PStrEntry(it).data == data:
|
||||
while it != nil:
|
||||
if PStrEntry(it).data == data:
|
||||
return true
|
||||
it = it.next
|
||||
|
||||
proc newStrEntry(data: string): PStrEntry =
|
||||
|
||||
proc newStrEntry(data: string): PStrEntry =
|
||||
new(result)
|
||||
result.data = data
|
||||
|
||||
proc appendStr*(list: var TLinkedList, data: string) =
|
||||
proc appendStr*(list: var TLinkedList, data: string) =
|
||||
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
|
||||
appendStr(list, data) # else: add to list
|
||||
|
||||
proc prepend*(list: var TLinkedList, entry: PListEntry) =
|
||||
proc prepend*(list: var TLinkedList, entry: PListEntry) =
|
||||
inc(list.counter)
|
||||
entry.prev = nil
|
||||
entry.next = list.head
|
||||
if list.head != nil:
|
||||
if list.head != nil:
|
||||
assert(list.head.prev == nil)
|
||||
list.head.prev = entry
|
||||
list.head = 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))
|
||||
|
||||
proc insertBefore*(list: var TLinkedList, pos, entry: PListEntry) =
|
||||
proc insertBefore*(list: var TLinkedList, pos, entry: PListEntry) =
|
||||
assert(pos != nil)
|
||||
if pos == list.head:
|
||||
if pos == list.head:
|
||||
prepend(list, entry)
|
||||
else:
|
||||
else:
|
||||
inc(list.counter)
|
||||
entry.next = pos
|
||||
entry.prev = pos.prev
|
||||
if pos.prev != nil: pos.prev.next = entry
|
||||
pos.prev = entry
|
||||
|
||||
proc remove*(list: var TLinkedList, entry: PListEntry) =
|
||||
|
||||
proc remove*(list: var TLinkedList, entry: PListEntry) =
|
||||
dec(list.counter)
|
||||
if entry == list.tail:
|
||||
if entry == list.tail:
|
||||
list.tail = entry.prev
|
||||
if entry == list.head:
|
||||
if entry == list.head:
|
||||
list.head = entry.next
|
||||
if entry.next != nil: entry.next.prev = entry.prev
|
||||
if entry.prev != nil: entry.prev.next = entry.next
|
||||
@@ -112,8 +112,8 @@ proc excludePath*(list: var TLinkedList, data: string) =
|
||||
remove(list, it)
|
||||
it = nxt
|
||||
|
||||
proc find*(list: TLinkedList, fn: TCompareProc, closure: pointer): PListEntry =
|
||||
proc find*(list: TLinkedList, fn: TCompareProc, closure: pointer): PListEntry =
|
||||
result = list.head
|
||||
while result != nil:
|
||||
if fn(result, closure): return
|
||||
if fn(result, closure): return
|
||||
result = result.next
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
# Built-in types and compilerprocs are registered here.
|
||||
|
||||
import
|
||||
import
|
||||
ast, astalgo, hashes, msgs, platform, nversion, times, idents, rodread
|
||||
|
||||
var systemModule*: PSym
|
||||
@@ -29,23 +29,23 @@ var
|
||||
|
||||
proc nilOrSysInt*: PType = gSysTypes[tyInt]
|
||||
|
||||
proc registerSysType(t: PType) =
|
||||
proc registerSysType(t: PType) =
|
||||
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.size = size
|
||||
result.align = size.int16
|
||||
|
||||
proc getSysSym(name: string): PSym =
|
||||
proc getSysSym(name: string): PSym =
|
||||
result = strTableGet(systemModule.tab, getIdent(name))
|
||||
if result == nil:
|
||||
if result == nil:
|
||||
rawMessage(errSystemNeeds, name)
|
||||
result = newSym(skError, getIdent(name), systemModule, systemModule.info)
|
||||
result.typ = newType(tyError, systemModule)
|
||||
if result.kind == skStub: loadStub(result)
|
||||
if result.kind == skAlias: result = result.owner
|
||||
|
||||
|
||||
proc getSysMagic*(name: string, m: TMagic): PSym =
|
||||
var ti: TIdentIter
|
||||
let id = getIdent(name)
|
||||
@@ -57,13 +57,13 @@ proc getSysMagic*(name: string, m: TMagic): PSym =
|
||||
rawMessage(errSystemNeeds, name)
|
||||
result = newSym(skError, id, systemModule, systemModule.info)
|
||||
result.typ = newType(tyError, systemModule)
|
||||
|
||||
proc sysTypeFromName*(name: string): PType =
|
||||
|
||||
proc sysTypeFromName*(name: string): PType =
|
||||
result = getSysSym(name).typ
|
||||
|
||||
proc getSysType(kind: TTypeKind): PType =
|
||||
proc getSysType(kind: TTypeKind): PType =
|
||||
result = gSysTypes[kind]
|
||||
if result == nil:
|
||||
if result == nil:
|
||||
case kind
|
||||
of tyInt: result = sysTypeFromName("int")
|
||||
of tyInt8: result = sysTypeFromName("int8")
|
||||
@@ -87,7 +87,7 @@ proc getSysType(kind: TTypeKind): PType =
|
||||
of tyNil: result = newSysType(tyNil, ptrSize)
|
||||
else: internalError("request for typekind: " & $kind)
|
||||
gSysTypes[kind] = result
|
||||
if result.kind != kind:
|
||||
if result.kind != kind:
|
||||
internalError("wanted: " & $kind & " got: " & $result.kind)
|
||||
if result == nil: internalError("type not found: " & $kind)
|
||||
|
||||
@@ -163,7 +163,7 @@ proc setIntLitType*(result: PNode) =
|
||||
result.typ = getSysType(tyInt64)
|
||||
else: internalError(result.info, "invalid int size")
|
||||
|
||||
proc getCompilerProc(name: string): PSym =
|
||||
proc getCompilerProc(name: string): PSym =
|
||||
var ident = getIdent(name, hashIgnoreStyle(name))
|
||||
result = strTableGet(compilerprocs, ident)
|
||||
if result == nil:
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
|
||||
import parseutils, strutils, strtabs, os, options, msgs, lists
|
||||
|
||||
proc addPath*(path: string, info: TLineInfo) =
|
||||
if not contains(options.searchPaths, path):
|
||||
proc addPath*(path: string, info: TLineInfo) =
|
||||
if not contains(options.searchPaths, path):
|
||||
lists.prependStr(options.searchPaths, path)
|
||||
|
||||
proc versionSplitPos(s: string): int =
|
||||
@@ -23,7 +23,7 @@ proc versionSplitPos(s: string): int =
|
||||
const
|
||||
latest = "head"
|
||||
|
||||
proc `<.`(a, b: string): bool =
|
||||
proc `<.`(a, b: string): bool =
|
||||
# wether a has a smaller version than b:
|
||||
if a == latest: return false
|
||||
var i = 0
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
## exposes the Nim VM to clients.
|
||||
|
||||
import
|
||||
ast, modules, passes, passaux, condsyms,
|
||||
ast, modules, passes, passaux, condsyms,
|
||||
options, nimconf, lists, sem, semdata, llstream, vm
|
||||
|
||||
proc execute*(program: string) =
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
# handling that exists! Only at line endings checks are necessary
|
||||
# if the buffer needs refilling.
|
||||
|
||||
import
|
||||
import
|
||||
llstream, strutils
|
||||
|
||||
const
|
||||
const
|
||||
Lrz* = ' '
|
||||
Apo* = '\''
|
||||
Tabulator* = '\x09'
|
||||
@@ -27,7 +27,7 @@ const
|
||||
BACKSPACE* = '\x08'
|
||||
VT* = '\x0B'
|
||||
|
||||
const
|
||||
const
|
||||
EndOfFile* = '\0' # end of file marker
|
||||
# A little picture makes everything clear :-)
|
||||
# buf:
|
||||
@@ -36,7 +36,7 @@ const
|
||||
#
|
||||
NewLines* = {CR, LF}
|
||||
|
||||
type
|
||||
type
|
||||
TBaseLexer* = object of RootObj
|
||||
bufpos*: int
|
||||
buf*: cstring
|
||||
@@ -46,9 +46,9 @@ type
|
||||
# private data:
|
||||
sentinel*: int
|
||||
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)
|
||||
# 8K is a reasonable buffer size
|
||||
proc closeBaseLexer*(L: var TBaseLexer)
|
||||
@@ -64,15 +64,15 @@ proc handleLF*(L: var TBaseLexer, pos: int): int
|
||||
# of the LF.
|
||||
# implementation
|
||||
|
||||
const
|
||||
const
|
||||
chrSize = sizeof(char)
|
||||
|
||||
proc closeBaseLexer(L: var TBaseLexer) =
|
||||
proc closeBaseLexer(L: var TBaseLexer) =
|
||||
dealloc(L.buf)
|
||||
llStreamClose(L.stream)
|
||||
|
||||
proc fillBuffer(L: var TBaseLexer) =
|
||||
var
|
||||
proc fillBuffer(L: var TBaseLexer) =
|
||||
var
|
||||
charsRead, toCopy, s: int # all are in characters,
|
||||
# not bytes (in case this
|
||||
# is not the same)
|
||||
@@ -82,68 +82,68 @@ proc fillBuffer(L: var TBaseLexer) =
|
||||
assert(L.sentinel < L.bufLen)
|
||||
toCopy = L.bufLen - L.sentinel - 1
|
||||
assert(toCopy >= 0)
|
||||
if toCopy > 0:
|
||||
moveMem(L.buf, addr(L.buf[L.sentinel + 1]), toCopy * chrSize)
|
||||
if toCopy > 0:
|
||||
moveMem(L.buf, addr(L.buf[L.sentinel + 1]), toCopy * chrSize)
|
||||
# "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
|
||||
s = toCopy + charsRead
|
||||
if charsRead < L.sentinel + 1:
|
||||
if charsRead < L.sentinel + 1:
|
||||
L.buf[s] = EndOfFile # set end marker
|
||||
L.sentinel = s
|
||||
else:
|
||||
else:
|
||||
# compute sentinel:
|
||||
dec(s) # BUGFIX (valgrind)
|
||||
while true:
|
||||
while true:
|
||||
assert(s < L.bufLen)
|
||||
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:
|
||||
L.sentinel = s
|
||||
break
|
||||
else:
|
||||
break
|
||||
else:
|
||||
# rather than to give up here because the line is too long,
|
||||
# double the buffer's size and try again:
|
||||
oldBufLen = L.bufLen
|
||||
L.bufLen = L.bufLen * 2
|
||||
L.buf = cast[cstring](realloc(L.buf, L.bufLen * chrSize))
|
||||
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
|
||||
if charsRead < oldBufLen:
|
||||
if charsRead < oldBufLen:
|
||||
L.buf[oldBufLen + charsRead] = EndOfFile
|
||||
L.sentinel = oldBufLen + charsRead
|
||||
break
|
||||
break
|
||||
s = L.bufLen - 1
|
||||
|
||||
proc fillBaseLexer(L: var TBaseLexer, pos: int): int =
|
||||
proc fillBaseLexer(L: var TBaseLexer, pos: int): int =
|
||||
assert(pos <= L.sentinel)
|
||||
if pos < L.sentinel:
|
||||
if pos < L.sentinel:
|
||||
result = pos + 1 # nothing to do
|
||||
else:
|
||||
else:
|
||||
fillBuffer(L)
|
||||
L.bufpos = 0 # XXX: is this really correct?
|
||||
result = 0
|
||||
L.lineStart = result
|
||||
|
||||
proc handleCR(L: var TBaseLexer, pos: int): int =
|
||||
proc handleCR(L: var TBaseLexer, pos: int): int =
|
||||
assert(L.buf[pos] == CR)
|
||||
inc(L.lineNumber)
|
||||
result = fillBaseLexer(L, pos)
|
||||
if L.buf[result] == LF:
|
||||
if L.buf[result] == LF:
|
||||
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)
|
||||
inc(L.lineNumber)
|
||||
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':
|
||||
inc(L.bufpos, 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)
|
||||
L.bufpos = 0
|
||||
L.bufLen = bufLen
|
||||
@@ -155,15 +155,15 @@ proc openBaseLexer(L: var TBaseLexer, inputstream: PLLStream, bufLen = 8192) =
|
||||
fillBuffer(L)
|
||||
skipUTF8BOM(L)
|
||||
|
||||
proc getColNumber(L: TBaseLexer, pos: int): int =
|
||||
proc getColNumber(L: TBaseLexer, pos: int): int =
|
||||
result = abs(pos - L.lineStart)
|
||||
|
||||
proc getCurrentLine(L: TBaseLexer, marker: bool = true): string =
|
||||
proc getCurrentLine(L: TBaseLexer, marker: bool = true): string =
|
||||
result = ""
|
||||
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])
|
||||
inc(i)
|
||||
result.add("\n")
|
||||
if marker:
|
||||
if marker:
|
||||
result.add(spaces(getColNumber(L, L.bufpos)) & '^' & "\n")
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
# this unit handles Nim sets; it implements symbolic sets
|
||||
|
||||
import
|
||||
import
|
||||
ast, astalgo, trees, nversion, msgs, platform, bitsets, types, renderer
|
||||
|
||||
proc toBitSet*(s: PNode, b: var TBitSet)
|
||||
@@ -30,17 +30,17 @@ proc equalSets*(a, b: PNode): bool
|
||||
proc cardSet*(s: PNode): BiggestInt
|
||||
# implementation
|
||||
|
||||
proc inSet(s: PNode, elem: PNode): bool =
|
||||
if s.kind != nkCurly:
|
||||
proc inSet(s: PNode, elem: PNode): bool =
|
||||
if s.kind != nkCurly:
|
||||
internalError(s.info, "inSet")
|
||||
return false
|
||||
for i in countup(0, sonsLen(s) - 1):
|
||||
if s.sons[i].kind == nkRange:
|
||||
for i in countup(0, sonsLen(s) - 1):
|
||||
if s.sons[i].kind == nkRange:
|
||||
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
|
||||
else:
|
||||
if sameValue(s.sons[i], elem):
|
||||
else:
|
||||
if sameValue(s.sons[i], elem):
|
||||
return true
|
||||
result = false
|
||||
|
||||
@@ -58,37 +58,37 @@ proc overlap(a, b: PNode): bool =
|
||||
else:
|
||||
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
|
||||
if s.kind != nkCurly:
|
||||
internalError(s.info, "SomeInSet")
|
||||
return false
|
||||
for i in countup(0, sonsLen(s) - 1):
|
||||
if s.sons[i].kind == nkRange:
|
||||
for i in countup(0, sonsLen(s) - 1):
|
||||
if s.sons[i].kind == nkRange:
|
||||
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
|
||||
else:
|
||||
else:
|
||||
# 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
|
||||
result = false
|
||||
|
||||
proc toBitSet(s: PNode, b: var TBitSet) =
|
||||
proc toBitSet(s: PNode, b: var TBitSet) =
|
||||
var first, j: BiggestInt
|
||||
first = firstOrd(s.typ.sons[0])
|
||||
bitSetInit(b, int(getSize(s.typ)))
|
||||
for i in countup(0, sonsLen(s) - 1):
|
||||
if s.sons[i].kind == nkRange:
|
||||
for i in countup(0, sonsLen(s) - 1):
|
||||
if s.sons[i].kind == nkRange:
|
||||
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)
|
||||
inc(j)
|
||||
else:
|
||||
else:
|
||||
bitSetIncl(b, getOrdValue(s.sons[i]) - first)
|
||||
|
||||
proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode =
|
||||
var
|
||||
|
||||
proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode =
|
||||
var
|
||||
a, b, e, first: BiggestInt # a, b are interval borders
|
||||
elemType: PType
|
||||
n: PNode
|
||||
@@ -98,17 +98,17 @@ proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode =
|
||||
result.typ = settype
|
||||
result.info = info
|
||||
e = 0
|
||||
while e < len(s) * ElemSize:
|
||||
if bitSetIn(s, e):
|
||||
while e < len(s) * ElemSize:
|
||||
if bitSetIn(s, e):
|
||||
a = e
|
||||
b = e
|
||||
while true:
|
||||
while true:
|
||||
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)
|
||||
if a == b:
|
||||
if a == b:
|
||||
addSon(result, newIntTypeNode(nkIntLit, a + first, elemType))
|
||||
else:
|
||||
else:
|
||||
n = newNodeI(nkRange, info)
|
||||
n.typ = elemType
|
||||
addSon(n, newIntTypeNode(nkIntLit, a + first, elemType))
|
||||
@@ -117,7 +117,7 @@ proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode =
|
||||
e = b
|
||||
inc(e)
|
||||
|
||||
template nodeSetOp(a, b: PNode, op: expr) {.dirty.} =
|
||||
template nodeSetOp(a, b: PNode, op: expr) {.dirty.} =
|
||||
var x, y: TBitSet
|
||||
toBitSet(a, x)
|
||||
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 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
|
||||
toBitSet(a, x)
|
||||
toBitSet(b, y)
|
||||
result = bitSetContains(x, y)
|
||||
|
||||
proc equalSets(a, b: PNode): bool =
|
||||
proc equalSets(a, b: PNode): bool =
|
||||
var x, y: TBitSet
|
||||
toBitSet(a, x)
|
||||
toBitSet(b, y)
|
||||
@@ -147,26 +147,26 @@ proc complement*(a: PNode): PNode =
|
||||
for i in countup(0, high(x)): x[i] = not x[i]
|
||||
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
|
||||
# we just count the elements directly
|
||||
result = 0
|
||||
for i in countup(0, sonsLen(s) - 1):
|
||||
if s.sons[i].kind == nkRange:
|
||||
for i in countup(0, sonsLen(s) - 1):
|
||||
if s.sons[i].kind == nkRange:
|
||||
result = result + getOrdValue(s.sons[i].sons[1]) -
|
||||
getOrdValue(s.sons[i].sons[0]) + 1
|
||||
else:
|
||||
else:
|
||||
inc(result)
|
||||
|
||||
proc setHasRange(s: PNode): bool =
|
||||
|
||||
proc setHasRange(s: PNode): bool =
|
||||
if s.kind != nkCurly:
|
||||
internalError(s.info, "SetHasRange")
|
||||
return false
|
||||
for i in countup(0, sonsLen(s) - 1):
|
||||
if s.sons[i].kind == nkRange:
|
||||
for i in countup(0, sonsLen(s) - 1):
|
||||
if s.sons[i].kind == nkRange:
|
||||
return true
|
||||
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)
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
# This module contains Nim's version. It is the only place where it needs
|
||||
# to be changed.
|
||||
|
||||
const
|
||||
const
|
||||
MaxSetElements* = 1 shl 16 # (2^16) to support unicode character sets?
|
||||
VersionAsString* = system.NimVersion
|
||||
RodFileVersion* = "1215" # modify this if the rod-format changes!
|
||||
|
||||
@@ -9,38 +9,38 @@
|
||||
|
||||
## implements some little helper passes
|
||||
|
||||
import
|
||||
import
|
||||
strutils, ast, astalgo, passes, msgs, options, idgen
|
||||
|
||||
proc verboseOpen(s: PSym): PPassContext =
|
||||
#MessageOut('compiling ' + s.name.s);
|
||||
result = nil # we don't need a context
|
||||
rawMessage(hintProcessing, s.name.s)
|
||||
|
||||
proc verboseProcess(context: PPassContext, n: PNode): PNode =
|
||||
|
||||
proc verboseProcess(context: PPassContext, n: PNode): PNode =
|
||||
result = n
|
||||
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
|
||||
# messages nonetheless, so we activate them again unconditionally:
|
||||
incl(msgs.gNotes, hintProcessing)
|
||||
message(n.info, hintProcessing, $idgen.gBackendId)
|
||||
|
||||
|
||||
const verbosePass* = makePass(open = verboseOpen, process = verboseProcess)
|
||||
|
||||
proc cleanUp(c: PPassContext, n: PNode): PNode =
|
||||
proc cleanUp(c: PPassContext, n: PNode): PNode =
|
||||
result = n
|
||||
# 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
|
||||
of nkStmtList:
|
||||
of nkStmtList:
|
||||
for i in countup(0, sonsLen(n) - 1): discard cleanUp(c, n.sons[i])
|
||||
of nkProcDef, nkMethodDef:
|
||||
if n.sons[namePos].kind == nkSym:
|
||||
of nkProcDef, nkMethodDef:
|
||||
if n.sons[namePos].kind == nkSym:
|
||||
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
|
||||
else:
|
||||
else:
|
||||
discard
|
||||
|
||||
const cleanupPass* = makePass(process = cleanUp, close = cleanUp)
|
||||
|
||||
@@ -68,7 +68,7 @@ proc inSymChoice(sc, x: PNode): bool =
|
||||
elif sc.kind == nkOpenSymChoice:
|
||||
# same name suffices for open sym choices!
|
||||
result = sc.sons[0].sym.name.id == x.sym.name.id
|
||||
|
||||
|
||||
proc checkTypes(c: PPatternContext, p: PSym, n: PNode): bool =
|
||||
# check param constraints first here as this is quite optimized:
|
||||
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])
|
||||
elif n.kind == nkHiddenStdConv and n.sons[1].kind == nkBracket:
|
||||
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
|
||||
elif checkTypes(c, p.sons[2].sym, n):
|
||||
add(arglist, n)
|
||||
else:
|
||||
result = false
|
||||
|
||||
|
||||
if n.kind notin nkCallKinds: return false
|
||||
if matches(c, p.sons[1], n.sons[0]):
|
||||
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 = not matches(c, p.sons[1], n)
|
||||
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)
|
||||
elif p.kind == nkCurlyExpr:
|
||||
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
|
||||
return false
|
||||
result = true
|
||||
|
||||
|
||||
if p.kind == nkStmtList and n.kind == p.kind and p.len < n.len:
|
||||
let n = flattenStmts(n)
|
||||
# no need to flatten 'p' here as that has already been done
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
import
|
||||
import
|
||||
llstream, lexer, parser, idents, strutils, ast, msgs
|
||||
|
||||
proc parseAll*(p: var TParser): PNode =
|
||||
proc parseAll*(p: var TParser): PNode =
|
||||
result = nil
|
||||
|
||||
proc parseTopLevelStmt*(p: var TParser): PNode =
|
||||
proc parseTopLevelStmt*(p: var TParser): PNode =
|
||||
result = nil
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym =
|
||||
discard
|
||||
|
||||
result = nextIdentIter(it, scope.symbols)
|
||||
|
||||
|
||||
return nil
|
||||
|
||||
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 if result != nil: result.typ else: nil
|
||||
debug if old != nil: old.typ else: nil
|
||||
|
||||
|
||||
when false:
|
||||
proc paramsFitBorrow(child, parent: PNode): bool =
|
||||
proc paramsFitBorrow(child, parent: PNode): bool =
|
||||
var length = sonsLen(child)
|
||||
result = false
|
||||
if length == sonsLen(parent):
|
||||
for i in countup(1, length - 1):
|
||||
if length == sonsLen(parent):
|
||||
for i in countup(1, length - 1):
|
||||
var m = child.sons[i].sym
|
||||
var n = parent.sons[i].sym
|
||||
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,
|
||||
dcEqOrDistinctOf): return
|
||||
result = true
|
||||
@@ -120,10 +120,10 @@ when false:
|
||||
var it: TIdentIter
|
||||
for scope in walkScopes(startScope):
|
||||
result = initIdentIter(it, scope.symbols, fn.Name)
|
||||
while result != nil:
|
||||
while result != nil:
|
||||
# watchout! result must not be the same as fn!
|
||||
if (result.Kind == fn.kind) and (result.id != fn.id):
|
||||
if equalGenericParams(result.ast.sons[genericParamsPos],
|
||||
fn.ast.sons[genericParamsPos]):
|
||||
if paramsFitBorrow(fn.typ.n, result.typ.n): return
|
||||
if (result.Kind == fn.kind) and (result.id != fn.id):
|
||||
if equalGenericParams(result.ast.sons[genericParamsPos],
|
||||
fn.ast.sons[genericParamsPos]):
|
||||
if paramsFitBorrow(fn.typ.n, result.typ.n): return
|
||||
result = NextIdentIter(it, scope.symbols)
|
||||
|
||||
@@ -12,7 +12,7 @@ import strutils
|
||||
|
||||
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:
|
||||
result = "NAN"
|
||||
elif f == 0.0:
|
||||
@@ -21,17 +21,17 @@ proc toStrMaxPrecision*(f: BiggestFloat): string =
|
||||
if f > 0.0: result = "INF"
|
||||
else: result = "-INF"
|
||||
else:
|
||||
var buf: array [0..80, char]
|
||||
c_sprintf(buf, "%#.16e", f)
|
||||
var buf: array [0..80, char]
|
||||
c_sprintf(buf, "%#.16e", f)
|
||||
result = $buf
|
||||
|
||||
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]
|
||||
of 'a'..'z', 'A'..'Z', '0'..'9', '_': add(result, s[i])
|
||||
else: add(result, '\\' & toHex(ord(s[i]), 2))
|
||||
|
||||
proc hexChar(c: char, xi: var int) =
|
||||
proc hexChar(c: char, xi: var int) =
|
||||
case c
|
||||
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)
|
||||
@@ -41,18 +41,18 @@ proc hexChar(c: char, xi: var int) =
|
||||
proc decodeStr*(s: cstring, pos: var int): string =
|
||||
var i = pos
|
||||
result = ""
|
||||
while true:
|
||||
while true:
|
||||
case s[i]
|
||||
of '\\':
|
||||
of '\\':
|
||||
inc(i, 3)
|
||||
var xi = 0
|
||||
hexChar(s[i-2], xi)
|
||||
hexChar(s[i-1], xi)
|
||||
add(result, chr(xi))
|
||||
of 'a'..'z', 'A'..'Z', '0'..'9', '_':
|
||||
of 'a'..'z', 'A'..'Z', '0'..'9', '_':
|
||||
add(result, s[i])
|
||||
inc(i)
|
||||
else: break
|
||||
else: break
|
||||
pos = i
|
||||
|
||||
const
|
||||
@@ -68,11 +68,11 @@ template encodeIntImpl(self: expr) =
|
||||
var d: char
|
||||
var v = x
|
||||
var rem = v mod 190
|
||||
if rem < 0:
|
||||
if rem < 0:
|
||||
add(result, '-')
|
||||
v = - (v div 190)
|
||||
rem = - rem
|
||||
else:
|
||||
else:
|
||||
v = v div 190
|
||||
var idx = int(rem)
|
||||
if idx < 62: d = chars[idx]
|
||||
@@ -89,11 +89,11 @@ proc encodeVBiggestInt*(x: BiggestInt, result: var string) =
|
||||
encodeVBiggestIntAux(x +% vintDelta, result)
|
||||
# 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.
|
||||
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.
|
||||
encodeVIntAux(x +% vintDelta, result)
|
||||
|
||||
@@ -101,11 +101,11 @@ template decodeIntImpl() =
|
||||
var i = pos
|
||||
var sign = - 1
|
||||
assert(s[i] in {'a'..'z', 'A'..'Z', '0'..'9', '-', '\x80'..'\xFF'})
|
||||
if s[i] == '-':
|
||||
if s[i] == '-':
|
||||
inc(i)
|
||||
sign = 1
|
||||
result = 0
|
||||
while true:
|
||||
while true:
|
||||
case s[i]
|
||||
of '0'..'9': result = result * 190 - (ord(s[i]) - ord('0'))
|
||||
of 'a'..'z': result = result * 190 - (ord(s[i]) - ord('a') + 10)
|
||||
@@ -116,7 +116,7 @@ template decodeIntImpl() =
|
||||
result = result * sign -% vintDelta
|
||||
pos = i
|
||||
|
||||
proc decodeVInt*(s: cstring, pos: var int): int =
|
||||
proc decodeVInt*(s: cstring, pos: var int): int =
|
||||
decodeIntImpl()
|
||||
|
||||
proc decodeVBiggestInt*(s: cstring, pos: var int): BiggestInt =
|
||||
|
||||
@@ -306,7 +306,7 @@ const
|
||||
|
||||
proc equalsFile*(r: Rope, f: File): bool =
|
||||
## returns true if the contents of the file `f` equal `r`.
|
||||
var
|
||||
var
|
||||
buf: array[bufSize, char]
|
||||
bpos = buf.len
|
||||
blen = buf.len
|
||||
|
||||
@@ -72,7 +72,7 @@ proc `|*|`*(a, b: BiggestInt): BiggestInt =
|
||||
# 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
|
||||
if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd):
|
||||
return result
|
||||
|
||||
|
||||
if floatProd >= 0.0:
|
||||
result = high(result)
|
||||
else:
|
||||
|
||||
@@ -320,7 +320,7 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
|
||||
x.call.add newSymNode(s, n.info)
|
||||
else:
|
||||
internalAssert false
|
||||
|
||||
|
||||
result = x.call
|
||||
instGenericConvertersSons(c, result, x)
|
||||
result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
|
||||
|
||||
@@ -76,7 +76,7 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
|
||||
let L = forLoop.len
|
||||
let call = forLoop.sons[L-2]
|
||||
if call.len > 2:
|
||||
localError(forLoop.info, errGenerated,
|
||||
localError(forLoop.info, errGenerated,
|
||||
"parallel 'fields' iterator does not work for 'case' objects")
|
||||
return
|
||||
# iterate over the selector:
|
||||
@@ -106,7 +106,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
|
||||
# a 'while true: stmt; break' loop ...
|
||||
result = newNodeI(nkWhileStmt, n.info, 2)
|
||||
var trueSymbol = strTableGet(magicsys.systemModule.tab, getIdent"true")
|
||||
if trueSymbol == nil:
|
||||
if trueSymbol == nil:
|
||||
localError(n.info, errSystemNeeds, "true")
|
||||
trueSymbol = newSym(skUnknown, getIdent"true", getCurrOwner(), n.info)
|
||||
trueSymbol.typ = getSysType(tyBool)
|
||||
@@ -114,13 +114,13 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
|
||||
result.sons[0] = newSymNode(trueSymbol, n.info)
|
||||
var stmts = newNodeI(nkStmtList, n.info)
|
||||
result.sons[1] = stmts
|
||||
|
||||
|
||||
var length = sonsLen(n)
|
||||
var call = n.sons[length-2]
|
||||
if length-2 != sonsLen(call)-1 + ord(m==mFieldPairs):
|
||||
localError(n.info, errWrongNumberOfVariables)
|
||||
return result
|
||||
|
||||
|
||||
var tupleTypeA = skipTypes(call.sons[1].typ, abstractVar-{tyTypeDesc})
|
||||
if tupleTypeA.kind notin {tyTuple, tyObject}:
|
||||
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})
|
||||
if not sameType(tupleTypeA, tupleTypeB):
|
||||
typeMismatch(call.sons[i], tupleTypeA, tupleTypeB)
|
||||
|
||||
|
||||
inc(c.p.nestedLoopCounter)
|
||||
if tupleTypeA.kind == tyTuple:
|
||||
var loopBody = n.sons[length-1]
|
||||
|
||||
@@ -1327,7 +1327,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
||||
# because in that case there is no point in continuing.
|
||||
var bothMetaCounter = 0
|
||||
var lastBindingsLength = -1
|
||||
while r == isBothMetaConvertible and
|
||||
while r == isBothMetaConvertible and
|
||||
lastBindingsLength != m.bindings.counter and
|
||||
bothMetaCounter < 100:
|
||||
lastBindingsLength = m.bindings.counter
|
||||
|
||||
@@ -34,7 +34,7 @@ var
|
||||
|
||||
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)
|
||||
if optIdeTerse in gGlobalOptions:
|
||||
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(s.name.s)
|
||||
|
||||
if s.typ != nil:
|
||||
if s.typ != nil:
|
||||
result.forth = typeToString(s.typ)
|
||||
else:
|
||||
result.forth = ""
|
||||
@@ -62,7 +62,7 @@ proc symToSuggest(s: PSym, isLocal: bool, section: string, li: TLineInfo): Sugge
|
||||
when not defined(noDocgen):
|
||||
result.doc = s.extractDocComment
|
||||
|
||||
proc `$`(suggest: Suggest): string =
|
||||
proc `$`(suggest: Suggest): string =
|
||||
result = $suggest.section
|
||||
result.add(sep)
|
||||
result.add($suggest.symkind)
|
||||
@@ -80,7 +80,7 @@ proc `$`(suggest: Suggest): string =
|
||||
when not defined(noDocgen):
|
||||
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)
|
||||
|
||||
proc suggestResult(s: Suggest) =
|
||||
@@ -104,7 +104,7 @@ proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} =
|
||||
result = true
|
||||
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):
|
||||
suggestResult(symToSuggest(s, isLocal=true, $ideSug))
|
||||
inc outputs
|
||||
@@ -122,17 +122,17 @@ template wholeSymTab(cond, section: expr) {.immediate.} =
|
||||
suggestResult(symToSuggest(it, isLocal = isLocal, section))
|
||||
inc outputs
|
||||
|
||||
proc suggestSymList(c: PContext, list: PNode, outputs: var int) =
|
||||
for i in countup(0, sonsLen(list) - 1):
|
||||
proc suggestSymList(c: PContext, list: PNode, outputs: var int) =
|
||||
for i in countup(0, sonsLen(list) - 1):
|
||||
if list.sons[i].kind == nkSym:
|
||||
suggestField(c, list.sons[i].sym, outputs)
|
||||
#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
|
||||
of nkRecList:
|
||||
of nkRecList:
|
||||
for i in countup(0, sonsLen(n)-1): suggestObject(c, n.sons[i], outputs)
|
||||
of nkRecCase:
|
||||
of nkRecCase:
|
||||
var L = sonsLen(n)
|
||||
if L > 0:
|
||||
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)
|
||||
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]
|
||||
if op.kind in {nkOpenSymChoice, nkClosedSymChoice}: op = op.sons[0]
|
||||
var opr: PIdent
|
||||
@@ -150,8 +150,8 @@ proc nameFits(c: PContext, s: PSym, n: PNode): bool =
|
||||
else: return false
|
||||
result = opr.id == s.name.id
|
||||
|
||||
proc argsFit(c: PContext, candidate: PSym, n, nOrig: PNode): bool =
|
||||
case candidate.kind
|
||||
proc argsFit(c: PContext, candidate: PSym, n, nOrig: PNode): bool =
|
||||
case candidate.kind
|
||||
of OverloadableSyms:
|
||||
var m: TCandidate
|
||||
initCandidate(c, m, candidate, nil)
|
||||
@@ -160,11 +160,11 @@ proc argsFit(c: PContext, candidate: PSym, n, nOrig: PNode): bool =
|
||||
else:
|
||||
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),
|
||||
$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:
|
||||
# special rule: if system and some weird generic match via 'tyExpr'
|
||||
# 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
|
||||
if typ == nil:
|
||||
# a module symbol has no type for example:
|
||||
if n.kind == nkSym and n.sym.kind == skModule:
|
||||
if n.sym == c.module:
|
||||
if n.kind == nkSym and n.sym.kind == skModule:
|
||||
if n.sym == c.module:
|
||||
# all symbols accessible, because we are in the current module:
|
||||
for it in items(c.topLevelScope.symbols):
|
||||
if filterSym(it):
|
||||
if filterSym(it):
|
||||
suggestResult(symToSuggest(it, isLocal=false, $ideSug))
|
||||
inc outputs
|
||||
else:
|
||||
for it in items(n.sym.tab):
|
||||
if filterSym(it):
|
||||
else:
|
||||
for it in items(n.sym.tab):
|
||||
if filterSym(it):
|
||||
suggestResult(symToSuggest(it, isLocal=false, $ideSug))
|
||||
inc outputs
|
||||
else:
|
||||
# fallback:
|
||||
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:
|
||||
var t = typ
|
||||
while t != nil:
|
||||
while t != nil:
|
||||
suggestSymList(c, t.n, outputs)
|
||||
t = t.sons[0]
|
||||
suggestOperations(c, n, typ, outputs)
|
||||
else:
|
||||
typ = skipTypes(typ, {tyGenericInst, tyVar, tyPtr, tyRef})
|
||||
if typ.kind == tyObject:
|
||||
if typ.kind == tyObject:
|
||||
var t = typ
|
||||
while true:
|
||||
while true:
|
||||
suggestObject(c, t.n, outputs)
|
||||
if t.sons[0] == nil: break
|
||||
if t.sons[0] == nil: break
|
||||
t = skipTypes(t.sons[0], {tyGenericInst})
|
||||
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)
|
||||
suggestOperations(c, n, typ, outputs)
|
||||
else:
|
||||
suggestOperations(c, n, typ, outputs)
|
||||
|
||||
type
|
||||
TCheckPointResult = enum
|
||||
TCheckPointResult = enum
|
||||
cpNone, cpFuzzy, cpExact
|
||||
|
||||
proc inCheckpoint(current: TLineInfo): TCheckPointResult =
|
||||
proc inCheckpoint(current: TLineInfo): TCheckPointResult =
|
||||
if current.fileIndex == gTrackPos.fileIndex:
|
||||
if current.line == gTrackPos.line and
|
||||
abs(current.col-gTrackPos.col) < 4:
|
||||
@@ -255,8 +255,8 @@ proc findClosestDot(n: PNode): PNode =
|
||||
result = findClosestDot(n.sons[i])
|
||||
if result != nil: return
|
||||
|
||||
proc findClosestCall(n: PNode): PNode =
|
||||
if n.kind in nkCallKinds and inCheckpoint(n.info) == cpExact:
|
||||
proc findClosestCall(n: PNode): PNode =
|
||||
if n.kind in nkCallKinds and inCheckpoint(n.info) == cpExact:
|
||||
result = n
|
||||
else:
|
||||
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:
|
||||
return true
|
||||
|
||||
proc findClosestSym(n: PNode): PNode =
|
||||
if n.kind == nkSym and inCheckpoint(n.info) == cpExact:
|
||||
proc findClosestSym(n: PNode): PNode =
|
||||
if n.kind == nkSym and inCheckpoint(n.info) == cpExact:
|
||||
result = n
|
||||
elif n.kind notin {nkNone..nkNilLit}:
|
||||
for i in 0.. <sonsLen(n):
|
||||
@@ -328,7 +328,7 @@ proc safeSemExpr*(c: PContext, n: PNode): PNode =
|
||||
except ERecoverableError:
|
||||
result = ast.emptyNode
|
||||
|
||||
proc suggestExpr*(c: PContext, node: PNode) =
|
||||
proc suggestExpr*(c: PContext, node: PNode) =
|
||||
if nfIsCursor notin node.flags:
|
||||
if gTrackPos.line < 0: return
|
||||
var cp = inCheckpoint(node.info)
|
||||
@@ -349,7 +349,7 @@ proc suggestExpr*(c: PContext, node: PNode) =
|
||||
#writeStackTrace()
|
||||
else:
|
||||
suggestEverything(c, n, outputs)
|
||||
|
||||
|
||||
elif gIdeCmd == ideCon:
|
||||
var n = if nfIsCursor in node.flags: node else: findClosestCall(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
|
||||
addSon(a, x)
|
||||
suggestCall(c, a, n, outputs)
|
||||
|
||||
|
||||
dec(c.inCompilesContext)
|
||||
if outputs > 0 and gIdeCmd != ideUse: suggestQuit()
|
||||
|
||||
proc suggestStmt*(c: PContext, n: PNode) =
|
||||
proc suggestStmt*(c: PContext, n: PNode) =
|
||||
suggestExpr(c, n)
|
||||
|
||||
@@ -9,24 +9,24 @@
|
||||
|
||||
## Implements the dispatcher for the different parsers.
|
||||
|
||||
import
|
||||
strutils, llstream, ast, astalgo, idents, lexer, options, msgs, parser,
|
||||
import
|
||||
strutils, llstream, ast, astalgo, idents, lexer, options, msgs, parser,
|
||||
pbraces, filters, filter_tmpl, renderer
|
||||
|
||||
type
|
||||
TFilterKind* = enum
|
||||
type
|
||||
TFilterKind* = enum
|
||||
filtNone, filtTemplate, filtReplace, filtStrip
|
||||
TParserKind* = enum
|
||||
TParserKind* = enum
|
||||
skinStandard, skinStrongSpaces, skinBraces, skinEndX
|
||||
|
||||
const
|
||||
const
|
||||
parserNames*: array[TParserKind, string] = ["standard", "strongspaces",
|
||||
"braces", "endx"]
|
||||
filterNames*: array[TFilterKind, string] = ["none", "stdtmpl", "replace",
|
||||
"strip"]
|
||||
|
||||
type
|
||||
TParsers*{.final.} = object
|
||||
TParsers*{.final.} = object
|
||||
skin*: TParserKind
|
||||
parser*: TParser
|
||||
|
||||
@@ -42,53 +42,53 @@ proc parseTopLevelStmt*(p: var TParsers): PNode
|
||||
# implementation
|
||||
|
||||
proc parseFile(fileIdx: int32): PNode =
|
||||
var
|
||||
var
|
||||
p: TParsers
|
||||
f: File
|
||||
let filename = fileIdx.toFullPathConsiderDirty
|
||||
if not open(f, filename):
|
||||
rawMessage(errCannotOpenFile, filename)
|
||||
return
|
||||
return
|
||||
openParsers(p, fileIdx, llStreamOpen(f))
|
||||
result = parseAll(p)
|
||||
closeParsers(p)
|
||||
|
||||
proc parseAll(p: var TParsers): PNode =
|
||||
proc parseAll(p: var TParsers): PNode =
|
||||
case p.skin
|
||||
of skinStandard, skinStrongSpaces:
|
||||
result = parser.parseAll(p.parser)
|
||||
of skinBraces:
|
||||
of skinBraces:
|
||||
result = pbraces.parseAll(p.parser)
|
||||
of skinEndX:
|
||||
internalError("parser to implement")
|
||||
of skinEndX:
|
||||
internalError("parser to implement")
|
||||
result = ast.emptyNode
|
||||
|
||||
proc parseTopLevelStmt(p: var TParsers): PNode =
|
||||
|
||||
proc parseTopLevelStmt(p: var TParsers): PNode =
|
||||
case p.skin
|
||||
of skinStandard, skinStrongSpaces:
|
||||
result = parser.parseTopLevelStmt(p.parser)
|
||||
of skinBraces:
|
||||
of skinBraces:
|
||||
result = pbraces.parseTopLevelStmt(p.parser)
|
||||
of skinEndX:
|
||||
internalError("parser to implement")
|
||||
of skinEndX:
|
||||
internalError("parser to implement")
|
||||
result = ast.emptyNode
|
||||
|
||||
proc utf8Bom(s: string): int =
|
||||
if (s[0] == '\xEF') and (s[1] == '\xBB') and (s[2] == '\xBF'):
|
||||
|
||||
proc utf8Bom(s: string): int =
|
||||
if (s[0] == '\xEF') and (s[1] == '\xBB') and (s[2] == '\xBF'):
|
||||
result = 3
|
||||
else:
|
||||
else:
|
||||
result = 0
|
||||
|
||||
proc containsShebang(s: string, i: int): bool =
|
||||
if (s[i] == '#') and (s[i + 1] == '!'):
|
||||
|
||||
proc containsShebang(s: string, i: int): bool =
|
||||
if (s[i] == '#') and (s[i + 1] == '!'):
|
||||
var j = i + 2
|
||||
while s[j] in Whitespace: inc(j)
|
||||
result = s[j] == '/'
|
||||
|
||||
proc parsePipe(filename: string, inputStream: PLLStream): PNode =
|
||||
proc parsePipe(filename: string, inputStream: PLLStream): PNode =
|
||||
result = ast.emptyNode
|
||||
var s = llStreamOpen(filename, fmRead)
|
||||
if s != nil:
|
||||
if s != nil:
|
||||
var line = newStringOfCap(80)
|
||||
discard llStreamReadLine(s, line)
|
||||
var i = utf8Bom(line)
|
||||
@@ -104,50 +104,50 @@ proc parsePipe(filename: string, inputStream: PLLStream): PNode =
|
||||
closeParser(q)
|
||||
llStreamClose(s)
|
||||
|
||||
proc getFilter(ident: PIdent): TFilterKind =
|
||||
for i in countup(low(TFilterKind), high(TFilterKind)):
|
||||
if identEq(ident, filterNames[i]):
|
||||
proc getFilter(ident: PIdent): TFilterKind =
|
||||
for i in countup(low(TFilterKind), high(TFilterKind)):
|
||||
if identEq(ident, filterNames[i]):
|
||||
return i
|
||||
result = filtNone
|
||||
|
||||
proc getParser(ident: PIdent): TParserKind =
|
||||
for i in countup(low(TParserKind), high(TParserKind)):
|
||||
if identEq(ident, parserNames[i]):
|
||||
proc getParser(ident: PIdent): TParserKind =
|
||||
for i in countup(low(TParserKind), high(TParserKind)):
|
||||
if identEq(ident, parserNames[i]):
|
||||
return i
|
||||
rawMessage(errInvalidDirectiveX, ident.s)
|
||||
|
||||
proc getCallee(n: PNode): PIdent =
|
||||
if n.kind in nkCallKinds and n.sons[0].kind == nkIdent:
|
||||
proc getCallee(n: PNode): PIdent =
|
||||
if n.kind in nkCallKinds and n.sons[0].kind == nkIdent:
|
||||
result = n.sons[0].ident
|
||||
elif n.kind == nkIdent:
|
||||
elif n.kind == nkIdent:
|
||||
result = n.ident
|
||||
else:
|
||||
else:
|
||||
rawMessage(errXNotAllowedHere, renderTree(n))
|
||||
|
||||
proc applyFilter(p: var TParsers, n: PNode, filename: string,
|
||||
stdin: PLLStream): PLLStream =
|
||||
|
||||
proc applyFilter(p: var TParsers, n: PNode, filename: string,
|
||||
stdin: PLLStream): PLLStream =
|
||||
var ident = getCallee(n)
|
||||
var f = getFilter(ident)
|
||||
case f
|
||||
of filtNone:
|
||||
of filtNone:
|
||||
p.skin = getParser(ident)
|
||||
result = stdin
|
||||
of filtTemplate:
|
||||
of filtTemplate:
|
||||
result = filterTmpl(stdin, filename, n)
|
||||
of filtStrip:
|
||||
of filtStrip:
|
||||
result = filterStrip(stdin, filename, n)
|
||||
of filtReplace:
|
||||
of filtReplace:
|
||||
result = filterReplace(stdin, filename, n)
|
||||
if f != filtNone:
|
||||
if f != filtNone:
|
||||
if hintCodeBegin in gNotes:
|
||||
rawMessage(hintCodeBegin, [])
|
||||
msgWriteln(result.s)
|
||||
rawMessage(hintCodeEnd, [])
|
||||
|
||||
proc evalPipe(p: var TParsers, n: PNode, filename: string,
|
||||
start: PLLStream): PLLStream =
|
||||
proc evalPipe(p: var TParsers, n: PNode, filename: string,
|
||||
start: PLLStream): PLLStream =
|
||||
result = start
|
||||
if n.kind == nkEmpty: return
|
||||
if n.kind == nkEmpty: return
|
||||
if n.kind == nkInfix and n.sons[0].kind == nkIdent and
|
||||
identEq(n.sons[0].ident, "|"):
|
||||
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)
|
||||
else:
|
||||
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
|
||||
p.skin = skinStandard
|
||||
let filename = fileIdx.toFullPathConsiderDirty
|
||||
@@ -172,6 +172,6 @@ proc openParsers(p: var TParsers, fileIdx: int32, inputstream: PLLStream) =
|
||||
parser.openParser(p.parser, fileIdx, s, false)
|
||||
of skinStrongSpaces:
|
||||
parser.openParser(p.parser, fileIdx, s, true)
|
||||
|
||||
|
||||
proc closeParsers(p: var TParsers) =
|
||||
parser.closeParser(p.parser)
|
||||
|
||||
@@ -12,10 +12,10 @@ import
|
||||
|
||||
{.compile: "../tinyc/libtcc.c".}
|
||||
|
||||
proc tinyCErrorHandler(closure: pointer, msg: cstring) {.cdecl.} =
|
||||
proc tinyCErrorHandler(closure: pointer, msg: cstring) {.cdecl.} =
|
||||
rawMessage(errGenerated, $msg)
|
||||
|
||||
proc initTinyCState: PccState =
|
||||
|
||||
proc initTinyCState: PccState =
|
||||
result = openCCState()
|
||||
setErrorFunc(result, nil, tinyCErrorHandler)
|
||||
|
||||
@@ -23,25 +23,25 @@ var
|
||||
gTinyC = initTinyCState()
|
||||
libIncluded = false
|
||||
|
||||
proc addFile(filename: string) =
|
||||
proc addFile(filename: string) =
|
||||
if addFile(gTinyC, filename) != 0'i32:
|
||||
rawMessage(errCannotOpenFile, filename)
|
||||
|
||||
proc setupEnvironment =
|
||||
proc setupEnvironment =
|
||||
when defined(amd64):
|
||||
defineSymbol(gTinyC, "__x86_64__", nil)
|
||||
elif defined(i386):
|
||||
defineSymbol(gTinyC, "__i386__", nil)
|
||||
defineSymbol(gTinyC, "__i386__", nil)
|
||||
when defined(linux):
|
||||
defineSymbol(gTinyC, "__linux__", nil)
|
||||
defineSymbol(gTinyC, "__linux", nil)
|
||||
var nimrodDir = getPrefixDir()
|
||||
|
||||
addIncludePath(gTinyC, libpath)
|
||||
when defined(windows):
|
||||
when defined(windows):
|
||||
addSysincludePath(gTinyC, nimrodDir / "tinyc/win32/include")
|
||||
addSysincludePath(gTinyC, nimrodDir / "tinyc/include")
|
||||
when defined(windows):
|
||||
when defined(windows):
|
||||
defineSymbol(gTinyC, "_WIN32", nil)
|
||||
# we need Mingw's headers too:
|
||||
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\dllmain.o")
|
||||
addFile(nimrodDir / r"tinyc\win32\libtcc1.o")
|
||||
|
||||
|
||||
#addFile(nimrodDir / r"tinyc\win32\lib\crt1.c")
|
||||
#addFile(nimrodDir / r"tinyc\lib\libtcc1.c")
|
||||
else:
|
||||
@@ -62,12 +62,12 @@ proc setupEnvironment =
|
||||
when defined(amd64):
|
||||
addSysincludePath(gTinyC, "/usr/include/x86_64-linux-gnu")
|
||||
|
||||
proc compileCCode*(ccode: string) =
|
||||
proc compileCCode*(ccode: string) =
|
||||
if not libIncluded:
|
||||
libIncluded = true
|
||||
setupEnvironment()
|
||||
discard compileString(gTinyC, ccode)
|
||||
|
||||
|
||||
proc run*(args: string) =
|
||||
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
|
||||
|
||||
@@ -9,36 +9,36 @@
|
||||
|
||||
# Implements a table from trees to trees. Does structural equivalence checking.
|
||||
|
||||
import
|
||||
import
|
||||
hashes, ast, astalgo, types
|
||||
|
||||
proc hashTree(n: PNode): Hash =
|
||||
if n == nil: return
|
||||
proc hashTree(n: PNode): Hash =
|
||||
if n == nil: return
|
||||
result = ord(n.kind)
|
||||
case n.kind
|
||||
of nkEmpty, nkNilLit, nkType:
|
||||
of nkEmpty, nkNilLit, nkType:
|
||||
discard
|
||||
of nkIdent:
|
||||
of nkIdent:
|
||||
result = result !& n.ident.h
|
||||
of nkSym:
|
||||
result = result !& n.sym.name.h
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
if (n.intVal >= low(int)) and (n.intVal <= high(int)):
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
if (n.intVal >= low(int)) and (n.intVal <= high(int)):
|
||||
result = result !& int(n.intVal)
|
||||
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)
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
if not n.strVal.isNil:
|
||||
result = result !& hash(n.strVal)
|
||||
else:
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
else:
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
result = result !& hashTree(n.sons[i])
|
||||
|
||||
proc treesEquivalent(a, b: PNode): bool =
|
||||
if a == b:
|
||||
|
||||
proc treesEquivalent(a, b: PNode): bool =
|
||||
if a == b:
|
||||
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
|
||||
of nkEmpty, nkNilLit, nkType: result = true
|
||||
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 nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
|
||||
of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
|
||||
else:
|
||||
if sonsLen(a) == sonsLen(b):
|
||||
for i in countup(0, sonsLen(a) - 1):
|
||||
if not treesEquivalent(a.sons[i], b.sons[i]): return
|
||||
else:
|
||||
if sonsLen(a) == sonsLen(b):
|
||||
for i in countup(0, sonsLen(a) - 1):
|
||||
if not treesEquivalent(a.sons[i], b.sons[i]): return
|
||||
result = true
|
||||
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)
|
||||
while t.data[h].key != nil:
|
||||
if (t.data[h].h == k) and treesEquivalent(t.data[h].key, key):
|
||||
while t.data[h].key != nil:
|
||||
if (t.data[h].h == k) and treesEquivalent(t.data[h].key, key):
|
||||
return h
|
||||
h = nextTry(h, high(t.data))
|
||||
result = -1
|
||||
|
||||
proc nodeTableGet*(t: TNodeTable, key: PNode): int =
|
||||
proc nodeTableGet*(t: TNodeTable, key: PNode): int =
|
||||
var index = nodeTableRawGet(t, hashTree(key), key)
|
||||
if index >= 0: result = t.data[index].val
|
||||
else: result = low(int)
|
||||
|
||||
proc nodeTableRawInsert(data: var TNodePairSeq, k: Hash, key: PNode,
|
||||
val: int) =
|
||||
|
||||
proc nodeTableRawInsert(data: var TNodePairSeq, k: Hash, key: PNode,
|
||||
val: int) =
|
||||
var h: Hash = k and high(data)
|
||||
while data[h].key != nil: h = nextTry(h, high(data))
|
||||
assert(data[h].key == nil)
|
||||
@@ -75,35 +75,35 @@ proc nodeTableRawInsert(data: var TNodePairSeq, k: Hash, key: PNode,
|
||||
data[h].key = key
|
||||
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 k: Hash = hashTree(key)
|
||||
var index = nodeTableRawGet(t, k, key)
|
||||
if index >= 0:
|
||||
if index >= 0:
|
||||
assert(t.data[index].key != nil)
|
||||
t.data[index].val = val
|
||||
else:
|
||||
if mustRehash(len(t.data), t.counter):
|
||||
else:
|
||||
if mustRehash(len(t.data), t.counter):
|
||||
newSeq(n, len(t.data) * GrowthFactor)
|
||||
for i in countup(0, high(t.data)):
|
||||
if t.data[i].key != nil:
|
||||
for i in countup(0, high(t.data)):
|
||||
if t.data[i].key != nil:
|
||||
nodeTableRawInsert(n, t.data[i].h, t.data[i].key, t.data[i].val)
|
||||
swap(t.data, n)
|
||||
nodeTableRawInsert(t.data, k, key, val)
|
||||
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 k: Hash = hashTree(key)
|
||||
var index = nodeTableRawGet(t, k, key)
|
||||
if index >= 0:
|
||||
if index >= 0:
|
||||
assert(t.data[index].key != nil)
|
||||
result = t.data[index].val
|
||||
else:
|
||||
if mustRehash(len(t.data), t.counter):
|
||||
else:
|
||||
if mustRehash(len(t.data), t.counter):
|
||||
newSeq(n, len(t.data) * GrowthFactor)
|
||||
for i in countup(0, high(t.data)):
|
||||
if t.data[i].key != nil:
|
||||
for i in countup(0, high(t.data)):
|
||||
if t.data[i].key != nil:
|
||||
nodeTableRawInsert(n, t.data[i].h, t.data[i].key, t.data[i].val)
|
||||
swap(t.data, n)
|
||||
nodeTableRawInsert(t.data, k, key, val)
|
||||
|
||||
@@ -9,5 +9,4 @@ Mario Ray Mahardhika
|
||||
Alex Mitchell
|
||||
Dominik Picheta
|
||||
Jonathan Plona
|
||||
Alexander R<EFBFBD>dseth
|
||||
|
||||
Alexander Rødseth
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
Nim -- a Compiler for Nim. http://nim-lang.org/
|
||||
|
||||
Copyright (C) 2006-2015 Andreas Rumpf. All rights reserved.
|
||||
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
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
|
||||
THE SOFTWARE.
|
||||
|
||||
[ MIT license: http://www.opensource.org/licenses/mit-license.php ]
|
||||
[ MIT license: http://www.opensource.org/licenses/mit-license.php ]
|
||||
|
||||
@@ -20,7 +20,7 @@ English word To use Notes
|
||||
------------------- ------------ --------------------------------------
|
||||
initialize initT ``init`` is used to create a
|
||||
value type ``T``
|
||||
new newP ``new`` is used to create a
|
||||
new newP ``new`` is used to create a
|
||||
reference type ``P``
|
||||
find find should return the position where
|
||||
something was found; for a bool result
|
||||
|
||||
@@ -28,7 +28,7 @@ The documentation consists of several documents:
|
||||
builtin templating system.
|
||||
|
||||
- | `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.
|
||||
|
||||
- | `Internal documentation <intern.html>`_
|
||||
|
||||
402
doc/endb.txt
402
doc/endb.txt
@@ -1,203 +1,203 @@
|
||||
==============================================
|
||||
Embedded Nim Debugger (ENDB) User Guide
|
||||
==============================================
|
||||
|
||||
:Author: Andreas Rumpf
|
||||
:Version: |nimversion|
|
||||
|
||||
.. contents::
|
||||
|
||||
==============================================
|
||||
Embedded Nim Debugger (ENDB) User Guide
|
||||
==============================================
|
||||
|
||||
:Author: Andreas Rumpf
|
||||
:Version: |nimversion|
|
||||
|
||||
.. contents::
|
||||
|
||||
**WARNING**: ENDB is not maintained anymore! Please help if you're interested
|
||||
in this tool.
|
||||
|
||||
Nim comes with a platform independent debugger -
|
||||
the Embedded Nim Debugger (ENDB). The debugger is
|
||||
*embedded* into your executable if it has been
|
||||
compiled with the ``--debugger:on`` command line option.
|
||||
This also defines the conditional symbol ``ENDB`` for you.
|
||||
|
||||
Note: You must not compile your program with the ``--app:gui``
|
||||
command line option because then there would be no console
|
||||
available for the debugger.
|
||||
|
||||
If you start your program the debugger will immediately show
|
||||
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
|
||||
underscores and case do not matter. Optional components of a command
|
||||
are listed in brackets ``[...]`` here.
|
||||
|
||||
|
||||
General Commands
|
||||
================
|
||||
|
||||
``h``, ``help``
|
||||
Display a quick reference of the possible commands.
|
||||
|
||||
``q``, ``quit``
|
||||
Quit the debugger and the program.
|
||||
|
||||
<ENTER>
|
||||
(Without any typed command) repeat the previous debugger command.
|
||||
If there is no previous command, ``step_into`` is assumed.
|
||||
|
||||
Executing Commands
|
||||
==================
|
||||
|
||||
``s``, ``step_into``
|
||||
Single step, stepping into routine calls.
|
||||
|
||||
``n``, ``step_over``
|
||||
Single step, without stepping into routine calls.
|
||||
|
||||
``f``, ``skip_current``
|
||||
Continue execution until the current routine finishes.
|
||||
|
||||
``c``, ``continue``
|
||||
Continue execution until the next breakpoint.
|
||||
|
||||
``i``, ``ignore``
|
||||
Continue execution, ignore all breakpoints. This effectively quits
|
||||
the debugger and runs the program until it finishes.
|
||||
|
||||
|
||||
Breakpoint Commands
|
||||
===================
|
||||
|
||||
``b``, ``setbreak`` [fromline [toline]] [file]
|
||||
Set a new breakpoint for the given file
|
||||
and line numbers. If no file is given, the current execution point's
|
||||
filename is used. If the filename has no extension, ``.nim`` is
|
||||
appended for your convenience.
|
||||
If no line numbers are given, the current execution point's
|
||||
line is used. If both ``fromline`` and ``toline`` are given the
|
||||
breakpoint contains a line number range. Some examples if it is still
|
||||
unclear:
|
||||
|
||||
* ``b 12 15 thallo`` creates a breakpoint that
|
||||
will be triggered if the instruction pointer reaches one of the
|
||||
lines 12-15 in the file ``thallo.nim``.
|
||||
* ``b 12 thallo`` creates a breakpoint that
|
||||
will be triggered if the instruction pointer reaches the
|
||||
line 12 in the file ``thallo.nim``.
|
||||
* ``b 12`` creates a breakpoint that
|
||||
will be triggered if the instruction pointer reaches the
|
||||
line 12 in the current file.
|
||||
* ``b`` creates a breakpoint that
|
||||
will be triggered if the instruction pointer reaches the
|
||||
current line in the current file again.
|
||||
|
||||
``breakpoints``
|
||||
Display the entire breakpoint list.
|
||||
|
||||
``disable`` <identifier>
|
||||
Disable a breakpoint. It remains disabled until you turn it on again
|
||||
with the ``enable`` command.
|
||||
|
||||
``enable`` <identifier>
|
||||
Enable a breakpoint.
|
||||
|
||||
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
|
||||
necessary: A special pragma has been defined for this:
|
||||
|
||||
|
||||
The ``breakpoint`` pragma
|
||||
-------------------------
|
||||
|
||||
The ``breakpoint`` pragma is syntactically a statement. It can be used
|
||||
to mark the *following line* as a breakpoint:
|
||||
|
||||
.. code-block:: Nim
|
||||
write("1")
|
||||
{.breakpoint: "before_write_2".}
|
||||
write("2")
|
||||
|
||||
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
|
||||
if you leave it out.
|
||||
|
||||
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
|
||||
debugging.
|
||||
|
||||
|
||||
The ``watchpoint`` pragma
|
||||
-------------------------
|
||||
|
||||
The ``watchpoint`` pragma is syntactically a statement. It can be used
|
||||
to mark a location as a watchpoint:
|
||||
|
||||
.. code-block:: Nim
|
||||
var a: array [0..20, int]
|
||||
|
||||
{.watchpoint: a[3].}
|
||||
for i in 0 .. 20: a[i] = i
|
||||
|
||||
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
|
||||
contents and so locations that are not word sized may encounter false
|
||||
negatives in very rare cases.
|
||||
|
||||
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
|
||||
debugging.
|
||||
|
||||
Due to the primitive implementation watchpoints are even slower than
|
||||
breakpoints: After *every* executed Nim code line it is checked whether the
|
||||
location changed.
|
||||
|
||||
|
||||
Data Display Commands
|
||||
=====================
|
||||
|
||||
``e``, ``eval`` <exp>
|
||||
Evaluate the expression <exp>. Note that ENDB has no full-blown expression
|
||||
evaluator built-in. So expressions are limited:
|
||||
|
||||
* To display global variables prefix their names with their
|
||||
owning module: ``nim1.globalVar``
|
||||
* To display local variables or parameters just type in
|
||||
their name: ``localVar``. If you want to inspect variables that are not
|
||||
in the current stack frame, use the ``up`` or ``down`` command.
|
||||
|
||||
Unfortunately, only inspecting variables is possible at the moment. Maybe
|
||||
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.
|
||||
|
||||
Since displaying the whole data structures is often not needed and
|
||||
painfully slow, the debugger uses a *maximal display depth* concept for
|
||||
displaying.
|
||||
|
||||
You can alter the maximal display depth with the ``maxdisplay``
|
||||
command.
|
||||
|
||||
``maxdisplay`` <natural>
|
||||
Sets the maximal display depth to the given integer value. A value of 0
|
||||
means there is no maximal display depth. Default is 3.
|
||||
|
||||
``o``, ``out`` <filename> <exp>
|
||||
Evaluate the expression <exp> and store its string representation into a
|
||||
file named <filename>. If the file does not exist, it will be created,
|
||||
otherwise it will be opened for appending.
|
||||
|
||||
``w``, ``where``
|
||||
Display the current execution point.
|
||||
|
||||
``u``, ``up``
|
||||
Go up in the call stack.
|
||||
|
||||
``d``, ``down``
|
||||
Go down in the call stack.
|
||||
|
||||
``stackframe`` [file]
|
||||
Displays the content of the current stack frame in ``stdout`` or
|
||||
appends it to the file, depending on whether a file is given.
|
||||
|
||||
``callstack``
|
||||
Display the entire call stack (but not its content).
|
||||
|
||||
``l``, ``locals``
|
||||
Display the available local variables in the current stack frame.
|
||||
|
||||
``g``, ``globals``
|
||||
Display all the global variables that are available for inspection.
|
||||
|
||||
Nim comes with a platform independent debugger -
|
||||
the Embedded Nim Debugger (ENDB). The debugger is
|
||||
*embedded* into your executable if it has been
|
||||
compiled with the ``--debugger:on`` command line option.
|
||||
This also defines the conditional symbol ``ENDB`` for you.
|
||||
|
||||
Note: You must not compile your program with the ``--app:gui``
|
||||
command line option because then there would be no console
|
||||
available for the debugger.
|
||||
|
||||
If you start your program the debugger will immediately show
|
||||
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
|
||||
underscores and case do not matter. Optional components of a command
|
||||
are listed in brackets ``[...]`` here.
|
||||
|
||||
|
||||
General Commands
|
||||
================
|
||||
|
||||
``h``, ``help``
|
||||
Display a quick reference of the possible commands.
|
||||
|
||||
``q``, ``quit``
|
||||
Quit the debugger and the program.
|
||||
|
||||
<ENTER>
|
||||
(Without any typed command) repeat the previous debugger command.
|
||||
If there is no previous command, ``step_into`` is assumed.
|
||||
|
||||
Executing Commands
|
||||
==================
|
||||
|
||||
``s``, ``step_into``
|
||||
Single step, stepping into routine calls.
|
||||
|
||||
``n``, ``step_over``
|
||||
Single step, without stepping into routine calls.
|
||||
|
||||
``f``, ``skip_current``
|
||||
Continue execution until the current routine finishes.
|
||||
|
||||
``c``, ``continue``
|
||||
Continue execution until the next breakpoint.
|
||||
|
||||
``i``, ``ignore``
|
||||
Continue execution, ignore all breakpoints. This effectively quits
|
||||
the debugger and runs the program until it finishes.
|
||||
|
||||
|
||||
Breakpoint Commands
|
||||
===================
|
||||
|
||||
``b``, ``setbreak`` [fromline [toline]] [file]
|
||||
Set a new breakpoint for the given file
|
||||
and line numbers. If no file is given, the current execution point's
|
||||
filename is used. If the filename has no extension, ``.nim`` is
|
||||
appended for your convenience.
|
||||
If no line numbers are given, the current execution point's
|
||||
line is used. If both ``fromline`` and ``toline`` are given the
|
||||
breakpoint contains a line number range. Some examples if it is still
|
||||
unclear:
|
||||
|
||||
* ``b 12 15 thallo`` creates a breakpoint that
|
||||
will be triggered if the instruction pointer reaches one of the
|
||||
lines 12-15 in the file ``thallo.nim``.
|
||||
* ``b 12 thallo`` creates a breakpoint that
|
||||
will be triggered if the instruction pointer reaches the
|
||||
line 12 in the file ``thallo.nim``.
|
||||
* ``b 12`` creates a breakpoint that
|
||||
will be triggered if the instruction pointer reaches the
|
||||
line 12 in the current file.
|
||||
* ``b`` creates a breakpoint that
|
||||
will be triggered if the instruction pointer reaches the
|
||||
current line in the current file again.
|
||||
|
||||
``breakpoints``
|
||||
Display the entire breakpoint list.
|
||||
|
||||
``disable`` <identifier>
|
||||
Disable a breakpoint. It remains disabled until you turn it on again
|
||||
with the ``enable`` command.
|
||||
|
||||
``enable`` <identifier>
|
||||
Enable a breakpoint.
|
||||
|
||||
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
|
||||
necessary: A special pragma has been defined for this:
|
||||
|
||||
|
||||
The ``breakpoint`` pragma
|
||||
-------------------------
|
||||
|
||||
The ``breakpoint`` pragma is syntactically a statement. It can be used
|
||||
to mark the *following line* as a breakpoint:
|
||||
|
||||
.. code-block:: Nim
|
||||
write("1")
|
||||
{.breakpoint: "before_write_2".}
|
||||
write("2")
|
||||
|
||||
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
|
||||
if you leave it out.
|
||||
|
||||
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
|
||||
debugging.
|
||||
|
||||
|
||||
The ``watchpoint`` pragma
|
||||
-------------------------
|
||||
|
||||
The ``watchpoint`` pragma is syntactically a statement. It can be used
|
||||
to mark a location as a watchpoint:
|
||||
|
||||
.. code-block:: Nim
|
||||
var a: array [0..20, int]
|
||||
|
||||
{.watchpoint: a[3].}
|
||||
for i in 0 .. 20: a[i] = i
|
||||
|
||||
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
|
||||
contents and so locations that are not word sized may encounter false
|
||||
negatives in very rare cases.
|
||||
|
||||
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
|
||||
debugging.
|
||||
|
||||
Due to the primitive implementation watchpoints are even slower than
|
||||
breakpoints: After *every* executed Nim code line it is checked whether the
|
||||
location changed.
|
||||
|
||||
|
||||
Data Display Commands
|
||||
=====================
|
||||
|
||||
``e``, ``eval`` <exp>
|
||||
Evaluate the expression <exp>. Note that ENDB has no full-blown expression
|
||||
evaluator built-in. So expressions are limited:
|
||||
|
||||
* To display global variables prefix their names with their
|
||||
owning module: ``nim1.globalVar``
|
||||
* To display local variables or parameters just type in
|
||||
their name: ``localVar``. If you want to inspect variables that are not
|
||||
in the current stack frame, use the ``up`` or ``down`` command.
|
||||
|
||||
Unfortunately, only inspecting variables is possible at the moment. Maybe
|
||||
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.
|
||||
|
||||
Since displaying the whole data structures is often not needed and
|
||||
painfully slow, the debugger uses a *maximal display depth* concept for
|
||||
displaying.
|
||||
|
||||
You can alter the maximal display depth with the ``maxdisplay``
|
||||
command.
|
||||
|
||||
``maxdisplay`` <natural>
|
||||
Sets the maximal display depth to the given integer value. A value of 0
|
||||
means there is no maximal display depth. Default is 3.
|
||||
|
||||
``o``, ``out`` <filename> <exp>
|
||||
Evaluate the expression <exp> and store its string representation into a
|
||||
file named <filename>. If the file does not exist, it will be created,
|
||||
otherwise it will be opened for appending.
|
||||
|
||||
``w``, ``where``
|
||||
Display the current execution point.
|
||||
|
||||
``u``, ``up``
|
||||
Go up in the call stack.
|
||||
|
||||
``d``, ``down``
|
||||
Go down in the call stack.
|
||||
|
||||
``stackframe`` [file]
|
||||
Displays the content of the current stack frame in ``stdout`` or
|
||||
appends it to the file, depending on whether a file is given.
|
||||
|
||||
``callstack``
|
||||
Display the entire call stack (but not its content).
|
||||
|
||||
``l``, ``locals``
|
||||
Display the available local variables in the current stack frame.
|
||||
|
||||
``g``, ``globals``
|
||||
Display all the global variables that are available for inspection.
|
||||
|
||||
46
doc/estp.txt
46
doc/estp.txt
@@ -1,30 +1,30 @@
|
||||
===================================================
|
||||
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:
|
||||
===================================================
|
||||
Embedded Stack Trace Profiler (ESTP) User Guide
|
||||
===================================================
|
||||
|
||||
* 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
|
||||
* import the ``nimprof`` module
|
||||
* 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.
|
||||
|
||||
The setting ``--profiler:on`` defines the conditional symbol ``profiler``.
|
||||
|
||||
After your program has finished the profiler will create a
|
||||
|
||||
The setting ``--profiler:on`` defines the conditional symbol ``profiler``.
|
||||
|
||||
After your program has finished the profiler will create a
|
||||
file ``profile_results.txt`` containing the profiling results.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ Memory profiler
|
||||
===============
|
||||
|
||||
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:
|
||||
|
||||
* 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
|
||||
* 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.
|
||||
|
||||
|
||||
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
|
||||
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 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
|
||||
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.
|
||||
|
||||
::
|
||||
|
||||
@@ -4,8 +4,8 @@ Source Code Filters
|
||||
|
||||
.. contents::
|
||||
|
||||
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
|
||||
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
|
||||
systems or preprocessors.
|
||||
|
||||
To use a filter for a source file the *shebang* notation is used::
|
||||
@@ -52,7 +52,7 @@ Parameters and their defaults:
|
||||
|
||||
``sub: string = ""``
|
||||
the substring that is searched for
|
||||
|
||||
|
||||
``by: string = ""``
|
||||
the string the substring is replaced with
|
||||
|
||||
@@ -71,7 +71,7 @@ Parameters and their defaults:
|
||||
|
||||
``leading: bool = true``
|
||||
strip leading whitespace
|
||||
|
||||
|
||||
``trailing: bool = true``
|
||||
strip trailing whitespace
|
||||
|
||||
@@ -89,16 +89,16 @@ Parameters and their defaults:
|
||||
|
||||
``metaChar: char = '#'``
|
||||
prefix for a line that contains Nim code
|
||||
|
||||
|
||||
``subsChar: char = '$'``
|
||||
prefix for a Nim expression within a template line
|
||||
|
||||
|
||||
``conc: string = " & "``
|
||||
the operation for concatenation
|
||||
|
||||
|
||||
``emit: string = "result.add"``
|
||||
the operation to emit a string literal
|
||||
|
||||
|
||||
``toString: string = "$"``
|
||||
the operation that is applied to each expression
|
||||
|
||||
@@ -106,7 +106,7 @@ Example::
|
||||
|
||||
#! stdtmpl | standard
|
||||
#proc generateHTMLPage(title, currentTab, content: string,
|
||||
# tabs: openArray[string]): string =
|
||||
# tabs: openArray[string]): string =
|
||||
# result = ""
|
||||
<head><title>$title</title></head>
|
||||
<body>
|
||||
@@ -114,7 +114,7 @@ Example::
|
||||
<ul>
|
||||
#for tab in items(tabs):
|
||||
#if currentTab == tab:
|
||||
<li><a id="selected"
|
||||
<li><a id="selected"
|
||||
#else:
|
||||
<li><a
|
||||
#end if
|
||||
@@ -132,11 +132,11 @@ The filter transforms this into:
|
||||
|
||||
.. code-block:: nim
|
||||
proc generateHTMLPage(title, currentTab, content: string,
|
||||
tabs: openArray[string]): string =
|
||||
tabs: openArray[string]): string =
|
||||
result = ""
|
||||
result.add("<head><title>" & $(title) & "</title></head>\n" &
|
||||
"<body>\n" &
|
||||
" <div id=\"menu\">\n" &
|
||||
result.add("<head><title>" & $(title) & "</title></head>\n" &
|
||||
"<body>\n" &
|
||||
" <div id=\"menu\">\n" &
|
||||
" <ul>\n")
|
||||
for tab in items(tabs):
|
||||
if currentTab == tab:
|
||||
@@ -146,17 +146,17 @@ The filter transforms this into:
|
||||
#end
|
||||
result.add(" href=\"" & $(tab) & ".html\">" & $(tab) & "</a></li>\n")
|
||||
#end
|
||||
result.add(" </ul>\n" &
|
||||
" </div>\n" &
|
||||
" <div id=\"content\">\n" &
|
||||
" " & $(content) & "\n" &
|
||||
" A dollar: $.\n" &
|
||||
" </div>\n" &
|
||||
result.add(" </ul>\n" &
|
||||
" </div>\n" &
|
||||
" <div id=\"content\">\n" &
|
||||
" " & $(content) & "\n" &
|
||||
" A dollar: $.\n" &
|
||||
" </div>\n" &
|
||||
"</body>\n")
|
||||
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
#proc writeHTMLPage(f: File, title, currentTab, content: string,
|
||||
# tabs: openArray[string]) =
|
||||
# tabs: openArray[string]) =
|
||||
<head><title>$title</title></head>
|
||||
<body>
|
||||
<div id="menu">
|
||||
<ul>
|
||||
#for tab in items(tabs):
|
||||
#if currentTab == tab:
|
||||
<li><a id="selected"
|
||||
<li><a id="selected"
|
||||
#else:
|
||||
<li><a
|
||||
#end if
|
||||
|
||||
28
doc/gc.txt
28
doc/gc.txt
@@ -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
|
||||
compiler analyses the types for their possibility to build cycles, but often
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ Realtime support
|
||||
================
|
||||
|
||||
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:
|
||||
|
||||
.. 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.
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
possible (and common) that the work is nevertheless not evenly distributed
|
||||
as each call to ``new`` can trigger the GC and thus take ``MaxPause``
|
||||
GC run tries to not take longer than ``MaxPause`` time. However, it is
|
||||
possible (and common) that the work is nevertheless not evenly distributed
|
||||
as each call to ``new`` can trigger the GC and thus take ``MaxPause``
|
||||
time.
|
||||
|
||||
(2) GC_step Mode
|
||||
|
||||
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
|
||||
bind all GC activity to a ``GC_step`` call, deactivate the GC with
|
||||
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
|
||||
``GC_disable`` at program startup.
|
||||
|
||||
These procs provide a "best effort" realtime guarantee; in particular the
|
||||
@@ -87,7 +87,7 @@ is triggered.
|
||||
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):
|
||||
|
||||
1) ``QueryPerformanceCounter`` and ``QueryPerformanceFrequency`` on Windows.
|
||||
@@ -95,7 +95,7 @@ implementation):
|
||||
3) ``gettimeofday`` on Posix systems.
|
||||
|
||||
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
|
||||
@@ -106,7 +106,7 @@ later versions of the collector.
|
||||
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
|
||||
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
|
||||
|
||||
@@ -22,8 +22,8 @@ Path Purpose
|
||||
``bin`` generated binary files
|
||||
``build`` generated C code for the installation
|
||||
``compiler`` the Nim compiler itself; note that this
|
||||
code has been translated from a bootstrapping
|
||||
version written in Pascal, so the code is **not**
|
||||
code has been translated from a bootstrapping
|
||||
version written in Pascal, so the code is **not**
|
||||
a poster child of good Nim code
|
||||
``config`` configuration files for Nim
|
||||
``dist`` additional packages for the distribution
|
||||
@@ -38,8 +38,8 @@ Path Purpose
|
||||
Bootstrapping the compiler
|
||||
==========================
|
||||
|
||||
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
|
||||
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
|
||||
been rewritten in Nim too.
|
||||
|
||||
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
|
||||
======================
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
examples how the AST represents each syntactic structure.
|
||||
examples how the AST represents each syntactic structure.
|
||||
|
||||
|
||||
How the RTL is compiled
|
||||
@@ -202,7 +202,7 @@ Compilation cache
|
||||
=================
|
||||
|
||||
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
|
||||
program*.
|
||||
|
||||
@@ -214,31 +214,31 @@ Methods and type converters
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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
|
||||
the new method into account. So the dispatching logic is only completely known
|
||||
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:
|
||||
|
||||
.. code-block:: nim
|
||||
# module A
|
||||
converter toBool(x: int): bool =
|
||||
result = x != 0
|
||||
|
||||
|
||||
.. code-block:: nim
|
||||
# module B
|
||||
import A
|
||||
|
||||
|
||||
if 1:
|
||||
echo "ugly, but should work"
|
||||
|
||||
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
|
||||
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*
|
||||
when the ROD file is read.
|
||||
|
||||
@@ -266,7 +266,7 @@ Backend issues
|
||||
- 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
|
||||
Gtk2 module...) is compiled with dead code elimination turned on. So none
|
||||
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,
|
||||
``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``.
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
it is referred from which ultimately means the main module and putting
|
||||
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
|
||||
which can only be removed by recompiling a project with the compilation cache
|
||||
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".
|
||||
|
||||
|
||||
@@ -394,7 +394,7 @@ Consider this example:
|
||||
# r is on the stack
|
||||
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:
|
||||
|
||||
.. code-block:: C
|
||||
@@ -422,7 +422,7 @@ Design
|
||||
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
|
||||
``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
|
||||
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.
|
||||
|
||||
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
|
||||
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
|
||||
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.
|
||||
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.
|
||||
|
||||
|
||||
Example code:
|
||||
|
||||
.. code-block:: nim
|
||||
proc add(x: int): proc (y: int): int {.closure.} =
|
||||
return proc (y: int): int =
|
||||
proc add(x: int): proc (y: int): int {.closure.} =
|
||||
return proc (y: int): int =
|
||||
return x + y
|
||||
|
||||
var add2 = add(2)
|
||||
@@ -458,16 +458,16 @@ This should produce roughly this code:
|
||||
type
|
||||
PEnv = ref object
|
||||
x: int # data
|
||||
|
||||
proc anon(y: int, c: PClosure): int =
|
||||
|
||||
proc anon(y: int, c: PClosure): int =
|
||||
return y + c.x
|
||||
|
||||
|
||||
proc add(x: int): tuple[prc, data] =
|
||||
var env: PEnv
|
||||
new env
|
||||
env.x = x
|
||||
result = (anon, env)
|
||||
|
||||
|
||||
var add2 = add(2)
|
||||
let tmp = if add2.data == nil: add2.prc(5) else: add2.prc(5, add2.data)
|
||||
echo tmp
|
||||
@@ -476,9 +476,9 @@ This should produce roughly this code:
|
||||
Beware of nesting:
|
||||
|
||||
.. code-block:: nim
|
||||
proc add(x: int): proc (y: int): proc (z: int): int {.closure.} {.closure.} =
|
||||
return lamba (y: int): proc (z: int): int {.closure.} =
|
||||
return lambda (z: int): int =
|
||||
proc add(x: int): proc (y: int): proc (z: int): int {.closure.} {.closure.} =
|
||||
return lamba (y: int): proc (z: int): int {.closure.} =
|
||||
return lambda (z: int): int =
|
||||
return x + y + z
|
||||
|
||||
var add24 = add(2)(4)
|
||||
@@ -490,26 +490,26 @@ This should produce roughly this code:
|
||||
type
|
||||
PEnvX = ref object
|
||||
x: int # data
|
||||
|
||||
|
||||
PEnvY = ref object
|
||||
y: int
|
||||
ex: PEnvX
|
||||
|
||||
|
||||
proc lambdaZ(z: int, ey: PEnvY): int =
|
||||
return ey.ex.x + ey.y + z
|
||||
|
||||
|
||||
proc lambdaY(y: int, ex: PEnvX): tuple[prc, data: PEnvY] =
|
||||
var ey: PEnvY
|
||||
new ey
|
||||
ey.y = y
|
||||
ey.ex = ex
|
||||
result = (lambdaZ, ey)
|
||||
|
||||
|
||||
proc add(x: int): tuple[prc, data: PEnvX] =
|
||||
var ex: PEnvX
|
||||
ex.x = x
|
||||
result = (labmdaY, ex)
|
||||
|
||||
|
||||
var tmp = add(2)
|
||||
var tmp2 = tmp.fn(4, tmp.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.
|
||||
More useful is escape analysis and stack allocation of the environment,
|
||||
More useful is escape analysis and stack allocation of the environment,
|
||||
however.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -532,21 +532,21 @@ Accumulator
|
||||
-----------
|
||||
|
||||
.. code-block:: nim
|
||||
proc getAccumulator(start: int): proc (): int {.closure} =
|
||||
proc getAccumulator(start: int): proc (): int {.closure} =
|
||||
var i = start
|
||||
return lambda: int =
|
||||
return lambda: int =
|
||||
inc i
|
||||
return i
|
||||
|
||||
|
||||
proc p =
|
||||
var delta = 7
|
||||
proc accumulator(start: int): proc(): int =
|
||||
var x = start-1
|
||||
result = proc (): int =
|
||||
result = proc (): int =
|
||||
x = x + delta
|
||||
inc delta
|
||||
return x
|
||||
|
||||
|
||||
var a = accumulator(3)
|
||||
var b = accumulator(4)
|
||||
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
|
||||
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
|
||||
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
|
||||
backend somehow. We deal with this by modifying ``s.ast[paramPos]`` to contain
|
||||
the formal hidden parameter, but not ``s.typ``!
|
||||
|
||||
@@ -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
|
||||
not consumed. It will be consumed in the following rule.
|
||||
|
||||
The ``|``, ``/`` symbols are used to mark alternatives and have the lowest
|
||||
precedence. ``/`` is the ordered choice that requires the parser to try the
|
||||
The ``|``, ``/`` symbols are used to mark alternatives and have the lowest
|
||||
precedence. ``/`` is the ordered choice that requires the parser to try the
|
||||
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
|
||||
UPPERCASE. Verbatim terminal symbols (including keywords) are quoted
|
||||
with ``'``. An example::
|
||||
|
||||
ifStmt = 'if' expr ':' stmts ('elif' expr ':' stmts)* ('else' stmts)?
|
||||
|
||||
|
||||
The binary ``^*`` operator is used as a shorthand for 0 or more occurrences
|
||||
separated by its second argument; likewise ``^+`` means 1 or more
|
||||
occurrences: ``a ^+ b`` is short for ``a (b a)*``
|
||||
separated by its second argument; likewise ``^+`` means 1 or more
|
||||
occurrences: ``a ^+ b`` is short for ``a (b a)*``
|
||||
and ``a ^* b`` is short for ``(a (b a)*)?``. Example::
|
||||
|
||||
arrayConstructor = '[' expr ^* ',' ']'
|
||||
|
||||
@@ -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
|
||||
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_
|
||||
for details.
|
||||
for details.
|
||||
|
||||
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
|
||||
|
||||
@@ -5,7 +5,7 @@ Exception tracking
|
||||
------------------
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: nim
|
||||
@@ -24,7 +24,7 @@ An empty ``raises`` list (``raises: []``) means that no exception may be raised:
|
||||
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:
|
||||
|
||||
.. code-block:: nim
|
||||
@@ -35,7 +35,7 @@ compatibility:
|
||||
|
||||
proc p(x: string) =
|
||||
raise newException(OSError, "OS")
|
||||
|
||||
|
||||
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
|
||||
thus any exception unless ``T`` has an explicit ``raises`` list.
|
||||
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.
|
||||
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
|
||||
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
|
||||
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
|
||||
declaration or an ``importc`` pragma) is assumed to
|
||||
3. Every call to a proc ``q`` which has an unknown body (due to a forward
|
||||
declaration or an ``importc`` pragma) is assumed to
|
||||
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.
|
||||
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.
|
||||
|
||||
Rules 1-2 ensure the following works:
|
||||
Rules 1-2 ensure the following works:
|
||||
|
||||
.. code-block:: nim
|
||||
proc noRaise(x: proc()) {.raises: [].} =
|
||||
# unknown call that might raise anything, but valid:
|
||||
x()
|
||||
|
||||
|
||||
proc doRaise() {.raises: [IOError].} =
|
||||
raise newException(IOError, "IO")
|
||||
|
||||
|
||||
proc use() {.raises: [].} =
|
||||
# doesn't compile! Can raise IOError!
|
||||
noRaise(doRaise)
|
||||
@@ -82,21 +82,21 @@ Tag tracking
|
||||
------------
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: nim
|
||||
type IO = object ## input/output effect
|
||||
proc readLine(): string {.tags: [IO].}
|
||||
|
||||
|
||||
proc no_IO_please() {.tags: [].} =
|
||||
# the compiler prevents this:
|
||||
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.
|
||||
|
||||
The inference for tag tracking is analogous to the inference for
|
||||
The inference for tag tracking is analogous to the inference for
|
||||
exception tracking.
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ Read/Write tracking
|
||||
|
||||
**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.
|
||||
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
@@ -36,7 +36,7 @@ With this notation we can now easily define the core of the grammar: A block of
|
||||
statements (simplified example)::
|
||||
|
||||
ifStmt = 'if' expr ':' stmt
|
||||
(IND{=} 'elif' expr ':' stmt)*
|
||||
(IND{=} 'elif' expr ':' stmt)*
|
||||
(IND{=} 'else' ':' stmt)?
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -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
|
||||
ending of the string literal is defined by the pattern ``"""[^"]``, so this:
|
||||
|
||||
.. code-block:: nim
|
||||
.. code-block:: nim
|
||||
""""long string within quotes""""
|
||||
|
||||
Produces::
|
||||
@@ -187,9 +187,9 @@ Raw string literals
|
||||
|
||||
Terminal symbol in the grammar: ``RSTR_LIT``.
|
||||
|
||||
There are also raw string literals that are preceded with the
|
||||
letter ``r`` (or ``R``) and are delimited by matching double quotes (just
|
||||
like ordinary string literals) and do not interpret the escape sequences.
|
||||
There are also raw string literals that are preceded with the
|
||||
letter ``r`` (or ``R``) and are delimited by matching double quotes (just
|
||||
like ordinary string literals) and do not interpret the escape sequences.
|
||||
This is especially convenient for regular expressions or Windows paths:
|
||||
|
||||
.. code-block:: nim
|
||||
@@ -201,21 +201,21 @@ To produce a single ``"`` within a raw string literal, it has to be doubled:
|
||||
.. code-block:: nim
|
||||
|
||||
r"a""b"
|
||||
|
||||
|
||||
Produces::
|
||||
|
||||
|
||||
a"b
|
||||
|
||||
``r""""`` is not possible with this notation, because the three leading
|
||||
quotes introduce a triple quoted string literal. ``r"""`` is the same
|
||||
as ``"""`` since triple quoted string literals do not interpret escape
|
||||
``r""""`` is not possible with this notation, because the three leading
|
||||
quotes introduce a triple quoted string literal. ``r"""`` is the same
|
||||
as ``"""`` since triple quoted string literals do not interpret escape
|
||||
sequences either.
|
||||
|
||||
|
||||
Generalized raw string literals
|
||||
-------------------------------
|
||||
|
||||
Terminal symbols in the grammar: ``GENERALIZED_STR_LIT``,
|
||||
Terminal symbols in the grammar: ``GENERALIZED_STR_LIT``,
|
||||
``GENERALIZED_TRIPLESTR_LIT``.
|
||||
|
||||
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 )*
|
||||
OCT_LIT = '0' ('o' | 'c' | 'C') octdigit ( ['_'] octdigit )*
|
||||
BIN_LIT = '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )*
|
||||
|
||||
|
||||
INT_LIT = HEX_LIT
|
||||
| DEC_LIT
|
||||
| OCT_LIT
|
||||
@@ -383,7 +383,7 @@ The following strings denote other tokens::
|
||||
` ( ) { } [ ] , ; [. .] {. .} (. .)
|
||||
|
||||
|
||||
The `slice`:idx: operator `..`:tok: takes precedence over other tokens that
|
||||
contain a dot: `{..}`:tok: are the three tokens `{`:tok:, `..`:tok:, `}`:tok:
|
||||
The `slice`:idx: operator `..`:tok: takes precedence over other tokens that
|
||||
contain a dot: `{..}`:tok: are the three tokens `{`:tok:, `..`:tok:, `}`:tok:
|
||||
and not the two tokens `{.`:tok:, `.}`:tok:.
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ model low level lockfree mechanisms:
|
||||
.. code-block:: nim
|
||||
var dummyLock {.compileTime.}: int
|
||||
var atomicCounter {.guard: dummyLock.}: int
|
||||
|
||||
|
||||
template atomicRead(x): expr =
|
||||
{.locks: [dummyLock].}:
|
||||
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
|
||||
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
|
||||
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:
|
||||
|
||||
.. code-block:: nim
|
||||
var a, b: TLock[2]
|
||||
var x: TLock[1]
|
||||
# invalid locking order: TLock[1] cannot be acquired before TLock[2]:
|
||||
{.locks: [x].}:
|
||||
{.locks: [x].}:
|
||||
{.locks: [a].}:
|
||||
...
|
||||
# valid locking order: TLock[2] acquired before TLock[1]:
|
||||
{.locks: [a].}:
|
||||
{.locks: [a].}:
|
||||
{.locks: [x].}:
|
||||
...
|
||||
|
||||
# invalid locking order: TLock[2] acquired before TLock[2]:
|
||||
{.locks: [a].}:
|
||||
{.locks: [a].}:
|
||||
{.locks: [b].}:
|
||||
...
|
||||
|
||||
# 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:
|
||||
|
||||
.. code-block:: nim
|
||||
proc p() {.locks: 3.} = discard
|
||||
proc p() {.locks: 3.} = discard
|
||||
|
||||
var a: TLock[4]
|
||||
{.locks: [a].}:
|
||||
@@ -195,6 +195,6 @@ This is essential so that procs can be called within a ``locks`` section:
|
||||
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.}``
|
||||
iff (M <= N).
|
||||
|
||||
@@ -81,8 +81,8 @@ A module alias can be introduced via the ``as`` keyword:
|
||||
|
||||
echo su.format("$1", "lalelu")
|
||||
|
||||
The original module name is then not accessible. The
|
||||
notations ``path/to/module`` or ``path.to.module`` or ``"path/to/module"``
|
||||
The original module name is then not accessible. The
|
||||
notations ``path/to/module`` or ``path.to.module`` or ``"path/to/module"``
|
||||
can be used to refer to a module in subdirectories:
|
||||
|
||||
.. code-block:: nim
|
||||
@@ -104,7 +104,7 @@ Likewise the following does not make sense as the name is ``strutils`` already:
|
||||
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
|
||||
full qualification:
|
||||
|
||||
@@ -115,8 +115,8 @@ full qualification:
|
||||
# always possible: full qualification:
|
||||
echo strutils.replace("abc", "a", "z")
|
||||
|
||||
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
|
||||
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
|
||||
in ``module``.
|
||||
|
||||
|
||||
@@ -134,15 +134,15 @@ modules don't need to import a module's dependencies:
|
||||
# module A
|
||||
import B
|
||||
export B.MyObject
|
||||
|
||||
|
||||
proc `$`*(x: MyObject): string = "my object"
|
||||
|
||||
|
||||
.. code-block:: nim
|
||||
# module C
|
||||
import A
|
||||
|
||||
# B.MyObject has been imported implicitly here:
|
||||
|
||||
# B.MyObject has been imported implicitly here:
|
||||
var x: MyObject
|
||||
echo($x)
|
||||
|
||||
@@ -185,7 +185,7 @@ following places:
|
||||
Module scope
|
||||
~~~~~~~~~~~~
|
||||
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.
|
||||
|
||||
If a module imports an identifier by two different modules, each occurrence of
|
||||
|
||||
@@ -120,7 +120,7 @@ current module:
|
||||
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
|
||||
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:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
|
||||
echo("abc".len) # is the same as echo(len("abc"))
|
||||
echo("abc".toUpper())
|
||||
echo({'a', 'b', 'c'}.card)
|
||||
@@ -143,11 +143,11 @@ See also: `Limitations of the method call syntax`_.
|
||||
Properties
|
||||
----------
|
||||
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:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
|
||||
type
|
||||
Socket* = ref object of RootObj
|
||||
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.} =
|
||||
## setter of hostAddr
|
||||
s.FHost = value
|
||||
|
||||
|
||||
proc host*(s: Socket): int {.inline.} =
|
||||
## getter of hostAddr
|
||||
s.FHost
|
||||
@@ -180,18 +180,18 @@ more argument in this case:
|
||||
.. code-block:: nim
|
||||
proc optarg(x: int, y: int = 0): int = x + y
|
||||
proc singlearg(x: int): int = 20*x
|
||||
|
||||
|
||||
echo optarg 1, " ", singlearg 2 # prints "1 40"
|
||||
|
||||
|
||||
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 y = 1.optarg optarg 8 # same thing as above, w/o the parenthesis
|
||||
assert x == y
|
||||
|
||||
The command invocation syntax also can't have complex expressions as arguments.
|
||||
For example: (`anonymous procs`_), ``if``, ``case`` or ``try``. The (`do
|
||||
notation`_) is limited, but usable for a single proc (see the example in the
|
||||
corresponding section). Function calls with no arguments still needs () to
|
||||
The command invocation syntax also can't have complex expressions as arguments.
|
||||
For example: (`anonymous procs`_), ``if``, ``case`` or ``try``. The (`do
|
||||
notation`_) is limited, but usable for a single proc (see the example in the
|
||||
corresponding section). Function calls with no arguments still needs () to
|
||||
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))
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -237,10 +237,10 @@ calls can use the ``do`` keyword:
|
||||
sort(cities) do (x,y: string) -> int:
|
||||
cmp(x.len, y.len)
|
||||
# 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
|
||||
|
||||
``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.
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
Thus they act more like keywords than like ordinary identifiers; unlike a
|
||||
keyword however, a redefinition may `shadow`:idx: the definition in
|
||||
Thus they act more like keywords than like ordinary identifiers; unlike a
|
||||
keyword however, a redefinition may `shadow`:idx: the definition in
|
||||
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
|
||||
to ``f``::
|
||||
@@ -342,11 +342,11 @@ returned value is an l-value and can be modified by the caller:
|
||||
|
||||
proc WriteAccessToG(): var int =
|
||||
result = g
|
||||
|
||||
|
||||
WriteAccessToG() = 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:
|
||||
|
||||
.. code-block:: nim
|
||||
@@ -354,7 +354,7 @@ used to access a location beyond its lifetime:
|
||||
var g = 0
|
||||
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
|
||||
iterator mpairs(a: var seq[string]): tuple[key: int, val: var string] =
|
||||
@@ -384,28 +384,28 @@ dispatch.
|
||||
x: int
|
||||
PlusExpr = ref object of Expression
|
||||
a, b: Expression
|
||||
|
||||
|
||||
method eval(e: Expression): int =
|
||||
# override this base method
|
||||
quit "to override!"
|
||||
|
||||
|
||||
method eval(e: Literal): int = return e.x
|
||||
|
||||
|
||||
method eval(e: PlusExpr): int =
|
||||
# watch out: relies on dynamic binding
|
||||
result = eval(e.a) + eval(e.b)
|
||||
|
||||
|
||||
proc newLit(x: int): Literal =
|
||||
new(result)
|
||||
result.x = x
|
||||
|
||||
|
||||
proc newPlus(a, b: Expression): PlusExpr =
|
||||
new(result)
|
||||
result.a = a
|
||||
result.b = b
|
||||
|
||||
echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
|
||||
|
||||
|
||||
In the example the constructors ``newLit`` and ``newPlus`` are procs
|
||||
because they should use static binding, but ``eval`` is a method because it
|
||||
requires dynamic binding.
|
||||
@@ -418,24 +418,24 @@ dispatching:
|
||||
Thing = ref object of RootObj
|
||||
Unit = ref object of Thing
|
||||
x: int
|
||||
|
||||
|
||||
method collide(a, b: Thing) {.inline.} =
|
||||
quit "to override!"
|
||||
|
||||
|
||||
method collide(a: Thing, b: Unit) {.inline.} =
|
||||
echo "1"
|
||||
|
||||
|
||||
method collide(a: Unit, b: Thing) {.inline.} =
|
||||
echo "2"
|
||||
|
||||
|
||||
var a, b: Unit
|
||||
new a
|
||||
new b
|
||||
collide(a, b) # output: 2
|
||||
|
||||
|
||||
Invocation of a multi-method cannot be ambiguous: collide 2 is preferred over
|
||||
collide 1 because the resolution works from left to right.
|
||||
Invocation of a multi-method cannot be ambiguous: collide 2 is preferred over
|
||||
collide 1 because the resolution works from left to right.
|
||||
In the example ``Unit, Thing`` is preferred over ``Thing, Unit``.
|
||||
|
||||
**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
|
||||
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
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
Implict items/pairs invocations
|
||||
@@ -498,11 +498,11 @@ ie. an ``items`` iterator is implicitly invoked:
|
||||
|
||||
.. code-block:: nim
|
||||
for x in [1,2,3]: echo x
|
||||
|
||||
|
||||
If the for loop has exactly 2 variables, a ``pairs`` iterator is implicitly
|
||||
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
|
||||
into account.
|
||||
|
||||
@@ -511,7 +511,7 @@ First class 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
|
||||
increase in code size. Inline iterators are second class citizens;
|
||||
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
|
||||
iterator count0(): int {.closure.} =
|
||||
yield 0
|
||||
|
||||
|
||||
iterator count2(): int {.closure.} =
|
||||
var x = 1
|
||||
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.
|
||||
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.
|
||||
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
|
||||
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
|
||||
a `collaborative tasking`:idx: system:
|
||||
|
||||
@@ -650,7 +650,7 @@ parameters of an outer factory proc:
|
||||
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`_):
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
@@ -34,7 +34,7 @@ templates and macros), depending on the desired effect:
|
||||
The following dot operators are available:
|
||||
|
||||
operator `.`
|
||||
------------
|
||||
------------
|
||||
This operator will be matched against both field accesses and method calls.
|
||||
|
||||
operator `.()`
|
||||
|
||||
@@ -176,8 +176,8 @@ The rules for compile-time computability are:
|
||||
1. Literals are compile-time computable.
|
||||
2. Type conversions are compile-time computable.
|
||||
3. Procedure calls of the form ``p(X)`` are compile-time computable if
|
||||
``p`` is a proc without side-effects (see the `noSideEffect pragma
|
||||
<#pragmas-nosideeffect-pragma>`_ for details) and if ``X`` is a
|
||||
``p`` is a proc without side-effects (see the `noSideEffect pragma
|
||||
<#pragmas-nosideeffect-pragma>`_ for details) and if ``X`` is a
|
||||
(possibly empty) list of compile-time computable arguments.
|
||||
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
Taint mode
|
||||
==========
|
||||
|
||||
The Nim compiler and most parts of the standard library support
|
||||
a taint mode. Input strings are declared with the `TaintedString`:idx:
|
||||
The Nim compiler and most parts of the standard library support
|
||||
a taint mode. Input strings are declared with the `TaintedString`:idx:
|
||||
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
|
||||
validation errors:
|
||||
|
||||
@@ -13,7 +13,7 @@ validation errors:
|
||||
echo "your name: "
|
||||
var name: TaintedString = stdin.readline
|
||||
# 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
|
||||
|
||||
If the taint mode is turned off, ``TaintedString`` is simply an alias for
|
||||
|
||||
@@ -16,17 +16,17 @@ Example:
|
||||
|
||||
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:
|
||||
|
||||
| ``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.
|
||||
|
||||
The "types" of templates can be the symbols ``expr`` (stands for *expression*),
|
||||
``stmt`` (stands for *statement*) or ``typedesc`` (stands for *type
|
||||
description*). These are "meta types", they can only be used in certain
|
||||
contexts. Real types can be used too; this implies that expressions are
|
||||
The "types" of templates can be the symbols ``expr`` (stands for *expression*),
|
||||
``stmt`` (stands for *statement*) or ``typedesc`` (stands for *type
|
||||
description*). These are "meta types", they can only be used in certain
|
||||
contexts. Real types can be used too; this implies that expressions are
|
||||
expected.
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ So ordinary templates cannot receive undeclared identifiers:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
template declareInt(x: expr) =
|
||||
template declareInt(x: expr) =
|
||||
var x: int
|
||||
|
||||
declareInt(x) # error: unknown identifier: 'x'
|
||||
@@ -51,7 +51,7 @@ receive undeclared identifiers:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
template declareInt(x: expr) {.immediate.} =
|
||||
template declareInt(x: expr) {.immediate.} =
|
||||
var x: int
|
||||
|
||||
declareInt(x) # valid
|
||||
@@ -75,11 +75,11 @@ special ``:`` syntax:
|
||||
close(f)
|
||||
else:
|
||||
quit("cannot open: " & fn)
|
||||
|
||||
|
||||
withFile(txt, "ttempl3.txt", fmWrite):
|
||||
txt.writeLine("line 1")
|
||||
txt.writeLine("line 2")
|
||||
|
||||
|
||||
In the example the two ``writeLine`` statements are bound to the ``actions``
|
||||
parameter.
|
||||
|
||||
@@ -92,9 +92,9 @@ bound from the definition scope of the template:
|
||||
|
||||
.. code-block:: nim
|
||||
# Module A
|
||||
var
|
||||
var
|
||||
lastId = 0
|
||||
|
||||
|
||||
template genId*: expr =
|
||||
inc(lastId)
|
||||
lastId
|
||||
@@ -102,10 +102,10 @@ bound from the definition scope of the template:
|
||||
.. code-block:: nim
|
||||
# Module B
|
||||
import A
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -117,11 +117,11 @@ In templates identifiers can be constructed with the backticks notation:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
template typedef(name: expr, typ: typedesc) {.immediate.} =
|
||||
template typedef(name: expr, typ: typedesc) {.immediate.} =
|
||||
type
|
||||
`T name`* {.inject.} = typ
|
||||
`P name`* {.inject.} = ref `T name`
|
||||
|
||||
|
||||
typedef(myint, int)
|
||||
var x: PMyInt
|
||||
|
||||
@@ -150,7 +150,7 @@ shadowed by the same argument name even when fully qualified:
|
||||
|
||||
tstLev(levA)
|
||||
# produces: 'levA levA'
|
||||
|
||||
|
||||
But the global symbol can properly be captured by a ``bind`` statement:
|
||||
|
||||
.. 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:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
|
||||
template newException*(exceptn: typedesc, message: string): expr =
|
||||
var
|
||||
e: ref exceptn # e is implicitly gensym'ed here
|
||||
new(e)
|
||||
e.msg = message
|
||||
e
|
||||
|
||||
|
||||
# so this works:
|
||||
let e = "message"
|
||||
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``
|
||||
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:
|
||||
|
||||
.. code-block:: nim
|
||||
@@ -204,7 +204,7 @@ template parameter, it is an inject'ed symbol:
|
||||
block:
|
||||
var f: File # since 'f' is a template param, it's injected implicitly
|
||||
...
|
||||
|
||||
|
||||
withFile(txt, "ttempl3.txt", fmWrite):
|
||||
txt.writeLine("line 1")
|
||||
txt.writeLine("line 2")
|
||||
@@ -215,7 +215,7 @@ no semantics outside of a template definition and cannot be abstracted over:
|
||||
|
||||
.. code-block:: nim
|
||||
{.pragma myInject: inject.}
|
||||
|
||||
|
||||
template t() =
|
||||
var x {.myInject.}: int # does NOT work
|
||||
|
||||
@@ -334,9 +334,9 @@ BindSym
|
||||
-------
|
||||
|
||||
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
|
||||
(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:
|
||||
|
||||
.. code-block:: nim
|
||||
@@ -418,8 +418,8 @@ powerful programming construct that still suffices. So the "check list" is:
|
||||
Macros as pragmas
|
||||
-----------------
|
||||
|
||||
Whole routines (procs, iterators etc.) can also be passed to a template or
|
||||
a macro via the pragma notation:
|
||||
Whole routines (procs, iterators etc.) can also be passed to a template or
|
||||
a macro via the pragma notation:
|
||||
|
||||
.. code-block:: nim
|
||||
template m(s: stmt) = discard
|
||||
|
||||
@@ -3,32 +3,32 @@ Term rewriting macros
|
||||
|
||||
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
|
||||
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:
|
||||
|
||||
.. code-block:: nim
|
||||
template optMul{`*`(a, 2)}(a: int): int = a+a
|
||||
|
||||
|
||||
let x = 3
|
||||
echo x * 2
|
||||
|
||||
The compiler now rewrites ``x * 2`` as ``x + x``. The code inside the
|
||||
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
|
||||
needs to be used.
|
||||
|
||||
|
||||
Unfortunately optimizations are hard to get right and even the tiny example
|
||||
is **wrong**:
|
||||
is **wrong**:
|
||||
|
||||
.. code-block:: nim
|
||||
template optMul{`*`(a, 2)}(a: int): int = a+a
|
||||
|
||||
|
||||
proc f(): int =
|
||||
echo "side effect!"
|
||||
result = 55
|
||||
|
||||
|
||||
echo f() * 2
|
||||
|
||||
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
|
||||
template optMul{`*`(a, 2)}(a: int{noSideEffect}): int = a+a
|
||||
|
||||
|
||||
proc f(): int =
|
||||
echo "side effect!"
|
||||
result = 55
|
||||
|
||||
|
||||
echo f() * 2 # not optimized ;-)
|
||||
|
||||
You can make one overload matching with a constraint and one without, and the
|
||||
@@ -53,13 +53,13 @@ blindly:
|
||||
|
||||
.. code-block:: nim
|
||||
template mulIsCommutative{`*`(a, b)}(a, b: int): int = b*a
|
||||
|
||||
|
||||
What optimizers really need to do is a *canonicalization*:
|
||||
|
||||
.. code-block:: nim
|
||||
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.
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ type ``int``, but only if it's a literal.
|
||||
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:
|
||||
|
||||
=================== =====================================================
|
||||
@@ -75,7 +75,7 @@ Predicate Meaning
|
||||
=================== =====================================================
|
||||
``atom`` The matching node has no children.
|
||||
``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).
|
||||
``ident`` The matching node must be an identifier (an unbound
|
||||
identifier).
|
||||
@@ -101,15 +101,15 @@ Predicate Meaning
|
||||
``enumfield`` A symbol which is a field in an enumeration.
|
||||
``forvar`` A for loop variable.
|
||||
``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.)
|
||||
``alias`` States that the marked parameter needs to alias
|
||||
``alias`` States that the marked parameter needs to alias
|
||||
with *some* other parameter.
|
||||
``noalias`` States that *every* other parameter must not alias
|
||||
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` ``.
|
||||
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
|
||||
@@ -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 ``echo`` statement. However, a term rewriting macro should not change the
|
||||
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
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A pattern expression can be bound to a pattern parameter via the ``expr{param}``
|
||||
notation:
|
||||
notation:
|
||||
|
||||
.. code-block:: nim
|
||||
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 =
|
||||
x = y
|
||||
if x: x = z
|
||||
|
||||
|
||||
var
|
||||
a = false
|
||||
b = true
|
||||
@@ -189,12 +189,12 @@ The ``*`` operator
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The ``*`` operator can *flatten* a nested binary expression like ``a & b & c``
|
||||
to ``&(a, b, c)``:
|
||||
to ``&(a, b, c)``:
|
||||
|
||||
.. code-block:: nim
|
||||
var
|
||||
calls = 0
|
||||
|
||||
|
||||
proc `&&`(s: varargs[string]): string =
|
||||
result = s[0]
|
||||
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
|
||||
arguments. The expression ``"my" && (space & "awe" && "some " ) && "concat"``
|
||||
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``
|
||||
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``
|
||||
produces:
|
||||
|
||||
.. code-block:: nim
|
||||
@@ -245,7 +245,7 @@ all the arguments, but also the matched operators in reverse polish notation:
|
||||
|
||||
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
|
||||
an ``nnkArgList`` node containing::
|
||||
@@ -265,7 +265,7 @@ an ``nnkArgList`` node containing::
|
||||
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
|
||||
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)
|
||||
}(x, y: varargs[expr], f: File, w: expr) =
|
||||
w(f, x, y)
|
||||
|
||||
|
||||
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
@@ -343,7 +343,7 @@ ordinary routines.
|
||||
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:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
@@ -301,7 +301,7 @@ matches better than just ``T`` then.
|
||||
proc sayHi(x: var int): string =
|
||||
# matches a var int
|
||||
result = $(x + 10)
|
||||
|
||||
|
||||
proc sayHello(x: int) =
|
||||
var m = x # a mutable version of x
|
||||
echo sayHi(x) # matches the non-var version of sayHi
|
||||
|
||||
@@ -18,6 +18,6 @@ Example:
|
||||
A type section begins with the ``type`` keyword. It contains multiple
|
||||
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
|
||||
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.
|
||||
|
||||
|
||||
@@ -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
|
||||
contexts. Furthermore, just as
|
||||
`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
|
||||
codebase and official projects, such as the Nim compiler, the standard library,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
=====================
|
||||
Nimfix User Guide
|
||||
=====================
|
||||
|
||||
:Author: Andreas Rumpf
|
||||
:Version: |nimversion|
|
||||
|
||||
=====================
|
||||
Nimfix User Guide
|
||||
=====================
|
||||
|
||||
:Author: Andreas Rumpf
|
||||
:Version: |nimversion|
|
||||
|
||||
**WARNING**: Nimfix is currently beta-quality.
|
||||
|
||||
|
||||
Nimfix is a tool to help you upgrade from Nimrod (<= version 0.9.6) to
|
||||
Nim (=> version 0.10.0).
|
||||
|
||||
@@ -48,7 +48,7 @@ Options:
|
||||
--wholeProject overwrite every processed file.
|
||||
--checkExtern:on|off style check also extern names
|
||||
--styleCheck:on|off|auto performs style checking for identifiers
|
||||
and suggests an alternative spelling;
|
||||
and suggests an alternative spelling;
|
||||
'auto' corrects the spelling.
|
||||
|
||||
In addition, all command line options of Nim are supported.
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
|
||||
|
||||
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.
|
||||
|
||||
Nimgrep has particularly good support for Nim's
|
||||
eccentric *style insensitivity*. Apart from that it is a generic text
|
||||
Nimgrep has particularly good support for Nim's
|
||||
eccentric *style insensitivity*. Apart from that it is a generic text
|
||||
manipulation tool.
|
||||
|
||||
|
||||
@@ -34,10 +34,10 @@ Options:
|
||||
--find, -f find the pattern (default)
|
||||
--replace, -r replace the pattern
|
||||
--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
|
||||
--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
|
||||
--stdin read pattern from stdin (to avoid the shell's confusing
|
||||
quoting rules)
|
||||
|
||||
@@ -11,12 +11,12 @@ Introduction
|
||||
============
|
||||
|
||||
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
|
||||
installation/deinstallation scripts for UNIX. Later versions will support
|
||||
installation/deinstallation scripts for UNIX. Later versions will support
|
||||
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
|
||||
systems.
|
||||
|
||||
@@ -24,7 +24,7 @@ systems.
|
||||
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:
|
||||
|
||||
.. include:: doc/mytest.cfg
|
||||
@@ -54,10 +54,10 @@ Key description
|
||||
``OS`` the OSes to generate C code for; for example:
|
||||
``"windows;linux;macosx"``
|
||||
``CPU`` the CPUs to generate C code for; for example:
|
||||
``"i386;amd64;powerpc"``
|
||||
``"i386;amd64;powerpc"``
|
||||
``Authors`` the project's authors
|
||||
``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
|
||||
for Windows to open up the command line shell.
|
||||
``License`` the filename of the application's license
|
||||
@@ -69,7 +69,7 @@ Key description
|
||||
|
||||
Many sections support the ``files`` key. Listed filenames
|
||||
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::
|
||||
|
||||
[Config]
|
||||
@@ -87,35 +87,35 @@ will be installed into the OS's configuration directory.
|
||||
Documentation section
|
||||
---------------------
|
||||
|
||||
The ``documentation`` section supports the ``files`` key.
|
||||
Listed files will be installed into the OS's native documentation directory
|
||||
The ``documentation`` section supports the ``files`` key.
|
||||
Listed files will be installed into the OS's native documentation directory
|
||||
(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.
|
||||
|
||||
|
||||
Other section
|
||||
-------------
|
||||
|
||||
The ``other`` section currently only supports the ``files`` key.
|
||||
Listed files will be installed into the application installation directory
|
||||
The ``other`` section currently only supports the ``files`` key.
|
||||
Listed files will be installed into the application installation directory
|
||||
(``$appdir``).
|
||||
|
||||
|
||||
Lib section
|
||||
-----------
|
||||
|
||||
The ``lib`` section currently only supports the ``files`` key.
|
||||
Listed files will be installed into the OS's native library directory
|
||||
The ``lib`` section currently only supports the ``files`` key.
|
||||
Listed files will be installed into the OS's native library directory
|
||||
(which might be ``$appdir/lib``).
|
||||
|
||||
|
||||
Windows section
|
||||
---------------
|
||||
|
||||
The ``windows`` section supports the ``files`` key for Windows specific files.
|
||||
Listed files will be installed into the application installation directory
|
||||
The ``windows`` section supports the ``files`` key for Windows specific files.
|
||||
Listed files will be installed into the application installation directory
|
||||
(``$appdir``).
|
||||
|
||||
Other possible options are:
|
||||
@@ -133,8 +133,8 @@ Key description
|
||||
UnixBin section
|
||||
---------------
|
||||
|
||||
The ``UnixBin`` section currently only supports the ``files`` key.
|
||||
Listed files will be installed into the OS's native bin directory
|
||||
The ``UnixBin`` section currently only supports the ``files`` key.
|
||||
Listed files will be installed into the OS's native bin directory
|
||||
(e.g. ``/usr/local/bin``). The exact location depends on the
|
||||
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
|
||||
should be generated. Example: ``InstallScript: "Yes"``
|
||||
``UninstallScript`` boolean flag whether a deinstallation shell script
|
||||
should be generated.
|
||||
should be generated.
|
||||
Example: ``UninstallScript: "Yes"``
|
||||
==================== =======================================================
|
||||
|
||||
@@ -163,7 +163,7 @@ Possible options are:
|
||||
==================== =======================================================
|
||||
Key description
|
||||
==================== =======================================================
|
||||
``path`` Path to Inno Setup.
|
||||
``path`` Path to Inno Setup.
|
||||
Example: ``path = r"c:\inno setup 5\iscc.exe"``
|
||||
``flags`` Flags to pass to Inno Setup.
|
||||
Example: ``flags = "/Q"``
|
||||
@@ -178,7 +178,7 @@ Possible options are:
|
||||
==================== =======================================================
|
||||
Key description
|
||||
==================== =======================================================
|
||||
``path`` Path to the C compiler.
|
||||
``path`` Path to the C compiler.
|
||||
``flags`` Flags to pass to the C Compiler.
|
||||
Example: ``flags = "-w"``
|
||||
==================== =======================================================
|
||||
|
||||
@@ -14,7 +14,7 @@ notation meaning
|
||||
order, to the text ahead, until one of them succeeds and
|
||||
possibly consumes some text. Indicate success if one of
|
||||
expressions succeeded. Otherwise do not consume any text
|
||||
and indicate failure.
|
||||
and indicate failure.
|
||||
``A ... Z`` Sequence: Apply expressions `A`, ..., `Z`, in this order,
|
||||
to consume consecutive portions of the text ahead, as long
|
||||
as they succeed. Indicate success if all succeeded.
|
||||
@@ -27,11 +27,11 @@ notation meaning
|
||||
``{E}`` Capture: Apply expression `E` and store the substring
|
||||
that matched `E` into a *capture* that can be accessed
|
||||
after the matching process.
|
||||
``$i`` Back reference to the ``i``th capture. ``i`` counts from 1.
|
||||
``$`` Anchor: Matches at the end of the input. No character
|
||||
is consumed. Same as ``!.``.
|
||||
``^`` Anchor: Matches at the start of the input. No character
|
||||
is consumed.
|
||||
``$i`` Back reference to the ``i``th capture. ``i`` counts from 1.
|
||||
``$`` Anchor: Matches at the end of the input. No character
|
||||
is consumed. Same as ``!.``.
|
||||
``^`` Anchor: Matches at the start of the input. No character
|
||||
is consumed.
|
||||
``&E`` And predicate: Indicate success if expression `E` matches
|
||||
the text ahead; otherwise indicate failure. Do not consume
|
||||
any text.
|
||||
@@ -41,15 +41,15 @@ notation meaning
|
||||
``E+`` One or more: Apply expression `E` repeatedly to match
|
||||
the text ahead, as long as it succeeds. Consume the matched
|
||||
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
|
||||
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,
|
||||
consume it. Always indicate success.
|
||||
consume it. Always indicate success.
|
||||
``[s]`` Character class: If the character ahead appears in the
|
||||
string `s`, consume it and indicate success. Otherwise
|
||||
indicate failure.
|
||||
indicate failure.
|
||||
``[a-b]`` Character range: If the character ahead is one from the
|
||||
range `a` through `b`, consume it and indicate success.
|
||||
Otherwise indicate failure.
|
||||
@@ -70,7 +70,7 @@ notation meaning
|
||||
failure.
|
||||
``@E`` Search: Shorthand for ``(!E .)* E``. (Search loop for the
|
||||
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
|
||||
`E` is captured.
|
||||
``@@ E`` Same as ``{@} E``.
|
||||
@@ -79,7 +79,7 @@ notation meaning
|
||||
matching engine.**
|
||||
``\identifier`` Built-in macro for a longer expression.
|
||||
``\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``
|
||||
``\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
|
||||
``\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:
|
||||
``\skip(\s*) {\ident} ':' {\ident}`` matches key value
|
||||
``\skip(\s*) {\ident} ':' {\ident}`` matches key value
|
||||
pairs ignoring whitespace around the ``':'``.
|
||||
``\ident`` a standard ASCII identifier: ``[a-zA-Z_][a-zA-Z_0-9]*``
|
||||
``\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.
|
||||
# Comments start with '#'.
|
||||
# First symbol is the start symbol.
|
||||
|
||||
|
||||
grammar <- rule* / expr
|
||||
|
||||
|
||||
identifier <- [A-Za-z][A-Za-z0-9_]*
|
||||
charsetchar <- "\\" . / [^\]]
|
||||
charset <- "[" "^"? (charsetchar ("-" charsetchar)?)+ "]"
|
||||
stringlit <- identifier? ("\"" ("\\" . / [^"])* "\"" /
|
||||
"'" ("\\" . / [^'])* "'")
|
||||
builtin <- "\\" identifier / [^\13\10]
|
||||
|
||||
|
||||
comment <- '#' @ \n
|
||||
ig <- (\s / comment)* # things to ignore
|
||||
|
||||
|
||||
rule <- identifier \s* "<-" expr ig
|
||||
identNoArrow <- identifier !(\s* "<-")
|
||||
prefixOpr <- ig '&' / ig '!' / ig '@' / ig '{@}' / ig '@@'
|
||||
literal <- ig identifier? '$' [0-9]+ / '$' / '^' /
|
||||
ig identNoArrow /
|
||||
ig charset /
|
||||
ig stringlit /
|
||||
ig builtin /
|
||||
ig '.' /
|
||||
ig '_' /
|
||||
ig identNoArrow /
|
||||
ig charset /
|
||||
ig stringlit /
|
||||
ig builtin /
|
||||
ig '.' /
|
||||
ig '_' /
|
||||
(ig "(" expr ig ")")
|
||||
postfixOpr <- ig '?' / ig '*' / ig '+'
|
||||
primary <- prefixOpr* (literal postfixOpr*)
|
||||
|
||||
|
||||
# Concatenation has higher priority than choice:
|
||||
# ``a b / c`` means ``(a b) / c``
|
||||
|
||||
|
||||
seqExpr <- primary+
|
||||
expr <- seqExpr (ig "/" expr)*
|
||||
|
||||
|
||||
**Note**: As a special syntactic extension if the whole PEG is only a single
|
||||
expression, identifiers are not interpreted as non-terminals, but are
|
||||
**Note**: As a special syntactic extension if the whole PEG is only a single
|
||||
expression, identifiers are not interpreted as non-terminals, but are
|
||||
interpreted as verbatim string:
|
||||
|
||||
.. code-block:: nim
|
||||
@@ -185,7 +185,7 @@ Check if `s` matches Nim's "while" keyword:
|
||||
.. code-block:: nim
|
||||
s =~ peg" y'while'"
|
||||
|
||||
Exchange (key, val)-pairs:
|
||||
Exchange (key, val)-pairs:
|
||||
|
||||
.. code-block:: nim
|
||||
"key: val; key2: val2".replacef(peg"{\ident} \s* ':' \s* {\ident}", "$2: $1")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
============================
|
||||
Nim's documenation system
|
||||
============================
|
||||
|
||||
This folder contains Nim's documentation. The documentation
|
||||
is written in a format called *reStructuredText*, a markup language that reads
|
||||
like ASCII and can be converted to HTML automatically!
|
||||
============================
|
||||
Nim's documenation system
|
||||
============================
|
||||
|
||||
This folder contains Nim's documentation. The documentation
|
||||
is written in a format called *reStructuredText*, a markup language that reads
|
||||
like ASCII and can be converted to HTML automatically!
|
||||
|
||||
@@ -175,17 +175,17 @@ for themselves. For example:
|
||||
example meaning
|
||||
============== ============================================================
|
||||
``\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
|
||||
``\7`` is always a back reference
|
||||
``\11`` might be a back reference, or another way of writing a tab
|
||||
``\011`` is always a tab
|
||||
``\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
|
||||
``\377`` might be a back reference, otherwise the byte consisting
|
||||
``\377`` might be a back reference, otherwise the byte consisting
|
||||
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"
|
||||
============== ============================================================
|
||||
|
||||
@@ -240,7 +240,7 @@ even when Unicode character property support is available.
|
||||
|
||||
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
|
||||
a match, without consuming any characters from the subject string. The use of
|
||||
subpatterns for more complicated assertions is described below. The
|
||||
|
||||
@@ -4,7 +4,7 @@ basetype can only be an ordinal type of a certain size, namely:
|
||||
* ``uint8``/``byte``-``uint16``
|
||||
* ``char``
|
||||
* ``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
|
||||
result in an error:
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Parallel & Spawn
|
||||
==========================================================
|
||||
|
||||
Nim has two flavors of parallelism:
|
||||
Nim has two flavors of parallelism:
|
||||
1) `Structured`:idx parallelism via the ``parallel`` statement.
|
||||
2) `Unstructured`:idx: parallelism via the standalone ``spawn`` statement.
|
||||
|
||||
@@ -30,7 +30,7 @@ variables at the same time:
|
||||
|
||||
.. code-block:: nim
|
||||
import threadpool, ...
|
||||
|
||||
|
||||
# wait until 2 out of 3 servers received the update:
|
||||
proc main =
|
||||
var responses = newSeq[FlowVarBase](3)
|
||||
@@ -89,7 +89,7 @@ restrictions / changes:
|
||||
the ``parallel`` section. This is called the *immutability check*. Currently
|
||||
it is not specified what exactly "complex location" means. We need to make
|
||||
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*.
|
||||
* Slices are optimized so that no copy is performed. This optimization is not
|
||||
yet performed for ordinary slices outside of a ``parallel`` section. Slices
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
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
|
||||
*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.
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ Notation meaning
|
||||
``$[zero|one|def]1`` use ``X = parseInt(arg[1])`` to determine which
|
||||
branch to use. If ``X == 0`` the 'zero' 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.
|
||||
If a branch needs to contain ``|``, ``]`` put
|
||||
them in single quotes. To produce a verbatim single
|
||||
@@ -50,12 +50,12 @@ Examples
|
||||
.. code-block:: nim
|
||||
|
||||
subex"$1($', '{2..})" % ["f", "a", "b", "c"] == "f(a, b, c)"
|
||||
|
||||
|
||||
subex"$1 $[files|file|files]{1} copied" % ["1"] == "1 file copied"
|
||||
|
||||
|
||||
subex"$['''|'|''''|']']#" % "0" == "'|"
|
||||
|
||||
|
||||
subex("type\n TEnum = enum\n $', '40c'\n '{..}") % [
|
||||
"fieldNameA", "fieldNameB", "fieldNameC", "fieldNameD"]
|
||||
|
||||
|
||||
|
||||
|
||||
10
doc/tut1.txt
10
doc/tut1.txt
@@ -1280,19 +1280,19 @@ with a compatible base type can be passed to an openarray parameter, the index
|
||||
type does not matter.
|
||||
|
||||
.. code-block:: nim
|
||||
var
|
||||
var
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
fruits.add("Banana") # sequence 'fruits' is dynamically expandable during runtime
|
||||
fruits.add("Mango")
|
||||
|
||||
|
||||
proc openArraySize(oa: openArray[string]): int =
|
||||
oa.len
|
||||
|
||||
|
||||
assert openArraySize(fruits) == 2 # procedure accepts a sequence as parameter
|
||||
assert openArraySize(capitals) == 3 # but also an array type
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ template any(container, cond: expr): expr {.immediate.} =
|
||||
break
|
||||
result
|
||||
|
||||
if all("mystring", {'a'..'z'}.contains) and any("myohmy", 'y'.`==`):
|
||||
if all("mystring", {'a'..'z'}.contains) and any("myohmy", 'y'.`==`):
|
||||
echo "works"
|
||||
else:
|
||||
else:
|
||||
echo "does not work"
|
||||
|
||||
|
||||
|
||||
@@ -18,13 +18,13 @@ type
|
||||
TDimension2d {.final, header: irr, importc: "dimension2d".} = object
|
||||
Tvector3df {.final, header: irr, importc: "vector3df".} = object
|
||||
TColor {.final, header: irr, importc: "SColor".} = object
|
||||
|
||||
|
||||
TIrrlichtDevice {.final, header: irr, importc: "IrrlichtDevice".} = object
|
||||
TIVideoDriver {.final, header: irr, importc: "IVideoDriver".} = object
|
||||
TISceneManager {.final, header: irr, importc: "ISceneManager".} = object
|
||||
TIGUIEnvironment {.final, header: irr, importc: "IGUIEnvironment".} = object
|
||||
TIAnimatedMesh {.final, header: irr, importc: "IAnimatedMesh".} = object
|
||||
TIAnimatedMeshSceneNode {.final, header: irr,
|
||||
TIAnimatedMeshSceneNode {.final, header: irr,
|
||||
importc: "IAnimatedMeshSceneNode".} = object
|
||||
TITexture {.final, header: irr, importc: "ITexture".} = object
|
||||
|
||||
@@ -100,7 +100,7 @@ var node = smgr.addAnimatedMeshSceneNode(mesh)
|
||||
if node != nil:
|
||||
#node->setMaterialFlag(EMF_LIGHTING, false)
|
||||
#node->setMD2Animation(scene::EMAT_STAND)
|
||||
node.setMaterialTexture(0,
|
||||
node.setMaterialTexture(0,
|
||||
driver.getTexture(
|
||||
"/home/andreas/download/irrlicht-1.7.2/media/media/sydney.bmp"))
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
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,
|
||||
use this command:
|
||||
|
||||
nim c --app:gui --no_main --no_linking backend.nim
|
||||
|
||||
Open the ``nimlaz.lpi`` file in Lazarus and run the program.
|
||||
|
||||
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,
|
||||
use this command:
|
||||
|
||||
nim c --app:gui --no_main --no_linking backend.nim
|
||||
|
||||
Open the ``nimlaz.lpi`` file in Lazarus and run the program.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Backend for the different user interfaces.
|
||||
|
||||
proc myAdd*(x, y: int): int {.cdecl, exportc.} =
|
||||
result = x + y
|
||||
|
||||
# Backend for the different user interfaces.
|
||||
|
||||
proc myAdd*(x, y: int): int {.cdecl, exportc.} =
|
||||
result = x + y
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
The cross platform calculator illustrates how to use Nim to create a backend
|
||||
called by different native user interfaces.
|
||||
|
||||
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.
|
||||
By keeping your program logic in Nim you can easily reuse it in different
|
||||
platforms.
|
||||
|
||||
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
|
||||
generating C code in a temporary build directory.
|
||||
|
||||
For a more elaborate and useful example see the cross_todo example.
|
||||
The cross platform calculator illustrates how to use Nim to create a backend
|
||||
called by different native user interfaces.
|
||||
|
||||
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.
|
||||
By keeping your program logic in Nim you can easily reuse it in different
|
||||
platforms.
|
||||
|
||||
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
|
||||
generating C code in a temporary build directory.
|
||||
|
||||
For a more elaborate and useful example see the cross_todo example.
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
This directory contains the nim backend code for the todo cross platform
|
||||
example.
|
||||
|
||||
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
|
||||
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
|
||||
to access internal data types and procs not exported and replicate the
|
||||
environment of client code.
|
||||
|
||||
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
|
||||
documentation to another programer for her to implement an interface without
|
||||
having to look at the source code.
|
||||
This directory contains the nim backend code for the todo cross platform
|
||||
example.
|
||||
|
||||
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
|
||||
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
|
||||
to access internal data types and procs not exported and replicate the
|
||||
environment of client code.
|
||||
|
||||
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
|
||||
documentation to another programer for her to implement an interface without
|
||||
having to look at the source code.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
The cross platform todo illustrates how to use Nim to create a backend
|
||||
called by different native user interfaces.
|
||||
|
||||
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.
|
||||
The cross platform todo illustrates how to use Nim to create a backend
|
||||
called by different native user interfaces.
|
||||
|
||||
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.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import
|
||||
import
|
||||
libcurl
|
||||
|
||||
var hCurl = easy_init()
|
||||
if hCurl != nil:
|
||||
if hCurl != nil:
|
||||
discard easy_setopt(hCurl, OPT_VERBOSE, true)
|
||||
discard easy_setopt(hCurl, OPT_URL, "http://nim-lang.org/")
|
||||
discard easy_perform(hCurl)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#! stdtmpl | standard
|
||||
#proc generateHTMLPage(title, currentTab, content: string,
|
||||
# tabs: openArray[string]): string =
|
||||
# tabs: openArray[string]): string =
|
||||
# result = ""
|
||||
<head><title>$title</title></head>
|
||||
<body>
|
||||
@@ -8,7 +8,7 @@
|
||||
<ul>
|
||||
#for tab in items(tabs):
|
||||
#if currentTab == tab:
|
||||
<li><a id="selected"
|
||||
<li><a id="selected"
|
||||
#else:
|
||||
<li><a
|
||||
#end if
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
|
||||
import os, streams, parsexml, strutils
|
||||
|
||||
proc `=?=` (a, b: string): bool =
|
||||
proc `=?=` (a, b: string): bool =
|
||||
# little trick: define our own comparator that ignores case
|
||||
return cmpIgnoreCase(a, b) == 0
|
||||
|
||||
if paramCount() < 1:
|
||||
if paramCount() < 1:
|
||||
quit("Usage: htmlrefs filename[.html]")
|
||||
|
||||
var links = 0 # count the number of links
|
||||
@@ -21,17 +21,17 @@ next(x) # get first event
|
||||
block mainLoop:
|
||||
while true:
|
||||
case x.kind
|
||||
of xmlElementOpen:
|
||||
of xmlElementOpen:
|
||||
# the <a href = "xyz"> tag we are interested in always has an attribute,
|
||||
# thus we search for ``xmlElementOpen`` and not for ``xmlElementStart``
|
||||
if x.elementName =?= "a":
|
||||
if x.elementName =?= "a":
|
||||
x.next()
|
||||
if x.kind == xmlAttribute:
|
||||
if x.kind == xmlAttribute:
|
||||
if x.attrKey =?= "href":
|
||||
var link = x.attrValue
|
||||
inc(links)
|
||||
# skip until we have an ``xmlElementClose`` event
|
||||
while true:
|
||||
while true:
|
||||
x.next()
|
||||
case x.kind
|
||||
of xmlEof: break mainLoop
|
||||
@@ -40,14 +40,14 @@ block mainLoop:
|
||||
x.next() # skip ``xmlElementClose``
|
||||
# now we have the description for the ``a`` element
|
||||
var desc = ""
|
||||
while x.kind == xmlCharData:
|
||||
while x.kind == xmlCharData:
|
||||
desc.add(x.charData)
|
||||
x.next()
|
||||
echo(desc & ": " & link)
|
||||
else:
|
||||
x.next()
|
||||
x.next()
|
||||
of xmlEof: break # end of file reached
|
||||
of xmlError:
|
||||
of xmlError:
|
||||
echo(errorMsg(x))
|
||||
x.next()
|
||||
else: x.next() # skip other events
|
||||
|
||||
@@ -15,11 +15,11 @@ open(x, s, filename)
|
||||
while true:
|
||||
x.next()
|
||||
case x.kind
|
||||
of xmlElementStart:
|
||||
if cmpIgnoreCase(x.elementName, "title") == 0:
|
||||
of xmlElementStart:
|
||||
if cmpIgnoreCase(x.elementName, "title") == 0:
|
||||
var title = ""
|
||||
x.next() # skip "<title>"
|
||||
while x.kind == xmlCharData:
|
||||
while x.kind == xmlCharData:
|
||||
title.add(x.charData)
|
||||
x.next()
|
||||
if x.kind == xmlElementEnd and cmpIgnoreCase(x.elementName, "title") == 0:
|
||||
@@ -27,7 +27,7 @@ while true:
|
||||
quit(0) # Success!
|
||||
else:
|
||||
echo(x.errorMsgExpected("/title"))
|
||||
|
||||
|
||||
of xmlEof: break # end of file reached
|
||||
else: discard # ignore other events
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ proc serveFile(client: Socket, filename: string) =
|
||||
|
||||
# ------------------ CGI execution -------------------------------------------
|
||||
|
||||
proc executeCgi(server: var TServer, client: Socket, path, query: string,
|
||||
proc executeCgi(server: var TServer, client: Socket, path, query: string,
|
||||
meth: TRequestMethod) =
|
||||
var env = newStringTable(modeCaseInsensitive)
|
||||
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)
|
||||
|
||||
var process = startProcess(command=path, env=env)
|
||||
|
||||
|
||||
var job: TJob
|
||||
job.process = process
|
||||
job.client = client
|
||||
server.job.add(job)
|
||||
|
||||
|
||||
if meth == reqPost:
|
||||
# get from client and post to CGI program:
|
||||
var buf = alloc(contentLength)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Test high level features
|
||||
|
||||
import strutils
|
||||
|
||||
echo "Give a list of numbers (separated by spaces): "
|
||||
stdin.readLine.split.map(parseInt).max.`$`.echo(" is the maximum!")
|
||||
# Test high level features
|
||||
|
||||
import strutils
|
||||
|
||||
echo "Give a list of numbers (separated by spaces): "
|
||||
stdin.readLine.split.map(parseInt).max.`$`.echo(" is the maximum!")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
import
|
||||
os, parsecfg, strutils, streams
|
||||
|
||||
|
||||
var f = newFileStream(paramStr(1), fmRead)
|
||||
if f != nil:
|
||||
var p: CfgParser
|
||||
@@ -9,7 +9,7 @@ if f != nil:
|
||||
while true:
|
||||
var e = next(p)
|
||||
case e.kind
|
||||
of cfgEof:
|
||||
of cfgEof:
|
||||
echo("EOF!")
|
||||
break
|
||||
of cfgSectionStart: ## a ``[section]`` has been parsed
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
In this directory you will find several examples for how to use the Nimrod
|
||||
library.
|
||||
In this directory you will find several examples for how to use the Nimrod
|
||||
library.
|
||||
|
||||
Copyright (c) 2004-2012 Andreas Rumpf.
|
||||
All rights reserved.
|
||||
|
||||
@@ -1,52 +1,52 @@
|
||||
# Test the SDL interface:
|
||||
|
||||
import
|
||||
sdl, sdl_image, colors
|
||||
|
||||
var
|
||||
screen, greeting: PSurface
|
||||
r: TRect
|
||||
event: TEvent
|
||||
bgColor = colChocolate.int32
|
||||
|
||||
if init(INIT_VIDEO) != 0:
|
||||
quit "SDL failed to initialize!"
|
||||
|
||||
screen = setVideoMode(640, 480, 16, SWSURFACE or ANYFORMAT)
|
||||
if screen.isNil:
|
||||
quit($sdl.getError())
|
||||
|
||||
greeting = imgLoad("tux.png")
|
||||
if greeting.isNil:
|
||||
echo "Failed to load tux.png"
|
||||
else:
|
||||
## convert the image to alpha and free the old one
|
||||
var s = greeting.displayFormatAlpha()
|
||||
swap(greeting, s)
|
||||
s.freeSurface()
|
||||
|
||||
r.x = 0
|
||||
r.y = 0
|
||||
|
||||
block game_loop:
|
||||
while true:
|
||||
|
||||
while pollEvent(addr event) > 0:
|
||||
case event.kind
|
||||
of QUITEV:
|
||||
break game_loop
|
||||
of KEYDOWN:
|
||||
if evKeyboard(addr event).keysym.sym == K_ESCAPE:
|
||||
break game_loop
|
||||
else:
|
||||
discard
|
||||
|
||||
discard fillRect(screen, nil, bgColor)
|
||||
discard blitSurface(greeting, nil, screen, addr r)
|
||||
discard flip(screen)
|
||||
|
||||
greeting.freeSurface()
|
||||
screen.freeSurface()
|
||||
sdl.quit()
|
||||
|
||||
## fowl wuz here 10/2012
|
||||
# Test the SDL interface:
|
||||
|
||||
import
|
||||
sdl, sdl_image, colors
|
||||
|
||||
var
|
||||
screen, greeting: PSurface
|
||||
r: TRect
|
||||
event: TEvent
|
||||
bgColor = colChocolate.int32
|
||||
|
||||
if init(INIT_VIDEO) != 0:
|
||||
quit "SDL failed to initialize!"
|
||||
|
||||
screen = setVideoMode(640, 480, 16, SWSURFACE or ANYFORMAT)
|
||||
if screen.isNil:
|
||||
quit($sdl.getError())
|
||||
|
||||
greeting = imgLoad("tux.png")
|
||||
if greeting.isNil:
|
||||
echo "Failed to load tux.png"
|
||||
else:
|
||||
## convert the image to alpha and free the old one
|
||||
var s = greeting.displayFormatAlpha()
|
||||
swap(greeting, s)
|
||||
s.freeSurface()
|
||||
|
||||
r.x = 0
|
||||
r.y = 0
|
||||
|
||||
block game_loop:
|
||||
while true:
|
||||
|
||||
while pollEvent(addr event) > 0:
|
||||
case event.kind
|
||||
of QUITEV:
|
||||
break game_loop
|
||||
of KEYDOWN:
|
||||
if evKeyboard(addr event).keysym.sym == K_ESCAPE:
|
||||
break game_loop
|
||||
else:
|
||||
discard
|
||||
|
||||
discard fillRect(screen, nil, bgColor)
|
||||
discard blitSurface(greeting, nil, screen, addr r)
|
||||
discard flip(screen)
|
||||
|
||||
greeting.freeSurface()
|
||||
screen.freeSurface()
|
||||
sdl.quit()
|
||||
|
||||
## fowl wuz here 10/2012
|
||||
|
||||
@@ -28,7 +28,7 @@ while readRow(x):
|
||||
for i in 0..x.row.len-1: header[i] = "Col " & $(i+1)
|
||||
else:
|
||||
# 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]))
|
||||
x.close()
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
template htmlTag(tag: expr) {.immediate.} =
|
||||
proc tag(): string = "<" & astToStr(tag) & ">"
|
||||
|
||||
|
||||
htmlTag(br)
|
||||
htmlTag(html)
|
||||
|
||||
|
||||
@@ -1,47 +1,47 @@
|
||||
import
|
||||
unittest, macros
|
||||
|
||||
var
|
||||
a = 1
|
||||
b = 22
|
||||
c = 1
|
||||
d = 3
|
||||
|
||||
suite "my suite":
|
||||
setup:
|
||||
echo "suite setup"
|
||||
var testVar = "from setup"
|
||||
|
||||
teardown:
|
||||
echo "suite teardown"
|
||||
|
||||
test "first suite test":
|
||||
testVar = "modified"
|
||||
echo "test var: " & testVar
|
||||
check a > b
|
||||
|
||||
test "second suite test":
|
||||
echo "test var: " & testVar
|
||||
|
||||
proc foo: bool =
|
||||
echo "running foo"
|
||||
return true
|
||||
|
||||
proc err =
|
||||
raise newException(EArithmetic, "some exception")
|
||||
|
||||
test "final test":
|
||||
echo "inside suite-less test"
|
||||
|
||||
check:
|
||||
a == c
|
||||
foo()
|
||||
d > 10
|
||||
|
||||
test "arithmetic failure":
|
||||
expect(EArithmetic):
|
||||
err()
|
||||
|
||||
expect(EArithmetic, ESystem):
|
||||
discard foo()
|
||||
|
||||
import
|
||||
unittest, macros
|
||||
|
||||
var
|
||||
a = 1
|
||||
b = 22
|
||||
c = 1
|
||||
d = 3
|
||||
|
||||
suite "my suite":
|
||||
setup:
|
||||
echo "suite setup"
|
||||
var testVar = "from setup"
|
||||
|
||||
teardown:
|
||||
echo "suite teardown"
|
||||
|
||||
test "first suite test":
|
||||
testVar = "modified"
|
||||
echo "test var: " & testVar
|
||||
check a > b
|
||||
|
||||
test "second suite test":
|
||||
echo "test var: " & testVar
|
||||
|
||||
proc foo: bool =
|
||||
echo "running foo"
|
||||
return true
|
||||
|
||||
proc err =
|
||||
raise newException(EArithmetic, "some exception")
|
||||
|
||||
test "final test":
|
||||
echo "inside suite-less test"
|
||||
|
||||
check:
|
||||
a == c
|
||||
foo()
|
||||
d > 10
|
||||
|
||||
test "arithmetic failure":
|
||||
expect(EArithmetic):
|
||||
err()
|
||||
|
||||
expect(EArithmetic, ESystem):
|
||||
discard foo()
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# 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.
|
||||
|
||||
import strutils, mysql
|
||||
@@ -29,22 +29,22 @@ type
|
||||
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
|
||||
|
||||
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:
|
||||
## ``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.
|
||||
result = SqlQuery(query)
|
||||
|
||||
proc dbError(db: DbConn) {.noreturn.} =
|
||||
proc dbError(db: DbConn) {.noreturn.} =
|
||||
## raises an EDb exception.
|
||||
var e: ref EDb
|
||||
new(e)
|
||||
e.msg = $mysql.error(db)
|
||||
raise e
|
||||
|
||||
proc dbError*(msg: string) {.noreturn.} =
|
||||
proc dbError*(msg: string) {.noreturn.} =
|
||||
## raises an EDb exception with message `msg`.
|
||||
var e: ref EDb
|
||||
new(e)
|
||||
@@ -55,9 +55,9 @@ when false:
|
||||
proc dbQueryOpt*(db: DbConn, query: string, args: varargs[string, `$`]) =
|
||||
var stmt = mysql_stmt_init(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)
|
||||
var
|
||||
var
|
||||
binding: seq[MYSQL_BIND]
|
||||
discard mysql_stmt_close(stmt)
|
||||
|
||||
@@ -79,9 +79,9 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
|
||||
else:
|
||||
add(result, dbQuote(args[a]))
|
||||
inc(a)
|
||||
else:
|
||||
else:
|
||||
add(result, c)
|
||||
|
||||
|
||||
proc tryExec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {.
|
||||
tags: [FReadDB, FWriteDb].} =
|
||||
## 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.
|
||||
var q = dbFormat(query, args)
|
||||
if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db)
|
||||
|
||||
proc newRow(L: int): Row =
|
||||
|
||||
proc newRow(L: int): Row =
|
||||
newSeq(result, L)
|
||||
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:
|
||||
while mysql.fetchRow(sqlres) != nil: discard
|
||||
mysql.freeResult(sqlres)
|
||||
|
||||
|
||||
iterator fastRows*(db: DbConn, query: SqlQuery,
|
||||
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
|
||||
## query, the results can be undefined. For MySQL this is the case!.
|
||||
rawExec(db, query, args)
|
||||
@@ -121,7 +121,7 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
|
||||
while true:
|
||||
row = mysql.fetchRow(sqlres)
|
||||
if row == nil: break
|
||||
for i in 0..L-1:
|
||||
for i in 0..L-1:
|
||||
setLen(result[i], 0)
|
||||
if row[i] == nil:
|
||||
result[i] = nil
|
||||
@@ -164,8 +164,8 @@ proc getRow*(db: DbConn, query: SqlQuery,
|
||||
var L = int(mysql.numFields(sqlres))
|
||||
result = newRow(L)
|
||||
var row = mysql.fetchRow(sqlres)
|
||||
if row != nil:
|
||||
for i in 0..L-1:
|
||||
if row != nil:
|
||||
for i in 0..L-1:
|
||||
setLen(result[i], 0)
|
||||
if row[i] == nil:
|
||||
result[i] = nil
|
||||
@@ -173,7 +173,7 @@ proc getRow*(db: DbConn, query: SqlQuery,
|
||||
add(result[i], row[i])
|
||||
properFreeResult(sqlres, row)
|
||||
|
||||
proc getAllRows*(db: DbConn, query: SqlQuery,
|
||||
proc getAllRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): seq[Row] {.tags: [FReadDB].} =
|
||||
## executes the query and returns the whole result dataset.
|
||||
result = @[]
|
||||
@@ -196,44 +196,44 @@ proc getAllRows*(db: DbConn, query: SqlQuery,
|
||||
inc(j)
|
||||
mysql.freeResult(sqlres)
|
||||
|
||||
iterator rows*(db: DbConn, query: SqlQuery,
|
||||
iterator rows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
||||
## same as `fastRows`, but slower and safe.
|
||||
for r in items(getAllRows(db, query, args)): yield r
|
||||
|
||||
proc getValue*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): string {.tags: [FReadDB].} =
|
||||
proc getValue*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): string {.tags: [FReadDB].} =
|
||||
## 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
|
||||
## value is NULL.
|
||||
result = getRow(db, query, args)[0]
|
||||
|
||||
proc tryInsertId*(db: DbConn, query: SqlQuery,
|
||||
proc tryInsertId*(db: DbConn, query: SqlQuery,
|
||||
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.
|
||||
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
|
||||
else:
|
||||
result = mysql.insertId(db)
|
||||
|
||||
proc insertId*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
|
||||
proc insertId*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
## generated ID for the row.
|
||||
result = tryInsertID(db, query, args)
|
||||
if result < 0: dbError(db)
|
||||
|
||||
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
||||
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.
|
||||
tags: [FReadDB, FWriteDb].} =
|
||||
tags: [FReadDB, FWriteDb].} =
|
||||
## runs the query (typically "UPDATE") and returns the
|
||||
## number of affected rows
|
||||
rawExec(db, query, args)
|
||||
result = mysql.affectedRows(db)
|
||||
|
||||
proc close*(db: DbConn) {.tags: [FDb].} =
|
||||
proc close*(db: DbConn) {.tags: [FDb].} =
|
||||
## closes the database connection.
|
||||
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
|
||||
## be established.
|
||||
result = mysql.init(nil)
|
||||
if result == nil: dbError("could not open database connection")
|
||||
if result == nil: dbError("could not open database connection")
|
||||
let
|
||||
colonPos = connection.find(':')
|
||||
host = if colonPos < 0: connection
|
||||
else: substr(connection, 0, colonPos-1)
|
||||
port: int32 = if colonPos < 0: 0'i32
|
||||
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:
|
||||
var errmsg = $mysql.error(result)
|
||||
db_mysql.close(result)
|
||||
@@ -257,6 +257,6 @@ proc open*(connection, user, password, database: string): DbConn {.
|
||||
|
||||
proc setEncoding*(connection: DbConn, encoding: string): bool {.
|
||||
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.
|
||||
result = mysql.set_character_set(connection, encoding) == 0
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# 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.
|
||||
|
||||
import strutils, postgres
|
||||
@@ -20,7 +20,7 @@ type
|
||||
## used to get a row's
|
||||
## column text on demand
|
||||
EDb* = object of IOError ## exception that is raised if a database error occurs
|
||||
|
||||
|
||||
SqlQuery* = distinct string ## an SQL query string
|
||||
SqlPrepared* = distinct string ## a identifier for the prepared queries
|
||||
|
||||
@@ -30,15 +30,15 @@ type
|
||||
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn,
|
||||
TSqlPrepared: SqlPrepared].}
|
||||
|
||||
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
|
||||
## constructs a SqlQuery from the string `query`. This is supposed to be
|
||||
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
|
||||
## constructs a SqlQuery from the string `query`. This is supposed to be
|
||||
## used as a raw-string-literal modifier:
|
||||
## ``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.
|
||||
result = SqlQuery(query)
|
||||
|
||||
|
||||
proc dbError*(db: DbConn) {.noreturn.} =
|
||||
## raises an EDb exception.
|
||||
var e: ref EDb
|
||||
@@ -73,7 +73,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
|
||||
inc(a)
|
||||
else:
|
||||
add(result, c)
|
||||
|
||||
|
||||
proc tryExec*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): bool {.tags: [FReadDB, FWriteDb].} =
|
||||
## 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 =
|
||||
newSeq(result, L)
|
||||
for i in 0..L-1: result[i] = ""
|
||||
|
||||
|
||||
proc setupQuery(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string]): PPGresult =
|
||||
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)
|
||||
if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db)
|
||||
return SqlPrepared(stmtName)
|
||||
|
||||
|
||||
proc setRow(res: PPGresult, r: var Row, line, cols: int32) =
|
||||
for col in 0..cols-1:
|
||||
setLen(r[col], 0)
|
||||
@@ -140,7 +140,7 @@ proc setRow(res: PPGresult, r: var Row, line, cols: int32) =
|
||||
|
||||
iterator fastRows*(db: DbConn, query: SqlQuery,
|
||||
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
|
||||
## query, the results can be undefined. For Postgres it is safe though.
|
||||
var res = setupQuery(db, query, args)
|
||||
@@ -224,14 +224,14 @@ proc getValue*(db: DbConn, query: SqlQuery,
|
||||
## value is NULL.
|
||||
var x = pqgetvalue(setupQuery(db, query, args), 0, 0)
|
||||
result = if isNil(x): "" else: $x
|
||||
|
||||
|
||||
proc tryInsertID*(db: DbConn, query: SqlQuery,
|
||||
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
|
||||
## ``RETURNING id`` to the query, so it only works if your primary key is
|
||||
## named ``id``.
|
||||
var x = pqgetvalue(setupQuery(db, SqlQuery(string(query) & " RETURNING id"),
|
||||
## named ``id``.
|
||||
var x = pqgetvalue(setupQuery(db, SqlQuery(string(query) & " RETURNING id"),
|
||||
args), 0, 0)
|
||||
if not isNil(x):
|
||||
result = parseBiggestInt($x)
|
||||
@@ -240,13 +240,13 @@ proc tryInsertID*(db: DbConn, query: SqlQuery,
|
||||
|
||||
proc insertID*(db: DbConn, query: SqlQuery,
|
||||
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
|
||||
## ``RETURNING id`` to the query, so it only works if your primary key is
|
||||
## named ``id``.
|
||||
## named ``id``.
|
||||
result = tryInsertID(db, query, args)
|
||||
if result < 0: dbError(db)
|
||||
|
||||
|
||||
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.tags: [
|
||||
FReadDB, FWriteDb].} =
|
||||
@@ -286,6 +286,6 @@ proc open*(connection, user, password, database: string): DbConn {.
|
||||
|
||||
proc setEncoding*(connection: DbConn, encoding: string): bool {.
|
||||
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.
|
||||
return pqsetClientEncoding(connection, encoding) == 0
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# 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.
|
||||
|
||||
import strutils, sqlite3
|
||||
@@ -19,31 +19,31 @@ type
|
||||
InstantRow* = Pstmt ## a handle that can be used to get a row's column
|
||||
## text on demand
|
||||
EDb* = object of IOError ## exception that is raised if a database error occurs
|
||||
|
||||
|
||||
SqlQuery* = distinct string ## an SQL query string
|
||||
|
||||
|
||||
FDb* = object of IOEffect ## effect that denotes a database operation
|
||||
FReadDb* = object of FDb ## effect that denotes a read operation
|
||||
FWriteDb* = object of FDb ## effect that denotes a write operation
|
||||
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
|
||||
|
||||
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
|
||||
## constructs a SqlQuery from the string `query`. This is supposed to be
|
||||
|
||||
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
|
||||
## constructs a SqlQuery from the string `query`. This is supposed to be
|
||||
## used as a raw-string-literal modifier:
|
||||
## ``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.
|
||||
result = SqlQuery(query)
|
||||
|
||||
proc dbError(db: DbConn) {.noreturn.} =
|
||||
|
||||
proc dbError(db: DbConn) {.noreturn.} =
|
||||
## raises an EDb exception.
|
||||
var e: ref EDb
|
||||
new(e)
|
||||
e.msg = $sqlite3.errmsg(db)
|
||||
raise e
|
||||
|
||||
proc dbError*(msg: string) {.noreturn.} =
|
||||
proc dbError*(msg: string) {.noreturn.} =
|
||||
## raises an EDb exception with message `msg`.
|
||||
var e: ref EDb
|
||||
new(e)
|
||||
@@ -67,8 +67,8 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
|
||||
inc(a)
|
||||
else:
|
||||
add(result, c)
|
||||
|
||||
proc tryExec*(db: DbConn, query: SqlQuery,
|
||||
|
||||
proc tryExec*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): bool {.tags: [FReadDb, FWriteDb].} =
|
||||
## tries to execute the query and returns true if successful, false otherwise.
|
||||
var q = dbFormat(query, args)
|
||||
@@ -81,32 +81,32 @@ proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
|
||||
tags: [FReadDb, FWriteDb].} =
|
||||
## executes the query and raises EDB if not successful.
|
||||
if not tryExec(db, query, args): dbError(db)
|
||||
|
||||
|
||||
proc newRow(L: int): Row =
|
||||
newSeq(result, L)
|
||||
for i in 0..L-1: result[i] = ""
|
||||
|
||||
proc setupQuery(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string]): Pstmt =
|
||||
|
||||
proc setupQuery(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string]): Pstmt =
|
||||
var q = dbFormat(query, args)
|
||||
if prepare_v2(db, q, q.len.cint, result, nil) != SQLITE_OK: dbError(db)
|
||||
|
||||
|
||||
proc setRow(stmt: Pstmt, r: var Row, cols: cint) =
|
||||
for col in 0..cols-1:
|
||||
setLen(r[col], column_bytes(stmt, col)) # set capacity
|
||||
setLen(r[col], 0)
|
||||
let x = column_text(stmt, col)
|
||||
if not isNil(x): add(r[col], x)
|
||||
|
||||
|
||||
iterator fastRows*(db: DbConn, query: SqlQuery,
|
||||
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
|
||||
## query, the results can be undefined. For Sqlite it is safe though.
|
||||
var stmt = setupQuery(db, query, args)
|
||||
var L = (column_count(stmt))
|
||||
var result = newRow(L)
|
||||
while step(stmt) == SQLITE_ROW:
|
||||
while step(stmt) == SQLITE_ROW:
|
||||
setRow(stmt, result, L)
|
||||
yield result
|
||||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||
@@ -136,31 +136,31 @@ proc getRow*(db: DbConn, query: SqlQuery,
|
||||
var stmt = setupQuery(db, query, args)
|
||||
var L = (column_count(stmt))
|
||||
result = newRow(L)
|
||||
if step(stmt) == SQLITE_ROW:
|
||||
if step(stmt) == SQLITE_ROW:
|
||||
setRow(stmt, result, L)
|
||||
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].} =
|
||||
## executes the query and returns the whole result dataset.
|
||||
result = @[]
|
||||
for r in fastRows(db, query, args):
|
||||
result.add(r)
|
||||
|
||||
iterator rows*(db: DbConn, query: SqlQuery,
|
||||
iterator rows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
|
||||
## same as `FastRows`, but slower and safe.
|
||||
for r in fastRows(db, query, args): yield r
|
||||
|
||||
proc getValue*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): string {.tags: [FReadDb].} =
|
||||
proc getValue*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): string {.tags: [FReadDb].} =
|
||||
## 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
|
||||
## value is NULL.
|
||||
var stmt = setupQuery(db, query, args)
|
||||
if step(stmt) == SQLITE_ROW:
|
||||
let cb = column_bytes(stmt, 0)
|
||||
if cb == 0:
|
||||
if cb == 0:
|
||||
result = ""
|
||||
else:
|
||||
result = newStringOfCap(cb)
|
||||
@@ -168,12 +168,12 @@ proc getValue*(db: DbConn, query: SqlQuery,
|
||||
else:
|
||||
result = ""
|
||||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||
|
||||
proc tryInsertID*(db: DbConn, query: SqlQuery,
|
||||
|
||||
proc tryInsertID*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64
|
||||
{.tags: [FWriteDb], raises: [].} =
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
## generated ID for the row or -1 in case of an error.
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
## generated ID for the row or -1 in case of an error.
|
||||
var q = dbFormat(query, args)
|
||||
var stmt: sqlite3.Pstmt
|
||||
result = -1
|
||||
@@ -183,27 +183,27 @@ proc tryInsertID*(db: DbConn, query: SqlQuery,
|
||||
if finalize(stmt) != SQLITE_OK:
|
||||
result = -1
|
||||
|
||||
proc insertID*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
proc insertID*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
## generated ID for the row. For Postgre this adds
|
||||
## ``RETURNING id`` to the query, so it only works if your primary key is
|
||||
## named ``id``.
|
||||
## named ``id``.
|
||||
result = tryInsertID(db, query, args)
|
||||
if result < 0: dbError(db)
|
||||
|
||||
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
||||
|
||||
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.
|
||||
tags: [FReadDb, FWriteDb].} =
|
||||
tags: [FReadDb, FWriteDb].} =
|
||||
## executes the query (typically "UPDATE") and returns the
|
||||
## number of affected rows.
|
||||
exec(db, query, args)
|
||||
result = changes(db)
|
||||
|
||||
proc close*(db: DbConn) {.tags: [FDb].} =
|
||||
proc close*(db: DbConn) {.tags: [FDb].} =
|
||||
## closes the database connection.
|
||||
if sqlite3.close(db) != SQLITE_OK: dbError(db)
|
||||
|
||||
|
||||
proc open*(connection, user, password, database: string): DbConn {.
|
||||
tags: [FDb].} =
|
||||
## 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 {.
|
||||
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.
|
||||
##
|
||||
## Note that the encoding cannot be changed once it's been set.
|
||||
## According to SQLite3 documentation, any attempt to change
|
||||
## the encoding after the database is created will be silently
|
||||
## According to SQLite3 documentation, any attempt to change
|
||||
## the encoding after the database is created will be silently
|
||||
## ignored.
|
||||
exec(connection, sql"PRAGMA encoding = ?", [encoding])
|
||||
result = connection.getValue(sql"PRAGMA encoding") == encoding
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
## This module implements graphical output for Nim; the current
|
||||
## 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.
|
||||
|
||||
import colors, math
|
||||
@@ -24,7 +24,7 @@ type
|
||||
Surface* {.pure, final.} = object
|
||||
w*, h*: Natural
|
||||
s*: sdl.PSurface
|
||||
|
||||
|
||||
EGraphics* = object of IOError
|
||||
|
||||
Font {.pure, final.} = object
|
||||
@@ -35,7 +35,7 @@ type
|
||||
|
||||
proc toSdlColor*(c: Color): sdl.Color =
|
||||
## Convert colors.Color to sdl.Color
|
||||
var x = c.extractRGB
|
||||
var x = c.extractRGB
|
||||
result.r = x.r and 0xff
|
||||
result.g = x.g 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 =
|
||||
## Creates a color using ``sdl.MapRGBA``.
|
||||
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)
|
||||
|
||||
proc toSdlRect*(r: Rect): sdl.Rect =
|
||||
@@ -53,26 +53,26 @@ proc toSdlRect*(r: Rect): sdl.Rect =
|
||||
result.w = uint16(r.width)
|
||||
result.h = uint16(r.height)
|
||||
|
||||
proc raiseEGraphics =
|
||||
proc raiseEGraphics =
|
||||
raise newException(EGraphics, $sdl.getError())
|
||||
|
||||
|
||||
proc surfaceFinalizer(s: PSurface) = sdl.freeSurface(s.s)
|
||||
|
||||
|
||||
proc newSurface*(width, height: int): PSurface =
|
||||
## creates a new surface.
|
||||
new(result, surfaceFinalizer)
|
||||
result.w = width
|
||||
result.h = height
|
||||
result.s = sdl.createRGBSurface(sdl.SWSURFACE, width, height,
|
||||
result.s = sdl.createRGBSurface(sdl.SWSURFACE, width, height,
|
||||
32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0)
|
||||
if result.s == nil:
|
||||
raiseEGraphics()
|
||||
|
||||
|
||||
assert(not sdl.mustLock(result.s))
|
||||
|
||||
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.
|
||||
new(result, fontFinalizer)
|
||||
result.f = openFont(name, size.cint)
|
||||
@@ -84,7 +84,7 @@ var
|
||||
defaultFont*: PFont ## default font that is used; this needs to initialized
|
||||
## 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.
|
||||
defaultFont = newFont(name, size, color)
|
||||
|
||||
@@ -98,7 +98,7 @@ proc newScreenSurface*(width, height: int): PSurface =
|
||||
raiseEGraphics()
|
||||
|
||||
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.
|
||||
if sdl.saveBMP(sur.s, filename) != 0:
|
||||
raise newException(IOError, "cannot write: " & filename)
|
||||
@@ -111,7 +111,7 @@ type
|
||||
template setPix(video, pitch, x, y, col: expr): stmt =
|
||||
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])
|
||||
|
||||
const
|
||||
@@ -120,7 +120,7 @@ const
|
||||
proc getPixel(sur: PSurface, x, y: Natural): colors.Color {.inline.} =
|
||||
assert x <% sur.w
|
||||
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)
|
||||
|
||||
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!
|
||||
setPixel(sur, x, y, col)
|
||||
|
||||
proc blit*(destSurf: PSurface, destRect: Rect, srcSurf: PSurface,
|
||||
proc blit*(destSurf: PSurface, destRect: Rect, srcSurf: PSurface,
|
||||
srcRect: Rect) =
|
||||
## Copies ``srcSurf`` into ``destSurf``
|
||||
var destTRect, srcTRect: sdl.Rect
|
||||
@@ -175,7 +175,7 @@ proc drawText*(sur: PSurface, p: Point, text: string, font = defaultFont) =
|
||||
## font.
|
||||
var textSur: PSurface # This surface will have the text drawn on it
|
||||
new(textSur, surfaceFinalizer)
|
||||
|
||||
|
||||
# Render the text
|
||||
textSur.s = sdl_ttf.renderTextBlended(font.f, text, font.color)
|
||||
# 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,
|
||||
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.
|
||||
var textSur: PSurface # This surface will have the text drawn on it
|
||||
new(textSur, surfaceFinalizer)
|
||||
textSur.s = sdl_ttf.renderTextShaded(font.f, text, font.color, toSdlColor(bg))
|
||||
# Merge the text surface with sur
|
||||
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) =
|
||||
## draws a circle with center `p` and radius `r` with the given color
|
||||
## onto the surface `sur`.
|
||||
@@ -205,7 +205,7 @@ proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) =
|
||||
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 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)
|
||||
@@ -213,7 +213,7 @@ proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) =
|
||||
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 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)
|
||||
@@ -225,10 +225,10 @@ proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) =
|
||||
py = py - 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
|
||||
|
||||
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
|
||||
|
||||
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 dx = x1 - x0
|
||||
if dy < 0:
|
||||
dy = -dy
|
||||
dy = -dy
|
||||
stepy = -1
|
||||
else:
|
||||
stepy = 1
|
||||
@@ -251,7 +251,7 @@ proc drawLine*(sur: PSurface, p1, p2: Point, color: Color) =
|
||||
stepx = -1
|
||||
else:
|
||||
stepx = 1
|
||||
dy = dy * 2
|
||||
dy = dy * 2
|
||||
dx = dx * 2
|
||||
var video = cast[PPixels](sur.s.pixels)
|
||||
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):
|
||||
var minW = min(sur.s.w - r.x, r.width)
|
||||
var minH = min(sur.s.h - r.y, r.height)
|
||||
|
||||
|
||||
# Draw Top
|
||||
for i in 0 .. minW - 1:
|
||||
setPix(video, pitch, r.x + i, r.y, color)
|
||||
setPix(video, pitch, r.x + i, r.y + minH - 1, color) # Draw bottom
|
||||
|
||||
|
||||
# Draw left side
|
||||
for i in 0 .. minH - 1:
|
||||
setPix(video, pitch, r.x, r.y + i, color)
|
||||
setPix(video, pitch, r.x + minW - 1, r.y + i, color) # Draw right side
|
||||
|
||||
|
||||
proc fillRect*(sur: PSurface, r: Rect, col: Color) =
|
||||
## Fills a rectangle using sdl's ``FillRect`` function.
|
||||
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
|
||||
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 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)
|
||||
|
||||
proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural,
|
||||
proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural,
|
||||
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
|
||||
## of the ellipse.
|
||||
var
|
||||
var
|
||||
x, y: Natural
|
||||
xChange, yChange: int
|
||||
ellipseError: Natural
|
||||
twoASquare, twoBSquare: Natural
|
||||
stoppingX, stoppingY: Natural
|
||||
|
||||
|
||||
twoASquare = 2 * xRadius * xRadius
|
||||
twoBSquare = 2 * yRadius * yRadius
|
||||
x = xRadius
|
||||
@@ -376,7 +376,7 @@ proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural,
|
||||
ellipseError = 0
|
||||
stoppingX = twoBSquare * xRadius
|
||||
stoppingY = 0
|
||||
|
||||
|
||||
while stoppingX >= stoppingY: # 1st set of points, y` > - 1
|
||||
sur.plot4EllipsePoints(cx, cy, x, y, col)
|
||||
inc(y)
|
||||
@@ -388,7 +388,7 @@ proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural,
|
||||
dec(stoppingX, twoBSquare)
|
||||
inc(ellipseError, xChange)
|
||||
inc(xChange, twoBSquare)
|
||||
|
||||
|
||||
# 1st point set is done; start the 2nd set of points
|
||||
x = 0
|
||||
y = yRadius
|
||||
@@ -408,7 +408,7 @@ proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural,
|
||||
dec(stoppingY, twoASquare)
|
||||
inc(ellipseError, yChange)
|
||||
inc(yChange,twoASquare)
|
||||
|
||||
|
||||
|
||||
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):
|
||||
@@ -419,43 +419,43 @@ proc plotAA(sur: PSurface, x, y: int, c: float, color: Color) =
|
||||
|
||||
setPix(video, pitch, x, y,
|
||||
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 fpart(x: expr): expr = x - ipart(x)
|
||||
template rfpart(x: expr): expr = 1.0 - fpart(x)
|
||||
|
||||
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
|
||||
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())
|
||||
var dx = x2 - x1
|
||||
var dy = y2 - y1
|
||||
|
||||
|
||||
var ax = dx
|
||||
if ax < 0'f64:
|
||||
ax = 0'f64 - ax
|
||||
var ay = dy
|
||||
if ay < 0'f64:
|
||||
ay = 0'f64 - ay
|
||||
|
||||
|
||||
if ax < ay:
|
||||
swap(x1, y1)
|
||||
swap(x2, y2)
|
||||
swap(dx, dy)
|
||||
|
||||
|
||||
template doPlot(x, y: int, c: float, color: Color): stmt =
|
||||
if ax < ay:
|
||||
sur.plotAA(y, x, c, color)
|
||||
else:
|
||||
sur.plotAA(x, y, c, color)
|
||||
|
||||
|
||||
if x2 < x1:
|
||||
swap(x1, x2)
|
||||
swap(y1, y2)
|
||||
|
||||
|
||||
var gradient = dy / dx
|
||||
# handle first endpoint
|
||||
var xend = cround(x1)
|
||||
@@ -509,19 +509,19 @@ when not defined(testing) and isMainModule:
|
||||
# Draw the shapes
|
||||
surf.drawLineAA((150, 170), (400, 471), colTan)
|
||||
surf.drawLine((100, 170), (400, 471), colRed)
|
||||
|
||||
|
||||
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.
|
||||
surf.drawVerLine(200, 300 - 30 + 1, 60, colViolet) # ^^ | i suppose it is
|
||||
|
||||
|
||||
surf.drawEllipse(400, 300, 300, 300, colOrange)
|
||||
surf.drawEllipse(5, 5, 5, 5, colGreen)
|
||||
|
||||
|
||||
surf.drawHorLine(5, 5, 900, colRed)
|
||||
surf.drawVerLine(5, 60, 800, colRed)
|
||||
surf.drawCircle((600, 500), 60, colRed)
|
||||
|
||||
|
||||
surf.fillRect((50, 50, 100, 100), colFuchsia)
|
||||
surf.fillRect((150, 50, 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.drawLineAA((592, 160), (592, 280), colPurple)
|
||||
|
||||
|
||||
#surf.drawText((300, 300), "TEST", colMidnightBlue)
|
||||
#var textSize = textBounds("TEST")
|
||||
#surf.drawText((300, 300 + textSize.height), $textSize.width & ", " &
|
||||
# $textSize.height, colDarkGreen)
|
||||
|
||||
|
||||
var mouseStartX = -1
|
||||
var mouseStartY = -1
|
||||
withEvents(surf, event):
|
||||
@@ -561,17 +561,17 @@ when not defined(testing) and isMainModule:
|
||||
surf.drawLineAA((mouseStartX, mouseStartY), (int(mbd.x), int(mbd.y)), colPurple)
|
||||
mouseStartX = -1
|
||||
mouseStartY = -1
|
||||
|
||||
|
||||
of sdl.MOUSEMOTION:
|
||||
var mm = sdl.evMouseMotion(eventp)
|
||||
if mouseStartX != -1 and mouseStartY != -1:
|
||||
surf.drawLineAA((mouseStartX, mouseStartY), (int(mm.x), int(mm.y)), colPurple)
|
||||
#echo(mm.x, " ", mm.y, " ", mm.yrel)
|
||||
|
||||
|
||||
else:
|
||||
discard "echo(event.kind)"
|
||||
|
||||
|
||||
sdl.updateRect(surf.s, 0, 0, 800, 600)
|
||||
|
||||
|
||||
surf.writeToBMP("test.bmp")
|
||||
sdl.quit()
|
||||
|
||||
@@ -41,11 +41,11 @@ type
|
||||
reExtended = 3, ## ignore whitespace and ``#`` comments
|
||||
reStudy = 4 ## study the expression (may be omitted if the
|
||||
## expression will be used only once)
|
||||
|
||||
RegexDesc = object
|
||||
|
||||
RegexDesc = object
|
||||
h: ptr Pcre
|
||||
e: ptr ExtraData
|
||||
|
||||
|
||||
Regex* {.deprecated.} = ref RegexDesc ## a compiled regular expression
|
||||
|
||||
RegexError* = object of ValueError
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# 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.
|
||||
|
||||
{.deprecated.}
|
||||
@@ -20,37 +20,37 @@ type
|
||||
bio: BIO
|
||||
{.deprecated: [TSecureSocket: SecureSocket].}
|
||||
|
||||
proc connect*(sock: var SecureSocket, address: string,
|
||||
proc connect*(sock: var SecureSocket, address: string,
|
||||
port: int): int =
|
||||
## Connects to the specified `address` on the specified `port`.
|
||||
## Returns the result of the certificate validation.
|
||||
SslLoadErrorStrings()
|
||||
ERR_load_BIO_strings()
|
||||
|
||||
|
||||
if SSL_library_init() != 1:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
|
||||
var ctx = SSL_CTX_new(SSLv23_client_method())
|
||||
if ctx == nil:
|
||||
ERR_print_errors_fp(stderr)
|
||||
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:
|
||||
# echo("Failed load verify locations")
|
||||
# ERR_print_errors_fp(stderr)
|
||||
|
||||
|
||||
sock.bio = BIO_new_ssl_connect(ctx)
|
||||
if BIO_get_ssl(sock.bio, addr(sock.ssl)) == 0:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
if BIO_set_conn_hostname(sock.bio, address & ":" & $port) != 1:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
|
||||
if BIO_do_connect(sock.bio) <= 0:
|
||||
ERR_print_errors_fp(stderr)
|
||||
raiseOSError(osLastError())
|
||||
|
||||
|
||||
result = SSL_get_verify_result(sock.ssl)
|
||||
|
||||
proc recvLine*(sock: SecureSocket, line: var TaintedString): bool =
|
||||
@@ -86,12 +86,12 @@ proc close*(sock: SecureSocket) =
|
||||
when not defined(testing) and isMainModule:
|
||||
var s: SecureSocket
|
||||
echo connect(s, "smtp.gmail.com", 465)
|
||||
|
||||
|
||||
#var buffer: array[0..255, char]
|
||||
#echo BIO_read(bio, buffer, buffer.len)
|
||||
var buffer: string = ""
|
||||
|
||||
|
||||
echo s.recvLine(buffer)
|
||||
echo buffer
|
||||
echo buffer
|
||||
echo buffer.len
|
||||
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Main file to generate a DLL from the standard library.
|
||||
## The default Nimrtl does not only contain the ``system`` module, but these
|
||||
## Main file to generate a DLL from the standard library.
|
||||
## The default Nimrtl does not only contain the ``system`` module, but these
|
||||
## too:
|
||||
##
|
||||
## * parseutils
|
||||
@@ -22,12 +22,12 @@
|
||||
## * unicode
|
||||
## * pegs
|
||||
## * ropes
|
||||
##
|
||||
##
|
||||
|
||||
when system.appType != "lib":
|
||||
{.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'".}
|
||||
|
||||
import
|
||||
|
||||
@@ -29,34 +29,34 @@ const
|
||||
|
||||
# Valid opcodes ( "op" parameter ) to issue to epoll_ctl().
|
||||
|
||||
const
|
||||
EPOLL_CTL_ADD* = 1 # Add a file descriptor to the interface.
|
||||
EPOLL_CTL_DEL* = 2 # Remove a file descriptor from the interface.
|
||||
EPOLL_CTL_MOD* = 3 # Change file descriptor epoll_event structure.
|
||||
const
|
||||
EPOLL_CTL_ADD* = 1 # Add a file descriptor to the interface.
|
||||
EPOLL_CTL_DEL* = 2 # Remove a file descriptor from the interface.
|
||||
EPOLL_CTL_MOD* = 3 # Change file descriptor epoll_event structure.
|
||||
|
||||
type
|
||||
epoll_data* {.importc: "union epoll_data",
|
||||
type
|
||||
epoll_data* {.importc: "union epoll_data",
|
||||
header: "<sys/epoll.h>", pure, final.} = object # TODO: This is actually a union.
|
||||
#thePtr* {.importc: "ptr".}: pointer
|
||||
fd* {.importc: "fd".}: cint # \
|
||||
#u32*: uint32
|
||||
#u64*: uint64
|
||||
|
||||
epoll_event* {.importc: "struct epoll_event", header: "<sys/epoll.h>", pure, final.} = object
|
||||
events*: uint32 # Epoll events
|
||||
data*: epoll_data # User data variable
|
||||
epoll_event* {.importc: "struct epoll_event", header: "<sys/epoll.h>", pure, final.} = object
|
||||
events*: uint32 # Epoll events
|
||||
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>".}
|
||||
## Creates an epoll instance. Returns an fd for the new instance.
|
||||
## The "size" parameter is a hint specifying the number of file
|
||||
## 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>".}
|
||||
## 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 {.
|
||||
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_*
|
||||
## constants defined above. The "fd" parameter is the target of the
|
||||
## 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;
|
||||
timeout: cint): cint {.importc: "epoll_wait",
|
||||
proc epoll_wait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
|
||||
timeout: cint): cint {.importc: "epoll_wait",
|
||||
header: "<sys/epoll.h>".}
|
||||
## Wait for events on an epoll instance "epfd". Returns the number 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.
|
||||
|
||||
|
||||
#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 {.
|
||||
# importc: "epoll_pwait", header: "<sys/epoll.h>".}
|
||||
# Same as epoll_wait, but the thread's signal mask is temporarily
|
||||
# and atomically replaced with the one provided as parameter.
|
||||
#
|
||||
# This function is a cancellation point and therefore not marked with
|
||||
# __THROW.
|
||||
# __THROW.
|
||||
|
||||
@@ -9,65 +9,65 @@
|
||||
|
||||
{.deadCodeElim:on.}
|
||||
|
||||
# Get the platform-dependent flags.
|
||||
# Structure describing an inotify event.
|
||||
type
|
||||
InotifyEvent*{.pure, final, importc: "struct inotify_event",
|
||||
header: "<sys/inotify.h>".} = object
|
||||
wd*{.importc: "wd".}: cint # Watch descriptor.
|
||||
mask*{.importc: "mask".}: uint32 # Watch mask.
|
||||
cookie*{.importc: "cookie".}: uint32 # Cookie to synchronize two events.
|
||||
len*{.importc: "len".}: uint32 # Length (including NULs) of name.
|
||||
name*{.importc: "name".}: char # Name.
|
||||
# Get the platform-dependent flags.
|
||||
# Structure describing an inotify event.
|
||||
type
|
||||
InotifyEvent*{.pure, final, importc: "struct inotify_event",
|
||||
header: "<sys/inotify.h>".} = object
|
||||
wd*{.importc: "wd".}: cint # Watch descriptor.
|
||||
mask*{.importc: "mask".}: uint32 # Watch mask.
|
||||
cookie*{.importc: "cookie".}: uint32 # Cookie to synchronize two events.
|
||||
len*{.importc: "len".}: uint32 # Length (including NULs) of name.
|
||||
name*{.importc: "name".}: char # Name.
|
||||
{.deprecated: [Tinotify_event: InotifyEvent].}
|
||||
|
||||
# Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH.
|
||||
const
|
||||
IN_ACCESS* = 0x00000001 # File was accessed.
|
||||
IN_MODIFY* = 0x00000002 # File was modified.
|
||||
IN_ATTRIB* = 0x00000004 # Metadata changed.
|
||||
IN_CLOSE_WRITE* = 0x00000008 # Writtable file was closed.
|
||||
IN_CLOSE_NOWRITE* = 0x00000010 # Unwrittable file closed.
|
||||
IN_CLOSE* = (IN_CLOSE_WRITE or IN_CLOSE_NOWRITE) # Close.
|
||||
IN_OPEN* = 0x00000020 # File was opened.
|
||||
IN_MOVED_FROM* = 0x00000040 # File was moved from X.
|
||||
IN_MOVED_TO* = 0x00000080 # File was moved to Y.
|
||||
IN_MOVE* = (IN_MOVED_FROM or IN_MOVED_TO) # Moves.
|
||||
IN_CREATE* = 0x00000100 # Subfile was created.
|
||||
IN_DELETE* = 0x00000200 # Subfile was deleted.
|
||||
IN_DELETE_SELF* = 0x00000400 # Self was deleted.
|
||||
IN_MOVE_SELF* = 0x00000800 # Self was moved.
|
||||
# Events sent by the kernel.
|
||||
const
|
||||
IN_UNMOUNT* = 0x00002000 # Backing fs was unmounted.
|
||||
IN_Q_OVERFLOW* = 0x00004000 # Event queued overflowed.
|
||||
IN_IGNORED* = 0x00008000 # File was ignored.
|
||||
# Special flags.
|
||||
const
|
||||
# Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH.
|
||||
const
|
||||
IN_ACCESS* = 0x00000001 # File was accessed.
|
||||
IN_MODIFY* = 0x00000002 # File was modified.
|
||||
IN_ATTRIB* = 0x00000004 # Metadata changed.
|
||||
IN_CLOSE_WRITE* = 0x00000008 # Writtable file was closed.
|
||||
IN_CLOSE_NOWRITE* = 0x00000010 # Unwrittable file closed.
|
||||
IN_CLOSE* = (IN_CLOSE_WRITE or IN_CLOSE_NOWRITE) # Close.
|
||||
IN_OPEN* = 0x00000020 # File was opened.
|
||||
IN_MOVED_FROM* = 0x00000040 # File was moved from X.
|
||||
IN_MOVED_TO* = 0x00000080 # File was moved to Y.
|
||||
IN_MOVE* = (IN_MOVED_FROM or IN_MOVED_TO) # Moves.
|
||||
IN_CREATE* = 0x00000100 # Subfile was created.
|
||||
IN_DELETE* = 0x00000200 # Subfile was deleted.
|
||||
IN_DELETE_SELF* = 0x00000400 # Self was deleted.
|
||||
IN_MOVE_SELF* = 0x00000800 # Self was moved.
|
||||
# Events sent by the kernel.
|
||||
const
|
||||
IN_UNMOUNT* = 0x00002000 # Backing fs was unmounted.
|
||||
IN_Q_OVERFLOW* = 0x00004000 # Event queued overflowed.
|
||||
IN_IGNORED* = 0x00008000 # File was ignored.
|
||||
# Special flags.
|
||||
const
|
||||
IN_ONLYDIR* = 0x01000000 # Only watch the path if it is a
|
||||
# directory.
|
||||
IN_DONT_FOLLOW* = 0x02000000 # Do not follow a sym link.
|
||||
# directory.
|
||||
IN_DONT_FOLLOW* = 0x02000000 # Do not follow a sym link.
|
||||
IN_EXCL_UNLINK* = 0x04000000 # Exclude events on unlinked
|
||||
# objects.
|
||||
# objects.
|
||||
IN_MASK_ADD* = 0x20000000 # Add to the mask of an already
|
||||
# existing watch.
|
||||
IN_ISDIR* = 0x40000000 # Event occurred against dir.
|
||||
IN_ONESHOT* = 0x80000000 # Only send event once.
|
||||
# All events which a program can wait on.
|
||||
const
|
||||
# existing watch.
|
||||
IN_ISDIR* = 0x40000000 # Event occurred against dir.
|
||||
IN_ONESHOT* = 0x80000000 # Only send event once.
|
||||
# All events which a program can wait on.
|
||||
const
|
||||
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_CREATE or IN_DELETE or IN_DELETE_SELF or IN_MOVE_SELF)
|
||||
# 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>".}
|
||||
# Create and initialize inotify instance.
|
||||
proc inotify_init1*(flags: cint): cint{.cdecl, importc: "inotify_init1",
|
||||
# Create and initialize inotify instance.
|
||||
proc inotify_init1*(flags: cint): cint{.cdecl, importc: "inotify_init1",
|
||||
header: "<sys/inotify.h>".}
|
||||
# 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{.
|
||||
cdecl, importc: "inotify_add_watch", header: "<sys/inotify.h>".}
|
||||
# Remove the watch specified by WD from the inotify instance FD.
|
||||
proc inotify_rm_watch*(fd: cint; wd: cint): cint{.cdecl,
|
||||
# Remove the watch specified by WD from the inotify instance FD.
|
||||
proc inotify_rm_watch*(fd: cint; wd: cint): cint{.cdecl,
|
||||
importc: "inotify_rm_watch", header: "<sys/inotify.h>".}
|
||||
|
||||
@@ -24,5 +24,5 @@ const
|
||||
|
||||
# fn should be of type proc (a2: pointer): void {.cdecl.}
|
||||
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>".}
|
||||
|
||||
@@ -41,7 +41,7 @@ type
|
||||
Actor[In, Out] = object{.pure, final.}
|
||||
i: Channel[Task[In, Out]]
|
||||
t: TThread[ptr Actor[In, Out]]
|
||||
|
||||
|
||||
PActor*[In, Out] = ptr Actor[In, Out] ## an 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)
|
||||
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) =
|
||||
## sends a message to `receiver`'s inbox.
|
||||
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) =
|
||||
## 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
|
||||
## for messages still in ether. This will change in a later
|
||||
## version, however.
|
||||
@@ -146,7 +146,7 @@ proc sync*[In, Out](a: var ActorPool[In, Out], polling=50) =
|
||||
while true:
|
||||
var wait = false
|
||||
for i in 0..high(a.actors):
|
||||
if not a.actors[i].i.ready:
|
||||
if not a.actors[i].i.ready:
|
||||
wait = true
|
||||
allReadyCount = 0
|
||||
break
|
||||
@@ -222,7 +222,7 @@ proc spawn*[In](p: var ActorPool[In, void], input: In,
|
||||
var t: Task[In, void]
|
||||
setupTask()
|
||||
schedule()
|
||||
|
||||
|
||||
when not defined(testing) and isMainModule:
|
||||
var
|
||||
a: ActorPool[int, void]
|
||||
|
||||
@@ -195,10 +195,10 @@ proc read*(f: AsyncFile, size: int): Future[string] =
|
||||
readBuffer.setLen(res)
|
||||
f.offset.inc(res)
|
||||
retFuture.complete(readBuffer)
|
||||
|
||||
|
||||
if not cb(f.fd):
|
||||
addRead(f.fd, cb)
|
||||
|
||||
|
||||
return retFuture
|
||||
|
||||
proc readLine*(f: AsyncFile): Future[string] {.async.} =
|
||||
@@ -222,7 +222,7 @@ proc getFilePos*(f: AsyncFile): int64 =
|
||||
|
||||
proc setFilePos*(f: AsyncFile, pos: int64) =
|
||||
## 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
|
||||
when not defined(windows) and not defined(nimdoc):
|
||||
let ret = lseek(f.fd.cint, pos, SEEK_SET)
|
||||
@@ -291,7 +291,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
|
||||
retFuture.complete()
|
||||
else:
|
||||
var written = 0
|
||||
|
||||
|
||||
proc cb(fd: AsyncFD): bool =
|
||||
result = true
|
||||
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.
|
||||
else:
|
||||
retFuture.complete()
|
||||
|
||||
|
||||
if not cb(f.fd):
|
||||
addWrite(f.fd, cb)
|
||||
return retFuture
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user