Merge pull request #3289 from nanoant/patch/fix-whitespace
Patch/fix whitespace
This commit is contained in:
commit
cd9d126a2f
541 changed files with 8539 additions and 8540 deletions
|
|
@ -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
|
import
|
||||||
ast, astalgo, types, trees, intsets, msgs
|
ast, astalgo, types, trees, intsets, msgs
|
||||||
|
|
||||||
type
|
type
|
||||||
TAnalysisResult* = enum
|
TAnalysisResult* = enum
|
||||||
arNo, arMaybe, arYes
|
arNo, arMaybe, arYes
|
||||||
|
|
@ -21,42 +21,42 @@ proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult
|
||||||
proc isPartOfAux(n: PNode, b: PType, marker: var IntSet): TAnalysisResult =
|
proc isPartOfAux(n: PNode, b: PType, marker: var IntSet): TAnalysisResult =
|
||||||
result = arNo
|
result = arNo
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkRecList:
|
of nkRecList:
|
||||||
for i in countup(0, sonsLen(n) - 1):
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
result = isPartOfAux(n.sons[i], b, marker)
|
result = isPartOfAux(n.sons[i], b, marker)
|
||||||
if result == arYes: return
|
if result == arYes: return
|
||||||
of nkRecCase:
|
of nkRecCase:
|
||||||
assert(n.sons[0].kind == nkSym)
|
assert(n.sons[0].kind == nkSym)
|
||||||
result = isPartOfAux(n.sons[0], b, marker)
|
result = isPartOfAux(n.sons[0], b, marker)
|
||||||
if result == arYes: return
|
if result == arYes: return
|
||||||
for i in countup(1, sonsLen(n) - 1):
|
for i in countup(1, sonsLen(n) - 1):
|
||||||
case n.sons[i].kind
|
case n.sons[i].kind
|
||||||
of nkOfBranch, nkElse:
|
of nkOfBranch, nkElse:
|
||||||
result = isPartOfAux(lastSon(n.sons[i]), b, marker)
|
result = isPartOfAux(lastSon(n.sons[i]), b, marker)
|
||||||
if result == arYes: return
|
if result == arYes: return
|
||||||
else: internalError("isPartOfAux(record case branch)")
|
else: internalError("isPartOfAux(record case branch)")
|
||||||
of nkSym:
|
of nkSym:
|
||||||
result = isPartOfAux(n.sym.typ, b, marker)
|
result = isPartOfAux(n.sym.typ, b, marker)
|
||||||
else: internalError(n.info, "isPartOfAux()")
|
else: internalError(n.info, "isPartOfAux()")
|
||||||
|
|
||||||
proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult =
|
proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult =
|
||||||
result = arNo
|
result = arNo
|
||||||
if a == nil or b == nil: return
|
if a == nil or b == nil: return
|
||||||
if containsOrIncl(marker, a.id): return
|
if containsOrIncl(marker, a.id): return
|
||||||
if compareTypes(a, b, dcEqIgnoreDistinct): return arYes
|
if compareTypes(a, b, dcEqIgnoreDistinct): return arYes
|
||||||
case a.kind
|
case a.kind
|
||||||
of tyObject:
|
of tyObject:
|
||||||
result = isPartOfAux(a.sons[0], b, marker)
|
result = isPartOfAux(a.sons[0], b, marker)
|
||||||
if result == arNo: result = isPartOfAux(a.n, b, marker)
|
if result == arNo: result = isPartOfAux(a.n, b, marker)
|
||||||
of tyGenericInst, tyDistinct:
|
of tyGenericInst, tyDistinct:
|
||||||
result = isPartOfAux(lastSon(a), b, marker)
|
result = isPartOfAux(lastSon(a), b, marker)
|
||||||
of tyArray, tyArrayConstr, tySet, tyTuple:
|
of tyArray, tyArrayConstr, tySet, tyTuple:
|
||||||
for i in countup(0, sonsLen(a) - 1):
|
for i in countup(0, sonsLen(a) - 1):
|
||||||
result = isPartOfAux(a.sons[i], b, marker)
|
result = isPartOfAux(a.sons[i], b, marker)
|
||||||
if result == arYes: return
|
if result == arYes: return
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc isPartOf(a, b: PType): TAnalysisResult =
|
proc isPartOf(a, b: PType): TAnalysisResult =
|
||||||
## checks iff 'a' can be part of 'b'. Iterates over VALUE types!
|
## checks iff 'a' can be part of 'b'. Iterates over VALUE types!
|
||||||
var marker = initIntSet()
|
var marker = initIntSet()
|
||||||
# watch out: parameters reversed because I'm too lazy to change the code...
|
# watch out: parameters reversed because I'm too lazy to change the code...
|
||||||
|
|
@ -70,14 +70,14 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||||
## type. Since however type analysis is more expensive, we perform it only
|
## type. Since however type analysis is more expensive, we perform it only
|
||||||
## if necessary.
|
## if necessary.
|
||||||
##
|
##
|
||||||
## cases:
|
## cases:
|
||||||
##
|
##
|
||||||
## YES-cases:
|
## YES-cases:
|
||||||
## x <| x # for general trees
|
## x <| x # for general trees
|
||||||
## x[] <| x
|
## x[] <| x
|
||||||
## x[i] <| x
|
## x[i] <| x
|
||||||
## x.f <| x
|
## x.f <| x
|
||||||
##
|
##
|
||||||
## NO-cases:
|
## NO-cases:
|
||||||
## x !<| y # depending on type and symbol kind
|
## x !<| y # depending on type and symbol kind
|
||||||
## x[constA] !<| x[constB]
|
## x[constA] !<| x[constB]
|
||||||
|
|
@ -88,16 +88,16 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||||
##
|
##
|
||||||
## x[] ?<| y[] iff compatible type
|
## x[] ?<| y[] iff compatible type
|
||||||
##
|
##
|
||||||
##
|
##
|
||||||
## x[] ?<| y depending on type
|
## x[] ?<| y depending on type
|
||||||
##
|
##
|
||||||
if a.kind == b.kind:
|
if a.kind == b.kind:
|
||||||
case a.kind
|
case a.kind
|
||||||
of nkSym:
|
of nkSym:
|
||||||
const varKinds = {skVar, skTemp, skProc}
|
const varKinds = {skVar, skTemp, skProc}
|
||||||
# same symbol: aliasing:
|
# same symbol: aliasing:
|
||||||
if a.sym.id == b.sym.id: result = arYes
|
if a.sym.id == b.sym.id: result = arYes
|
||||||
elif a.sym.kind in varKinds or b.sym.kind in varKinds:
|
elif a.sym.kind in varKinds or b.sym.kind in varKinds:
|
||||||
# actually, a param could alias a var but we know that cannot happen
|
# actually, a param could alias a var but we know that cannot happen
|
||||||
# here. XXX make this more generic
|
# here. XXX make this more generic
|
||||||
result = arNo
|
result = arNo
|
||||||
|
|
@ -110,11 +110,11 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||||
if len(a) >= 2 and len(b) >= 2:
|
if len(a) >= 2 and len(b) >= 2:
|
||||||
# array accesses:
|
# array accesses:
|
||||||
if result == arYes and isDeepConstExpr(a[1]) and isDeepConstExpr(b[1]):
|
if result == arYes and isDeepConstExpr(a[1]) and isDeepConstExpr(b[1]):
|
||||||
# we know it's the same array and we have 2 constant indexes;
|
# we know it's the same array and we have 2 constant indexes;
|
||||||
# if they are
|
# if they are
|
||||||
var x = if a[1].kind == nkHiddenStdConv: a[1][1] else: a[1]
|
var x = if a[1].kind == nkHiddenStdConv: a[1][1] else: a[1]
|
||||||
var y = if b[1].kind == nkHiddenStdConv: b[1][1] else: b[1]
|
var y = if b[1].kind == nkHiddenStdConv: b[1][1] else: b[1]
|
||||||
|
|
||||||
if sameValue(x, y): result = arYes
|
if sameValue(x, y): result = arYes
|
||||||
else: result = arNo
|
else: result = arNo
|
||||||
# else: maybe and no are accurate
|
# else: maybe and no are accurate
|
||||||
|
|
@ -122,7 +122,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||||
# pointer derefs:
|
# pointer derefs:
|
||||||
if result != arYes:
|
if result != arYes:
|
||||||
if isPartOf(a.typ, b.typ) != arNo: result = arMaybe
|
if isPartOf(a.typ, b.typ) != arNo: result = arMaybe
|
||||||
|
|
||||||
of nkDotExpr:
|
of nkDotExpr:
|
||||||
result = isPartOf(a[0], b[0])
|
result = isPartOf(a[0], b[0])
|
||||||
if result != arNo:
|
if result != arNo:
|
||||||
|
|
@ -135,7 +135,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||||
# weaken because of indirection:
|
# weaken because of indirection:
|
||||||
if result != arYes:
|
if result != arYes:
|
||||||
if isPartOf(a.typ, b.typ) != arNo: result = arMaybe
|
if isPartOf(a.typ, b.typ) != arNo: result = arMaybe
|
||||||
|
|
||||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||||
result = isPartOf(a[1], b[1])
|
result = isPartOf(a[1], b[1])
|
||||||
of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
|
of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
|
||||||
|
|
@ -144,7 +144,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||||
# Calls return a new location, so a default of ``arNo`` is fine.
|
# Calls return a new location, so a default of ``arNo`` is fine.
|
||||||
else:
|
else:
|
||||||
# go down recursively; this is quite demanding:
|
# go down recursively; this is quite demanding:
|
||||||
const
|
const
|
||||||
Ix0Kinds = {nkDotExpr, nkBracketExpr, nkObjUpConv, nkObjDownConv,
|
Ix0Kinds = {nkDotExpr, nkBracketExpr, nkObjUpConv, nkObjDownConv,
|
||||||
nkCheckedFieldExpr}
|
nkCheckedFieldExpr}
|
||||||
Ix1Kinds = {nkHiddenStdConv, nkHiddenSubConv, nkConv}
|
Ix1Kinds = {nkHiddenStdConv, nkHiddenSubConv, nkConv}
|
||||||
|
|
@ -153,17 +153,17 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||||
of Ix0Kinds:
|
of Ix0Kinds:
|
||||||
# a* !<| b.f iff a* !<| b
|
# a* !<| b.f iff a* !<| b
|
||||||
result = isPartOf(a, b[0])
|
result = isPartOf(a, b[0])
|
||||||
|
|
||||||
of DerefKinds:
|
of DerefKinds:
|
||||||
# a* !<| b[] iff
|
# a* !<| b[] iff
|
||||||
if isPartOf(a.typ, b.typ) != arNo:
|
if isPartOf(a.typ, b.typ) != arNo:
|
||||||
result = isPartOf(a, b[0])
|
result = isPartOf(a, b[0])
|
||||||
if result == arNo: result = arMaybe
|
if result == arNo: result = arMaybe
|
||||||
|
|
||||||
of Ix1Kinds:
|
of Ix1Kinds:
|
||||||
# a* !<| T(b) iff a* !<| b
|
# a* !<| T(b) iff a* !<| b
|
||||||
result = isPartOf(a, b[1])
|
result = isPartOf(a, b[1])
|
||||||
|
|
||||||
of nkSym:
|
of nkSym:
|
||||||
# b is an atom, so we have to check a:
|
# b is an atom, so we have to check a:
|
||||||
case a.kind
|
case a.kind
|
||||||
|
|
@ -172,7 +172,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||||
result = isPartOf(a[0], b)
|
result = isPartOf(a[0], b)
|
||||||
of Ix1Kinds:
|
of Ix1Kinds:
|
||||||
result = isPartOf(a[1], b)
|
result = isPartOf(a[1], b)
|
||||||
|
|
||||||
of DerefKinds:
|
of DerefKinds:
|
||||||
if isPartOf(a.typ, b.typ) != arNo:
|
if isPartOf(a.typ, b.typ) != arNo:
|
||||||
result = isPartOf(a[0], b)
|
result = isPartOf(a[0], b)
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,12 @@
|
||||||
# this unit handles Nim sets; it implements bit sets
|
# this unit handles Nim sets; it implements bit sets
|
||||||
# the code here should be reused in the Nim standard library
|
# the code here should be reused in the Nim standard library
|
||||||
|
|
||||||
type
|
type
|
||||||
TBitSet* = seq[int8] # we use byte here to avoid issues with
|
TBitSet* = seq[int8] # we use byte here to avoid issues with
|
||||||
# cross-compiling; uint would be more efficient
|
# cross-compiling; uint would be more efficient
|
||||||
# however
|
# however
|
||||||
|
|
||||||
const
|
const
|
||||||
ElemSize* = sizeof(int8) * 8
|
ElemSize* = sizeof(int8) * 8
|
||||||
|
|
||||||
proc bitSetInit*(b: var TBitSet, length: int)
|
proc bitSetInit*(b: var TBitSet, length: int)
|
||||||
|
|
@ -30,42 +30,42 @@ proc bitSetEquals*(x, y: TBitSet): bool
|
||||||
proc bitSetContains*(x, y: TBitSet): bool
|
proc bitSetContains*(x, y: TBitSet): bool
|
||||||
# implementation
|
# implementation
|
||||||
|
|
||||||
proc bitSetIn(x: TBitSet, e: BiggestInt): bool =
|
proc bitSetIn(x: TBitSet, e: BiggestInt): bool =
|
||||||
result = (x[int(e div ElemSize)] and toU8(int(1 shl (e mod ElemSize)))) !=
|
result = (x[int(e div ElemSize)] and toU8(int(1 shl (e mod ElemSize)))) !=
|
||||||
toU8(0)
|
toU8(0)
|
||||||
|
|
||||||
proc bitSetIncl(x: var TBitSet, elem: BiggestInt) =
|
proc bitSetIncl(x: var TBitSet, elem: BiggestInt) =
|
||||||
assert(elem >= 0)
|
assert(elem >= 0)
|
||||||
x[int(elem div ElemSize)] = x[int(elem div ElemSize)] or
|
x[int(elem div ElemSize)] = x[int(elem div ElemSize)] or
|
||||||
toU8(int(1 shl (elem mod ElemSize)))
|
toU8(int(1 shl (elem mod ElemSize)))
|
||||||
|
|
||||||
proc bitSetExcl(x: var TBitSet, elem: BiggestInt) =
|
proc bitSetExcl(x: var TBitSet, elem: BiggestInt) =
|
||||||
x[int(elem div ElemSize)] = x[int(elem div ElemSize)] and
|
x[int(elem div ElemSize)] = x[int(elem div ElemSize)] and
|
||||||
not toU8(int(1 shl (elem mod ElemSize)))
|
not toU8(int(1 shl (elem mod ElemSize)))
|
||||||
|
|
||||||
proc bitSetInit(b: var TBitSet, length: int) =
|
proc bitSetInit(b: var TBitSet, length: int) =
|
||||||
newSeq(b, length)
|
newSeq(b, length)
|
||||||
|
|
||||||
proc bitSetUnion(x: var TBitSet, y: TBitSet) =
|
proc bitSetUnion(x: var TBitSet, y: TBitSet) =
|
||||||
for i in countup(0, high(x)): x[i] = x[i] or y[i]
|
for i in countup(0, high(x)): x[i] = x[i] or y[i]
|
||||||
|
|
||||||
proc bitSetDiff(x: var TBitSet, y: TBitSet) =
|
proc bitSetDiff(x: var TBitSet, y: TBitSet) =
|
||||||
for i in countup(0, high(x)): x[i] = x[i] and not y[i]
|
for i in countup(0, high(x)): x[i] = x[i] and not y[i]
|
||||||
|
|
||||||
proc bitSetSymDiff(x: var TBitSet, y: TBitSet) =
|
proc bitSetSymDiff(x: var TBitSet, y: TBitSet) =
|
||||||
for i in countup(0, high(x)): x[i] = x[i] xor y[i]
|
for i in countup(0, high(x)): x[i] = x[i] xor y[i]
|
||||||
|
|
||||||
proc bitSetIntersect(x: var TBitSet, y: TBitSet) =
|
proc bitSetIntersect(x: var TBitSet, y: TBitSet) =
|
||||||
for i in countup(0, high(x)): x[i] = x[i] and y[i]
|
for i in countup(0, high(x)): x[i] = x[i] and y[i]
|
||||||
|
|
||||||
proc bitSetEquals(x, y: TBitSet): bool =
|
proc bitSetEquals(x, y: TBitSet): bool =
|
||||||
for i in countup(0, high(x)):
|
for i in countup(0, high(x)):
|
||||||
if x[i] != y[i]:
|
if x[i] != y[i]:
|
||||||
return false
|
return false
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
proc bitSetContains(x, y: TBitSet): bool =
|
proc bitSetContains(x, y: TBitSet): bool =
|
||||||
for i in countup(0, high(x)):
|
for i in countup(0, high(x)):
|
||||||
if (x[i] and not y[i]) != int8(0):
|
if (x[i] and not y[i]) != int8(0):
|
||||||
return false
|
return false
|
||||||
result = true
|
result = true
|
||||||
|
|
|
||||||
|
|
@ -30,39 +30,39 @@ type
|
||||||
#
|
#
|
||||||
# This is a good compromise between correctness and brevity. ;-)
|
# This is a good compromise between correctness and brevity. ;-)
|
||||||
|
|
||||||
const
|
const
|
||||||
cb64 = [
|
cb64 = [
|
||||||
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
|
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
|
||||||
"O", "P", "Q", "R", "S", "T" "U", "V", "W", "X", "Y", "Z",
|
"O", "P", "Q", "R", "S", "T" "U", "V", "W", "X", "Y", "Z",
|
||||||
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
|
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
|
||||||
"o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
|
"o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
|
||||||
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||||
"_A", "_B"]
|
"_A", "_B"]
|
||||||
|
|
||||||
proc toBase64a(s: cstring, len: int): string =
|
proc toBase64a(s: cstring, len: int): string =
|
||||||
## encodes `s` into base64 representation. After `lineLen` characters, a
|
## encodes `s` into base64 representation. After `lineLen` characters, a
|
||||||
## `newline` is added.
|
## `newline` is added.
|
||||||
result = newStringOfCap(((len + 2) div 3) * 4)
|
result = newStringOfCap(((len + 2) div 3) * 4)
|
||||||
var i = 0
|
var i = 0
|
||||||
while i < s.len - 2:
|
while i < s.len - 2:
|
||||||
let a = ord(s[i])
|
let a = ord(s[i])
|
||||||
let b = ord(s[i+1])
|
let b = ord(s[i+1])
|
||||||
let c = ord(s[i+2])
|
let c = ord(s[i+2])
|
||||||
result.add cb64[a shr 2]
|
result.add cb64[a shr 2]
|
||||||
result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||||
result.add cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
|
result.add cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
|
||||||
result.add cb64[c and 0x3F]
|
result.add cb64[c and 0x3F]
|
||||||
inc(i, 3)
|
inc(i, 3)
|
||||||
if i < s.len-1:
|
if i < s.len-1:
|
||||||
let a = ord(s[i])
|
let a = ord(s[i])
|
||||||
let b = ord(s[i+1])
|
let b = ord(s[i+1])
|
||||||
result.add cb64[a shr 2]
|
result.add cb64[a shr 2]
|
||||||
result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||||
result.add cb64[((b and 0x0F) shl 2)]
|
result.add cb64[((b and 0x0F) shl 2)]
|
||||||
elif i < s.len:
|
elif i < s.len:
|
||||||
let a = ord(s[i])
|
let a = ord(s[i])
|
||||||
result.add cb64[a shr 2]
|
result.add cb64[a shr 2]
|
||||||
result.add cb64[(a and 3) shl 4]
|
result.add cb64[(a and 3) shl 4]
|
||||||
|
|
||||||
proc toBase64a(u: TUid): string = toBase64a(cast[cstring](u), sizeof(u))
|
proc toBase64a(u: TUid): string = toBase64a(cast[cstring](u), sizeof(u))
|
||||||
|
|
||||||
|
|
@ -73,7 +73,7 @@ proc hashSym(c: var MD5Context, s: PSym) =
|
||||||
c &= ":anon"
|
c &= ":anon"
|
||||||
else:
|
else:
|
||||||
var it = s.owner
|
var it = s.owner
|
||||||
while it != nil:
|
while it != nil:
|
||||||
hashSym(c, it)
|
hashSym(c, it)
|
||||||
c &= "."
|
c &= "."
|
||||||
it = s.owner
|
it = s.owner
|
||||||
|
|
@ -106,18 +106,18 @@ proc hashTree(c: var MD5Context, n: PNode) =
|
||||||
|
|
||||||
proc hashType(c: var MD5Context, t: PType) =
|
proc hashType(c: var MD5Context, t: PType) =
|
||||||
# modelled after 'typeToString'
|
# modelled after 'typeToString'
|
||||||
if t == nil:
|
if t == nil:
|
||||||
c &= "\254"
|
c &= "\254"
|
||||||
return
|
return
|
||||||
|
|
||||||
var k = t.kind
|
var k = t.kind
|
||||||
md5Update(c, cast[cstring](addr(k)), 1)
|
md5Update(c, cast[cstring](addr(k)), 1)
|
||||||
|
|
||||||
if t.sym != nil and sfAnon notin t.sym.flags:
|
if t.sym != nil and sfAnon notin t.sym.flags:
|
||||||
# t.n for literals, but not for e.g. objects!
|
# t.n for literals, but not for e.g. objects!
|
||||||
if t.kind in {tyFloat, tyInt}: c.hashNode(t.n)
|
if t.kind in {tyFloat, tyInt}: c.hashNode(t.n)
|
||||||
c.hashSym(t.sym)
|
c.hashSym(t.sym)
|
||||||
|
|
||||||
case t.kind
|
case t.kind
|
||||||
of tyGenericBody, tyGenericInst, tyGenericInvocation:
|
of tyGenericBody, tyGenericInst, tyGenericInvocation:
|
||||||
for i in countup(0, sonsLen(t) -1 -ord(t.kind != tyGenericInvocation)):
|
for i in countup(0, sonsLen(t) -1 -ord(t.kind != tyGenericInvocation)):
|
||||||
|
|
@ -135,10 +135,10 @@ proc hashType(c: var MD5Context, t: PType) =
|
||||||
of tyArrayConstr:
|
of tyArrayConstr:
|
||||||
c.hashTree(t.sons[0].n)
|
c.hashTree(t.sons[0].n)
|
||||||
c.hashType(t.sons[1])
|
c.hashType(t.sons[1])
|
||||||
of tyTuple:
|
of tyTuple:
|
||||||
if t.n != nil:
|
if t.n != nil:
|
||||||
assert(sonsLen(t.n) == sonsLen(t))
|
assert(sonsLen(t.n) == sonsLen(t))
|
||||||
for i in countup(0, sonsLen(t.n) - 1):
|
for i in countup(0, sonsLen(t.n) - 1):
|
||||||
assert(t.n.sons[i].kind == nkSym)
|
assert(t.n.sons[i].kind == nkSym)
|
||||||
c &= t.n.sons[i].sym.name.s
|
c &= t.n.sons[i].sym.name.s
|
||||||
c &= ":"
|
c &= ":"
|
||||||
|
|
@ -184,18 +184,18 @@ proc pushSym(w: PRodWriter, s: PSym) =
|
||||||
if iiTableGet(w.index.tab, s.id) == InvalidKey:
|
if iiTableGet(w.index.tab, s.id) == InvalidKey:
|
||||||
w.sstack.add(s)
|
w.sstack.add(s)
|
||||||
|
|
||||||
proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
|
proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
|
||||||
result: var string) =
|
result: var string) =
|
||||||
if n == nil:
|
if n == nil:
|
||||||
# nil nodes have to be stored too:
|
# nil nodes have to be stored too:
|
||||||
result.add("()")
|
result.add("()")
|
||||||
return
|
return
|
||||||
result.add('(')
|
result.add('(')
|
||||||
encodeVInt(ord(n.kind), result)
|
encodeVInt(ord(n.kind), result)
|
||||||
# we do not write comments for now
|
# we do not write comments for now
|
||||||
# Line information takes easily 20% or more of the filesize! Therefore we
|
# Line information takes easily 20% or more of the filesize! Therefore we
|
||||||
# omit line information if it is the same as the father's line information:
|
# omit line information if it is the same as the father's line information:
|
||||||
if fInfo.fileIndex != n.info.fileIndex:
|
if fInfo.fileIndex != n.info.fileIndex:
|
||||||
result.add('?')
|
result.add('?')
|
||||||
encodeVInt(n.info.col, result)
|
encodeVInt(n.info.col, result)
|
||||||
result.add(',')
|
result.add(',')
|
||||||
|
|
@ -211,7 +211,7 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
|
||||||
result.add('?')
|
result.add('?')
|
||||||
encodeVInt(n.info.col, result)
|
encodeVInt(n.info.col, result)
|
||||||
var f = n.flags * PersistentNodeFlags
|
var f = n.flags * PersistentNodeFlags
|
||||||
if f != {}:
|
if f != {}:
|
||||||
result.add('$')
|
result.add('$')
|
||||||
encodeVInt(cast[int32](f), result)
|
encodeVInt(cast[int32](f), result)
|
||||||
if n.typ != nil:
|
if n.typ != nil:
|
||||||
|
|
@ -219,16 +219,16 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
|
||||||
encodeVInt(n.typ.id, result)
|
encodeVInt(n.typ.id, result)
|
||||||
pushType(w, n.typ)
|
pushType(w, n.typ)
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkCharLit..nkInt64Lit:
|
of nkCharLit..nkInt64Lit:
|
||||||
if n.intVal != 0:
|
if n.intVal != 0:
|
||||||
result.add('!')
|
result.add('!')
|
||||||
encodeVBiggestInt(n.intVal, result)
|
encodeVBiggestInt(n.intVal, result)
|
||||||
of nkFloatLit..nkFloat64Lit:
|
of nkFloatLit..nkFloat64Lit:
|
||||||
if n.floatVal != 0.0:
|
if n.floatVal != 0.0:
|
||||||
result.add('!')
|
result.add('!')
|
||||||
encodeStr($n.floatVal, result)
|
encodeStr($n.floatVal, result)
|
||||||
of nkStrLit..nkTripleStrLit:
|
of nkStrLit..nkTripleStrLit:
|
||||||
if n.strVal != "":
|
if n.strVal != "":
|
||||||
result.add('!')
|
result.add('!')
|
||||||
encodeStr(n.strVal, result)
|
encodeStr(n.strVal, result)
|
||||||
of nkIdent:
|
of nkIdent:
|
||||||
|
|
@ -239,7 +239,7 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
|
||||||
encodeVInt(n.sym.id, result)
|
encodeVInt(n.sym.id, result)
|
||||||
pushSym(w, n.sym)
|
pushSym(w, n.sym)
|
||||||
else:
|
else:
|
||||||
for i in countup(0, sonsLen(n) - 1):
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
encodeNode(w, n.info, n.sons[i], result)
|
encodeNode(w, n.info, n.sons[i], result)
|
||||||
add(result, ')')
|
add(result, ')')
|
||||||
|
|
||||||
|
|
@ -268,9 +268,9 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
|
||||||
setLen(result, oldLen)
|
setLen(result, oldLen)
|
||||||
else:
|
else:
|
||||||
add(result, '>')
|
add(result, '>')
|
||||||
|
|
||||||
proc encodeType(w: PRodWriter, t: PType, result: var string) =
|
proc encodeType(w: PRodWriter, t: PType, result: var string) =
|
||||||
if t == nil:
|
if t == nil:
|
||||||
# nil nodes have to be stored too:
|
# nil nodes have to be stored too:
|
||||||
result.add("[]")
|
result.add("[]")
|
||||||
return
|
return
|
||||||
|
|
@ -282,38 +282,38 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
|
||||||
encodeVInt(ord(t.kind), result)
|
encodeVInt(ord(t.kind), result)
|
||||||
add(result, '+')
|
add(result, '+')
|
||||||
encodeVInt(t.id, result)
|
encodeVInt(t.id, result)
|
||||||
if t.n != nil:
|
if t.n != nil:
|
||||||
encodeNode(w, unknownLineInfo(), t.n, result)
|
encodeNode(w, unknownLineInfo(), t.n, result)
|
||||||
if t.flags != {}:
|
if t.flags != {}:
|
||||||
add(result, '$')
|
add(result, '$')
|
||||||
encodeVInt(cast[int32](t.flags), result)
|
encodeVInt(cast[int32](t.flags), result)
|
||||||
if t.callConv != low(t.callConv):
|
if t.callConv != low(t.callConv):
|
||||||
add(result, '?')
|
add(result, '?')
|
||||||
encodeVInt(ord(t.callConv), result)
|
encodeVInt(ord(t.callConv), result)
|
||||||
if t.owner != nil:
|
if t.owner != nil:
|
||||||
add(result, '*')
|
add(result, '*')
|
||||||
encodeVInt(t.owner.id, result)
|
encodeVInt(t.owner.id, result)
|
||||||
pushSym(w, t.owner)
|
pushSym(w, t.owner)
|
||||||
if t.sym != nil:
|
if t.sym != nil:
|
||||||
add(result, '&')
|
add(result, '&')
|
||||||
encodeVInt(t.sym.id, result)
|
encodeVInt(t.sym.id, result)
|
||||||
pushSym(w, t.sym)
|
pushSym(w, t.sym)
|
||||||
if t.size != - 1:
|
if t.size != - 1:
|
||||||
add(result, '/')
|
add(result, '/')
|
||||||
encodeVBiggestInt(t.size, result)
|
encodeVBiggestInt(t.size, result)
|
||||||
if t.align != 2:
|
if t.align != 2:
|
||||||
add(result, '=')
|
add(result, '=')
|
||||||
encodeVInt(t.align, result)
|
encodeVInt(t.align, result)
|
||||||
encodeLoc(w, t.loc, result)
|
encodeLoc(w, t.loc, result)
|
||||||
for i in countup(0, sonsLen(t) - 1):
|
for i in countup(0, sonsLen(t) - 1):
|
||||||
if t.sons[i] == nil:
|
if t.sons[i] == nil:
|
||||||
add(result, "^()")
|
add(result, "^()")
|
||||||
else:
|
else:
|
||||||
add(result, '^')
|
add(result, '^')
|
||||||
encodeVInt(t.sons[i].id, result)
|
encodeVInt(t.sons[i].id, result)
|
||||||
pushType(w, t.sons[i])
|
pushType(w, t.sons[i])
|
||||||
|
|
||||||
proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
|
proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
|
||||||
add(result, '|')
|
add(result, '|')
|
||||||
encodeVInt(ord(lib.kind), result)
|
encodeVInt(ord(lib.kind), result)
|
||||||
add(result, '|')
|
add(result, '|')
|
||||||
|
|
@ -352,10 +352,10 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
|
||||||
if s.magic != mNone:
|
if s.magic != mNone:
|
||||||
result.add('@')
|
result.add('@')
|
||||||
encodeVInt(ord(s.magic), result)
|
encodeVInt(ord(s.magic), result)
|
||||||
if s.options != w.options:
|
if s.options != w.options:
|
||||||
result.add('!')
|
result.add('!')
|
||||||
encodeVInt(cast[int32](s.options), result)
|
encodeVInt(cast[int32](s.options), result)
|
||||||
if s.position != 0:
|
if s.position != 0:
|
||||||
result.add('%')
|
result.add('%')
|
||||||
encodeVInt(s.position, result)
|
encodeVInt(s.position, result)
|
||||||
if s.offset != - 1:
|
if s.offset != - 1:
|
||||||
|
|
@ -383,7 +383,7 @@ proc createDb() =
|
||||||
fullpath varchar(256) not null,
|
fullpath varchar(256) not null,
|
||||||
interfHash varchar(256) not null,
|
interfHash varchar(256) not null,
|
||||||
fullHash varchar(256) not null,
|
fullHash varchar(256) not null,
|
||||||
|
|
||||||
created timestamp not null default (DATETIME('now')),
|
created timestamp not null default (DATETIME('now')),
|
||||||
);""")
|
);""")
|
||||||
|
|
||||||
|
|
@ -397,7 +397,7 @@ proc createDb() =
|
||||||
|
|
||||||
foreign key (module) references module(id)
|
foreign key (module) references module(id)
|
||||||
);""")
|
);""")
|
||||||
|
|
||||||
db.exec(sql"""
|
db.exec(sql"""
|
||||||
create table if not exists Type(
|
create table if not exists Type(
|
||||||
id integer primary key,
|
id integer primary key,
|
||||||
|
|
|
||||||
|
|
@ -9,33 +9,33 @@
|
||||||
|
|
||||||
## This module implements code generation for multi methods.
|
## This module implements code generation for multi methods.
|
||||||
|
|
||||||
import
|
import
|
||||||
intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys,
|
intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys,
|
||||||
sempass2, strutils
|
sempass2, strutils
|
||||||
|
|
||||||
proc genConv(n: PNode, d: PType, downcast: bool): PNode =
|
proc genConv(n: PNode, d: PType, downcast: bool): PNode =
|
||||||
var dest = skipTypes(d, abstractPtrs)
|
var dest = skipTypes(d, abstractPtrs)
|
||||||
var source = skipTypes(n.typ, abstractPtrs)
|
var source = skipTypes(n.typ, abstractPtrs)
|
||||||
if (source.kind == tyObject) and (dest.kind == tyObject):
|
if (source.kind == tyObject) and (dest.kind == tyObject):
|
||||||
var diff = inheritanceDiff(dest, source)
|
var diff = inheritanceDiff(dest, source)
|
||||||
if diff == high(int): internalError(n.info, "cgmeth.genConv")
|
if diff == high(int): internalError(n.info, "cgmeth.genConv")
|
||||||
if diff < 0:
|
if diff < 0:
|
||||||
result = newNodeIT(nkObjUpConv, n.info, d)
|
result = newNodeIT(nkObjUpConv, n.info, d)
|
||||||
addSon(result, n)
|
addSon(result, n)
|
||||||
if downcast: internalError(n.info, "cgmeth.genConv: no upcast allowed")
|
if downcast: internalError(n.info, "cgmeth.genConv: no upcast allowed")
|
||||||
elif diff > 0:
|
elif diff > 0:
|
||||||
result = newNodeIT(nkObjDownConv, n.info, d)
|
result = newNodeIT(nkObjDownConv, n.info, d)
|
||||||
addSon(result, n)
|
addSon(result, n)
|
||||||
if not downcast:
|
if not downcast:
|
||||||
internalError(n.info, "cgmeth.genConv: no downcast allowed")
|
internalError(n.info, "cgmeth.genConv: no downcast allowed")
|
||||||
else:
|
else:
|
||||||
result = n
|
result = n
|
||||||
else:
|
else:
|
||||||
result = n
|
result = n
|
||||||
|
|
||||||
proc methodCall*(n: PNode): PNode =
|
proc methodCall*(n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
# replace ordinary method by dispatcher method:
|
# replace ordinary method by dispatcher method:
|
||||||
var disp = lastSon(result.sons[0].sym.ast).sym
|
var disp = lastSon(result.sons[0].sym.ast).sym
|
||||||
assert sfDispatcher in disp.flags
|
assert sfDispatcher in disp.flags
|
||||||
result.sons[0].sym = disp
|
result.sons[0].sym = disp
|
||||||
|
|
@ -47,23 +47,23 @@ proc methodCall*(n: PNode): PNode =
|
||||||
var
|
var
|
||||||
gMethods: seq[tuple[methods: TSymSeq, dispatcher: PSym]] = @[]
|
gMethods: seq[tuple[methods: TSymSeq, dispatcher: PSym]] = @[]
|
||||||
|
|
||||||
proc sameMethodBucket(a, b: PSym): bool =
|
proc sameMethodBucket(a, b: PSym): bool =
|
||||||
result = false
|
result = false
|
||||||
if a.name.id != b.name.id: return
|
if a.name.id != b.name.id: return
|
||||||
if sonsLen(a.typ) != sonsLen(b.typ):
|
if sonsLen(a.typ) != sonsLen(b.typ):
|
||||||
return # check for return type:
|
return # check for return type:
|
||||||
if not sameTypeOrNil(a.typ.sons[0], b.typ.sons[0]): return
|
if not sameTypeOrNil(a.typ.sons[0], b.typ.sons[0]): return
|
||||||
for i in countup(1, sonsLen(a.typ) - 1):
|
for i in countup(1, sonsLen(a.typ) - 1):
|
||||||
var aa = a.typ.sons[i]
|
var aa = a.typ.sons[i]
|
||||||
var bb = b.typ.sons[i]
|
var bb = b.typ.sons[i]
|
||||||
while true:
|
while true:
|
||||||
aa = skipTypes(aa, {tyGenericInst})
|
aa = skipTypes(aa, {tyGenericInst})
|
||||||
bb = skipTypes(bb, {tyGenericInst})
|
bb = skipTypes(bb, {tyGenericInst})
|
||||||
if (aa.kind == bb.kind) and (aa.kind in {tyVar, tyPtr, tyRef}):
|
if (aa.kind == bb.kind) and (aa.kind in {tyVar, tyPtr, tyRef}):
|
||||||
aa = aa.lastSon
|
aa = aa.lastSon
|
||||||
bb = bb.lastSon
|
bb = bb.lastSon
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
if sameType(aa, bb) or
|
if sameType(aa, bb) or
|
||||||
(aa.kind == tyObject) and (bb.kind == tyObject) and
|
(aa.kind == tyObject) and (bb.kind == tyObject) and
|
||||||
(inheritanceDiff(bb, aa) < 0):
|
(inheritanceDiff(bb, aa) < 0):
|
||||||
|
|
@ -140,7 +140,7 @@ proc methodDef*(s: PSym, fromCache: bool) =
|
||||||
attachDispatcher(s, lastSon(disp.ast))
|
attachDispatcher(s, lastSon(disp.ast))
|
||||||
fixupDispatcher(s, disp)
|
fixupDispatcher(s, disp)
|
||||||
when useEffectSystem: checkMethodEffects(disp, s)
|
when useEffectSystem: checkMethodEffects(disp, s)
|
||||||
return
|
return
|
||||||
# create a new dispatcher:
|
# create a new dispatcher:
|
||||||
add(gMethods, (methods: @[s], dispatcher: createDispatcher(s)))
|
add(gMethods, (methods: @[s], dispatcher: createDispatcher(s)))
|
||||||
if fromCache:
|
if fromCache:
|
||||||
|
|
@ -154,35 +154,35 @@ proc relevantCol(methods: TSymSeq, col: int): bool =
|
||||||
let t2 = skipTypes(methods[i].typ.sons[col], skipPtrs)
|
let t2 = skipTypes(methods[i].typ.sons[col], skipPtrs)
|
||||||
if not sameType(t2, t):
|
if not sameType(t2, t):
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int =
|
proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int =
|
||||||
for col in countup(1, sonsLen(a.typ) - 1):
|
for col in countup(1, sonsLen(a.typ) - 1):
|
||||||
if contains(relevantCols, col):
|
if contains(relevantCols, col):
|
||||||
var aa = skipTypes(a.typ.sons[col], skipPtrs)
|
var aa = skipTypes(a.typ.sons[col], skipPtrs)
|
||||||
var bb = skipTypes(b.typ.sons[col], skipPtrs)
|
var bb = skipTypes(b.typ.sons[col], skipPtrs)
|
||||||
var d = inheritanceDiff(aa, bb)
|
var d = inheritanceDiff(aa, bb)
|
||||||
if (d != high(int)):
|
if (d != high(int)):
|
||||||
return d
|
return d
|
||||||
|
|
||||||
proc sortBucket(a: var TSymSeq, relevantCols: IntSet) =
|
proc sortBucket(a: var TSymSeq, relevantCols: IntSet) =
|
||||||
# we use shellsort here; fast and simple
|
# we use shellsort here; fast and simple
|
||||||
var n = len(a)
|
var n = len(a)
|
||||||
var h = 1
|
var h = 1
|
||||||
while true:
|
while true:
|
||||||
h = 3 * h + 1
|
h = 3 * h + 1
|
||||||
if h > n: break
|
if h > n: break
|
||||||
while true:
|
while true:
|
||||||
h = h div 3
|
h = h div 3
|
||||||
for i in countup(h, n - 1):
|
for i in countup(h, n - 1):
|
||||||
var v = a[i]
|
var v = a[i]
|
||||||
var j = i
|
var j = i
|
||||||
while cmpSignatures(a[j - h], v, relevantCols) >= 0:
|
while cmpSignatures(a[j - h], v, relevantCols) >= 0:
|
||||||
a[j] = a[j - h]
|
a[j] = a[j - h]
|
||||||
j = j - h
|
j = j - h
|
||||||
if j < h: break
|
if j < h: break
|
||||||
a[j] = v
|
a[j] = v
|
||||||
if h == 1: break
|
if h == 1: break
|
||||||
|
|
||||||
proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
|
proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
|
||||||
var base = lastSon(methods[0].ast).sym
|
var base = lastSon(methods[0].ast).sym
|
||||||
result = base
|
result = base
|
||||||
|
|
@ -199,7 +199,7 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
|
||||||
addSon(isn, newSymNode(iss))
|
addSon(isn, newSymNode(iss))
|
||||||
addSon(isn, newSymNode(base.typ.n.sons[col].sym))
|
addSon(isn, newSymNode(base.typ.n.sons[col].sym))
|
||||||
addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col]))
|
addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col]))
|
||||||
if cond != nil:
|
if cond != nil:
|
||||||
var a = newNodeIT(nkCall, base.info, getSysType(tyBool))
|
var a = newNodeIT(nkCall, base.info, getSysType(tyBool))
|
||||||
addSon(a, newSymNode(ands))
|
addSon(a, newSymNode(ands))
|
||||||
addSon(a, cond)
|
addSon(a, cond)
|
||||||
|
|
@ -209,8 +209,8 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
|
||||||
cond = isn
|
cond = isn
|
||||||
var call = newNodeI(nkCall, base.info)
|
var call = newNodeI(nkCall, base.info)
|
||||||
addSon(call, newSymNode(curr))
|
addSon(call, newSymNode(curr))
|
||||||
for col in countup(1, paramLen - 1):
|
for col in countup(1, paramLen - 1):
|
||||||
addSon(call, genConv(newSymNode(base.typ.n.sons[col].sym),
|
addSon(call, genConv(newSymNode(base.typ.n.sons[col].sym),
|
||||||
curr.typ.sons[col], false))
|
curr.typ.sons[col], false))
|
||||||
var ret: PNode
|
var ret: PNode
|
||||||
if base.typ.sons[0] != nil:
|
if base.typ.sons[0] != nil:
|
||||||
|
|
@ -230,11 +230,11 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
|
||||||
disp = ret
|
disp = ret
|
||||||
result.ast.sons[bodyPos] = disp
|
result.ast.sons[bodyPos] = disp
|
||||||
|
|
||||||
proc generateMethodDispatchers*(): PNode =
|
proc generateMethodDispatchers*(): PNode =
|
||||||
result = newNode(nkStmtList)
|
result = newNode(nkStmtList)
|
||||||
for bucket in countup(0, len(gMethods) - 1):
|
for bucket in countup(0, len(gMethods) - 1):
|
||||||
var relevantCols = initIntSet()
|
var relevantCols = initIntSet()
|
||||||
for col in countup(1, sonsLen(gMethods[bucket].methods[0].typ) - 1):
|
for col in countup(1, sonsLen(gMethods[bucket].methods[0].typ) - 1):
|
||||||
if relevantCol(gMethods[bucket].methods, col): incl(relevantCols, col)
|
if relevantCol(gMethods[bucket].methods, col): incl(relevantCols, col)
|
||||||
sortBucket(gMethods[bucket].methods, relevantCols)
|
sortBucket(gMethods[bucket].methods, relevantCols)
|
||||||
addSon(result,
|
addSon(result,
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,10 @@
|
||||||
# This module implements a new documentation generator that runs after
|
# This module implements a new documentation generator that runs after
|
||||||
# semantic checking.
|
# semantic checking.
|
||||||
|
|
||||||
import
|
import
|
||||||
os, options, ast, astalgo, msgs, ropes, idents, passes, docgen
|
os, options, ast, astalgo, msgs, ropes, idents, passes, docgen
|
||||||
|
|
||||||
type
|
type
|
||||||
TGen = object of TPassContext
|
TGen = object of TPassContext
|
||||||
doc: PDoc
|
doc: PDoc
|
||||||
module: PSym
|
module: PSym
|
||||||
|
|
@ -29,12 +29,12 @@ proc close(p: PPassContext, n: PNode): PNode =
|
||||||
except IOError:
|
except IOError:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
proc processNode(c: PPassContext, n: PNode): PNode =
|
proc processNode(c: PPassContext, n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
var g = PGen(c)
|
var g = PGen(c)
|
||||||
generateDoc(g.doc, n)
|
generateDoc(g.doc, n)
|
||||||
|
|
||||||
proc myOpen(module: PSym): PPassContext =
|
proc myOpen(module: PSym): PPassContext =
|
||||||
var g: PGen
|
var g: PGen
|
||||||
new(g)
|
new(g)
|
||||||
g.module = module
|
g.module = module
|
||||||
|
|
@ -45,5 +45,5 @@ proc myOpen(module: PSym): PPassContext =
|
||||||
|
|
||||||
const docgen2Pass* = makePass(open = myOpen, process = processNode, close = close)
|
const docgen2Pass* = makePass(open = myOpen, process = processNode, close = close)
|
||||||
|
|
||||||
proc finishDoc2Pass*(project: string) =
|
proc finishDoc2Pass*(project: string) =
|
||||||
discard
|
discard
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
||||||
for y in items(x): result.add(y)
|
for y in items(x): result.add(y)
|
||||||
else:
|
else:
|
||||||
result.add copyTree(x)
|
result.add copyTree(x)
|
||||||
|
|
||||||
case templ.kind
|
case templ.kind
|
||||||
of nkSym:
|
of nkSym:
|
||||||
var s = templ.sym
|
var s = templ.sym
|
||||||
|
|
@ -81,7 +81,7 @@ proc evalTemplateArgs(n: PNode, s: PSym): PNode =
|
||||||
|
|
||||||
if totalParams > expectedRegularParams + genericParams:
|
if totalParams > expectedRegularParams + genericParams:
|
||||||
globalError(n.info, errWrongNumberOfArguments)
|
globalError(n.info, errWrongNumberOfArguments)
|
||||||
|
|
||||||
result = newNodeI(nkArgList, n.info)
|
result = newNodeI(nkArgList, n.info)
|
||||||
for i in 1 .. givenRegularParams:
|
for i in 1 .. givenRegularParams:
|
||||||
result.addSon n.sons[i]
|
result.addSon n.sons[i]
|
||||||
|
|
@ -96,7 +96,7 @@ proc evalTemplateArgs(n: PNode, s: PSym): PNode =
|
||||||
addSon(result, ast.emptyNode)
|
addSon(result, ast.emptyNode)
|
||||||
else:
|
else:
|
||||||
addSon(result, default.copyTree)
|
addSon(result, default.copyTree)
|
||||||
|
|
||||||
# add any generic paramaters
|
# add any generic paramaters
|
||||||
for i in 1 .. genericParams:
|
for i in 1 .. genericParams:
|
||||||
result.addSon n.sons[givenRegularParams + i]
|
result.addSon n.sons[givenRegularParams + i]
|
||||||
|
|
|
||||||
|
|
@ -9,18 +9,18 @@
|
||||||
|
|
||||||
# This module implements Nim's standard template filter.
|
# This module implements Nim's standard template filter.
|
||||||
|
|
||||||
import
|
import
|
||||||
llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
|
llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
|
||||||
renderer, filters
|
renderer, filters
|
||||||
|
|
||||||
proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode): PLLStream
|
proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode): PLLStream
|
||||||
# #! template(subsChar='$', metaChar='#') | standard(version="0.7.2")
|
# #! template(subsChar='$', metaChar='#') | standard(version="0.7.2")
|
||||||
# implementation
|
# implementation
|
||||||
|
|
||||||
type
|
type
|
||||||
TParseState = enum
|
TParseState = enum
|
||||||
psDirective, psTempl
|
psDirective, psTempl
|
||||||
TTmplParser{.final.} = object
|
TTmplParser{.final.} = object
|
||||||
inp: PLLStream
|
inp: PLLStream
|
||||||
state: TParseState
|
state: TParseState
|
||||||
info: TLineInfo
|
info: TLineInfo
|
||||||
|
|
@ -33,18 +33,18 @@ type
|
||||||
pendingExprLine: bool
|
pendingExprLine: bool
|
||||||
|
|
||||||
|
|
||||||
const
|
const
|
||||||
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'}
|
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'}
|
||||||
|
|
||||||
proc newLine(p: var TTmplParser) =
|
proc newLine(p: var TTmplParser) =
|
||||||
llStreamWrite(p.outp, repeat(')', p.emitPar))
|
llStreamWrite(p.outp, repeat(')', p.emitPar))
|
||||||
p.emitPar = 0
|
p.emitPar = 0
|
||||||
if p.info.line > int16(1): llStreamWrite(p.outp, "\n")
|
if p.info.line > int16(1): llStreamWrite(p.outp, "\n")
|
||||||
if p.pendingExprLine:
|
if p.pendingExprLine:
|
||||||
llStreamWrite(p.outp, spaces(2))
|
llStreamWrite(p.outp, spaces(2))
|
||||||
p.pendingExprLine = false
|
p.pendingExprLine = false
|
||||||
|
|
||||||
proc scanPar(p: var TTmplParser, d: int) =
|
proc scanPar(p: var TTmplParser, d: int) =
|
||||||
var i = d
|
var i = d
|
||||||
while true:
|
while true:
|
||||||
case p.x[i]
|
case p.x[i]
|
||||||
|
|
@ -58,44 +58,44 @@ proc scanPar(p: var TTmplParser, d: int) =
|
||||||
else: discard
|
else: discard
|
||||||
inc(i)
|
inc(i)
|
||||||
|
|
||||||
proc withInExpr(p: TTmplParser): bool {.inline.} =
|
proc withInExpr(p: TTmplParser): bool {.inline.} =
|
||||||
result = p.par > 0 or p.bracket > 0 or p.curly > 0
|
result = p.par > 0 or p.bracket > 0 or p.curly > 0
|
||||||
|
|
||||||
proc parseLine(p: var TTmplParser) =
|
proc parseLine(p: var TTmplParser) =
|
||||||
var
|
var
|
||||||
d, j, curly: int
|
d, j, curly: int
|
||||||
keyw: string
|
keyw: string
|
||||||
j = 0
|
j = 0
|
||||||
while p.x[j] == ' ': inc(j)
|
while p.x[j] == ' ': inc(j)
|
||||||
if (p.x[0] == p.nimDirective) and (p.x[0 + 1] == '!'):
|
if (p.x[0] == p.nimDirective) and (p.x[0 + 1] == '!'):
|
||||||
newLine(p)
|
newLine(p)
|
||||||
elif (p.x[j] == p.nimDirective):
|
elif (p.x[j] == p.nimDirective):
|
||||||
newLine(p)
|
newLine(p)
|
||||||
inc(j)
|
inc(j)
|
||||||
while p.x[j] == ' ': inc(j)
|
while p.x[j] == ' ': inc(j)
|
||||||
d = j
|
d = j
|
||||||
keyw = ""
|
keyw = ""
|
||||||
while p.x[j] in PatternChars:
|
while p.x[j] in PatternChars:
|
||||||
add(keyw, p.x[j])
|
add(keyw, p.x[j])
|
||||||
inc(j)
|
inc(j)
|
||||||
|
|
||||||
scanPar(p, j)
|
scanPar(p, j)
|
||||||
p.pendingExprLine = withInExpr(p) or llstream.endsWithOpr(p.x)
|
p.pendingExprLine = withInExpr(p) or llstream.endsWithOpr(p.x)
|
||||||
case whichKeyword(keyw)
|
case whichKeyword(keyw)
|
||||||
of wEnd:
|
of wEnd:
|
||||||
if p.indent >= 2:
|
if p.indent >= 2:
|
||||||
dec(p.indent, 2)
|
dec(p.indent, 2)
|
||||||
else:
|
else:
|
||||||
p.info.col = int16(j)
|
p.info.col = int16(j)
|
||||||
localError(p.info, errXNotAllowedHere, "end")
|
localError(p.info, errXNotAllowedHere, "end")
|
||||||
llStreamWrite(p.outp, spaces(p.indent))
|
llStreamWrite(p.outp, spaces(p.indent))
|
||||||
llStreamWrite(p.outp, "#end")
|
llStreamWrite(p.outp, "#end")
|
||||||
of wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator,
|
of wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator,
|
||||||
wConverter, wMacro, wTemplate, wMethod:
|
wConverter, wMacro, wTemplate, wMethod:
|
||||||
llStreamWrite(p.outp, spaces(p.indent))
|
llStreamWrite(p.outp, spaces(p.indent))
|
||||||
llStreamWrite(p.outp, substr(p.x, d))
|
llStreamWrite(p.outp, substr(p.x, d))
|
||||||
inc(p.indent, 2)
|
inc(p.indent, 2)
|
||||||
of wElif, wOf, wElse, wExcept, wFinally:
|
of wElif, wOf, wElse, wExcept, wFinally:
|
||||||
llStreamWrite(p.outp, spaces(p.indent - 2))
|
llStreamWrite(p.outp, spaces(p.indent - 2))
|
||||||
llStreamWrite(p.outp, substr(p.x, d))
|
llStreamWrite(p.outp, substr(p.x, d))
|
||||||
of wLet, wVar, wConst, wType:
|
of wLet, wVar, wConst, wType:
|
||||||
|
|
@ -108,7 +108,7 @@ proc parseLine(p: var TTmplParser) =
|
||||||
llStreamWrite(p.outp, spaces(p.indent))
|
llStreamWrite(p.outp, spaces(p.indent))
|
||||||
llStreamWrite(p.outp, substr(p.x, d))
|
llStreamWrite(p.outp, substr(p.x, d))
|
||||||
p.state = psDirective
|
p.state = psDirective
|
||||||
else:
|
else:
|
||||||
# data line
|
# data line
|
||||||
# reset counters
|
# reset counters
|
||||||
p.par = 0
|
p.par = 0
|
||||||
|
|
@ -116,42 +116,42 @@ proc parseLine(p: var TTmplParser) =
|
||||||
p.bracket = 0
|
p.bracket = 0
|
||||||
j = 0
|
j = 0
|
||||||
case p.state
|
case p.state
|
||||||
of psTempl:
|
of psTempl:
|
||||||
# next line of string literal:
|
# next line of string literal:
|
||||||
llStreamWrite(p.outp, p.conc)
|
llStreamWrite(p.outp, p.conc)
|
||||||
llStreamWrite(p.outp, "\n")
|
llStreamWrite(p.outp, "\n")
|
||||||
llStreamWrite(p.outp, spaces(p.indent + 2))
|
llStreamWrite(p.outp, spaces(p.indent + 2))
|
||||||
llStreamWrite(p.outp, "\"")
|
llStreamWrite(p.outp, "\"")
|
||||||
of psDirective:
|
of psDirective:
|
||||||
newLine(p)
|
newLine(p)
|
||||||
llStreamWrite(p.outp, spaces(p.indent))
|
llStreamWrite(p.outp, spaces(p.indent))
|
||||||
llStreamWrite(p.outp, p.emit)
|
llStreamWrite(p.outp, p.emit)
|
||||||
llStreamWrite(p.outp, "(\"")
|
llStreamWrite(p.outp, "(\"")
|
||||||
inc(p.emitPar)
|
inc(p.emitPar)
|
||||||
p.state = psTempl
|
p.state = psTempl
|
||||||
while true:
|
while true:
|
||||||
case p.x[j]
|
case p.x[j]
|
||||||
of '\0':
|
of '\0':
|
||||||
break
|
break
|
||||||
of '\x01'..'\x1F', '\x80'..'\xFF':
|
of '\x01'..'\x1F', '\x80'..'\xFF':
|
||||||
llStreamWrite(p.outp, "\\x")
|
llStreamWrite(p.outp, "\\x")
|
||||||
llStreamWrite(p.outp, toHex(ord(p.x[j]), 2))
|
llStreamWrite(p.outp, toHex(ord(p.x[j]), 2))
|
||||||
inc(j)
|
inc(j)
|
||||||
of '\\':
|
of '\\':
|
||||||
llStreamWrite(p.outp, "\\\\")
|
llStreamWrite(p.outp, "\\\\")
|
||||||
inc(j)
|
inc(j)
|
||||||
of '\'':
|
of '\'':
|
||||||
llStreamWrite(p.outp, "\\\'")
|
llStreamWrite(p.outp, "\\\'")
|
||||||
inc(j)
|
inc(j)
|
||||||
of '\"':
|
of '\"':
|
||||||
llStreamWrite(p.outp, "\\\"")
|
llStreamWrite(p.outp, "\\\"")
|
||||||
inc(j)
|
inc(j)
|
||||||
else:
|
else:
|
||||||
if p.x[j] == p.subsChar:
|
if p.x[j] == p.subsChar:
|
||||||
# parse Nim expression:
|
# parse Nim expression:
|
||||||
inc(j)
|
inc(j)
|
||||||
case p.x[j]
|
case p.x[j]
|
||||||
of '{':
|
of '{':
|
||||||
p.info.col = int16(j)
|
p.info.col = int16(j)
|
||||||
llStreamWrite(p.outp, '\"')
|
llStreamWrite(p.outp, '\"')
|
||||||
llStreamWrite(p.outp, p.conc)
|
llStreamWrite(p.outp, p.conc)
|
||||||
|
|
@ -159,50 +159,50 @@ proc parseLine(p: var TTmplParser) =
|
||||||
llStreamWrite(p.outp, '(')
|
llStreamWrite(p.outp, '(')
|
||||||
inc(j)
|
inc(j)
|
||||||
curly = 0
|
curly = 0
|
||||||
while true:
|
while true:
|
||||||
case p.x[j]
|
case p.x[j]
|
||||||
of '\0':
|
of '\0':
|
||||||
localError(p.info, errXExpected, "}")
|
localError(p.info, errXExpected, "}")
|
||||||
break
|
break
|
||||||
of '{':
|
of '{':
|
||||||
inc(j)
|
inc(j)
|
||||||
inc(curly)
|
inc(curly)
|
||||||
llStreamWrite(p.outp, '{')
|
llStreamWrite(p.outp, '{')
|
||||||
of '}':
|
of '}':
|
||||||
inc(j)
|
inc(j)
|
||||||
if curly == 0: break
|
if curly == 0: break
|
||||||
if curly > 0: dec(curly)
|
if curly > 0: dec(curly)
|
||||||
llStreamWrite(p.outp, '}')
|
llStreamWrite(p.outp, '}')
|
||||||
else:
|
else:
|
||||||
llStreamWrite(p.outp, p.x[j])
|
llStreamWrite(p.outp, p.x[j])
|
||||||
inc(j)
|
inc(j)
|
||||||
llStreamWrite(p.outp, ')')
|
llStreamWrite(p.outp, ')')
|
||||||
llStreamWrite(p.outp, p.conc)
|
llStreamWrite(p.outp, p.conc)
|
||||||
llStreamWrite(p.outp, '\"')
|
llStreamWrite(p.outp, '\"')
|
||||||
of 'a'..'z', 'A'..'Z', '\x80'..'\xFF':
|
of 'a'..'z', 'A'..'Z', '\x80'..'\xFF':
|
||||||
llStreamWrite(p.outp, '\"')
|
llStreamWrite(p.outp, '\"')
|
||||||
llStreamWrite(p.outp, p.conc)
|
llStreamWrite(p.outp, p.conc)
|
||||||
llStreamWrite(p.outp, p.toStr)
|
llStreamWrite(p.outp, p.toStr)
|
||||||
llStreamWrite(p.outp, '(')
|
llStreamWrite(p.outp, '(')
|
||||||
while p.x[j] in PatternChars:
|
while p.x[j] in PatternChars:
|
||||||
llStreamWrite(p.outp, p.x[j])
|
llStreamWrite(p.outp, p.x[j])
|
||||||
inc(j)
|
inc(j)
|
||||||
llStreamWrite(p.outp, ')')
|
llStreamWrite(p.outp, ')')
|
||||||
llStreamWrite(p.outp, p.conc)
|
llStreamWrite(p.outp, p.conc)
|
||||||
llStreamWrite(p.outp, '\"')
|
llStreamWrite(p.outp, '\"')
|
||||||
else:
|
else:
|
||||||
if p.x[j] == p.subsChar:
|
if p.x[j] == p.subsChar:
|
||||||
llStreamWrite(p.outp, p.subsChar)
|
llStreamWrite(p.outp, p.subsChar)
|
||||||
inc(j)
|
inc(j)
|
||||||
else:
|
else:
|
||||||
p.info.col = int16(j)
|
p.info.col = int16(j)
|
||||||
localError(p.info, errInvalidExpression, "$")
|
localError(p.info, errInvalidExpression, "$")
|
||||||
else:
|
else:
|
||||||
llStreamWrite(p.outp, p.x[j])
|
llStreamWrite(p.outp, p.x[j])
|
||||||
inc(j)
|
inc(j)
|
||||||
llStreamWrite(p.outp, "\\n\"")
|
llStreamWrite(p.outp, "\\n\"")
|
||||||
|
|
||||||
proc filterTmpl(stdin: PLLStream, filename: string, call: PNode): PLLStream =
|
proc filterTmpl(stdin: PLLStream, filename: string, call: PNode): PLLStream =
|
||||||
var p: TTmplParser
|
var p: TTmplParser
|
||||||
p.info = newLineInfo(filename, 0, 0)
|
p.info = newLineInfo(filename, 0, 0)
|
||||||
p.outp = llStreamOpen("")
|
p.outp = llStreamOpen("")
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
# This module implements Nim's simple filters and helpers for filters.
|
# This module implements Nim's simple filters and helpers for filters.
|
||||||
|
|
||||||
import
|
import
|
||||||
llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
|
llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
|
||||||
renderer
|
renderer
|
||||||
|
|
||||||
proc filterReplace*(stdin: PLLStream, filename: string, call: PNode): PLLStream
|
proc filterReplace*(stdin: PLLStream, filename: string, call: PNode): PLLStream
|
||||||
|
|
@ -21,40 +21,40 @@ proc strArg*(n: PNode, name: string, pos: int, default: string): string
|
||||||
proc boolArg*(n: PNode, name: string, pos: int, default: bool): bool
|
proc boolArg*(n: PNode, name: string, pos: int, default: bool): bool
|
||||||
# implementation
|
# implementation
|
||||||
|
|
||||||
proc invalidPragma(n: PNode) =
|
proc invalidPragma(n: PNode) =
|
||||||
localError(n.info, errXNotAllowedHere, renderTree(n, {renderNoComments}))
|
localError(n.info, errXNotAllowedHere, renderTree(n, {renderNoComments}))
|
||||||
|
|
||||||
proc getArg(n: PNode, name: string, pos: int): PNode =
|
proc getArg(n: PNode, name: string, pos: int): PNode =
|
||||||
result = nil
|
result = nil
|
||||||
if n.kind in {nkEmpty..nkNilLit}: return
|
if n.kind in {nkEmpty..nkNilLit}: return
|
||||||
for i in countup(1, sonsLen(n) - 1):
|
for i in countup(1, sonsLen(n) - 1):
|
||||||
if n.sons[i].kind == nkExprEqExpr:
|
if n.sons[i].kind == nkExprEqExpr:
|
||||||
if n.sons[i].sons[0].kind != nkIdent: invalidPragma(n)
|
if n.sons[i].sons[0].kind != nkIdent: invalidPragma(n)
|
||||||
if identEq(n.sons[i].sons[0].ident, name):
|
if identEq(n.sons[i].sons[0].ident, name):
|
||||||
return n.sons[i].sons[1]
|
return n.sons[i].sons[1]
|
||||||
elif i == pos:
|
elif i == pos:
|
||||||
return n.sons[i]
|
return n.sons[i]
|
||||||
|
|
||||||
proc charArg(n: PNode, name: string, pos: int, default: char): char =
|
proc charArg(n: PNode, name: string, pos: int, default: char): char =
|
||||||
var x = getArg(n, name, pos)
|
var x = getArg(n, name, pos)
|
||||||
if x == nil: result = default
|
if x == nil: result = default
|
||||||
elif x.kind == nkCharLit: result = chr(int(x.intVal))
|
elif x.kind == nkCharLit: result = chr(int(x.intVal))
|
||||||
else: invalidPragma(n)
|
else: invalidPragma(n)
|
||||||
|
|
||||||
proc strArg(n: PNode, name: string, pos: int, default: string): string =
|
proc strArg(n: PNode, name: string, pos: int, default: string): string =
|
||||||
var x = getArg(n, name, pos)
|
var x = getArg(n, name, pos)
|
||||||
if x == nil: result = default
|
if x == nil: result = default
|
||||||
elif x.kind in {nkStrLit..nkTripleStrLit}: result = x.strVal
|
elif x.kind in {nkStrLit..nkTripleStrLit}: result = x.strVal
|
||||||
else: invalidPragma(n)
|
else: invalidPragma(n)
|
||||||
|
|
||||||
proc boolArg(n: PNode, name: string, pos: int, default: bool): bool =
|
proc boolArg(n: PNode, name: string, pos: int, default: bool): bool =
|
||||||
var x = getArg(n, name, pos)
|
var x = getArg(n, name, pos)
|
||||||
if x == nil: result = default
|
if x == nil: result = default
|
||||||
elif (x.kind == nkIdent) and identEq(x.ident, "true"): result = true
|
elif (x.kind == nkIdent) and identEq(x.ident, "true"): result = true
|
||||||
elif (x.kind == nkIdent) and identEq(x.ident, "false"): result = false
|
elif (x.kind == nkIdent) and identEq(x.ident, "false"): result = false
|
||||||
else: invalidPragma(n)
|
else: invalidPragma(n)
|
||||||
|
|
||||||
proc filterStrip(stdin: PLLStream, filename: string, call: PNode): PLLStream =
|
proc filterStrip(stdin: PLLStream, filename: string, call: PNode): PLLStream =
|
||||||
var pattern = strArg(call, "startswith", 1, "")
|
var pattern = strArg(call, "startswith", 1, "")
|
||||||
var leading = boolArg(call, "leading", 2, true)
|
var leading = boolArg(call, "leading", 2, true)
|
||||||
var trailing = boolArg(call, "trailing", 3, true)
|
var trailing = boolArg(call, "trailing", 3, true)
|
||||||
|
|
@ -62,13 +62,13 @@ proc filterStrip(stdin: PLLStream, filename: string, call: PNode): PLLStream =
|
||||||
var line = newStringOfCap(80)
|
var line = newStringOfCap(80)
|
||||||
while llStreamReadLine(stdin, line):
|
while llStreamReadLine(stdin, line):
|
||||||
var stripped = strip(line, leading, trailing)
|
var stripped = strip(line, leading, trailing)
|
||||||
if (len(pattern) == 0) or startsWith(stripped, pattern):
|
if (len(pattern) == 0) or startsWith(stripped, pattern):
|
||||||
llStreamWriteln(result, stripped)
|
llStreamWriteln(result, stripped)
|
||||||
else:
|
else:
|
||||||
llStreamWriteln(result, line)
|
llStreamWriteln(result, line)
|
||||||
llStreamClose(stdin)
|
llStreamClose(stdin)
|
||||||
|
|
||||||
proc filterReplace(stdin: PLLStream, filename: string, call: PNode): PLLStream =
|
proc filterReplace(stdin: PLLStream, filename: string, call: PNode): PLLStream =
|
||||||
var sub = strArg(call, "sub", 1, "")
|
var sub = strArg(call, "sub", 1, "")
|
||||||
if len(sub) == 0: invalidPragma(call)
|
if len(sub) == 0: invalidPragma(call)
|
||||||
var by = strArg(call, "by", 2, "")
|
var by = strArg(call, "by", 2, "")
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,11 @@ import ast, astalgo
|
||||||
const
|
const
|
||||||
someCmp = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
|
someCmp = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
|
||||||
mEqUntracedRef, mLeI, mLeF64, mLeU, mLeU64, mLeEnum,
|
mEqUntracedRef, mLeI, mLeF64, mLeU, mLeU64, mLeEnum,
|
||||||
mLeCh, mLeB, mLePtr, mLtI, mLtF64, mLtU, mLtU64, mLtEnum,
|
mLeCh, mLeB, mLePtr, mLtI, mLtF64, mLtU, mLtU64, mLtEnum,
|
||||||
mLtCh, mLtB, mLtPtr}
|
mLtCh, mLtB, mLtPtr}
|
||||||
|
|
||||||
proc isCounter(s: PSym): bool {.inline.} =
|
proc isCounter(s: PSym): bool {.inline.} =
|
||||||
s.kind in {skResult, skVar, skLet, skTemp} and
|
s.kind in {skResult, skVar, skLet, skTemp} and
|
||||||
{sfGlobal, sfAddrTaken} * s.flags == {}
|
{sfGlobal, sfAddrTaken} * s.flags == {}
|
||||||
|
|
||||||
proc isCall(n: PNode): bool {.inline.} =
|
proc isCall(n: PNode): bool {.inline.} =
|
||||||
|
|
@ -29,7 +29,7 @@ proc fromSystem(op: PSym): bool = sfSystemModule in getModule(op).flags
|
||||||
proc getCounter(lastStmt: PNode): PSym =
|
proc getCounter(lastStmt: PNode): PSym =
|
||||||
if lastStmt.isCall:
|
if lastStmt.isCall:
|
||||||
let op = lastStmt.sym
|
let op = lastStmt.sym
|
||||||
if op.magic in {mDec, mInc} or
|
if op.magic in {mDec, mInc} or
|
||||||
((op.name.s == "+=" or op.name.s == "-=") and op.fromSystem):
|
((op.name.s == "+=" or op.name.s == "-=") and op.fromSystem):
|
||||||
if op[1].kind == nkSym and isCounter(op[1].sym):
|
if op[1].kind == nkSym and isCounter(op[1].sym):
|
||||||
result = op[1].sym
|
result = op[1].sym
|
||||||
|
|
@ -67,7 +67,7 @@ proc extractForLoop*(loop, fullTree: PNode): ForLoop =
|
||||||
|
|
||||||
if not cond.isCall: return
|
if not cond.isCall: return
|
||||||
if cond[0].sym.magic notin someCmp: return
|
if cond[0].sym.magic notin someCmp: return
|
||||||
|
|
||||||
var lastStmt = loop[1]
|
var lastStmt = loop[1]
|
||||||
while lastStmt.kind in {nkStmtList, nkStmtListExpr}:
|
while lastStmt.kind in {nkStmtList, nkStmtListExpr}:
|
||||||
lastStmt = lastStmt.lastSon
|
lastStmt = lastStmt.lastSon
|
||||||
|
|
@ -76,7 +76,7 @@ proc extractForLoop*(loop, fullTree: PNode): ForLoop =
|
||||||
if counter.isNil or counter.ast.isNil: return
|
if counter.isNil or counter.ast.isNil: return
|
||||||
|
|
||||||
template `=~`(a, b): expr = a.kind == nkSym and a.sym == b
|
template `=~`(a, b): expr = a.kind == nkSym and a.sym == b
|
||||||
|
|
||||||
if cond[1] =~ counter or cond[2] =~ counter:
|
if cond[1] =~ counter or cond[2] =~ counter:
|
||||||
# ok, now check 'counter' is not used *after* the loop
|
# ok, now check 'counter' is not used *after* the loop
|
||||||
if counterInTree(fullTree, loop, counter): return
|
if counterInTree(fullTree, loop, counter): return
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ proc evalPattern(c: PContext, n, orig: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
result = semDirectOp(c, n, {})
|
result = semDirectOp(c, n, {})
|
||||||
if optHints in gOptions and hintPattern in gNotes:
|
if optHints in gOptions and hintPattern in gNotes:
|
||||||
message(orig.info, hintPattern, rule & " --> '" &
|
message(orig.info, hintPattern, rule & " --> '" &
|
||||||
renderTree(result, {renderNoComments}) & "'")
|
renderTree(result, {renderNoComments}) & "'")
|
||||||
|
|
||||||
proc applyPatterns(c: PContext, n: PNode): PNode =
|
proc applyPatterns(c: PContext, n: PNode): PNode =
|
||||||
|
|
@ -68,7 +68,7 @@ proc hlo(c: PContext, n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
else:
|
else:
|
||||||
if n.kind in {nkFastAsgn, nkAsgn, nkIdentDefs, nkVarTuple} and
|
if n.kind in {nkFastAsgn, nkAsgn, nkIdentDefs, nkVarTuple} and
|
||||||
n.sons[0].kind == nkSym and
|
n.sons[0].kind == nkSym and
|
||||||
{sfGlobal, sfPure} * n.sons[0].sym.flags == {sfGlobal, sfPure}:
|
{sfGlobal, sfPure} * n.sons[0].sym.flags == {sfGlobal, sfPure}:
|
||||||
# do not optimize 'var g {.global} = re(...)' again!
|
# do not optimize 'var g {.global} = re(...)' again!
|
||||||
return n
|
return n
|
||||||
|
|
|
||||||
|
|
@ -11,13 +11,13 @@
|
||||||
# An identifier is a shared immutable string that can be compared by its
|
# An identifier is a shared immutable string that can be compared by its
|
||||||
# id. This module is essential for the compiler's performance.
|
# id. This module is essential for the compiler's performance.
|
||||||
|
|
||||||
import
|
import
|
||||||
hashes, strutils, etcpriv
|
hashes, strutils, etcpriv
|
||||||
|
|
||||||
type
|
type
|
||||||
TIdObj* = object of RootObj
|
TIdObj* = object of RootObj
|
||||||
id*: int # unique id; use this for comparisons and not the pointers
|
id*: int # unique id; use this for comparisons and not the pointers
|
||||||
|
|
||||||
PIdObj* = ref TIdObj
|
PIdObj* = ref TIdObj
|
||||||
PIdent* = ref TIdent
|
PIdent* = ref TIdent
|
||||||
TIdent*{.acyclic.} = object of TIdObj
|
TIdent*{.acyclic.} = object of TIdObj
|
||||||
|
|
@ -45,12 +45,12 @@ proc cmpIgnoreStyle(a, b: cstring, blen: int): int =
|
||||||
if aa >= 'A' and aa <= 'Z': aa = chr(ord(aa) + (ord('a') - ord('A')))
|
if aa >= 'A' and aa <= 'Z': aa = chr(ord(aa) + (ord('a') - ord('A')))
|
||||||
if bb >= 'A' and bb <= 'Z': bb = chr(ord(bb) + (ord('a') - ord('A')))
|
if bb >= 'A' and bb <= 'Z': bb = chr(ord(bb) + (ord('a') - ord('A')))
|
||||||
result = ord(aa) - ord(bb)
|
result = ord(aa) - ord(bb)
|
||||||
if (result != 0) or (aa == '\0'): break
|
if (result != 0) or (aa == '\0'): break
|
||||||
inc(i)
|
inc(i)
|
||||||
inc(j)
|
inc(j)
|
||||||
if result == 0:
|
if result == 0:
|
||||||
if a[i] != '\0': result = 1
|
if a[i] != '\0': result = 1
|
||||||
|
|
||||||
proc cmpExact(a, b: cstring, blen: int): int =
|
proc cmpExact(a, b: cstring, blen: int): int =
|
||||||
var i = 0
|
var i = 0
|
||||||
var j = 0
|
var j = 0
|
||||||
|
|
@ -59,10 +59,10 @@ proc cmpExact(a, b: cstring, blen: int): int =
|
||||||
var aa = a[i]
|
var aa = a[i]
|
||||||
var bb = b[j]
|
var bb = b[j]
|
||||||
result = ord(aa) - ord(bb)
|
result = ord(aa) - ord(bb)
|
||||||
if (result != 0) or (aa == '\0'): break
|
if (result != 0) or (aa == '\0'): break
|
||||||
inc(i)
|
inc(i)
|
||||||
inc(j)
|
inc(j)
|
||||||
if result == 0:
|
if result == 0:
|
||||||
if a[i] != '\0': result = 1
|
if a[i] != '\0': result = 1
|
||||||
|
|
||||||
var wordCounter = 1
|
var wordCounter = 1
|
||||||
|
|
@ -72,14 +72,14 @@ proc getIdent*(identifier: cstring, length: int, h: Hash): PIdent =
|
||||||
result = buckets[idx]
|
result = buckets[idx]
|
||||||
var last: PIdent = nil
|
var last: PIdent = nil
|
||||||
var id = 0
|
var id = 0
|
||||||
while result != nil:
|
while result != nil:
|
||||||
if cmpExact(cstring(result.s), identifier, length) == 0:
|
if cmpExact(cstring(result.s), identifier, length) == 0:
|
||||||
if last != nil:
|
if last != nil:
|
||||||
# make access to last looked up identifier faster:
|
# make access to last looked up identifier faster:
|
||||||
last.next = result.next
|
last.next = result.next
|
||||||
result.next = buckets[idx]
|
result.next = buckets[idx]
|
||||||
buckets[idx] = result
|
buckets[idx] = result
|
||||||
return
|
return
|
||||||
elif cmpIgnoreStyle(cstring(result.s), identifier, length) == 0:
|
elif cmpIgnoreStyle(cstring(result.s), identifier, length) == 0:
|
||||||
assert((id == 0) or (id == result.id))
|
assert((id == 0) or (id == result.id))
|
||||||
id = result.id
|
id = result.id
|
||||||
|
|
@ -91,20 +91,20 @@ proc getIdent*(identifier: cstring, length: int, h: Hash): PIdent =
|
||||||
for i in countup(0, length - 1): result.s[i] = identifier[i]
|
for i in countup(0, length - 1): result.s[i] = identifier[i]
|
||||||
result.next = buckets[idx]
|
result.next = buckets[idx]
|
||||||
buckets[idx] = result
|
buckets[idx] = result
|
||||||
if id == 0:
|
if id == 0:
|
||||||
inc(wordCounter)
|
inc(wordCounter)
|
||||||
result.id = -wordCounter
|
result.id = -wordCounter
|
||||||
else:
|
else:
|
||||||
result.id = id
|
result.id = id
|
||||||
|
|
||||||
proc getIdent*(identifier: string): PIdent =
|
proc getIdent*(identifier: string): PIdent =
|
||||||
result = getIdent(cstring(identifier), len(identifier),
|
result = getIdent(cstring(identifier), len(identifier),
|
||||||
hashIgnoreStyle(identifier))
|
hashIgnoreStyle(identifier))
|
||||||
|
|
||||||
proc getIdent*(identifier: string, h: Hash): PIdent =
|
proc getIdent*(identifier: string, h: Hash): PIdent =
|
||||||
result = getIdent(cstring(identifier), len(identifier), h)
|
result = getIdent(cstring(identifier), len(identifier), h)
|
||||||
|
|
||||||
proc identEq*(id: PIdent, name: string): bool =
|
proc identEq*(id: PIdent, name: string): bool =
|
||||||
result = id.id == getIdent(name).id
|
result = id.id == getIdent(name).id
|
||||||
|
|
||||||
var idAnon* = getIdent":anonymous"
|
var idAnon* = getIdent":anonymous"
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
# This module implements a generic doubled linked list.
|
# This module implements a generic doubled linked list.
|
||||||
# TODO Remove this and replace it with something sensible
|
# TODO Remove this and replace it with something sensible
|
||||||
import os
|
import os
|
||||||
type
|
type
|
||||||
PListEntry* = ref TListEntry
|
PListEntry* = ref TListEntry
|
||||||
TListEntry* = object of RootObj
|
TListEntry* = object of RootObj
|
||||||
prev*, next*: PListEntry
|
prev*, next*: PListEntry
|
||||||
|
|
@ -25,68 +25,68 @@ type
|
||||||
|
|
||||||
TCompareProc* = proc (entry: PListEntry, closure: pointer): bool {.nimcall.}
|
TCompareProc* = proc (entry: PListEntry, closure: pointer): bool {.nimcall.}
|
||||||
|
|
||||||
proc initLinkedList*(list: var TLinkedList) =
|
proc initLinkedList*(list: var TLinkedList) =
|
||||||
list.counter = 0
|
list.counter = 0
|
||||||
list.head = nil
|
list.head = nil
|
||||||
list.tail = nil
|
list.tail = nil
|
||||||
|
|
||||||
proc append*(list: var TLinkedList, entry: PListEntry) =
|
proc append*(list: var TLinkedList, entry: PListEntry) =
|
||||||
inc(list.counter)
|
inc(list.counter)
|
||||||
entry.next = nil
|
entry.next = nil
|
||||||
entry.prev = list.tail
|
entry.prev = list.tail
|
||||||
if list.tail != nil:
|
if list.tail != nil:
|
||||||
assert(list.tail.next == nil)
|
assert(list.tail.next == nil)
|
||||||
list.tail.next = entry
|
list.tail.next = entry
|
||||||
list.tail = entry
|
list.tail = entry
|
||||||
if list.head == nil: list.head = entry
|
if list.head == nil: list.head = entry
|
||||||
|
|
||||||
proc contains*(list: TLinkedList, data: string): bool =
|
proc contains*(list: TLinkedList, data: string): bool =
|
||||||
var it = list.head
|
var it = list.head
|
||||||
while it != nil:
|
while it != nil:
|
||||||
if PStrEntry(it).data == data:
|
if PStrEntry(it).data == data:
|
||||||
return true
|
return true
|
||||||
it = it.next
|
it = it.next
|
||||||
|
|
||||||
proc newStrEntry(data: string): PStrEntry =
|
proc newStrEntry(data: string): PStrEntry =
|
||||||
new(result)
|
new(result)
|
||||||
result.data = data
|
result.data = data
|
||||||
|
|
||||||
proc appendStr*(list: var TLinkedList, data: string) =
|
proc appendStr*(list: var TLinkedList, data: string) =
|
||||||
append(list, newStrEntry(data))
|
append(list, newStrEntry(data))
|
||||||
|
|
||||||
proc includeStr*(list: var TLinkedList, data: string): bool =
|
proc includeStr*(list: var TLinkedList, data: string): bool =
|
||||||
if contains(list, data): return true
|
if contains(list, data): return true
|
||||||
appendStr(list, data) # else: add to list
|
appendStr(list, data) # else: add to list
|
||||||
|
|
||||||
proc prepend*(list: var TLinkedList, entry: PListEntry) =
|
proc prepend*(list: var TLinkedList, entry: PListEntry) =
|
||||||
inc(list.counter)
|
inc(list.counter)
|
||||||
entry.prev = nil
|
entry.prev = nil
|
||||||
entry.next = list.head
|
entry.next = list.head
|
||||||
if list.head != nil:
|
if list.head != nil:
|
||||||
assert(list.head.prev == nil)
|
assert(list.head.prev == nil)
|
||||||
list.head.prev = entry
|
list.head.prev = entry
|
||||||
list.head = entry
|
list.head = entry
|
||||||
if list.tail == nil: list.tail = entry
|
if list.tail == nil: list.tail = entry
|
||||||
|
|
||||||
proc prependStr*(list: var TLinkedList, data: string) =
|
proc prependStr*(list: var TLinkedList, data: string) =
|
||||||
prepend(list, newStrEntry(data))
|
prepend(list, newStrEntry(data))
|
||||||
|
|
||||||
proc insertBefore*(list: var TLinkedList, pos, entry: PListEntry) =
|
proc insertBefore*(list: var TLinkedList, pos, entry: PListEntry) =
|
||||||
assert(pos != nil)
|
assert(pos != nil)
|
||||||
if pos == list.head:
|
if pos == list.head:
|
||||||
prepend(list, entry)
|
prepend(list, entry)
|
||||||
else:
|
else:
|
||||||
inc(list.counter)
|
inc(list.counter)
|
||||||
entry.next = pos
|
entry.next = pos
|
||||||
entry.prev = pos.prev
|
entry.prev = pos.prev
|
||||||
if pos.prev != nil: pos.prev.next = entry
|
if pos.prev != nil: pos.prev.next = entry
|
||||||
pos.prev = entry
|
pos.prev = entry
|
||||||
|
|
||||||
proc remove*(list: var TLinkedList, entry: PListEntry) =
|
proc remove*(list: var TLinkedList, entry: PListEntry) =
|
||||||
dec(list.counter)
|
dec(list.counter)
|
||||||
if entry == list.tail:
|
if entry == list.tail:
|
||||||
list.tail = entry.prev
|
list.tail = entry.prev
|
||||||
if entry == list.head:
|
if entry == list.head:
|
||||||
list.head = entry.next
|
list.head = entry.next
|
||||||
if entry.next != nil: entry.next.prev = entry.prev
|
if entry.next != nil: entry.next.prev = entry.prev
|
||||||
if entry.prev != nil: entry.prev.next = entry.next
|
if entry.prev != nil: entry.prev.next = entry.next
|
||||||
|
|
@ -112,8 +112,8 @@ proc excludePath*(list: var TLinkedList, data: string) =
|
||||||
remove(list, it)
|
remove(list, it)
|
||||||
it = nxt
|
it = nxt
|
||||||
|
|
||||||
proc find*(list: TLinkedList, fn: TCompareProc, closure: pointer): PListEntry =
|
proc find*(list: TLinkedList, fn: TCompareProc, closure: pointer): PListEntry =
|
||||||
result = list.head
|
result = list.head
|
||||||
while result != nil:
|
while result != nil:
|
||||||
if fn(result, closure): return
|
if fn(result, closure): return
|
||||||
result = result.next
|
result = result.next
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
# Built-in types and compilerprocs are registered here.
|
# Built-in types and compilerprocs are registered here.
|
||||||
|
|
||||||
import
|
import
|
||||||
ast, astalgo, hashes, msgs, platform, nversion, times, idents, rodread
|
ast, astalgo, hashes, msgs, platform, nversion, times, idents, rodread
|
||||||
|
|
||||||
var systemModule*: PSym
|
var systemModule*: PSym
|
||||||
|
|
@ -29,23 +29,23 @@ var
|
||||||
|
|
||||||
proc nilOrSysInt*: PType = gSysTypes[tyInt]
|
proc nilOrSysInt*: PType = gSysTypes[tyInt]
|
||||||
|
|
||||||
proc registerSysType(t: PType) =
|
proc registerSysType(t: PType) =
|
||||||
if gSysTypes[t.kind] == nil: gSysTypes[t.kind] = t
|
if gSysTypes[t.kind] == nil: gSysTypes[t.kind] = t
|
||||||
|
|
||||||
proc newSysType(kind: TTypeKind, size: int): PType =
|
proc newSysType(kind: TTypeKind, size: int): PType =
|
||||||
result = newType(kind, systemModule)
|
result = newType(kind, systemModule)
|
||||||
result.size = size
|
result.size = size
|
||||||
result.align = size.int16
|
result.align = size.int16
|
||||||
|
|
||||||
proc getSysSym(name: string): PSym =
|
proc getSysSym(name: string): PSym =
|
||||||
result = strTableGet(systemModule.tab, getIdent(name))
|
result = strTableGet(systemModule.tab, getIdent(name))
|
||||||
if result == nil:
|
if result == nil:
|
||||||
rawMessage(errSystemNeeds, name)
|
rawMessage(errSystemNeeds, name)
|
||||||
result = newSym(skError, getIdent(name), systemModule, systemModule.info)
|
result = newSym(skError, getIdent(name), systemModule, systemModule.info)
|
||||||
result.typ = newType(tyError, systemModule)
|
result.typ = newType(tyError, systemModule)
|
||||||
if result.kind == skStub: loadStub(result)
|
if result.kind == skStub: loadStub(result)
|
||||||
if result.kind == skAlias: result = result.owner
|
if result.kind == skAlias: result = result.owner
|
||||||
|
|
||||||
proc getSysMagic*(name: string, m: TMagic): PSym =
|
proc getSysMagic*(name: string, m: TMagic): PSym =
|
||||||
var ti: TIdentIter
|
var ti: TIdentIter
|
||||||
let id = getIdent(name)
|
let id = getIdent(name)
|
||||||
|
|
@ -57,13 +57,13 @@ proc getSysMagic*(name: string, m: TMagic): PSym =
|
||||||
rawMessage(errSystemNeeds, name)
|
rawMessage(errSystemNeeds, name)
|
||||||
result = newSym(skError, id, systemModule, systemModule.info)
|
result = newSym(skError, id, systemModule, systemModule.info)
|
||||||
result.typ = newType(tyError, systemModule)
|
result.typ = newType(tyError, systemModule)
|
||||||
|
|
||||||
proc sysTypeFromName*(name: string): PType =
|
proc sysTypeFromName*(name: string): PType =
|
||||||
result = getSysSym(name).typ
|
result = getSysSym(name).typ
|
||||||
|
|
||||||
proc getSysType(kind: TTypeKind): PType =
|
proc getSysType(kind: TTypeKind): PType =
|
||||||
result = gSysTypes[kind]
|
result = gSysTypes[kind]
|
||||||
if result == nil:
|
if result == nil:
|
||||||
case kind
|
case kind
|
||||||
of tyInt: result = sysTypeFromName("int")
|
of tyInt: result = sysTypeFromName("int")
|
||||||
of tyInt8: result = sysTypeFromName("int8")
|
of tyInt8: result = sysTypeFromName("int8")
|
||||||
|
|
@ -87,7 +87,7 @@ proc getSysType(kind: TTypeKind): PType =
|
||||||
of tyNil: result = newSysType(tyNil, ptrSize)
|
of tyNil: result = newSysType(tyNil, ptrSize)
|
||||||
else: internalError("request for typekind: " & $kind)
|
else: internalError("request for typekind: " & $kind)
|
||||||
gSysTypes[kind] = result
|
gSysTypes[kind] = result
|
||||||
if result.kind != kind:
|
if result.kind != kind:
|
||||||
internalError("wanted: " & $kind & " got: " & $result.kind)
|
internalError("wanted: " & $kind & " got: " & $result.kind)
|
||||||
if result == nil: internalError("type not found: " & $kind)
|
if result == nil: internalError("type not found: " & $kind)
|
||||||
|
|
||||||
|
|
@ -163,7 +163,7 @@ proc setIntLitType*(result: PNode) =
|
||||||
result.typ = getSysType(tyInt64)
|
result.typ = getSysType(tyInt64)
|
||||||
else: internalError(result.info, "invalid int size")
|
else: internalError(result.info, "invalid int size")
|
||||||
|
|
||||||
proc getCompilerProc(name: string): PSym =
|
proc getCompilerProc(name: string): PSym =
|
||||||
var ident = getIdent(name, hashIgnoreStyle(name))
|
var ident = getIdent(name, hashIgnoreStyle(name))
|
||||||
result = strTableGet(compilerprocs, ident)
|
result = strTableGet(compilerprocs, ident)
|
||||||
if result == nil:
|
if result == nil:
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@
|
||||||
|
|
||||||
import parseutils, strutils, strtabs, os, options, msgs, lists
|
import parseutils, strutils, strtabs, os, options, msgs, lists
|
||||||
|
|
||||||
proc addPath*(path: string, info: TLineInfo) =
|
proc addPath*(path: string, info: TLineInfo) =
|
||||||
if not contains(options.searchPaths, path):
|
if not contains(options.searchPaths, path):
|
||||||
lists.prependStr(options.searchPaths, path)
|
lists.prependStr(options.searchPaths, path)
|
||||||
|
|
||||||
proc versionSplitPos(s: string): int =
|
proc versionSplitPos(s: string): int =
|
||||||
|
|
@ -23,7 +23,7 @@ proc versionSplitPos(s: string): int =
|
||||||
const
|
const
|
||||||
latest = "head"
|
latest = "head"
|
||||||
|
|
||||||
proc `<.`(a, b: string): bool =
|
proc `<.`(a, b: string): bool =
|
||||||
# wether a has a smaller version than b:
|
# wether a has a smaller version than b:
|
||||||
if a == latest: return false
|
if a == latest: return false
|
||||||
var i = 0
|
var i = 0
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
## exposes the Nim VM to clients.
|
## exposes the Nim VM to clients.
|
||||||
|
|
||||||
import
|
import
|
||||||
ast, modules, passes, passaux, condsyms,
|
ast, modules, passes, passaux, condsyms,
|
||||||
options, nimconf, lists, sem, semdata, llstream, vm
|
options, nimconf, lists, sem, semdata, llstream, vm
|
||||||
|
|
||||||
proc execute*(program: string) =
|
proc execute*(program: string) =
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,10 @@
|
||||||
# handling that exists! Only at line endings checks are necessary
|
# handling that exists! Only at line endings checks are necessary
|
||||||
# if the buffer needs refilling.
|
# if the buffer needs refilling.
|
||||||
|
|
||||||
import
|
import
|
||||||
llstream, strutils
|
llstream, strutils
|
||||||
|
|
||||||
const
|
const
|
||||||
Lrz* = ' '
|
Lrz* = ' '
|
||||||
Apo* = '\''
|
Apo* = '\''
|
||||||
Tabulator* = '\x09'
|
Tabulator* = '\x09'
|
||||||
|
|
@ -27,7 +27,7 @@ const
|
||||||
BACKSPACE* = '\x08'
|
BACKSPACE* = '\x08'
|
||||||
VT* = '\x0B'
|
VT* = '\x0B'
|
||||||
|
|
||||||
const
|
const
|
||||||
EndOfFile* = '\0' # end of file marker
|
EndOfFile* = '\0' # end of file marker
|
||||||
# A little picture makes everything clear :-)
|
# A little picture makes everything clear :-)
|
||||||
# buf:
|
# buf:
|
||||||
|
|
@ -36,7 +36,7 @@ const
|
||||||
#
|
#
|
||||||
NewLines* = {CR, LF}
|
NewLines* = {CR, LF}
|
||||||
|
|
||||||
type
|
type
|
||||||
TBaseLexer* = object of RootObj
|
TBaseLexer* = object of RootObj
|
||||||
bufpos*: int
|
bufpos*: int
|
||||||
buf*: cstring
|
buf*: cstring
|
||||||
|
|
@ -46,9 +46,9 @@ type
|
||||||
# private data:
|
# private data:
|
||||||
sentinel*: int
|
sentinel*: int
|
||||||
lineStart*: int # index of last line start in buffer
|
lineStart*: int # index of last line start in buffer
|
||||||
|
|
||||||
|
|
||||||
proc openBaseLexer*(L: var TBaseLexer, inputstream: PLLStream,
|
|
||||||
|
proc openBaseLexer*(L: var TBaseLexer, inputstream: PLLStream,
|
||||||
bufLen: int = 8192)
|
bufLen: int = 8192)
|
||||||
# 8K is a reasonable buffer size
|
# 8K is a reasonable buffer size
|
||||||
proc closeBaseLexer*(L: var TBaseLexer)
|
proc closeBaseLexer*(L: var TBaseLexer)
|
||||||
|
|
@ -64,15 +64,15 @@ proc handleLF*(L: var TBaseLexer, pos: int): int
|
||||||
# of the LF.
|
# of the LF.
|
||||||
# implementation
|
# implementation
|
||||||
|
|
||||||
const
|
const
|
||||||
chrSize = sizeof(char)
|
chrSize = sizeof(char)
|
||||||
|
|
||||||
proc closeBaseLexer(L: var TBaseLexer) =
|
proc closeBaseLexer(L: var TBaseLexer) =
|
||||||
dealloc(L.buf)
|
dealloc(L.buf)
|
||||||
llStreamClose(L.stream)
|
llStreamClose(L.stream)
|
||||||
|
|
||||||
proc fillBuffer(L: var TBaseLexer) =
|
proc fillBuffer(L: var TBaseLexer) =
|
||||||
var
|
var
|
||||||
charsRead, toCopy, s: int # all are in characters,
|
charsRead, toCopy, s: int # all are in characters,
|
||||||
# not bytes (in case this
|
# not bytes (in case this
|
||||||
# is not the same)
|
# is not the same)
|
||||||
|
|
@ -82,68 +82,68 @@ proc fillBuffer(L: var TBaseLexer) =
|
||||||
assert(L.sentinel < L.bufLen)
|
assert(L.sentinel < L.bufLen)
|
||||||
toCopy = L.bufLen - L.sentinel - 1
|
toCopy = L.bufLen - L.sentinel - 1
|
||||||
assert(toCopy >= 0)
|
assert(toCopy >= 0)
|
||||||
if toCopy > 0:
|
if toCopy > 0:
|
||||||
moveMem(L.buf, addr(L.buf[L.sentinel + 1]), toCopy * chrSize)
|
moveMem(L.buf, addr(L.buf[L.sentinel + 1]), toCopy * chrSize)
|
||||||
# "moveMem" handles overlapping regions
|
# "moveMem" handles overlapping regions
|
||||||
charsRead = llStreamRead(L.stream, addr(L.buf[toCopy]),
|
charsRead = llStreamRead(L.stream, addr(L.buf[toCopy]),
|
||||||
(L.sentinel + 1) * chrSize) div chrSize
|
(L.sentinel + 1) * chrSize) div chrSize
|
||||||
s = toCopy + charsRead
|
s = toCopy + charsRead
|
||||||
if charsRead < L.sentinel + 1:
|
if charsRead < L.sentinel + 1:
|
||||||
L.buf[s] = EndOfFile # set end marker
|
L.buf[s] = EndOfFile # set end marker
|
||||||
L.sentinel = s
|
L.sentinel = s
|
||||||
else:
|
else:
|
||||||
# compute sentinel:
|
# compute sentinel:
|
||||||
dec(s) # BUGFIX (valgrind)
|
dec(s) # BUGFIX (valgrind)
|
||||||
while true:
|
while true:
|
||||||
assert(s < L.bufLen)
|
assert(s < L.bufLen)
|
||||||
while (s >= 0) and not (L.buf[s] in NewLines): dec(s)
|
while (s >= 0) and not (L.buf[s] in NewLines): dec(s)
|
||||||
if s >= 0:
|
if s >= 0:
|
||||||
# we found an appropriate character for a sentinel:
|
# we found an appropriate character for a sentinel:
|
||||||
L.sentinel = s
|
L.sentinel = s
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
# rather than to give up here because the line is too long,
|
# rather than to give up here because the line is too long,
|
||||||
# double the buffer's size and try again:
|
# double the buffer's size and try again:
|
||||||
oldBufLen = L.bufLen
|
oldBufLen = L.bufLen
|
||||||
L.bufLen = L.bufLen * 2
|
L.bufLen = L.bufLen * 2
|
||||||
L.buf = cast[cstring](realloc(L.buf, L.bufLen * chrSize))
|
L.buf = cast[cstring](realloc(L.buf, L.bufLen * chrSize))
|
||||||
assert(L.bufLen - oldBufLen == oldBufLen)
|
assert(L.bufLen - oldBufLen == oldBufLen)
|
||||||
charsRead = llStreamRead(L.stream, addr(L.buf[oldBufLen]),
|
charsRead = llStreamRead(L.stream, addr(L.buf[oldBufLen]),
|
||||||
oldBufLen * chrSize) div chrSize
|
oldBufLen * chrSize) div chrSize
|
||||||
if charsRead < oldBufLen:
|
if charsRead < oldBufLen:
|
||||||
L.buf[oldBufLen + charsRead] = EndOfFile
|
L.buf[oldBufLen + charsRead] = EndOfFile
|
||||||
L.sentinel = oldBufLen + charsRead
|
L.sentinel = oldBufLen + charsRead
|
||||||
break
|
break
|
||||||
s = L.bufLen - 1
|
s = L.bufLen - 1
|
||||||
|
|
||||||
proc fillBaseLexer(L: var TBaseLexer, pos: int): int =
|
proc fillBaseLexer(L: var TBaseLexer, pos: int): int =
|
||||||
assert(pos <= L.sentinel)
|
assert(pos <= L.sentinel)
|
||||||
if pos < L.sentinel:
|
if pos < L.sentinel:
|
||||||
result = pos + 1 # nothing to do
|
result = pos + 1 # nothing to do
|
||||||
else:
|
else:
|
||||||
fillBuffer(L)
|
fillBuffer(L)
|
||||||
L.bufpos = 0 # XXX: is this really correct?
|
L.bufpos = 0 # XXX: is this really correct?
|
||||||
result = 0
|
result = 0
|
||||||
L.lineStart = result
|
L.lineStart = result
|
||||||
|
|
||||||
proc handleCR(L: var TBaseLexer, pos: int): int =
|
proc handleCR(L: var TBaseLexer, pos: int): int =
|
||||||
assert(L.buf[pos] == CR)
|
assert(L.buf[pos] == CR)
|
||||||
inc(L.lineNumber)
|
inc(L.lineNumber)
|
||||||
result = fillBaseLexer(L, pos)
|
result = fillBaseLexer(L, pos)
|
||||||
if L.buf[result] == LF:
|
if L.buf[result] == LF:
|
||||||
result = fillBaseLexer(L, result)
|
result = fillBaseLexer(L, result)
|
||||||
|
|
||||||
proc handleLF(L: var TBaseLexer, pos: int): int =
|
proc handleLF(L: var TBaseLexer, pos: int): int =
|
||||||
assert(L.buf[pos] == LF)
|
assert(L.buf[pos] == LF)
|
||||||
inc(L.lineNumber)
|
inc(L.lineNumber)
|
||||||
result = fillBaseLexer(L, pos) #L.lastNL := result-1; // BUGFIX: was: result;
|
result = fillBaseLexer(L, pos) #L.lastNL := result-1; // BUGFIX: was: result;
|
||||||
|
|
||||||
proc skipUTF8BOM(L: var TBaseLexer) =
|
proc skipUTF8BOM(L: var TBaseLexer) =
|
||||||
if L.buf[0] == '\xEF' and L.buf[1] == '\xBB' and L.buf[2] == '\xBF':
|
if L.buf[0] == '\xEF' and L.buf[1] == '\xBB' and L.buf[2] == '\xBF':
|
||||||
inc(L.bufpos, 3)
|
inc(L.bufpos, 3)
|
||||||
inc(L.lineStart, 3)
|
inc(L.lineStart, 3)
|
||||||
|
|
||||||
proc openBaseLexer(L: var TBaseLexer, inputstream: PLLStream, bufLen = 8192) =
|
proc openBaseLexer(L: var TBaseLexer, inputstream: PLLStream, bufLen = 8192) =
|
||||||
assert(bufLen > 0)
|
assert(bufLen > 0)
|
||||||
L.bufpos = 0
|
L.bufpos = 0
|
||||||
L.bufLen = bufLen
|
L.bufLen = bufLen
|
||||||
|
|
@ -155,15 +155,15 @@ proc openBaseLexer(L: var TBaseLexer, inputstream: PLLStream, bufLen = 8192) =
|
||||||
fillBuffer(L)
|
fillBuffer(L)
|
||||||
skipUTF8BOM(L)
|
skipUTF8BOM(L)
|
||||||
|
|
||||||
proc getColNumber(L: TBaseLexer, pos: int): int =
|
proc getColNumber(L: TBaseLexer, pos: int): int =
|
||||||
result = abs(pos - L.lineStart)
|
result = abs(pos - L.lineStart)
|
||||||
|
|
||||||
proc getCurrentLine(L: TBaseLexer, marker: bool = true): string =
|
proc getCurrentLine(L: TBaseLexer, marker: bool = true): string =
|
||||||
result = ""
|
result = ""
|
||||||
var i = L.lineStart
|
var i = L.lineStart
|
||||||
while not (L.buf[i] in {CR, LF, EndOfFile}):
|
while not (L.buf[i] in {CR, LF, EndOfFile}):
|
||||||
add(result, L.buf[i])
|
add(result, L.buf[i])
|
||||||
inc(i)
|
inc(i)
|
||||||
result.add("\n")
|
result.add("\n")
|
||||||
if marker:
|
if marker:
|
||||||
result.add(spaces(getColNumber(L, L.bufpos)) & '^' & "\n")
|
result.add(spaces(getColNumber(L, L.bufpos)) & '^' & "\n")
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
# this unit handles Nim sets; it implements symbolic sets
|
# this unit handles Nim sets; it implements symbolic sets
|
||||||
|
|
||||||
import
|
import
|
||||||
ast, astalgo, trees, nversion, msgs, platform, bitsets, types, renderer
|
ast, astalgo, trees, nversion, msgs, platform, bitsets, types, renderer
|
||||||
|
|
||||||
proc toBitSet*(s: PNode, b: var TBitSet)
|
proc toBitSet*(s: PNode, b: var TBitSet)
|
||||||
|
|
@ -30,17 +30,17 @@ proc equalSets*(a, b: PNode): bool
|
||||||
proc cardSet*(s: PNode): BiggestInt
|
proc cardSet*(s: PNode): BiggestInt
|
||||||
# implementation
|
# implementation
|
||||||
|
|
||||||
proc inSet(s: PNode, elem: PNode): bool =
|
proc inSet(s: PNode, elem: PNode): bool =
|
||||||
if s.kind != nkCurly:
|
if s.kind != nkCurly:
|
||||||
internalError(s.info, "inSet")
|
internalError(s.info, "inSet")
|
||||||
return false
|
return false
|
||||||
for i in countup(0, sonsLen(s) - 1):
|
for i in countup(0, sonsLen(s) - 1):
|
||||||
if s.sons[i].kind == nkRange:
|
if s.sons[i].kind == nkRange:
|
||||||
if leValue(s.sons[i].sons[0], elem) and
|
if leValue(s.sons[i].sons[0], elem) and
|
||||||
leValue(elem, s.sons[i].sons[1]):
|
leValue(elem, s.sons[i].sons[1]):
|
||||||
return true
|
return true
|
||||||
else:
|
else:
|
||||||
if sameValue(s.sons[i], elem):
|
if sameValue(s.sons[i], elem):
|
||||||
return true
|
return true
|
||||||
result = false
|
result = false
|
||||||
|
|
||||||
|
|
@ -58,37 +58,37 @@ proc overlap(a, b: PNode): bool =
|
||||||
else:
|
else:
|
||||||
result = sameValue(a, b)
|
result = sameValue(a, b)
|
||||||
|
|
||||||
proc someInSet(s: PNode, a, b: PNode): bool =
|
proc someInSet(s: PNode, a, b: PNode): bool =
|
||||||
# checks if some element of a..b is in the set s
|
# checks if some element of a..b is in the set s
|
||||||
if s.kind != nkCurly:
|
if s.kind != nkCurly:
|
||||||
internalError(s.info, "SomeInSet")
|
internalError(s.info, "SomeInSet")
|
||||||
return false
|
return false
|
||||||
for i in countup(0, sonsLen(s) - 1):
|
for i in countup(0, sonsLen(s) - 1):
|
||||||
if s.sons[i].kind == nkRange:
|
if s.sons[i].kind == nkRange:
|
||||||
if leValue(s.sons[i].sons[0], b) and leValue(b, s.sons[i].sons[1]) or
|
if leValue(s.sons[i].sons[0], b) and leValue(b, s.sons[i].sons[1]) or
|
||||||
leValue(s.sons[i].sons[0], a) and leValue(a, s.sons[i].sons[1]):
|
leValue(s.sons[i].sons[0], a) and leValue(a, s.sons[i].sons[1]):
|
||||||
return true
|
return true
|
||||||
else:
|
else:
|
||||||
# a <= elem <= b
|
# a <= elem <= b
|
||||||
if leValue(a, s.sons[i]) and leValue(s.sons[i], b):
|
if leValue(a, s.sons[i]) and leValue(s.sons[i], b):
|
||||||
return true
|
return true
|
||||||
result = false
|
result = false
|
||||||
|
|
||||||
proc toBitSet(s: PNode, b: var TBitSet) =
|
proc toBitSet(s: PNode, b: var TBitSet) =
|
||||||
var first, j: BiggestInt
|
var first, j: BiggestInt
|
||||||
first = firstOrd(s.typ.sons[0])
|
first = firstOrd(s.typ.sons[0])
|
||||||
bitSetInit(b, int(getSize(s.typ)))
|
bitSetInit(b, int(getSize(s.typ)))
|
||||||
for i in countup(0, sonsLen(s) - 1):
|
for i in countup(0, sonsLen(s) - 1):
|
||||||
if s.sons[i].kind == nkRange:
|
if s.sons[i].kind == nkRange:
|
||||||
j = getOrdValue(s.sons[i].sons[0])
|
j = getOrdValue(s.sons[i].sons[0])
|
||||||
while j <= getOrdValue(s.sons[i].sons[1]):
|
while j <= getOrdValue(s.sons[i].sons[1]):
|
||||||
bitSetIncl(b, j - first)
|
bitSetIncl(b, j - first)
|
||||||
inc(j)
|
inc(j)
|
||||||
else:
|
else:
|
||||||
bitSetIncl(b, getOrdValue(s.sons[i]) - first)
|
bitSetIncl(b, getOrdValue(s.sons[i]) - first)
|
||||||
|
|
||||||
proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode =
|
proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode =
|
||||||
var
|
var
|
||||||
a, b, e, first: BiggestInt # a, b are interval borders
|
a, b, e, first: BiggestInt # a, b are interval borders
|
||||||
elemType: PType
|
elemType: PType
|
||||||
n: PNode
|
n: PNode
|
||||||
|
|
@ -98,17 +98,17 @@ proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode =
|
||||||
result.typ = settype
|
result.typ = settype
|
||||||
result.info = info
|
result.info = info
|
||||||
e = 0
|
e = 0
|
||||||
while e < len(s) * ElemSize:
|
while e < len(s) * ElemSize:
|
||||||
if bitSetIn(s, e):
|
if bitSetIn(s, e):
|
||||||
a = e
|
a = e
|
||||||
b = e
|
b = e
|
||||||
while true:
|
while true:
|
||||||
inc(b)
|
inc(b)
|
||||||
if (b >= len(s) * ElemSize) or not bitSetIn(s, b): break
|
if (b >= len(s) * ElemSize) or not bitSetIn(s, b): break
|
||||||
dec(b)
|
dec(b)
|
||||||
if a == b:
|
if a == b:
|
||||||
addSon(result, newIntTypeNode(nkIntLit, a + first, elemType))
|
addSon(result, newIntTypeNode(nkIntLit, a + first, elemType))
|
||||||
else:
|
else:
|
||||||
n = newNodeI(nkRange, info)
|
n = newNodeI(nkRange, info)
|
||||||
n.typ = elemType
|
n.typ = elemType
|
||||||
addSon(n, newIntTypeNode(nkIntLit, a + first, elemType))
|
addSon(n, newIntTypeNode(nkIntLit, a + first, elemType))
|
||||||
|
|
@ -117,7 +117,7 @@ proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode =
|
||||||
e = b
|
e = b
|
||||||
inc(e)
|
inc(e)
|
||||||
|
|
||||||
template nodeSetOp(a, b: PNode, op: expr) {.dirty.} =
|
template nodeSetOp(a, b: PNode, op: expr) {.dirty.} =
|
||||||
var x, y: TBitSet
|
var x, y: TBitSet
|
||||||
toBitSet(a, x)
|
toBitSet(a, x)
|
||||||
toBitSet(b, y)
|
toBitSet(b, y)
|
||||||
|
|
@ -129,13 +129,13 @@ proc diffSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetDiff)
|
||||||
proc intersectSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetIntersect)
|
proc intersectSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetIntersect)
|
||||||
proc symdiffSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetSymDiff)
|
proc symdiffSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetSymDiff)
|
||||||
|
|
||||||
proc containsSets(a, b: PNode): bool =
|
proc containsSets(a, b: PNode): bool =
|
||||||
var x, y: TBitSet
|
var x, y: TBitSet
|
||||||
toBitSet(a, x)
|
toBitSet(a, x)
|
||||||
toBitSet(b, y)
|
toBitSet(b, y)
|
||||||
result = bitSetContains(x, y)
|
result = bitSetContains(x, y)
|
||||||
|
|
||||||
proc equalSets(a, b: PNode): bool =
|
proc equalSets(a, b: PNode): bool =
|
||||||
var x, y: TBitSet
|
var x, y: TBitSet
|
||||||
toBitSet(a, x)
|
toBitSet(a, x)
|
||||||
toBitSet(b, y)
|
toBitSet(b, y)
|
||||||
|
|
@ -147,26 +147,26 @@ proc complement*(a: PNode): PNode =
|
||||||
for i in countup(0, high(x)): x[i] = not x[i]
|
for i in countup(0, high(x)): x[i] = not x[i]
|
||||||
result = toTreeSet(x, a.typ, a.info)
|
result = toTreeSet(x, a.typ, a.info)
|
||||||
|
|
||||||
proc cardSet(s: PNode): BiggestInt =
|
proc cardSet(s: PNode): BiggestInt =
|
||||||
# here we can do better than converting it into a compact set
|
# here we can do better than converting it into a compact set
|
||||||
# we just count the elements directly
|
# we just count the elements directly
|
||||||
result = 0
|
result = 0
|
||||||
for i in countup(0, sonsLen(s) - 1):
|
for i in countup(0, sonsLen(s) - 1):
|
||||||
if s.sons[i].kind == nkRange:
|
if s.sons[i].kind == nkRange:
|
||||||
result = result + getOrdValue(s.sons[i].sons[1]) -
|
result = result + getOrdValue(s.sons[i].sons[1]) -
|
||||||
getOrdValue(s.sons[i].sons[0]) + 1
|
getOrdValue(s.sons[i].sons[0]) + 1
|
||||||
else:
|
else:
|
||||||
inc(result)
|
inc(result)
|
||||||
|
|
||||||
proc setHasRange(s: PNode): bool =
|
proc setHasRange(s: PNode): bool =
|
||||||
if s.kind != nkCurly:
|
if s.kind != nkCurly:
|
||||||
internalError(s.info, "SetHasRange")
|
internalError(s.info, "SetHasRange")
|
||||||
return false
|
return false
|
||||||
for i in countup(0, sonsLen(s) - 1):
|
for i in countup(0, sonsLen(s) - 1):
|
||||||
if s.sons[i].kind == nkRange:
|
if s.sons[i].kind == nkRange:
|
||||||
return true
|
return true
|
||||||
result = false
|
result = false
|
||||||
|
|
||||||
proc emptyRange(a, b: PNode): bool =
|
proc emptyRange(a, b: PNode): bool =
|
||||||
result = not leValue(a, b) # a > b iff not (a <= b)
|
result = not leValue(a, b) # a > b iff not (a <= b)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
# This module contains Nim's version. It is the only place where it needs
|
# This module contains Nim's version. It is the only place where it needs
|
||||||
# to be changed.
|
# to be changed.
|
||||||
|
|
||||||
const
|
const
|
||||||
MaxSetElements* = 1 shl 16 # (2^16) to support unicode character sets?
|
MaxSetElements* = 1 shl 16 # (2^16) to support unicode character sets?
|
||||||
VersionAsString* = system.NimVersion
|
VersionAsString* = system.NimVersion
|
||||||
RodFileVersion* = "1215" # modify this if the rod-format changes!
|
RodFileVersion* = "1215" # modify this if the rod-format changes!
|
||||||
|
|
|
||||||
|
|
@ -9,38 +9,38 @@
|
||||||
|
|
||||||
## implements some little helper passes
|
## implements some little helper passes
|
||||||
|
|
||||||
import
|
import
|
||||||
strutils, ast, astalgo, passes, msgs, options, idgen
|
strutils, ast, astalgo, passes, msgs, options, idgen
|
||||||
|
|
||||||
proc verboseOpen(s: PSym): PPassContext =
|
proc verboseOpen(s: PSym): PPassContext =
|
||||||
#MessageOut('compiling ' + s.name.s);
|
#MessageOut('compiling ' + s.name.s);
|
||||||
result = nil # we don't need a context
|
result = nil # we don't need a context
|
||||||
rawMessage(hintProcessing, s.name.s)
|
rawMessage(hintProcessing, s.name.s)
|
||||||
|
|
||||||
proc verboseProcess(context: PPassContext, n: PNode): PNode =
|
proc verboseProcess(context: PPassContext, n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
if context != nil: internalError("logpass: context is not nil")
|
if context != nil: internalError("logpass: context is not nil")
|
||||||
if gVerbosity == 3:
|
if gVerbosity == 3:
|
||||||
# system.nim deactivates all hints, for verbosity:3 we want the processing
|
# system.nim deactivates all hints, for verbosity:3 we want the processing
|
||||||
# messages nonetheless, so we activate them again unconditionally:
|
# messages nonetheless, so we activate them again unconditionally:
|
||||||
incl(msgs.gNotes, hintProcessing)
|
incl(msgs.gNotes, hintProcessing)
|
||||||
message(n.info, hintProcessing, $idgen.gBackendId)
|
message(n.info, hintProcessing, $idgen.gBackendId)
|
||||||
|
|
||||||
const verbosePass* = makePass(open = verboseOpen, process = verboseProcess)
|
const verbosePass* = makePass(open = verboseOpen, process = verboseProcess)
|
||||||
|
|
||||||
proc cleanUp(c: PPassContext, n: PNode): PNode =
|
proc cleanUp(c: PPassContext, n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
# we cannot clean up if dead code elimination is activated
|
# we cannot clean up if dead code elimination is activated
|
||||||
if optDeadCodeElim in gGlobalOptions or n == nil: return
|
if optDeadCodeElim in gGlobalOptions or n == nil: return
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkStmtList:
|
of nkStmtList:
|
||||||
for i in countup(0, sonsLen(n) - 1): discard cleanUp(c, n.sons[i])
|
for i in countup(0, sonsLen(n) - 1): discard cleanUp(c, n.sons[i])
|
||||||
of nkProcDef, nkMethodDef:
|
of nkProcDef, nkMethodDef:
|
||||||
if n.sons[namePos].kind == nkSym:
|
if n.sons[namePos].kind == nkSym:
|
||||||
var s = n.sons[namePos].sym
|
var s = n.sons[namePos].sym
|
||||||
if sfDeadCodeElim notin getModule(s).flags and not astNeeded(s):
|
if sfDeadCodeElim notin getModule(s).flags and not astNeeded(s):
|
||||||
s.ast.sons[bodyPos] = ast.emptyNode # free the memory
|
s.ast.sons[bodyPos] = ast.emptyNode # free the memory
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
const cleanupPass* = makePass(process = cleanUp, close = cleanUp)
|
const cleanupPass* = makePass(process = cleanUp, close = cleanUp)
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ proc inSymChoice(sc, x: PNode): bool =
|
||||||
elif sc.kind == nkOpenSymChoice:
|
elif sc.kind == nkOpenSymChoice:
|
||||||
# same name suffices for open sym choices!
|
# same name suffices for open sym choices!
|
||||||
result = sc.sons[0].sym.name.id == x.sym.name.id
|
result = sc.sons[0].sym.name.id == x.sym.name.id
|
||||||
|
|
||||||
proc checkTypes(c: PPatternContext, p: PSym, n: PNode): bool =
|
proc checkTypes(c: PPatternContext, p: PSym, n: PNode): bool =
|
||||||
# check param constraints first here as this is quite optimized:
|
# check param constraints first here as this is quite optimized:
|
||||||
if p.constraint != nil:
|
if p.constraint != nil:
|
||||||
|
|
@ -115,13 +115,13 @@ proc matchNested(c: PPatternContext, p, n: PNode, rpn: bool): bool =
|
||||||
if rpn: arglist.add(n.sons[0])
|
if rpn: arglist.add(n.sons[0])
|
||||||
elif n.kind == nkHiddenStdConv and n.sons[1].kind == nkBracket:
|
elif n.kind == nkHiddenStdConv and n.sons[1].kind == nkBracket:
|
||||||
let n = n.sons[1]
|
let n = n.sons[1]
|
||||||
for i in 0.. <n.len:
|
for i in 0.. <n.len:
|
||||||
if not matchStarAux(c, op, n[i], arglist, rpn): return false
|
if not matchStarAux(c, op, n[i], arglist, rpn): return false
|
||||||
elif checkTypes(c, p.sons[2].sym, n):
|
elif checkTypes(c, p.sons[2].sym, n):
|
||||||
add(arglist, n)
|
add(arglist, n)
|
||||||
else:
|
else:
|
||||||
result = false
|
result = false
|
||||||
|
|
||||||
if n.kind notin nkCallKinds: return false
|
if n.kind notin nkCallKinds: return false
|
||||||
if matches(c, p.sons[1], n.sons[0]):
|
if matches(c, p.sons[1], n.sons[0]):
|
||||||
var arglist = newNodeI(nkArgList, n.info)
|
var arglist = newNodeI(nkArgList, n.info)
|
||||||
|
|
@ -151,7 +151,7 @@ proc matches(c: PPatternContext, p, n: PNode): bool =
|
||||||
of "**": result = matchNested(c, p, n, rpn=true)
|
of "**": result = matchNested(c, p, n, rpn=true)
|
||||||
of "~": result = not matches(c, p.sons[1], n)
|
of "~": result = not matches(c, p.sons[1], n)
|
||||||
else: internalError(p.info, "invalid pattern")
|
else: internalError(p.info, "invalid pattern")
|
||||||
# template {add(a, `&` * b)}(a: string{noalias}, b: varargs[string]) =
|
# template {add(a, `&` * b)}(a: string{noalias}, b: varargs[string]) =
|
||||||
# add(a, b)
|
# add(a, b)
|
||||||
elif p.kind == nkCurlyExpr:
|
elif p.kind == nkCurlyExpr:
|
||||||
if p.sons[1].kind == nkPrefix:
|
if p.sons[1].kind == nkPrefix:
|
||||||
|
|
@ -212,7 +212,7 @@ proc matchStmtList(c: PPatternContext, p, n: PNode): PNode =
|
||||||
if not isNil(c.mapping): c.mapping = nil
|
if not isNil(c.mapping): c.mapping = nil
|
||||||
return false
|
return false
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
if p.kind == nkStmtList and n.kind == p.kind and p.len < n.len:
|
if p.kind == nkStmtList and n.kind == p.kind and p.len < n.len:
|
||||||
let n = flattenStmts(n)
|
let n = flattenStmts(n)
|
||||||
# no need to flatten 'p' here as that has already been done
|
# no need to flatten 'p' here as that has already been done
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,12 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
import
|
import
|
||||||
llstream, lexer, parser, idents, strutils, ast, msgs
|
llstream, lexer, parser, idents, strutils, ast, msgs
|
||||||
|
|
||||||
proc parseAll*(p: var TParser): PNode =
|
proc parseAll*(p: var TParser): PNode =
|
||||||
result = nil
|
result = nil
|
||||||
|
|
||||||
proc parseTopLevelStmt*(p: var TParser): PNode =
|
proc parseTopLevelStmt*(p: var TParser): PNode =
|
||||||
result = nil
|
result = nil
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym =
|
||||||
discard
|
discard
|
||||||
|
|
||||||
result = nextIdentIter(it, scope.symbols)
|
result = nextIdentIter(it, scope.symbols)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym =
|
proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym =
|
||||||
|
|
@ -99,17 +99,17 @@ proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym =
|
||||||
debug fn.typ
|
debug fn.typ
|
||||||
debug if result != nil: result.typ else: nil
|
debug if result != nil: result.typ else: nil
|
||||||
debug if old != nil: old.typ else: nil
|
debug if old != nil: old.typ else: nil
|
||||||
|
|
||||||
when false:
|
when false:
|
||||||
proc paramsFitBorrow(child, parent: PNode): bool =
|
proc paramsFitBorrow(child, parent: PNode): bool =
|
||||||
var length = sonsLen(child)
|
var length = sonsLen(child)
|
||||||
result = false
|
result = false
|
||||||
if length == sonsLen(parent):
|
if length == sonsLen(parent):
|
||||||
for i in countup(1, length - 1):
|
for i in countup(1, length - 1):
|
||||||
var m = child.sons[i].sym
|
var m = child.sons[i].sym
|
||||||
var n = parent.sons[i].sym
|
var n = parent.sons[i].sym
|
||||||
assert((m.kind == skParam) and (n.kind == skParam))
|
assert((m.kind == skParam) and (n.kind == skParam))
|
||||||
if not compareTypes(m.typ, n.typ, dcEqOrDistinctOf): return
|
if not compareTypes(m.typ, n.typ, dcEqOrDistinctOf): return
|
||||||
if not compareTypes(child.sons[0].typ, parent.sons[0].typ,
|
if not compareTypes(child.sons[0].typ, parent.sons[0].typ,
|
||||||
dcEqOrDistinctOf): return
|
dcEqOrDistinctOf): return
|
||||||
result = true
|
result = true
|
||||||
|
|
@ -120,10 +120,10 @@ when false:
|
||||||
var it: TIdentIter
|
var it: TIdentIter
|
||||||
for scope in walkScopes(startScope):
|
for scope in walkScopes(startScope):
|
||||||
result = initIdentIter(it, scope.symbols, fn.Name)
|
result = initIdentIter(it, scope.symbols, fn.Name)
|
||||||
while result != nil:
|
while result != nil:
|
||||||
# watchout! result must not be the same as fn!
|
# watchout! result must not be the same as fn!
|
||||||
if (result.Kind == fn.kind) and (result.id != fn.id):
|
if (result.Kind == fn.kind) and (result.id != fn.id):
|
||||||
if equalGenericParams(result.ast.sons[genericParamsPos],
|
if equalGenericParams(result.ast.sons[genericParamsPos],
|
||||||
fn.ast.sons[genericParamsPos]):
|
fn.ast.sons[genericParamsPos]):
|
||||||
if paramsFitBorrow(fn.typ.n, result.typ.n): return
|
if paramsFitBorrow(fn.typ.n, result.typ.n): return
|
||||||
result = NextIdentIter(it, scope.symbols)
|
result = NextIdentIter(it, scope.symbols)
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import strutils
|
||||||
|
|
||||||
proc c_sprintf(buf, frmt: cstring) {.importc: "sprintf", header: "<stdio.h>", nodecl, varargs.}
|
proc c_sprintf(buf, frmt: cstring) {.importc: "sprintf", header: "<stdio.h>", nodecl, varargs.}
|
||||||
|
|
||||||
proc toStrMaxPrecision*(f: BiggestFloat): string =
|
proc toStrMaxPrecision*(f: BiggestFloat): string =
|
||||||
if f != f:
|
if f != f:
|
||||||
result = "NAN"
|
result = "NAN"
|
||||||
elif f == 0.0:
|
elif f == 0.0:
|
||||||
|
|
@ -21,17 +21,17 @@ proc toStrMaxPrecision*(f: BiggestFloat): string =
|
||||||
if f > 0.0: result = "INF"
|
if f > 0.0: result = "INF"
|
||||||
else: result = "-INF"
|
else: result = "-INF"
|
||||||
else:
|
else:
|
||||||
var buf: array [0..80, char]
|
var buf: array [0..80, char]
|
||||||
c_sprintf(buf, "%#.16e", f)
|
c_sprintf(buf, "%#.16e", f)
|
||||||
result = $buf
|
result = $buf
|
||||||
|
|
||||||
proc encodeStr*(s: string, result: var string) =
|
proc encodeStr*(s: string, result: var string) =
|
||||||
for i in countup(0, len(s) - 1):
|
for i in countup(0, len(s) - 1):
|
||||||
case s[i]
|
case s[i]
|
||||||
of 'a'..'z', 'A'..'Z', '0'..'9', '_': add(result, s[i])
|
of 'a'..'z', 'A'..'Z', '0'..'9', '_': add(result, s[i])
|
||||||
else: add(result, '\\' & toHex(ord(s[i]), 2))
|
else: add(result, '\\' & toHex(ord(s[i]), 2))
|
||||||
|
|
||||||
proc hexChar(c: char, xi: var int) =
|
proc hexChar(c: char, xi: var int) =
|
||||||
case c
|
case c
|
||||||
of '0'..'9': xi = (xi shl 4) or (ord(c) - ord('0'))
|
of '0'..'9': xi = (xi shl 4) or (ord(c) - ord('0'))
|
||||||
of 'a'..'f': xi = (xi shl 4) or (ord(c) - ord('a') + 10)
|
of 'a'..'f': xi = (xi shl 4) or (ord(c) - ord('a') + 10)
|
||||||
|
|
@ -41,18 +41,18 @@ proc hexChar(c: char, xi: var int) =
|
||||||
proc decodeStr*(s: cstring, pos: var int): string =
|
proc decodeStr*(s: cstring, pos: var int): string =
|
||||||
var i = pos
|
var i = pos
|
||||||
result = ""
|
result = ""
|
||||||
while true:
|
while true:
|
||||||
case s[i]
|
case s[i]
|
||||||
of '\\':
|
of '\\':
|
||||||
inc(i, 3)
|
inc(i, 3)
|
||||||
var xi = 0
|
var xi = 0
|
||||||
hexChar(s[i-2], xi)
|
hexChar(s[i-2], xi)
|
||||||
hexChar(s[i-1], xi)
|
hexChar(s[i-1], xi)
|
||||||
add(result, chr(xi))
|
add(result, chr(xi))
|
||||||
of 'a'..'z', 'A'..'Z', '0'..'9', '_':
|
of 'a'..'z', 'A'..'Z', '0'..'9', '_':
|
||||||
add(result, s[i])
|
add(result, s[i])
|
||||||
inc(i)
|
inc(i)
|
||||||
else: break
|
else: break
|
||||||
pos = i
|
pos = i
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|
@ -68,11 +68,11 @@ template encodeIntImpl(self: expr) =
|
||||||
var d: char
|
var d: char
|
||||||
var v = x
|
var v = x
|
||||||
var rem = v mod 190
|
var rem = v mod 190
|
||||||
if rem < 0:
|
if rem < 0:
|
||||||
add(result, '-')
|
add(result, '-')
|
||||||
v = - (v div 190)
|
v = - (v div 190)
|
||||||
rem = - rem
|
rem = - rem
|
||||||
else:
|
else:
|
||||||
v = v div 190
|
v = v div 190
|
||||||
var idx = int(rem)
|
var idx = int(rem)
|
||||||
if idx < 62: d = chars[idx]
|
if idx < 62: d = chars[idx]
|
||||||
|
|
@ -89,11 +89,11 @@ proc encodeVBiggestInt*(x: BiggestInt, result: var string) =
|
||||||
encodeVBiggestIntAux(x +% vintDelta, result)
|
encodeVBiggestIntAux(x +% vintDelta, result)
|
||||||
# encodeIntImpl(encodeVBiggestInt)
|
# encodeIntImpl(encodeVBiggestInt)
|
||||||
|
|
||||||
proc encodeVIntAux(x: int, result: var string) =
|
proc encodeVIntAux(x: int, result: var string) =
|
||||||
## encode an int as a variable length base 190 int.
|
## encode an int as a variable length base 190 int.
|
||||||
encodeIntImpl(encodeVIntAux)
|
encodeIntImpl(encodeVIntAux)
|
||||||
|
|
||||||
proc encodeVInt*(x: int, result: var string) =
|
proc encodeVInt*(x: int, result: var string) =
|
||||||
## encode an int as a variable length base 190 int.
|
## encode an int as a variable length base 190 int.
|
||||||
encodeVIntAux(x +% vintDelta, result)
|
encodeVIntAux(x +% vintDelta, result)
|
||||||
|
|
||||||
|
|
@ -101,11 +101,11 @@ template decodeIntImpl() =
|
||||||
var i = pos
|
var i = pos
|
||||||
var sign = - 1
|
var sign = - 1
|
||||||
assert(s[i] in {'a'..'z', 'A'..'Z', '0'..'9', '-', '\x80'..'\xFF'})
|
assert(s[i] in {'a'..'z', 'A'..'Z', '0'..'9', '-', '\x80'..'\xFF'})
|
||||||
if s[i] == '-':
|
if s[i] == '-':
|
||||||
inc(i)
|
inc(i)
|
||||||
sign = 1
|
sign = 1
|
||||||
result = 0
|
result = 0
|
||||||
while true:
|
while true:
|
||||||
case s[i]
|
case s[i]
|
||||||
of '0'..'9': result = result * 190 - (ord(s[i]) - ord('0'))
|
of '0'..'9': result = result * 190 - (ord(s[i]) - ord('0'))
|
||||||
of 'a'..'z': result = result * 190 - (ord(s[i]) - ord('a') + 10)
|
of 'a'..'z': result = result * 190 - (ord(s[i]) - ord('a') + 10)
|
||||||
|
|
@ -116,7 +116,7 @@ template decodeIntImpl() =
|
||||||
result = result * sign -% vintDelta
|
result = result * sign -% vintDelta
|
||||||
pos = i
|
pos = i
|
||||||
|
|
||||||
proc decodeVInt*(s: cstring, pos: var int): int =
|
proc decodeVInt*(s: cstring, pos: var int): int =
|
||||||
decodeIntImpl()
|
decodeIntImpl()
|
||||||
|
|
||||||
proc decodeVBiggestInt*(s: cstring, pos: var int): BiggestInt =
|
proc decodeVBiggestInt*(s: cstring, pos: var int): BiggestInt =
|
||||||
|
|
|
||||||
|
|
@ -306,7 +306,7 @@ const
|
||||||
|
|
||||||
proc equalsFile*(r: Rope, f: File): bool =
|
proc equalsFile*(r: Rope, f: File): bool =
|
||||||
## returns true if the contents of the file `f` equal `r`.
|
## returns true if the contents of the file `f` equal `r`.
|
||||||
var
|
var
|
||||||
buf: array[bufSize, char]
|
buf: array[bufSize, char]
|
||||||
bpos = buf.len
|
bpos = buf.len
|
||||||
blen = buf.len
|
blen = buf.len
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ proc `|*|`*(a, b: BiggestInt): BiggestInt =
|
||||||
# 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
|
# 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
|
||||||
if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd):
|
if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
if floatProd >= 0.0:
|
if floatProd >= 0.0:
|
||||||
result = high(result)
|
result = high(result)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -320,7 +320,7 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
|
||||||
x.call.add newSymNode(s, n.info)
|
x.call.add newSymNode(s, n.info)
|
||||||
else:
|
else:
|
||||||
internalAssert false
|
internalAssert false
|
||||||
|
|
||||||
result = x.call
|
result = x.call
|
||||||
instGenericConvertersSons(c, result, x)
|
instGenericConvertersSons(c, result, x)
|
||||||
result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
|
result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
|
||||||
let L = forLoop.len
|
let L = forLoop.len
|
||||||
let call = forLoop.sons[L-2]
|
let call = forLoop.sons[L-2]
|
||||||
if call.len > 2:
|
if call.len > 2:
|
||||||
localError(forLoop.info, errGenerated,
|
localError(forLoop.info, errGenerated,
|
||||||
"parallel 'fields' iterator does not work for 'case' objects")
|
"parallel 'fields' iterator does not work for 'case' objects")
|
||||||
return
|
return
|
||||||
# iterate over the selector:
|
# iterate over the selector:
|
||||||
|
|
@ -106,7 +106,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
|
||||||
# a 'while true: stmt; break' loop ...
|
# a 'while true: stmt; break' loop ...
|
||||||
result = newNodeI(nkWhileStmt, n.info, 2)
|
result = newNodeI(nkWhileStmt, n.info, 2)
|
||||||
var trueSymbol = strTableGet(magicsys.systemModule.tab, getIdent"true")
|
var trueSymbol = strTableGet(magicsys.systemModule.tab, getIdent"true")
|
||||||
if trueSymbol == nil:
|
if trueSymbol == nil:
|
||||||
localError(n.info, errSystemNeeds, "true")
|
localError(n.info, errSystemNeeds, "true")
|
||||||
trueSymbol = newSym(skUnknown, getIdent"true", getCurrOwner(), n.info)
|
trueSymbol = newSym(skUnknown, getIdent"true", getCurrOwner(), n.info)
|
||||||
trueSymbol.typ = getSysType(tyBool)
|
trueSymbol.typ = getSysType(tyBool)
|
||||||
|
|
@ -114,13 +114,13 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
|
||||||
result.sons[0] = newSymNode(trueSymbol, n.info)
|
result.sons[0] = newSymNode(trueSymbol, n.info)
|
||||||
var stmts = newNodeI(nkStmtList, n.info)
|
var stmts = newNodeI(nkStmtList, n.info)
|
||||||
result.sons[1] = stmts
|
result.sons[1] = stmts
|
||||||
|
|
||||||
var length = sonsLen(n)
|
var length = sonsLen(n)
|
||||||
var call = n.sons[length-2]
|
var call = n.sons[length-2]
|
||||||
if length-2 != sonsLen(call)-1 + ord(m==mFieldPairs):
|
if length-2 != sonsLen(call)-1 + ord(m==mFieldPairs):
|
||||||
localError(n.info, errWrongNumberOfVariables)
|
localError(n.info, errWrongNumberOfVariables)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
var tupleTypeA = skipTypes(call.sons[1].typ, abstractVar-{tyTypeDesc})
|
var tupleTypeA = skipTypes(call.sons[1].typ, abstractVar-{tyTypeDesc})
|
||||||
if tupleTypeA.kind notin {tyTuple, tyObject}:
|
if tupleTypeA.kind notin {tyTuple, tyObject}:
|
||||||
localError(n.info, errGenerated, "no object or tuple type")
|
localError(n.info, errGenerated, "no object or tuple type")
|
||||||
|
|
@ -129,7 +129,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
|
||||||
var tupleTypeB = skipTypes(call.sons[i].typ, abstractVar-{tyTypeDesc})
|
var tupleTypeB = skipTypes(call.sons[i].typ, abstractVar-{tyTypeDesc})
|
||||||
if not sameType(tupleTypeA, tupleTypeB):
|
if not sameType(tupleTypeA, tupleTypeB):
|
||||||
typeMismatch(call.sons[i], tupleTypeA, tupleTypeB)
|
typeMismatch(call.sons[i], tupleTypeA, tupleTypeB)
|
||||||
|
|
||||||
inc(c.p.nestedLoopCounter)
|
inc(c.p.nestedLoopCounter)
|
||||||
if tupleTypeA.kind == tyTuple:
|
if tupleTypeA.kind == tyTuple:
|
||||||
var loopBody = n.sons[length-1]
|
var loopBody = n.sons[length-1]
|
||||||
|
|
|
||||||
|
|
@ -1327,7 +1327,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
||||||
# because in that case there is no point in continuing.
|
# because in that case there is no point in continuing.
|
||||||
var bothMetaCounter = 0
|
var bothMetaCounter = 0
|
||||||
var lastBindingsLength = -1
|
var lastBindingsLength = -1
|
||||||
while r == isBothMetaConvertible and
|
while r == isBothMetaConvertible and
|
||||||
lastBindingsLength != m.bindings.counter and
|
lastBindingsLength != m.bindings.counter and
|
||||||
bothMetaCounter < 100:
|
bothMetaCounter < 100:
|
||||||
lastBindingsLength = m.bindings.counter
|
lastBindingsLength = m.bindings.counter
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ var
|
||||||
|
|
||||||
template origModuleName(m: PSym): string = m.name.s
|
template origModuleName(m: PSym): string = m.name.s
|
||||||
|
|
||||||
proc symToSuggest(s: PSym, isLocal: bool, section: string, li: TLineInfo): Suggest =
|
proc symToSuggest(s: PSym, isLocal: bool, section: string, li: TLineInfo): Suggest =
|
||||||
result.section = parseIdeCmd(section)
|
result.section = parseIdeCmd(section)
|
||||||
if optIdeTerse in gGlobalOptions:
|
if optIdeTerse in gGlobalOptions:
|
||||||
result.symkind = s.kind
|
result.symkind = s.kind
|
||||||
|
|
@ -52,7 +52,7 @@ proc symToSuggest(s: PSym, isLocal: bool, section: string, li: TLineInfo): Sugge
|
||||||
result.qualifiedPath.add(ow.origModuleName)
|
result.qualifiedPath.add(ow.origModuleName)
|
||||||
result.qualifiedPath.add(s.name.s)
|
result.qualifiedPath.add(s.name.s)
|
||||||
|
|
||||||
if s.typ != nil:
|
if s.typ != nil:
|
||||||
result.forth = typeToString(s.typ)
|
result.forth = typeToString(s.typ)
|
||||||
else:
|
else:
|
||||||
result.forth = ""
|
result.forth = ""
|
||||||
|
|
@ -62,7 +62,7 @@ proc symToSuggest(s: PSym, isLocal: bool, section: string, li: TLineInfo): Sugge
|
||||||
when not defined(noDocgen):
|
when not defined(noDocgen):
|
||||||
result.doc = s.extractDocComment
|
result.doc = s.extractDocComment
|
||||||
|
|
||||||
proc `$`(suggest: Suggest): string =
|
proc `$`(suggest: Suggest): string =
|
||||||
result = $suggest.section
|
result = $suggest.section
|
||||||
result.add(sep)
|
result.add(sep)
|
||||||
result.add($suggest.symkind)
|
result.add($suggest.symkind)
|
||||||
|
|
@ -80,7 +80,7 @@ proc `$`(suggest: Suggest): string =
|
||||||
when not defined(noDocgen):
|
when not defined(noDocgen):
|
||||||
result.add(suggest.doc.escape)
|
result.add(suggest.doc.escape)
|
||||||
|
|
||||||
proc symToSuggest(s: PSym, isLocal: bool, section: string): Suggest =
|
proc symToSuggest(s: PSym, isLocal: bool, section: string): Suggest =
|
||||||
result = symToSuggest(s, isLocal, section, s.info)
|
result = symToSuggest(s, isLocal, section, s.info)
|
||||||
|
|
||||||
proc suggestResult(s: Suggest) =
|
proc suggestResult(s: Suggest) =
|
||||||
|
|
@ -104,7 +104,7 @@ proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} =
|
||||||
result = true
|
result = true
|
||||||
break
|
break
|
||||||
|
|
||||||
proc suggestField(c: PContext, s: PSym, outputs: var int) =
|
proc suggestField(c: PContext, s: PSym, outputs: var int) =
|
||||||
if filterSym(s) and fieldVisible(c, s):
|
if filterSym(s) and fieldVisible(c, s):
|
||||||
suggestResult(symToSuggest(s, isLocal=true, $ideSug))
|
suggestResult(symToSuggest(s, isLocal=true, $ideSug))
|
||||||
inc outputs
|
inc outputs
|
||||||
|
|
@ -122,17 +122,17 @@ template wholeSymTab(cond, section: expr) {.immediate.} =
|
||||||
suggestResult(symToSuggest(it, isLocal = isLocal, section))
|
suggestResult(symToSuggest(it, isLocal = isLocal, section))
|
||||||
inc outputs
|
inc outputs
|
||||||
|
|
||||||
proc suggestSymList(c: PContext, list: PNode, outputs: var int) =
|
proc suggestSymList(c: PContext, list: PNode, outputs: var int) =
|
||||||
for i in countup(0, sonsLen(list) - 1):
|
for i in countup(0, sonsLen(list) - 1):
|
||||||
if list.sons[i].kind == nkSym:
|
if list.sons[i].kind == nkSym:
|
||||||
suggestField(c, list.sons[i].sym, outputs)
|
suggestField(c, list.sons[i].sym, outputs)
|
||||||
#else: InternalError(list.info, "getSymFromList")
|
#else: InternalError(list.info, "getSymFromList")
|
||||||
|
|
||||||
proc suggestObject(c: PContext, n: PNode, outputs: var int) =
|
proc suggestObject(c: PContext, n: PNode, outputs: var int) =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkRecList:
|
of nkRecList:
|
||||||
for i in countup(0, sonsLen(n)-1): suggestObject(c, n.sons[i], outputs)
|
for i in countup(0, sonsLen(n)-1): suggestObject(c, n.sons[i], outputs)
|
||||||
of nkRecCase:
|
of nkRecCase:
|
||||||
var L = sonsLen(n)
|
var L = sonsLen(n)
|
||||||
if L > 0:
|
if L > 0:
|
||||||
suggestObject(c, n.sons[0], outputs)
|
suggestObject(c, n.sons[0], outputs)
|
||||||
|
|
@ -140,7 +140,7 @@ proc suggestObject(c: PContext, n: PNode, outputs: var int) =
|
||||||
of nkSym: suggestField(c, n.sym, outputs)
|
of nkSym: suggestField(c, n.sym, outputs)
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc nameFits(c: PContext, s: PSym, n: PNode): bool =
|
proc nameFits(c: PContext, s: PSym, n: PNode): bool =
|
||||||
var op = n.sons[0]
|
var op = n.sons[0]
|
||||||
if op.kind in {nkOpenSymChoice, nkClosedSymChoice}: op = op.sons[0]
|
if op.kind in {nkOpenSymChoice, nkClosedSymChoice}: op = op.sons[0]
|
||||||
var opr: PIdent
|
var opr: PIdent
|
||||||
|
|
@ -150,8 +150,8 @@ proc nameFits(c: PContext, s: PSym, n: PNode): bool =
|
||||||
else: return false
|
else: return false
|
||||||
result = opr.id == s.name.id
|
result = opr.id == s.name.id
|
||||||
|
|
||||||
proc argsFit(c: PContext, candidate: PSym, n, nOrig: PNode): bool =
|
proc argsFit(c: PContext, candidate: PSym, n, nOrig: PNode): bool =
|
||||||
case candidate.kind
|
case candidate.kind
|
||||||
of OverloadableSyms:
|
of OverloadableSyms:
|
||||||
var m: TCandidate
|
var m: TCandidate
|
||||||
initCandidate(c, m, candidate, nil)
|
initCandidate(c, m, candidate, nil)
|
||||||
|
|
@ -160,11 +160,11 @@ proc argsFit(c: PContext, candidate: PSym, n, nOrig: PNode): bool =
|
||||||
else:
|
else:
|
||||||
result = false
|
result = false
|
||||||
|
|
||||||
proc suggestCall(c: PContext, n, nOrig: PNode, outputs: var int) =
|
proc suggestCall(c: PContext, n, nOrig: PNode, outputs: var int) =
|
||||||
wholeSymTab(filterSym(it) and nameFits(c, it, n) and argsFit(c, it, n, nOrig),
|
wholeSymTab(filterSym(it) and nameFits(c, it, n) and argsFit(c, it, n, nOrig),
|
||||||
$ideCon)
|
$ideCon)
|
||||||
|
|
||||||
proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} =
|
proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} =
|
||||||
if s.typ != nil and sonsLen(s.typ) > 1 and s.typ.sons[1] != nil:
|
if s.typ != nil and sonsLen(s.typ) > 1 and s.typ.sons[1] != nil:
|
||||||
# special rule: if system and some weird generic match via 'tyExpr'
|
# special rule: if system and some weird generic match via 'tyExpr'
|
||||||
# or 'tyGenericParam' we won't list it either to reduce the noise (nobody
|
# or 'tyGenericParam' we won't list it either to reduce the noise (nobody
|
||||||
|
|
@ -198,48 +198,48 @@ proc suggestFieldAccess(c: PContext, n: PNode, outputs: var int) =
|
||||||
var typ = n.typ
|
var typ = n.typ
|
||||||
if typ == nil:
|
if typ == nil:
|
||||||
# a module symbol has no type for example:
|
# a module symbol has no type for example:
|
||||||
if n.kind == nkSym and n.sym.kind == skModule:
|
if n.kind == nkSym and n.sym.kind == skModule:
|
||||||
if n.sym == c.module:
|
if n.sym == c.module:
|
||||||
# all symbols accessible, because we are in the current module:
|
# all symbols accessible, because we are in the current module:
|
||||||
for it in items(c.topLevelScope.symbols):
|
for it in items(c.topLevelScope.symbols):
|
||||||
if filterSym(it):
|
if filterSym(it):
|
||||||
suggestResult(symToSuggest(it, isLocal=false, $ideSug))
|
suggestResult(symToSuggest(it, isLocal=false, $ideSug))
|
||||||
inc outputs
|
inc outputs
|
||||||
else:
|
else:
|
||||||
for it in items(n.sym.tab):
|
for it in items(n.sym.tab):
|
||||||
if filterSym(it):
|
if filterSym(it):
|
||||||
suggestResult(symToSuggest(it, isLocal=false, $ideSug))
|
suggestResult(symToSuggest(it, isLocal=false, $ideSug))
|
||||||
inc outputs
|
inc outputs
|
||||||
else:
|
else:
|
||||||
# fallback:
|
# fallback:
|
||||||
suggestEverything(c, n, outputs)
|
suggestEverything(c, n, outputs)
|
||||||
elif typ.kind == tyEnum and n.kind == nkSym and n.sym.kind == skType:
|
elif typ.kind == tyEnum and n.kind == nkSym and n.sym.kind == skType:
|
||||||
# look up if the identifier belongs to the enum:
|
# look up if the identifier belongs to the enum:
|
||||||
var t = typ
|
var t = typ
|
||||||
while t != nil:
|
while t != nil:
|
||||||
suggestSymList(c, t.n, outputs)
|
suggestSymList(c, t.n, outputs)
|
||||||
t = t.sons[0]
|
t = t.sons[0]
|
||||||
suggestOperations(c, n, typ, outputs)
|
suggestOperations(c, n, typ, outputs)
|
||||||
else:
|
else:
|
||||||
typ = skipTypes(typ, {tyGenericInst, tyVar, tyPtr, tyRef})
|
typ = skipTypes(typ, {tyGenericInst, tyVar, tyPtr, tyRef})
|
||||||
if typ.kind == tyObject:
|
if typ.kind == tyObject:
|
||||||
var t = typ
|
var t = typ
|
||||||
while true:
|
while true:
|
||||||
suggestObject(c, t.n, outputs)
|
suggestObject(c, t.n, outputs)
|
||||||
if t.sons[0] == nil: break
|
if t.sons[0] == nil: break
|
||||||
t = skipTypes(t.sons[0], {tyGenericInst})
|
t = skipTypes(t.sons[0], {tyGenericInst})
|
||||||
suggestOperations(c, n, typ, outputs)
|
suggestOperations(c, n, typ, outputs)
|
||||||
elif typ.kind == tyTuple and typ.n != nil:
|
elif typ.kind == tyTuple and typ.n != nil:
|
||||||
suggestSymList(c, typ.n, outputs)
|
suggestSymList(c, typ.n, outputs)
|
||||||
suggestOperations(c, n, typ, outputs)
|
suggestOperations(c, n, typ, outputs)
|
||||||
else:
|
else:
|
||||||
suggestOperations(c, n, typ, outputs)
|
suggestOperations(c, n, typ, outputs)
|
||||||
|
|
||||||
type
|
type
|
||||||
TCheckPointResult = enum
|
TCheckPointResult = enum
|
||||||
cpNone, cpFuzzy, cpExact
|
cpNone, cpFuzzy, cpExact
|
||||||
|
|
||||||
proc inCheckpoint(current: TLineInfo): TCheckPointResult =
|
proc inCheckpoint(current: TLineInfo): TCheckPointResult =
|
||||||
if current.fileIndex == gTrackPos.fileIndex:
|
if current.fileIndex == gTrackPos.fileIndex:
|
||||||
if current.line == gTrackPos.line and
|
if current.line == gTrackPos.line and
|
||||||
abs(current.col-gTrackPos.col) < 4:
|
abs(current.col-gTrackPos.col) < 4:
|
||||||
|
|
@ -255,8 +255,8 @@ proc findClosestDot(n: PNode): PNode =
|
||||||
result = findClosestDot(n.sons[i])
|
result = findClosestDot(n.sons[i])
|
||||||
if result != nil: return
|
if result != nil: return
|
||||||
|
|
||||||
proc findClosestCall(n: PNode): PNode =
|
proc findClosestCall(n: PNode): PNode =
|
||||||
if n.kind in nkCallKinds and inCheckpoint(n.info) == cpExact:
|
if n.kind in nkCallKinds and inCheckpoint(n.info) == cpExact:
|
||||||
result = n
|
result = n
|
||||||
else:
|
else:
|
||||||
for i in 0.. <safeLen(n):
|
for i in 0.. <safeLen(n):
|
||||||
|
|
@ -270,8 +270,8 @@ proc isTracked(current: TLineInfo, tokenLen: int): bool =
|
||||||
if col >= current.col and col <= current.col+tokenLen-1:
|
if col >= current.col and col <= current.col+tokenLen-1:
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc findClosestSym(n: PNode): PNode =
|
proc findClosestSym(n: PNode): PNode =
|
||||||
if n.kind == nkSym and inCheckpoint(n.info) == cpExact:
|
if n.kind == nkSym and inCheckpoint(n.info) == cpExact:
|
||||||
result = n
|
result = n
|
||||||
elif n.kind notin {nkNone..nkNilLit}:
|
elif n.kind notin {nkNone..nkNilLit}:
|
||||||
for i in 0.. <sonsLen(n):
|
for i in 0.. <sonsLen(n):
|
||||||
|
|
@ -328,7 +328,7 @@ proc safeSemExpr*(c: PContext, n: PNode): PNode =
|
||||||
except ERecoverableError:
|
except ERecoverableError:
|
||||||
result = ast.emptyNode
|
result = ast.emptyNode
|
||||||
|
|
||||||
proc suggestExpr*(c: PContext, node: PNode) =
|
proc suggestExpr*(c: PContext, node: PNode) =
|
||||||
if nfIsCursor notin node.flags:
|
if nfIsCursor notin node.flags:
|
||||||
if gTrackPos.line < 0: return
|
if gTrackPos.line < 0: return
|
||||||
var cp = inCheckpoint(node.info)
|
var cp = inCheckpoint(node.info)
|
||||||
|
|
@ -349,7 +349,7 @@ proc suggestExpr*(c: PContext, node: PNode) =
|
||||||
#writeStackTrace()
|
#writeStackTrace()
|
||||||
else:
|
else:
|
||||||
suggestEverything(c, n, outputs)
|
suggestEverything(c, n, outputs)
|
||||||
|
|
||||||
elif gIdeCmd == ideCon:
|
elif gIdeCmd == ideCon:
|
||||||
var n = if nfIsCursor in node.flags: node else: findClosestCall(node)
|
var n = if nfIsCursor in node.flags: node else: findClosestCall(node)
|
||||||
if n == nil: n = node
|
if n == nil: n = node
|
||||||
|
|
@ -364,9 +364,9 @@ proc suggestExpr*(c: PContext, node: PNode) =
|
||||||
if x.kind == nkEmpty or x.typ == nil: break
|
if x.kind == nkEmpty or x.typ == nil: break
|
||||||
addSon(a, x)
|
addSon(a, x)
|
||||||
suggestCall(c, a, n, outputs)
|
suggestCall(c, a, n, outputs)
|
||||||
|
|
||||||
dec(c.inCompilesContext)
|
dec(c.inCompilesContext)
|
||||||
if outputs > 0 and gIdeCmd != ideUse: suggestQuit()
|
if outputs > 0 and gIdeCmd != ideUse: suggestQuit()
|
||||||
|
|
||||||
proc suggestStmt*(c: PContext, n: PNode) =
|
proc suggestStmt*(c: PContext, n: PNode) =
|
||||||
suggestExpr(c, n)
|
suggestExpr(c, n)
|
||||||
|
|
|
||||||
|
|
@ -9,24 +9,24 @@
|
||||||
|
|
||||||
## Implements the dispatcher for the different parsers.
|
## Implements the dispatcher for the different parsers.
|
||||||
|
|
||||||
import
|
import
|
||||||
strutils, llstream, ast, astalgo, idents, lexer, options, msgs, parser,
|
strutils, llstream, ast, astalgo, idents, lexer, options, msgs, parser,
|
||||||
pbraces, filters, filter_tmpl, renderer
|
pbraces, filters, filter_tmpl, renderer
|
||||||
|
|
||||||
type
|
type
|
||||||
TFilterKind* = enum
|
TFilterKind* = enum
|
||||||
filtNone, filtTemplate, filtReplace, filtStrip
|
filtNone, filtTemplate, filtReplace, filtStrip
|
||||||
TParserKind* = enum
|
TParserKind* = enum
|
||||||
skinStandard, skinStrongSpaces, skinBraces, skinEndX
|
skinStandard, skinStrongSpaces, skinBraces, skinEndX
|
||||||
|
|
||||||
const
|
const
|
||||||
parserNames*: array[TParserKind, string] = ["standard", "strongspaces",
|
parserNames*: array[TParserKind, string] = ["standard", "strongspaces",
|
||||||
"braces", "endx"]
|
"braces", "endx"]
|
||||||
filterNames*: array[TFilterKind, string] = ["none", "stdtmpl", "replace",
|
filterNames*: array[TFilterKind, string] = ["none", "stdtmpl", "replace",
|
||||||
"strip"]
|
"strip"]
|
||||||
|
|
||||||
type
|
type
|
||||||
TParsers*{.final.} = object
|
TParsers*{.final.} = object
|
||||||
skin*: TParserKind
|
skin*: TParserKind
|
||||||
parser*: TParser
|
parser*: TParser
|
||||||
|
|
||||||
|
|
@ -42,53 +42,53 @@ proc parseTopLevelStmt*(p: var TParsers): PNode
|
||||||
# implementation
|
# implementation
|
||||||
|
|
||||||
proc parseFile(fileIdx: int32): PNode =
|
proc parseFile(fileIdx: int32): PNode =
|
||||||
var
|
var
|
||||||
p: TParsers
|
p: TParsers
|
||||||
f: File
|
f: File
|
||||||
let filename = fileIdx.toFullPathConsiderDirty
|
let filename = fileIdx.toFullPathConsiderDirty
|
||||||
if not open(f, filename):
|
if not open(f, filename):
|
||||||
rawMessage(errCannotOpenFile, filename)
|
rawMessage(errCannotOpenFile, filename)
|
||||||
return
|
return
|
||||||
openParsers(p, fileIdx, llStreamOpen(f))
|
openParsers(p, fileIdx, llStreamOpen(f))
|
||||||
result = parseAll(p)
|
result = parseAll(p)
|
||||||
closeParsers(p)
|
closeParsers(p)
|
||||||
|
|
||||||
proc parseAll(p: var TParsers): PNode =
|
proc parseAll(p: var TParsers): PNode =
|
||||||
case p.skin
|
case p.skin
|
||||||
of skinStandard, skinStrongSpaces:
|
of skinStandard, skinStrongSpaces:
|
||||||
result = parser.parseAll(p.parser)
|
result = parser.parseAll(p.parser)
|
||||||
of skinBraces:
|
of skinBraces:
|
||||||
result = pbraces.parseAll(p.parser)
|
result = pbraces.parseAll(p.parser)
|
||||||
of skinEndX:
|
of skinEndX:
|
||||||
internalError("parser to implement")
|
internalError("parser to implement")
|
||||||
result = ast.emptyNode
|
result = ast.emptyNode
|
||||||
|
|
||||||
proc parseTopLevelStmt(p: var TParsers): PNode =
|
proc parseTopLevelStmt(p: var TParsers): PNode =
|
||||||
case p.skin
|
case p.skin
|
||||||
of skinStandard, skinStrongSpaces:
|
of skinStandard, skinStrongSpaces:
|
||||||
result = parser.parseTopLevelStmt(p.parser)
|
result = parser.parseTopLevelStmt(p.parser)
|
||||||
of skinBraces:
|
of skinBraces:
|
||||||
result = pbraces.parseTopLevelStmt(p.parser)
|
result = pbraces.parseTopLevelStmt(p.parser)
|
||||||
of skinEndX:
|
of skinEndX:
|
||||||
internalError("parser to implement")
|
internalError("parser to implement")
|
||||||
result = ast.emptyNode
|
result = ast.emptyNode
|
||||||
|
|
||||||
proc utf8Bom(s: string): int =
|
proc utf8Bom(s: string): int =
|
||||||
if (s[0] == '\xEF') and (s[1] == '\xBB') and (s[2] == '\xBF'):
|
if (s[0] == '\xEF') and (s[1] == '\xBB') and (s[2] == '\xBF'):
|
||||||
result = 3
|
result = 3
|
||||||
else:
|
else:
|
||||||
result = 0
|
result = 0
|
||||||
|
|
||||||
proc containsShebang(s: string, i: int): bool =
|
proc containsShebang(s: string, i: int): bool =
|
||||||
if (s[i] == '#') and (s[i + 1] == '!'):
|
if (s[i] == '#') and (s[i + 1] == '!'):
|
||||||
var j = i + 2
|
var j = i + 2
|
||||||
while s[j] in Whitespace: inc(j)
|
while s[j] in Whitespace: inc(j)
|
||||||
result = s[j] == '/'
|
result = s[j] == '/'
|
||||||
|
|
||||||
proc parsePipe(filename: string, inputStream: PLLStream): PNode =
|
proc parsePipe(filename: string, inputStream: PLLStream): PNode =
|
||||||
result = ast.emptyNode
|
result = ast.emptyNode
|
||||||
var s = llStreamOpen(filename, fmRead)
|
var s = llStreamOpen(filename, fmRead)
|
||||||
if s != nil:
|
if s != nil:
|
||||||
var line = newStringOfCap(80)
|
var line = newStringOfCap(80)
|
||||||
discard llStreamReadLine(s, line)
|
discard llStreamReadLine(s, line)
|
||||||
var i = utf8Bom(line)
|
var i = utf8Bom(line)
|
||||||
|
|
@ -104,50 +104,50 @@ proc parsePipe(filename: string, inputStream: PLLStream): PNode =
|
||||||
closeParser(q)
|
closeParser(q)
|
||||||
llStreamClose(s)
|
llStreamClose(s)
|
||||||
|
|
||||||
proc getFilter(ident: PIdent): TFilterKind =
|
proc getFilter(ident: PIdent): TFilterKind =
|
||||||
for i in countup(low(TFilterKind), high(TFilterKind)):
|
for i in countup(low(TFilterKind), high(TFilterKind)):
|
||||||
if identEq(ident, filterNames[i]):
|
if identEq(ident, filterNames[i]):
|
||||||
return i
|
return i
|
||||||
result = filtNone
|
result = filtNone
|
||||||
|
|
||||||
proc getParser(ident: PIdent): TParserKind =
|
proc getParser(ident: PIdent): TParserKind =
|
||||||
for i in countup(low(TParserKind), high(TParserKind)):
|
for i in countup(low(TParserKind), high(TParserKind)):
|
||||||
if identEq(ident, parserNames[i]):
|
if identEq(ident, parserNames[i]):
|
||||||
return i
|
return i
|
||||||
rawMessage(errInvalidDirectiveX, ident.s)
|
rawMessage(errInvalidDirectiveX, ident.s)
|
||||||
|
|
||||||
proc getCallee(n: PNode): PIdent =
|
proc getCallee(n: PNode): PIdent =
|
||||||
if n.kind in nkCallKinds and n.sons[0].kind == nkIdent:
|
if n.kind in nkCallKinds and n.sons[0].kind == nkIdent:
|
||||||
result = n.sons[0].ident
|
result = n.sons[0].ident
|
||||||
elif n.kind == nkIdent:
|
elif n.kind == nkIdent:
|
||||||
result = n.ident
|
result = n.ident
|
||||||
else:
|
else:
|
||||||
rawMessage(errXNotAllowedHere, renderTree(n))
|
rawMessage(errXNotAllowedHere, renderTree(n))
|
||||||
|
|
||||||
proc applyFilter(p: var TParsers, n: PNode, filename: string,
|
proc applyFilter(p: var TParsers, n: PNode, filename: string,
|
||||||
stdin: PLLStream): PLLStream =
|
stdin: PLLStream): PLLStream =
|
||||||
var ident = getCallee(n)
|
var ident = getCallee(n)
|
||||||
var f = getFilter(ident)
|
var f = getFilter(ident)
|
||||||
case f
|
case f
|
||||||
of filtNone:
|
of filtNone:
|
||||||
p.skin = getParser(ident)
|
p.skin = getParser(ident)
|
||||||
result = stdin
|
result = stdin
|
||||||
of filtTemplate:
|
of filtTemplate:
|
||||||
result = filterTmpl(stdin, filename, n)
|
result = filterTmpl(stdin, filename, n)
|
||||||
of filtStrip:
|
of filtStrip:
|
||||||
result = filterStrip(stdin, filename, n)
|
result = filterStrip(stdin, filename, n)
|
||||||
of filtReplace:
|
of filtReplace:
|
||||||
result = filterReplace(stdin, filename, n)
|
result = filterReplace(stdin, filename, n)
|
||||||
if f != filtNone:
|
if f != filtNone:
|
||||||
if hintCodeBegin in gNotes:
|
if hintCodeBegin in gNotes:
|
||||||
rawMessage(hintCodeBegin, [])
|
rawMessage(hintCodeBegin, [])
|
||||||
msgWriteln(result.s)
|
msgWriteln(result.s)
|
||||||
rawMessage(hintCodeEnd, [])
|
rawMessage(hintCodeEnd, [])
|
||||||
|
|
||||||
proc evalPipe(p: var TParsers, n: PNode, filename: string,
|
proc evalPipe(p: var TParsers, n: PNode, filename: string,
|
||||||
start: PLLStream): PLLStream =
|
start: PLLStream): PLLStream =
|
||||||
result = start
|
result = start
|
||||||
if n.kind == nkEmpty: return
|
if n.kind == nkEmpty: return
|
||||||
if n.kind == nkInfix and n.sons[0].kind == nkIdent and
|
if n.kind == nkInfix and n.sons[0].kind == nkIdent and
|
||||||
identEq(n.sons[0].ident, "|"):
|
identEq(n.sons[0].ident, "|"):
|
||||||
for i in countup(1, 2):
|
for i in countup(1, 2):
|
||||||
|
|
@ -159,8 +159,8 @@ proc evalPipe(p: var TParsers, n: PNode, filename: string,
|
||||||
result = evalPipe(p, n.sons[0], filename, result)
|
result = evalPipe(p, n.sons[0], filename, result)
|
||||||
else:
|
else:
|
||||||
result = applyFilter(p, n, filename, result)
|
result = applyFilter(p, n, filename, result)
|
||||||
|
|
||||||
proc openParsers(p: var TParsers, fileIdx: int32, inputstream: PLLStream) =
|
proc openParsers(p: var TParsers, fileIdx: int32, inputstream: PLLStream) =
|
||||||
var s: PLLStream
|
var s: PLLStream
|
||||||
p.skin = skinStandard
|
p.skin = skinStandard
|
||||||
let filename = fileIdx.toFullPathConsiderDirty
|
let filename = fileIdx.toFullPathConsiderDirty
|
||||||
|
|
@ -172,6 +172,6 @@ proc openParsers(p: var TParsers, fileIdx: int32, inputstream: PLLStream) =
|
||||||
parser.openParser(p.parser, fileIdx, s, false)
|
parser.openParser(p.parser, fileIdx, s, false)
|
||||||
of skinStrongSpaces:
|
of skinStrongSpaces:
|
||||||
parser.openParser(p.parser, fileIdx, s, true)
|
parser.openParser(p.parser, fileIdx, s, true)
|
||||||
|
|
||||||
proc closeParsers(p: var TParsers) =
|
proc closeParsers(p: var TParsers) =
|
||||||
parser.closeParser(p.parser)
|
parser.closeParser(p.parser)
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,10 @@ import
|
||||||
|
|
||||||
{.compile: "../tinyc/libtcc.c".}
|
{.compile: "../tinyc/libtcc.c".}
|
||||||
|
|
||||||
proc tinyCErrorHandler(closure: pointer, msg: cstring) {.cdecl.} =
|
proc tinyCErrorHandler(closure: pointer, msg: cstring) {.cdecl.} =
|
||||||
rawMessage(errGenerated, $msg)
|
rawMessage(errGenerated, $msg)
|
||||||
|
|
||||||
proc initTinyCState: PccState =
|
proc initTinyCState: PccState =
|
||||||
result = openCCState()
|
result = openCCState()
|
||||||
setErrorFunc(result, nil, tinyCErrorHandler)
|
setErrorFunc(result, nil, tinyCErrorHandler)
|
||||||
|
|
||||||
|
|
@ -23,25 +23,25 @@ var
|
||||||
gTinyC = initTinyCState()
|
gTinyC = initTinyCState()
|
||||||
libIncluded = false
|
libIncluded = false
|
||||||
|
|
||||||
proc addFile(filename: string) =
|
proc addFile(filename: string) =
|
||||||
if addFile(gTinyC, filename) != 0'i32:
|
if addFile(gTinyC, filename) != 0'i32:
|
||||||
rawMessage(errCannotOpenFile, filename)
|
rawMessage(errCannotOpenFile, filename)
|
||||||
|
|
||||||
proc setupEnvironment =
|
proc setupEnvironment =
|
||||||
when defined(amd64):
|
when defined(amd64):
|
||||||
defineSymbol(gTinyC, "__x86_64__", nil)
|
defineSymbol(gTinyC, "__x86_64__", nil)
|
||||||
elif defined(i386):
|
elif defined(i386):
|
||||||
defineSymbol(gTinyC, "__i386__", nil)
|
defineSymbol(gTinyC, "__i386__", nil)
|
||||||
when defined(linux):
|
when defined(linux):
|
||||||
defineSymbol(gTinyC, "__linux__", nil)
|
defineSymbol(gTinyC, "__linux__", nil)
|
||||||
defineSymbol(gTinyC, "__linux", nil)
|
defineSymbol(gTinyC, "__linux", nil)
|
||||||
var nimrodDir = getPrefixDir()
|
var nimrodDir = getPrefixDir()
|
||||||
|
|
||||||
addIncludePath(gTinyC, libpath)
|
addIncludePath(gTinyC, libpath)
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
addSysincludePath(gTinyC, nimrodDir / "tinyc/win32/include")
|
addSysincludePath(gTinyC, nimrodDir / "tinyc/win32/include")
|
||||||
addSysincludePath(gTinyC, nimrodDir / "tinyc/include")
|
addSysincludePath(gTinyC, nimrodDir / "tinyc/include")
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
defineSymbol(gTinyC, "_WIN32", nil)
|
defineSymbol(gTinyC, "_WIN32", nil)
|
||||||
# we need Mingw's headers too:
|
# we need Mingw's headers too:
|
||||||
var gccbin = getConfigVar("gcc.path") % ["nimrod", nimrodDir]
|
var gccbin = getConfigVar("gcc.path") % ["nimrod", nimrodDir]
|
||||||
|
|
@ -54,7 +54,7 @@ proc setupEnvironment =
|
||||||
#addFile(nimrodDir / r"tinyc\win32\dllcrt1.o")
|
#addFile(nimrodDir / r"tinyc\win32\dllcrt1.o")
|
||||||
#addFile(nimrodDir / r"tinyc\win32\dllmain.o")
|
#addFile(nimrodDir / r"tinyc\win32\dllmain.o")
|
||||||
addFile(nimrodDir / r"tinyc\win32\libtcc1.o")
|
addFile(nimrodDir / r"tinyc\win32\libtcc1.o")
|
||||||
|
|
||||||
#addFile(nimrodDir / r"tinyc\win32\lib\crt1.c")
|
#addFile(nimrodDir / r"tinyc\win32\lib\crt1.c")
|
||||||
#addFile(nimrodDir / r"tinyc\lib\libtcc1.c")
|
#addFile(nimrodDir / r"tinyc\lib\libtcc1.c")
|
||||||
else:
|
else:
|
||||||
|
|
@ -62,12 +62,12 @@ proc setupEnvironment =
|
||||||
when defined(amd64):
|
when defined(amd64):
|
||||||
addSysincludePath(gTinyC, "/usr/include/x86_64-linux-gnu")
|
addSysincludePath(gTinyC, "/usr/include/x86_64-linux-gnu")
|
||||||
|
|
||||||
proc compileCCode*(ccode: string) =
|
proc compileCCode*(ccode: string) =
|
||||||
if not libIncluded:
|
if not libIncluded:
|
||||||
libIncluded = true
|
libIncluded = true
|
||||||
setupEnvironment()
|
setupEnvironment()
|
||||||
discard compileString(gTinyC, ccode)
|
discard compileString(gTinyC, ccode)
|
||||||
|
|
||||||
proc run*(args: string) =
|
proc run*(args: string) =
|
||||||
var s = @[cstring(gProjectName)] & map(split(args), proc(x: string): cstring = cstring(x))
|
var s = @[cstring(gProjectName)] & map(split(args), proc(x: string): cstring = cstring(x))
|
||||||
var err = tinyc.run(gTinyC, cint(len(s)), cast[cstringArray](addr(s[0]))) != 0'i32
|
var err = tinyc.run(gTinyC, cint(len(s)), cast[cstringArray](addr(s[0]))) != 0'i32
|
||||||
|
|
|
||||||
|
|
@ -9,36 +9,36 @@
|
||||||
|
|
||||||
# Implements a table from trees to trees. Does structural equivalence checking.
|
# Implements a table from trees to trees. Does structural equivalence checking.
|
||||||
|
|
||||||
import
|
import
|
||||||
hashes, ast, astalgo, types
|
hashes, ast, astalgo, types
|
||||||
|
|
||||||
proc hashTree(n: PNode): Hash =
|
proc hashTree(n: PNode): Hash =
|
||||||
if n == nil: return
|
if n == nil: return
|
||||||
result = ord(n.kind)
|
result = ord(n.kind)
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkEmpty, nkNilLit, nkType:
|
of nkEmpty, nkNilLit, nkType:
|
||||||
discard
|
discard
|
||||||
of nkIdent:
|
of nkIdent:
|
||||||
result = result !& n.ident.h
|
result = result !& n.ident.h
|
||||||
of nkSym:
|
of nkSym:
|
||||||
result = result !& n.sym.name.h
|
result = result !& n.sym.name.h
|
||||||
of nkCharLit..nkUInt64Lit:
|
of nkCharLit..nkUInt64Lit:
|
||||||
if (n.intVal >= low(int)) and (n.intVal <= high(int)):
|
if (n.intVal >= low(int)) and (n.intVal <= high(int)):
|
||||||
result = result !& int(n.intVal)
|
result = result !& int(n.intVal)
|
||||||
of nkFloatLit..nkFloat64Lit:
|
of nkFloatLit..nkFloat64Lit:
|
||||||
if (n.floatVal >= - 1000000.0) and (n.floatVal <= 1000000.0):
|
if (n.floatVal >= - 1000000.0) and (n.floatVal <= 1000000.0):
|
||||||
result = result !& toInt(n.floatVal)
|
result = result !& toInt(n.floatVal)
|
||||||
of nkStrLit..nkTripleStrLit:
|
of nkStrLit..nkTripleStrLit:
|
||||||
if not n.strVal.isNil:
|
if not n.strVal.isNil:
|
||||||
result = result !& hash(n.strVal)
|
result = result !& hash(n.strVal)
|
||||||
else:
|
else:
|
||||||
for i in countup(0, sonsLen(n) - 1):
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
result = result !& hashTree(n.sons[i])
|
result = result !& hashTree(n.sons[i])
|
||||||
|
|
||||||
proc treesEquivalent(a, b: PNode): bool =
|
proc treesEquivalent(a, b: PNode): bool =
|
||||||
if a == b:
|
if a == b:
|
||||||
result = true
|
result = true
|
||||||
elif (a != nil) and (b != nil) and (a.kind == b.kind):
|
elif (a != nil) and (b != nil) and (a.kind == b.kind):
|
||||||
case a.kind
|
case a.kind
|
||||||
of nkEmpty, nkNilLit, nkType: result = true
|
of nkEmpty, nkNilLit, nkType: result = true
|
||||||
of nkSym: result = a.sym.id == b.sym.id
|
of nkSym: result = a.sym.id == b.sym.id
|
||||||
|
|
@ -46,28 +46,28 @@ proc treesEquivalent(a, b: PNode): bool =
|
||||||
of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal
|
of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal
|
||||||
of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
|
of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
|
||||||
of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
|
of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
|
||||||
else:
|
else:
|
||||||
if sonsLen(a) == sonsLen(b):
|
if sonsLen(a) == sonsLen(b):
|
||||||
for i in countup(0, sonsLen(a) - 1):
|
for i in countup(0, sonsLen(a) - 1):
|
||||||
if not treesEquivalent(a.sons[i], b.sons[i]): return
|
if not treesEquivalent(a.sons[i], b.sons[i]): return
|
||||||
result = true
|
result = true
|
||||||
if result: result = sameTypeOrNil(a.typ, b.typ)
|
if result: result = sameTypeOrNil(a.typ, b.typ)
|
||||||
|
|
||||||
proc nodeTableRawGet(t: TNodeTable, k: Hash, key: PNode): int =
|
proc nodeTableRawGet(t: TNodeTable, k: Hash, key: PNode): int =
|
||||||
var h: Hash = k and high(t.data)
|
var h: Hash = k and high(t.data)
|
||||||
while t.data[h].key != nil:
|
while t.data[h].key != nil:
|
||||||
if (t.data[h].h == k) and treesEquivalent(t.data[h].key, key):
|
if (t.data[h].h == k) and treesEquivalent(t.data[h].key, key):
|
||||||
return h
|
return h
|
||||||
h = nextTry(h, high(t.data))
|
h = nextTry(h, high(t.data))
|
||||||
result = -1
|
result = -1
|
||||||
|
|
||||||
proc nodeTableGet*(t: TNodeTable, key: PNode): int =
|
proc nodeTableGet*(t: TNodeTable, key: PNode): int =
|
||||||
var index = nodeTableRawGet(t, hashTree(key), key)
|
var index = nodeTableRawGet(t, hashTree(key), key)
|
||||||
if index >= 0: result = t.data[index].val
|
if index >= 0: result = t.data[index].val
|
||||||
else: result = low(int)
|
else: result = low(int)
|
||||||
|
|
||||||
proc nodeTableRawInsert(data: var TNodePairSeq, k: Hash, key: PNode,
|
proc nodeTableRawInsert(data: var TNodePairSeq, k: Hash, key: PNode,
|
||||||
val: int) =
|
val: int) =
|
||||||
var h: Hash = k and high(data)
|
var h: Hash = k and high(data)
|
||||||
while data[h].key != nil: h = nextTry(h, high(data))
|
while data[h].key != nil: h = nextTry(h, high(data))
|
||||||
assert(data[h].key == nil)
|
assert(data[h].key == nil)
|
||||||
|
|
@ -75,35 +75,35 @@ proc nodeTableRawInsert(data: var TNodePairSeq, k: Hash, key: PNode,
|
||||||
data[h].key = key
|
data[h].key = key
|
||||||
data[h].val = val
|
data[h].val = val
|
||||||
|
|
||||||
proc nodeTablePut*(t: var TNodeTable, key: PNode, val: int) =
|
proc nodeTablePut*(t: var TNodeTable, key: PNode, val: int) =
|
||||||
var n: TNodePairSeq
|
var n: TNodePairSeq
|
||||||
var k: Hash = hashTree(key)
|
var k: Hash = hashTree(key)
|
||||||
var index = nodeTableRawGet(t, k, key)
|
var index = nodeTableRawGet(t, k, key)
|
||||||
if index >= 0:
|
if index >= 0:
|
||||||
assert(t.data[index].key != nil)
|
assert(t.data[index].key != nil)
|
||||||
t.data[index].val = val
|
t.data[index].val = val
|
||||||
else:
|
else:
|
||||||
if mustRehash(len(t.data), t.counter):
|
if mustRehash(len(t.data), t.counter):
|
||||||
newSeq(n, len(t.data) * GrowthFactor)
|
newSeq(n, len(t.data) * GrowthFactor)
|
||||||
for i in countup(0, high(t.data)):
|
for i in countup(0, high(t.data)):
|
||||||
if t.data[i].key != nil:
|
if t.data[i].key != nil:
|
||||||
nodeTableRawInsert(n, t.data[i].h, t.data[i].key, t.data[i].val)
|
nodeTableRawInsert(n, t.data[i].h, t.data[i].key, t.data[i].val)
|
||||||
swap(t.data, n)
|
swap(t.data, n)
|
||||||
nodeTableRawInsert(t.data, k, key, val)
|
nodeTableRawInsert(t.data, k, key, val)
|
||||||
inc(t.counter)
|
inc(t.counter)
|
||||||
|
|
||||||
proc nodeTableTestOrSet*(t: var TNodeTable, key: PNode, val: int): int =
|
proc nodeTableTestOrSet*(t: var TNodeTable, key: PNode, val: int): int =
|
||||||
var n: TNodePairSeq
|
var n: TNodePairSeq
|
||||||
var k: Hash = hashTree(key)
|
var k: Hash = hashTree(key)
|
||||||
var index = nodeTableRawGet(t, k, key)
|
var index = nodeTableRawGet(t, k, key)
|
||||||
if index >= 0:
|
if index >= 0:
|
||||||
assert(t.data[index].key != nil)
|
assert(t.data[index].key != nil)
|
||||||
result = t.data[index].val
|
result = t.data[index].val
|
||||||
else:
|
else:
|
||||||
if mustRehash(len(t.data), t.counter):
|
if mustRehash(len(t.data), t.counter):
|
||||||
newSeq(n, len(t.data) * GrowthFactor)
|
newSeq(n, len(t.data) * GrowthFactor)
|
||||||
for i in countup(0, high(t.data)):
|
for i in countup(0, high(t.data)):
|
||||||
if t.data[i].key != nil:
|
if t.data[i].key != nil:
|
||||||
nodeTableRawInsert(n, t.data[i].h, t.data[i].key, t.data[i].val)
|
nodeTableRawInsert(n, t.data[i].h, t.data[i].key, t.data[i].val)
|
||||||
swap(t.data, n)
|
swap(t.data, n)
|
||||||
nodeTableRawInsert(t.data, k, key, val)
|
nodeTableRawInsert(t.data, k, key, val)
|
||||||
|
|
|
||||||
|
|
@ -9,5 +9,4 @@ Mario Ray Mahardhika
|
||||||
Alex Mitchell
|
Alex Mitchell
|
||||||
Dominik Picheta
|
Dominik Picheta
|
||||||
Jonathan Plona
|
Jonathan Plona
|
||||||
Alexander Rødseth
|
Alexander Rødseth
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,17 @@
|
||||||
Nim -- a Compiler for Nim. http://nim-lang.org/
|
Nim -- a Compiler for Nim. http://nim-lang.org/
|
||||||
|
|
||||||
Copyright (C) 2006-2015 Andreas Rumpf. All rights reserved.
|
Copyright (C) 2006-2015 Andreas Rumpf. All rights reserved.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
in the Software without restriction, including without limitation the rights
|
in the Software without restriction, including without limitation the rights
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
furnished to do so, subject to the following conditions:
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
The above copyright notice and this permission notice shall be included in
|
||||||
all copies or substantial portions of the Software.
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
|
@ -21,4 +21,4 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
[ MIT license: http://www.opensource.org/licenses/mit-license.php ]
|
[ MIT license: http://www.opensource.org/licenses/mit-license.php ]
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ English word To use Notes
|
||||||
------------------- ------------ --------------------------------------
|
------------------- ------------ --------------------------------------
|
||||||
initialize initT ``init`` is used to create a
|
initialize initT ``init`` is used to create a
|
||||||
value type ``T``
|
value type ``T``
|
||||||
new newP ``new`` is used to create a
|
new newP ``new`` is used to create a
|
||||||
reference type ``P``
|
reference type ``P``
|
||||||
find find should return the position where
|
find find should return the position where
|
||||||
something was found; for a bool result
|
something was found; for a bool result
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ The documentation consists of several documents:
|
||||||
builtin templating system.
|
builtin templating system.
|
||||||
|
|
||||||
- | `Term rewriting macros <trmacros.html>`_
|
- | `Term rewriting macros <trmacros.html>`_
|
||||||
| Term rewriting macros enhance the compilation process with user defined
|
| Term rewriting macros enhance the compilation process with user defined
|
||||||
optimizations.
|
optimizations.
|
||||||
|
|
||||||
- | `Internal documentation <intern.html>`_
|
- | `Internal documentation <intern.html>`_
|
||||||
|
|
|
||||||
402
doc/endb.txt
402
doc/endb.txt
|
|
@ -1,203 +1,203 @@
|
||||||
==============================================
|
==============================================
|
||||||
Embedded Nim Debugger (ENDB) User Guide
|
Embedded Nim Debugger (ENDB) User Guide
|
||||||
==============================================
|
==============================================
|
||||||
|
|
||||||
:Author: Andreas Rumpf
|
:Author: Andreas Rumpf
|
||||||
:Version: |nimversion|
|
:Version: |nimversion|
|
||||||
|
|
||||||
.. contents::
|
.. contents::
|
||||||
|
|
||||||
**WARNING**: ENDB is not maintained anymore! Please help if you're interested
|
**WARNING**: ENDB is not maintained anymore! Please help if you're interested
|
||||||
in this tool.
|
in this tool.
|
||||||
|
|
||||||
Nim comes with a platform independent debugger -
|
Nim comes with a platform independent debugger -
|
||||||
the Embedded Nim Debugger (ENDB). The debugger is
|
the Embedded Nim Debugger (ENDB). The debugger is
|
||||||
*embedded* into your executable if it has been
|
*embedded* into your executable if it has been
|
||||||
compiled with the ``--debugger:on`` command line option.
|
compiled with the ``--debugger:on`` command line option.
|
||||||
This also defines the conditional symbol ``ENDB`` for you.
|
This also defines the conditional symbol ``ENDB`` for you.
|
||||||
|
|
||||||
Note: You must not compile your program with the ``--app:gui``
|
Note: You must not compile your program with the ``--app:gui``
|
||||||
command line option because then there would be no console
|
command line option because then there would be no console
|
||||||
available for the debugger.
|
available for the debugger.
|
||||||
|
|
||||||
If you start your program the debugger will immediately show
|
If you start your program the debugger will immediately show
|
||||||
a prompt on the console. You can now enter a command. The next sections
|
a prompt on the console. You can now enter a command. The next sections
|
||||||
deal with the possible commands. As usual in Nim in all commands
|
deal with the possible commands. As usual in Nim in all commands
|
||||||
underscores and case do not matter. Optional components of a command
|
underscores and case do not matter. Optional components of a command
|
||||||
are listed in brackets ``[...]`` here.
|
are listed in brackets ``[...]`` here.
|
||||||
|
|
||||||
|
|
||||||
General Commands
|
General Commands
|
||||||
================
|
================
|
||||||
|
|
||||||
``h``, ``help``
|
``h``, ``help``
|
||||||
Display a quick reference of the possible commands.
|
Display a quick reference of the possible commands.
|
||||||
|
|
||||||
``q``, ``quit``
|
``q``, ``quit``
|
||||||
Quit the debugger and the program.
|
Quit the debugger and the program.
|
||||||
|
|
||||||
<ENTER>
|
<ENTER>
|
||||||
(Without any typed command) repeat the previous debugger command.
|
(Without any typed command) repeat the previous debugger command.
|
||||||
If there is no previous command, ``step_into`` is assumed.
|
If there is no previous command, ``step_into`` is assumed.
|
||||||
|
|
||||||
Executing Commands
|
Executing Commands
|
||||||
==================
|
==================
|
||||||
|
|
||||||
``s``, ``step_into``
|
``s``, ``step_into``
|
||||||
Single step, stepping into routine calls.
|
Single step, stepping into routine calls.
|
||||||
|
|
||||||
``n``, ``step_over``
|
``n``, ``step_over``
|
||||||
Single step, without stepping into routine calls.
|
Single step, without stepping into routine calls.
|
||||||
|
|
||||||
``f``, ``skip_current``
|
``f``, ``skip_current``
|
||||||
Continue execution until the current routine finishes.
|
Continue execution until the current routine finishes.
|
||||||
|
|
||||||
``c``, ``continue``
|
``c``, ``continue``
|
||||||
Continue execution until the next breakpoint.
|
Continue execution until the next breakpoint.
|
||||||
|
|
||||||
``i``, ``ignore``
|
``i``, ``ignore``
|
||||||
Continue execution, ignore all breakpoints. This effectively quits
|
Continue execution, ignore all breakpoints. This effectively quits
|
||||||
the debugger and runs the program until it finishes.
|
the debugger and runs the program until it finishes.
|
||||||
|
|
||||||
|
|
||||||
Breakpoint Commands
|
Breakpoint Commands
|
||||||
===================
|
===================
|
||||||
|
|
||||||
``b``, ``setbreak`` [fromline [toline]] [file]
|
``b``, ``setbreak`` [fromline [toline]] [file]
|
||||||
Set a new breakpoint for the given file
|
Set a new breakpoint for the given file
|
||||||
and line numbers. If no file is given, the current execution point's
|
and line numbers. If no file is given, the current execution point's
|
||||||
filename is used. If the filename has no extension, ``.nim`` is
|
filename is used. If the filename has no extension, ``.nim`` is
|
||||||
appended for your convenience.
|
appended for your convenience.
|
||||||
If no line numbers are given, the current execution point's
|
If no line numbers are given, the current execution point's
|
||||||
line is used. If both ``fromline`` and ``toline`` are given the
|
line is used. If both ``fromline`` and ``toline`` are given the
|
||||||
breakpoint contains a line number range. Some examples if it is still
|
breakpoint contains a line number range. Some examples if it is still
|
||||||
unclear:
|
unclear:
|
||||||
|
|
||||||
* ``b 12 15 thallo`` creates a breakpoint that
|
* ``b 12 15 thallo`` creates a breakpoint that
|
||||||
will be triggered if the instruction pointer reaches one of the
|
will be triggered if the instruction pointer reaches one of the
|
||||||
lines 12-15 in the file ``thallo.nim``.
|
lines 12-15 in the file ``thallo.nim``.
|
||||||
* ``b 12 thallo`` creates a breakpoint that
|
* ``b 12 thallo`` creates a breakpoint that
|
||||||
will be triggered if the instruction pointer reaches the
|
will be triggered if the instruction pointer reaches the
|
||||||
line 12 in the file ``thallo.nim``.
|
line 12 in the file ``thallo.nim``.
|
||||||
* ``b 12`` creates a breakpoint that
|
* ``b 12`` creates a breakpoint that
|
||||||
will be triggered if the instruction pointer reaches the
|
will be triggered if the instruction pointer reaches the
|
||||||
line 12 in the current file.
|
line 12 in the current file.
|
||||||
* ``b`` creates a breakpoint that
|
* ``b`` creates a breakpoint that
|
||||||
will be triggered if the instruction pointer reaches the
|
will be triggered if the instruction pointer reaches the
|
||||||
current line in the current file again.
|
current line in the current file again.
|
||||||
|
|
||||||
``breakpoints``
|
``breakpoints``
|
||||||
Display the entire breakpoint list.
|
Display the entire breakpoint list.
|
||||||
|
|
||||||
``disable`` <identifier>
|
``disable`` <identifier>
|
||||||
Disable a breakpoint. It remains disabled until you turn it on again
|
Disable a breakpoint. It remains disabled until you turn it on again
|
||||||
with the ``enable`` command.
|
with the ``enable`` command.
|
||||||
|
|
||||||
``enable`` <identifier>
|
``enable`` <identifier>
|
||||||
Enable a breakpoint.
|
Enable a breakpoint.
|
||||||
|
|
||||||
Often it happens when debugging that you keep retyping the breakpoints again
|
Often it happens when debugging that you keep retyping the breakpoints again
|
||||||
and again because they are lost when you restart your program. This is not
|
and again because they are lost when you restart your program. This is not
|
||||||
necessary: A special pragma has been defined for this:
|
necessary: A special pragma has been defined for this:
|
||||||
|
|
||||||
|
|
||||||
The ``breakpoint`` pragma
|
The ``breakpoint`` pragma
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
The ``breakpoint`` pragma is syntactically a statement. It can be used
|
The ``breakpoint`` pragma is syntactically a statement. It can be used
|
||||||
to mark the *following line* as a breakpoint:
|
to mark the *following line* as a breakpoint:
|
||||||
|
|
||||||
.. code-block:: Nim
|
.. code-block:: Nim
|
||||||
write("1")
|
write("1")
|
||||||
{.breakpoint: "before_write_2".}
|
{.breakpoint: "before_write_2".}
|
||||||
write("2")
|
write("2")
|
||||||
|
|
||||||
The name of the breakpoint here is ``before_write_2``. Of course the
|
The name of the breakpoint here is ``before_write_2``. Of course the
|
||||||
breakpoint's name is optional - the compiler will generate one for you
|
breakpoint's name is optional - the compiler will generate one for you
|
||||||
if you leave it out.
|
if you leave it out.
|
||||||
|
|
||||||
Code for the ``breakpoint`` pragma is only generated if the debugger
|
Code for the ``breakpoint`` pragma is only generated if the debugger
|
||||||
is turned on, so you don't need to remove it from your source code after
|
is turned on, so you don't need to remove it from your source code after
|
||||||
debugging.
|
debugging.
|
||||||
|
|
||||||
|
|
||||||
The ``watchpoint`` pragma
|
The ``watchpoint`` pragma
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
The ``watchpoint`` pragma is syntactically a statement. It can be used
|
The ``watchpoint`` pragma is syntactically a statement. It can be used
|
||||||
to mark a location as a watchpoint:
|
to mark a location as a watchpoint:
|
||||||
|
|
||||||
.. code-block:: Nim
|
.. code-block:: Nim
|
||||||
var a: array [0..20, int]
|
var a: array [0..20, int]
|
||||||
|
|
||||||
{.watchpoint: a[3].}
|
{.watchpoint: a[3].}
|
||||||
for i in 0 .. 20: a[i] = i
|
for i in 0 .. 20: a[i] = i
|
||||||
|
|
||||||
ENDB then writes a stack trace whenever the content of the location ``a[3]``
|
ENDB then writes a stack trace whenever the content of the location ``a[3]``
|
||||||
changes. The current implementation only tracks a hash value of the location's
|
changes. The current implementation only tracks a hash value of the location's
|
||||||
contents and so locations that are not word sized may encounter false
|
contents and so locations that are not word sized may encounter false
|
||||||
negatives in very rare cases.
|
negatives in very rare cases.
|
||||||
|
|
||||||
Code for the ``watchpoint`` pragma is only generated if the debugger
|
Code for the ``watchpoint`` pragma is only generated if the debugger
|
||||||
is turned on, so you don't need to remove it from your source code after
|
is turned on, so you don't need to remove it from your source code after
|
||||||
debugging.
|
debugging.
|
||||||
|
|
||||||
Due to the primitive implementation watchpoints are even slower than
|
Due to the primitive implementation watchpoints are even slower than
|
||||||
breakpoints: After *every* executed Nim code line it is checked whether the
|
breakpoints: After *every* executed Nim code line it is checked whether the
|
||||||
location changed.
|
location changed.
|
||||||
|
|
||||||
|
|
||||||
Data Display Commands
|
Data Display Commands
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
``e``, ``eval`` <exp>
|
``e``, ``eval`` <exp>
|
||||||
Evaluate the expression <exp>. Note that ENDB has no full-blown expression
|
Evaluate the expression <exp>. Note that ENDB has no full-blown expression
|
||||||
evaluator built-in. So expressions are limited:
|
evaluator built-in. So expressions are limited:
|
||||||
|
|
||||||
* To display global variables prefix their names with their
|
* To display global variables prefix their names with their
|
||||||
owning module: ``nim1.globalVar``
|
owning module: ``nim1.globalVar``
|
||||||
* To display local variables or parameters just type in
|
* To display local variables or parameters just type in
|
||||||
their name: ``localVar``. If you want to inspect variables that are not
|
their name: ``localVar``. If you want to inspect variables that are not
|
||||||
in the current stack frame, use the ``up`` or ``down`` command.
|
in the current stack frame, use the ``up`` or ``down`` command.
|
||||||
|
|
||||||
Unfortunately, only inspecting variables is possible at the moment. Maybe
|
Unfortunately, only inspecting variables is possible at the moment. Maybe
|
||||||
a future version will implement a full-blown Nim expression evaluator,
|
a future version will implement a full-blown Nim expression evaluator,
|
||||||
but this is not easy to do and would bloat the debugger's code.
|
but this is not easy to do and would bloat the debugger's code.
|
||||||
|
|
||||||
Since displaying the whole data structures is often not needed and
|
Since displaying the whole data structures is often not needed and
|
||||||
painfully slow, the debugger uses a *maximal display depth* concept for
|
painfully slow, the debugger uses a *maximal display depth* concept for
|
||||||
displaying.
|
displaying.
|
||||||
|
|
||||||
You can alter the maximal display depth with the ``maxdisplay``
|
You can alter the maximal display depth with the ``maxdisplay``
|
||||||
command.
|
command.
|
||||||
|
|
||||||
``maxdisplay`` <natural>
|
``maxdisplay`` <natural>
|
||||||
Sets the maximal display depth to the given integer value. A value of 0
|
Sets the maximal display depth to the given integer value. A value of 0
|
||||||
means there is no maximal display depth. Default is 3.
|
means there is no maximal display depth. Default is 3.
|
||||||
|
|
||||||
``o``, ``out`` <filename> <exp>
|
``o``, ``out`` <filename> <exp>
|
||||||
Evaluate the expression <exp> and store its string representation into a
|
Evaluate the expression <exp> and store its string representation into a
|
||||||
file named <filename>. If the file does not exist, it will be created,
|
file named <filename>. If the file does not exist, it will be created,
|
||||||
otherwise it will be opened for appending.
|
otherwise it will be opened for appending.
|
||||||
|
|
||||||
``w``, ``where``
|
``w``, ``where``
|
||||||
Display the current execution point.
|
Display the current execution point.
|
||||||
|
|
||||||
``u``, ``up``
|
``u``, ``up``
|
||||||
Go up in the call stack.
|
Go up in the call stack.
|
||||||
|
|
||||||
``d``, ``down``
|
``d``, ``down``
|
||||||
Go down in the call stack.
|
Go down in the call stack.
|
||||||
|
|
||||||
``stackframe`` [file]
|
``stackframe`` [file]
|
||||||
Displays the content of the current stack frame in ``stdout`` or
|
Displays the content of the current stack frame in ``stdout`` or
|
||||||
appends it to the file, depending on whether a file is given.
|
appends it to the file, depending on whether a file is given.
|
||||||
|
|
||||||
``callstack``
|
``callstack``
|
||||||
Display the entire call stack (but not its content).
|
Display the entire call stack (but not its content).
|
||||||
|
|
||||||
``l``, ``locals``
|
``l``, ``locals``
|
||||||
Display the available local variables in the current stack frame.
|
Display the available local variables in the current stack frame.
|
||||||
|
|
||||||
``g``, ``globals``
|
``g``, ``globals``
|
||||||
Display all the global variables that are available for inspection.
|
Display all the global variables that are available for inspection.
|
||||||
|
|
|
||||||
46
doc/estp.txt
46
doc/estp.txt
|
|
@ -1,30 +1,30 @@
|
||||||
===================================================
|
===================================================
|
||||||
Embedded Stack Trace Profiler (ESTP) User Guide
|
Embedded Stack Trace Profiler (ESTP) User Guide
|
||||||
===================================================
|
===================================================
|
||||||
|
|
||||||
:Author: Andreas Rumpf
|
|
||||||
:Version: |nimversion|
|
|
||||||
|
|
||||||
|
|
||||||
Nim comes with a platform independent profiler -
|
|
||||||
the Embedded Stack Trace Profiler (ESTP). The profiler
|
|
||||||
is *embedded* into your executable. To activate the profiler you need to do:
|
|
||||||
|
|
||||||
* compile your program with the ``--profiler:on --stackTrace:on`` command
|
:Author: Andreas Rumpf
|
||||||
|
:Version: |nimversion|
|
||||||
|
|
||||||
|
|
||||||
|
Nim comes with a platform independent profiler -
|
||||||
|
the Embedded Stack Trace Profiler (ESTP). The profiler
|
||||||
|
is *embedded* into your executable. To activate the profiler you need to do:
|
||||||
|
|
||||||
|
* compile your program with the ``--profiler:on --stackTrace:on`` command
|
||||||
line options
|
line options
|
||||||
* import the ``nimprof`` module
|
* import the ``nimprof`` module
|
||||||
* run your program as usual.
|
* run your program as usual.
|
||||||
|
|
||||||
You can in fact look at ``nimprof``'s source code to see how to implement
|
You can in fact look at ``nimprof``'s source code to see how to implement
|
||||||
your own profiler.
|
your own profiler.
|
||||||
|
|
||||||
The setting ``--profiler:on`` defines the conditional symbol ``profiler``.
|
The setting ``--profiler:on`` defines the conditional symbol ``profiler``.
|
||||||
|
|
||||||
After your program has finished the profiler will create a
|
After your program has finished the profiler will create a
|
||||||
file ``profile_results.txt`` containing the profiling results.
|
file ``profile_results.txt`` containing the profiling results.
|
||||||
|
|
||||||
Since the profiler works by examining stack traces, it's essential that
|
Since the profiler works by examining stack traces, it's essential that
|
||||||
the option ``--stackTrace:on`` is active! Unfortunately this means that a
|
the option ``--stackTrace:on`` is active! Unfortunately this means that a
|
||||||
profiling build is much slower than a release build.
|
profiling build is much slower than a release build.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -32,7 +32,7 @@ Memory profiler
|
||||||
===============
|
===============
|
||||||
|
|
||||||
You can also use ESTP as a memory profiler to see which stack traces allocate
|
You can also use ESTP as a memory profiler to see which stack traces allocate
|
||||||
the most memory and thus create the most GC pressure. It may also help to
|
the most memory and thus create the most GC pressure. It may also help to
|
||||||
find memory leaks. To activate the memory profiler you need to do:
|
find memory leaks. To activate the memory profiler you need to do:
|
||||||
|
|
||||||
* compile your program with the ``--profiler:off --stackTrace:on -d:memProfiler``
|
* compile your program with the ``--profiler:off --stackTrace:on -d:memProfiler``
|
||||||
|
|
@ -40,22 +40,22 @@ find memory leaks. To activate the memory profiler you need to do:
|
||||||
* import the ``nimprof`` module
|
* import the ``nimprof`` module
|
||||||
* run your program as usual.
|
* run your program as usual.
|
||||||
|
|
||||||
Define the symbol ``ignoreAllocationSize`` so that only the number of
|
Define the symbol ``ignoreAllocationSize`` so that only the number of
|
||||||
allocations is counted and the sizes of the memory allocations do not matter.
|
allocations is counted and the sizes of the memory allocations do not matter.
|
||||||
|
|
||||||
|
|
||||||
Example results file
|
Example results file
|
||||||
====================
|
====================
|
||||||
|
|
||||||
The results file lists stack traces ordered by significance.
|
The results file lists stack traces ordered by significance.
|
||||||
|
|
||||||
The following example file has been generated by profiling the Nim compiler
|
The following example file has been generated by profiling the Nim compiler
|
||||||
itself: It shows that in total 5.4% of the runtime has been spent
|
itself: It shows that in total 5.4% of the runtime has been spent
|
||||||
in ``crcFromRope`` or its children.
|
in ``crcFromRope`` or its children.
|
||||||
|
|
||||||
In general the stack traces show you immediately where the problem is because
|
In general the stack traces show you immediately where the problem is because
|
||||||
the trace acts like an explanation; in traditional profilers you can only find
|
the trace acts like an explanation; in traditional profilers you can only find
|
||||||
expensive leaf functions easily but the *reason* why they are invoked
|
expensive leaf functions easily but the *reason* why they are invoked
|
||||||
often remains mysterious.
|
often remains mysterious.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ Source Code Filters
|
||||||
|
|
||||||
.. contents::
|
.. contents::
|
||||||
|
|
||||||
A `Source Code Filter` transforms the input character stream to an in-memory
|
A `Source Code Filter` transforms the input character stream to an in-memory
|
||||||
output stream before parsing. A filter can be used to provide templating
|
output stream before parsing. A filter can be used to provide templating
|
||||||
systems or preprocessors.
|
systems or preprocessors.
|
||||||
|
|
||||||
To use a filter for a source file the *shebang* notation is used::
|
To use a filter for a source file the *shebang* notation is used::
|
||||||
|
|
@ -52,7 +52,7 @@ Parameters and their defaults:
|
||||||
|
|
||||||
``sub: string = ""``
|
``sub: string = ""``
|
||||||
the substring that is searched for
|
the substring that is searched for
|
||||||
|
|
||||||
``by: string = ""``
|
``by: string = ""``
|
||||||
the string the substring is replaced with
|
the string the substring is replaced with
|
||||||
|
|
||||||
|
|
@ -71,7 +71,7 @@ Parameters and their defaults:
|
||||||
|
|
||||||
``leading: bool = true``
|
``leading: bool = true``
|
||||||
strip leading whitespace
|
strip leading whitespace
|
||||||
|
|
||||||
``trailing: bool = true``
|
``trailing: bool = true``
|
||||||
strip trailing whitespace
|
strip trailing whitespace
|
||||||
|
|
||||||
|
|
@ -89,16 +89,16 @@ Parameters and their defaults:
|
||||||
|
|
||||||
``metaChar: char = '#'``
|
``metaChar: char = '#'``
|
||||||
prefix for a line that contains Nim code
|
prefix for a line that contains Nim code
|
||||||
|
|
||||||
``subsChar: char = '$'``
|
``subsChar: char = '$'``
|
||||||
prefix for a Nim expression within a template line
|
prefix for a Nim expression within a template line
|
||||||
|
|
||||||
``conc: string = " & "``
|
``conc: string = " & "``
|
||||||
the operation for concatenation
|
the operation for concatenation
|
||||||
|
|
||||||
``emit: string = "result.add"``
|
``emit: string = "result.add"``
|
||||||
the operation to emit a string literal
|
the operation to emit a string literal
|
||||||
|
|
||||||
``toString: string = "$"``
|
``toString: string = "$"``
|
||||||
the operation that is applied to each expression
|
the operation that is applied to each expression
|
||||||
|
|
||||||
|
|
@ -106,7 +106,7 @@ Example::
|
||||||
|
|
||||||
#! stdtmpl | standard
|
#! stdtmpl | standard
|
||||||
#proc generateHTMLPage(title, currentTab, content: string,
|
#proc generateHTMLPage(title, currentTab, content: string,
|
||||||
# tabs: openArray[string]): string =
|
# tabs: openArray[string]): string =
|
||||||
# result = ""
|
# result = ""
|
||||||
<head><title>$title</title></head>
|
<head><title>$title</title></head>
|
||||||
<body>
|
<body>
|
||||||
|
|
@ -114,7 +114,7 @@ Example::
|
||||||
<ul>
|
<ul>
|
||||||
#for tab in items(tabs):
|
#for tab in items(tabs):
|
||||||
#if currentTab == tab:
|
#if currentTab == tab:
|
||||||
<li><a id="selected"
|
<li><a id="selected"
|
||||||
#else:
|
#else:
|
||||||
<li><a
|
<li><a
|
||||||
#end if
|
#end if
|
||||||
|
|
@ -132,11 +132,11 @@ The filter transforms this into:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
proc generateHTMLPage(title, currentTab, content: string,
|
proc generateHTMLPage(title, currentTab, content: string,
|
||||||
tabs: openArray[string]): string =
|
tabs: openArray[string]): string =
|
||||||
result = ""
|
result = ""
|
||||||
result.add("<head><title>" & $(title) & "</title></head>\n" &
|
result.add("<head><title>" & $(title) & "</title></head>\n" &
|
||||||
"<body>\n" &
|
"<body>\n" &
|
||||||
" <div id=\"menu\">\n" &
|
" <div id=\"menu\">\n" &
|
||||||
" <ul>\n")
|
" <ul>\n")
|
||||||
for tab in items(tabs):
|
for tab in items(tabs):
|
||||||
if currentTab == tab:
|
if currentTab == tab:
|
||||||
|
|
@ -146,17 +146,17 @@ The filter transforms this into:
|
||||||
#end
|
#end
|
||||||
result.add(" href=\"" & $(tab) & ".html\">" & $(tab) & "</a></li>\n")
|
result.add(" href=\"" & $(tab) & ".html\">" & $(tab) & "</a></li>\n")
|
||||||
#end
|
#end
|
||||||
result.add(" </ul>\n" &
|
result.add(" </ul>\n" &
|
||||||
" </div>\n" &
|
" </div>\n" &
|
||||||
" <div id=\"content\">\n" &
|
" <div id=\"content\">\n" &
|
||||||
" " & $(content) & "\n" &
|
" " & $(content) & "\n" &
|
||||||
" A dollar: $.\n" &
|
" A dollar: $.\n" &
|
||||||
" </div>\n" &
|
" </div>\n" &
|
||||||
"</body>\n")
|
"</body>\n")
|
||||||
|
|
||||||
|
|
||||||
Each line that does not start with the meta character (ignoring leading
|
Each line that does not start with the meta character (ignoring leading
|
||||||
whitespace) is converted to a string literal that is added to ``result``.
|
whitespace) is converted to a string literal that is added to ``result``.
|
||||||
|
|
||||||
The substitution character introduces a Nim expression *e* within the
|
The substitution character introduces a Nim expression *e* within the
|
||||||
string literal. *e* is converted to a string with the *toString* operation
|
string literal. *e* is converted to a string with the *toString* operation
|
||||||
|
|
@ -174,14 +174,14 @@ writes the template code directly to a file::
|
||||||
|
|
||||||
#! stdtmpl(emit="f.write") | standard
|
#! stdtmpl(emit="f.write") | standard
|
||||||
#proc writeHTMLPage(f: File, title, currentTab, content: string,
|
#proc writeHTMLPage(f: File, title, currentTab, content: string,
|
||||||
# tabs: openArray[string]) =
|
# tabs: openArray[string]) =
|
||||||
<head><title>$title</title></head>
|
<head><title>$title</title></head>
|
||||||
<body>
|
<body>
|
||||||
<div id="menu">
|
<div id="menu">
|
||||||
<ul>
|
<ul>
|
||||||
#for tab in items(tabs):
|
#for tab in items(tabs):
|
||||||
#if currentTab == tab:
|
#if currentTab == tab:
|
||||||
<li><a id="selected"
|
<li><a id="selected"
|
||||||
#else:
|
#else:
|
||||||
<li><a
|
<li><a
|
||||||
#end if
|
#end if
|
||||||
|
|
|
||||||
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
|
the GC with ``GC_enableMarkAndSweep`` and ``GC_disableMarkAndSweep``. The
|
||||||
compiler analyses the types for their possibility to build cycles, but often
|
compiler analyses the types for their possibility to build cycles, but often
|
||||||
it is necessary to help this analysis with the ``acyclic`` pragma (see
|
it is necessary to help this analysis with the ``acyclic`` pragma (see
|
||||||
`acyclic <manual.html#acyclic-pragma>`_ for further information).
|
`acyclic <manual.html#acyclic-pragma>`_ for further information).
|
||||||
|
|
||||||
You can also use the ``acyclic`` pragma for data that is cyclic in reality and
|
You can also use the ``acyclic`` pragma for data that is cyclic in reality and
|
||||||
then break up the cycles explicitly with ``GC_addCycleRoot``. This can be a
|
then break up the cycles explicitly with ``GC_addCycleRoot``. This can be a
|
||||||
very valuable optimization; the Nim compiler itself relies on this
|
very valuable optimization; the Nim compiler itself relies on this
|
||||||
optimization trick to improve performance. Note that ``GC_addCycleRoot`` is
|
optimization trick to improve performance. Note that ``GC_addCycleRoot`` is
|
||||||
a quick operation; the root is only registered for the next run of the
|
a quick operation; the root is only registered for the next run of the
|
||||||
cycle collector.
|
cycle collector.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -51,7 +51,7 @@ Realtime support
|
||||||
================
|
================
|
||||||
|
|
||||||
To enable realtime support, the symbol `useRealtimeGC`:idx: needs to be
|
To enable realtime support, the symbol `useRealtimeGC`:idx: needs to be
|
||||||
defined via ``--define:useRealtimeGC`` (you can put this into your config
|
defined via ``--define:useRealtimeGC`` (you can put this into your config
|
||||||
file as well). With this switch the GC supports the following operations:
|
file as well). With this switch the GC supports the following operations:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
@ -60,21 +60,21 @@ file as well). With this switch the GC supports the following operations:
|
||||||
|
|
||||||
The unit of the parameters ``MaxPauseInUs`` and ``us`` is microseconds.
|
The unit of the parameters ``MaxPauseInUs`` and ``us`` is microseconds.
|
||||||
|
|
||||||
These two procs are the two modus operandi of the realtime GC:
|
These two procs are the two modus operandi of the realtime GC:
|
||||||
|
|
||||||
(1) GC_SetMaxPause Mode
|
(1) GC_SetMaxPause Mode
|
||||||
|
|
||||||
You can call ``GC_SetMaxPause`` at program startup and then each triggered
|
You can call ``GC_SetMaxPause`` at program startup and then each triggered
|
||||||
GC run tries to not take longer than ``MaxPause`` time. However, it is
|
GC run tries to not take longer than ``MaxPause`` time. However, it is
|
||||||
possible (and common) that the work is nevertheless not evenly distributed
|
possible (and common) that the work is nevertheless not evenly distributed
|
||||||
as each call to ``new`` can trigger the GC and thus take ``MaxPause``
|
as each call to ``new`` can trigger the GC and thus take ``MaxPause``
|
||||||
time.
|
time.
|
||||||
|
|
||||||
(2) GC_step Mode
|
(2) GC_step Mode
|
||||||
|
|
||||||
This allows the GC to perform some work for up to ``us`` time. This is
|
This allows the GC to perform some work for up to ``us`` time. This is
|
||||||
useful to call in a main loop to ensure the GC can do its work. To
|
useful to call in a main loop to ensure the GC can do its work. To
|
||||||
bind all GC activity to a ``GC_step`` call, deactivate the GC with
|
bind all GC activity to a ``GC_step`` call, deactivate the GC with
|
||||||
``GC_disable`` at program startup.
|
``GC_disable`` at program startup.
|
||||||
|
|
||||||
These procs provide a "best effort" realtime guarantee; in particular the
|
These procs provide a "best effort" realtime guarantee; in particular the
|
||||||
|
|
@ -87,7 +87,7 @@ is triggered.
|
||||||
Time measurement
|
Time measurement
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
The GC's way of measuring time uses (see ``lib/system/timers.nim`` for the
|
The GC's way of measuring time uses (see ``lib/system/timers.nim`` for the
|
||||||
implementation):
|
implementation):
|
||||||
|
|
||||||
1) ``QueryPerformanceCounter`` and ``QueryPerformanceFrequency`` on Windows.
|
1) ``QueryPerformanceCounter`` and ``QueryPerformanceFrequency`` on Windows.
|
||||||
|
|
@ -95,7 +95,7 @@ implementation):
|
||||||
3) ``gettimeofday`` on Posix systems.
|
3) ``gettimeofday`` on Posix systems.
|
||||||
|
|
||||||
As such it supports a resolution of nanoseconds internally; however the API
|
As such it supports a resolution of nanoseconds internally; however the API
|
||||||
uses microseconds for convenience.
|
uses microseconds for convenience.
|
||||||
|
|
||||||
|
|
||||||
Define the symbol ``reportMissedDeadlines`` to make the GC output whenever it
|
Define the symbol ``reportMissedDeadlines`` to make the GC output whenever it
|
||||||
|
|
@ -106,7 +106,7 @@ later versions of the collector.
|
||||||
Tweaking the GC
|
Tweaking the GC
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
The collector checks whether there is still time left for its work after
|
The collector checks whether there is still time left for its work after
|
||||||
every ``workPackage``'th iteration. This is currently set to 100 which means
|
every ``workPackage``'th iteration. This is currently set to 100 which means
|
||||||
that up to 100 objects are traversed and freed before it checks again. Thus
|
that up to 100 objects are traversed and freed before it checks again. Thus
|
||||||
``workPackage`` affects the timing granularity and may need to be tweaked in
|
``workPackage`` affects the timing granularity and may need to be tweaked in
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@ Path Purpose
|
||||||
``bin`` generated binary files
|
``bin`` generated binary files
|
||||||
``build`` generated C code for the installation
|
``build`` generated C code for the installation
|
||||||
``compiler`` the Nim compiler itself; note that this
|
``compiler`` the Nim compiler itself; note that this
|
||||||
code has been translated from a bootstrapping
|
code has been translated from a bootstrapping
|
||||||
version written in Pascal, so the code is **not**
|
version written in Pascal, so the code is **not**
|
||||||
a poster child of good Nim code
|
a poster child of good Nim code
|
||||||
``config`` configuration files for Nim
|
``config`` configuration files for Nim
|
||||||
``dist`` additional packages for the distribution
|
``dist`` additional packages for the distribution
|
||||||
|
|
@ -38,8 +38,8 @@ Path Purpose
|
||||||
Bootstrapping the compiler
|
Bootstrapping the compiler
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
As of version 0.8.5 the compiler is maintained in Nim. (The first versions
|
As of version 0.8.5 the compiler is maintained in Nim. (The first versions
|
||||||
have been implemented in Object Pascal.) The Python-based build system has
|
have been implemented in Object Pascal.) The Python-based build system has
|
||||||
been rewritten in Nim too.
|
been rewritten in Nim too.
|
||||||
|
|
||||||
Compiling the compiler is a simple matter of running::
|
Compiling the compiler is a simple matter of running::
|
||||||
|
|
@ -121,7 +121,7 @@ Look at the file ``lib/system/hti.nim`` for more information.
|
||||||
Debugging the compiler
|
Debugging the compiler
|
||||||
======================
|
======================
|
||||||
|
|
||||||
You can of course use GDB or Visual Studio to debug the
|
You can of course use GDB or Visual Studio to debug the
|
||||||
compiler (via ``--debuginfo --lineDir:on``). However, there
|
compiler (via ``--debuginfo --lineDir:on``). However, there
|
||||||
are also lots of procs that aid in debugging:
|
are also lots of procs that aid in debugging:
|
||||||
|
|
||||||
|
|
@ -180,7 +180,7 @@ children. Types and symbols are represented by other nodes, because they
|
||||||
may contain cycles. The AST changes its shape after semantic checking. This
|
may contain cycles. The AST changes its shape after semantic checking. This
|
||||||
is needed to make life easier for the code generators. See the "ast" module
|
is needed to make life easier for the code generators. See the "ast" module
|
||||||
for the type definitions. The `macros <macros.html>`_ module contains many
|
for the type definitions. The `macros <macros.html>`_ module contains many
|
||||||
examples how the AST represents each syntactic structure.
|
examples how the AST represents each syntactic structure.
|
||||||
|
|
||||||
|
|
||||||
How the RTL is compiled
|
How the RTL is compiled
|
||||||
|
|
@ -202,7 +202,7 @@ Compilation cache
|
||||||
=================
|
=================
|
||||||
|
|
||||||
The implementation of the compilation cache is tricky: There are lots
|
The implementation of the compilation cache is tricky: There are lots
|
||||||
of issues to be solved for the front- and backend. In the following
|
of issues to be solved for the front- and backend. In the following
|
||||||
sections *global* means *shared between modules* or *property of the whole
|
sections *global* means *shared between modules* or *property of the whole
|
||||||
program*.
|
program*.
|
||||||
|
|
||||||
|
|
@ -214,31 +214,31 @@ Methods and type converters
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Nim contains language features that are *global*. The best example for that
|
Nim contains language features that are *global*. The best example for that
|
||||||
are multi methods: Introducing a new method with the same name and some
|
are multi methods: Introducing a new method with the same name and some
|
||||||
compatible object parameter means that the method's dispatcher needs to take
|
compatible object parameter means that the method's dispatcher needs to take
|
||||||
the new method into account. So the dispatching logic is only completely known
|
the new method into account. So the dispatching logic is only completely known
|
||||||
after the whole program has been translated!
|
after the whole program has been translated!
|
||||||
|
|
||||||
Other features that are *implicitly* triggered cause problems for modularity
|
Other features that are *implicitly* triggered cause problems for modularity
|
||||||
too. Type converters fall into this category:
|
too. Type converters fall into this category:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
# module A
|
# module A
|
||||||
converter toBool(x: int): bool =
|
converter toBool(x: int): bool =
|
||||||
result = x != 0
|
result = x != 0
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
# module B
|
# module B
|
||||||
import A
|
import A
|
||||||
|
|
||||||
if 1:
|
if 1:
|
||||||
echo "ugly, but should work"
|
echo "ugly, but should work"
|
||||||
|
|
||||||
If in the above example module ``B`` is re-compiled, but ``A`` is not then
|
If in the above example module ``B`` is re-compiled, but ``A`` is not then
|
||||||
``B`` needs to be aware of ``toBool`` even though ``toBool`` is not referenced
|
``B`` needs to be aware of ``toBool`` even though ``toBool`` is not referenced
|
||||||
in ``B`` *explicitly*.
|
in ``B`` *explicitly*.
|
||||||
|
|
||||||
Both the multi method and the type converter problems are solved by storing
|
Both the multi method and the type converter problems are solved by storing
|
||||||
them in special sections in the ROD file that are loaded *unconditionally*
|
them in special sections in the ROD file that are loaded *unconditionally*
|
||||||
when the ROD file is read.
|
when the ROD file is read.
|
||||||
|
|
||||||
|
|
@ -266,7 +266,7 @@ Backend issues
|
||||||
- Emulated thread vars are global.
|
- Emulated thread vars are global.
|
||||||
|
|
||||||
|
|
||||||
However the biggest problem is that dead code elimination breaks modularity!
|
However the biggest problem is that dead code elimination breaks modularity!
|
||||||
To see why, consider this scenario: The module ``G`` (for example the huge
|
To see why, consider this scenario: The module ``G`` (for example the huge
|
||||||
Gtk2 module...) is compiled with dead code elimination turned on. So none
|
Gtk2 module...) is compiled with dead code elimination turned on. So none
|
||||||
of ``G``'s procs is generated at all.
|
of ``G``'s procs is generated at all.
|
||||||
|
|
@ -274,13 +274,13 @@ of ``G``'s procs is generated at all.
|
||||||
Then module ``B`` is compiled that requires ``G.P1``. Ok, no problem,
|
Then module ``B`` is compiled that requires ``G.P1``. Ok, no problem,
|
||||||
``G.P1`` is loaded from the symbol file and ``G.c`` now contains ``G.P1``.
|
``G.P1`` is loaded from the symbol file and ``G.c`` now contains ``G.P1``.
|
||||||
|
|
||||||
Then module ``A`` (that depends onto ``B`` and ``G``) is compiled and ``B``
|
Then module ``A`` (that depends onto ``B`` and ``G``) is compiled and ``B``
|
||||||
and ``G`` are left unchanged. ``A`` requires ``G.P2``.
|
and ``G`` are left unchanged. ``A`` requires ``G.P2``.
|
||||||
|
|
||||||
So now ``G.c`` MUST contain both ``P1`` and ``P2``, but we haven't even
|
So now ``G.c`` MUST contain both ``P1`` and ``P2``, but we haven't even
|
||||||
loaded ``P1`` from the symbol file, nor do we want to because we then quickly
|
loaded ``P1`` from the symbol file, nor do we want to because we then quickly
|
||||||
would restore large parts of the whole program. But we also don't want to
|
would restore large parts of the whole program. But we also don't want to
|
||||||
store ``P1`` in ``B.c`` because that would mean to store every symbol where
|
store ``P1`` in ``B.c`` because that would mean to store every symbol where
|
||||||
it is referred from which ultimately means the main module and putting
|
it is referred from which ultimately means the main module and putting
|
||||||
everything in a single C file.
|
everything in a single C file.
|
||||||
|
|
||||||
|
|
@ -290,7 +290,7 @@ that is implemented in the C code generator (have a look at the ``ccgmerge``
|
||||||
module). The merging may lead to *cruft* (aka dead code) in generated C code
|
module). The merging may lead to *cruft* (aka dead code) in generated C code
|
||||||
which can only be removed by recompiling a project with the compilation cache
|
which can only be removed by recompiling a project with the compilation cache
|
||||||
turned off. Nevertheless the merge solution is way superior to the
|
turned off. Nevertheless the merge solution is way superior to the
|
||||||
cheap solution "turn off dead code elimination if the compilation cache is
|
cheap solution "turn off dead code elimination if the compilation cache is
|
||||||
turned on".
|
turned on".
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -394,7 +394,7 @@ Consider this example:
|
||||||
# r is on the stack
|
# r is on the stack
|
||||||
setRef(r.left) # here we should update the refcounts!
|
setRef(r.left) # here we should update the refcounts!
|
||||||
|
|
||||||
We have to decide at runtime whether the reference is on the stack or not.
|
We have to decide at runtime whether the reference is on the stack or not.
|
||||||
The generated code looks roughly like this:
|
The generated code looks roughly like this:
|
||||||
|
|
||||||
.. code-block:: C
|
.. code-block:: C
|
||||||
|
|
@ -422,7 +422,7 @@ Design
|
||||||
A ``closure`` proc var can call ordinary procs of the default Nim calling
|
A ``closure`` proc var can call ordinary procs of the default Nim calling
|
||||||
convention. But not the other way round! A closure is implemented as a
|
convention. But not the other way round! A closure is implemented as a
|
||||||
``tuple[prc, env]``. ``env`` can be nil implying a call without a closure.
|
``tuple[prc, env]``. ``env`` can be nil implying a call without a closure.
|
||||||
This means that a call through a closure generates an ``if`` but the
|
This means that a call through a closure generates an ``if`` but the
|
||||||
interoperability is worth the cost of the ``if``. Thunk generation would be
|
interoperability is worth the cost of the ``if``. Thunk generation would be
|
||||||
possible too, but it's slightly more effort to implement.
|
possible too, but it's slightly more effort to implement.
|
||||||
|
|
||||||
|
|
@ -430,7 +430,7 @@ Tests with GCC on Amd64 showed that it's really beneficical if the
|
||||||
'environment' pointer is passed as the last argument, not as the first argument.
|
'environment' pointer is passed as the last argument, not as the first argument.
|
||||||
|
|
||||||
Proper thunk generation is harder because the proc that is to wrap
|
Proper thunk generation is harder because the proc that is to wrap
|
||||||
could stem from a complex expression:
|
could stem from a complex expression:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
receivesClosure(returnsDefaultCC[i])
|
receivesClosure(returnsDefaultCC[i])
|
||||||
|
|
@ -438,15 +438,15 @@ could stem from a complex expression:
|
||||||
A thunk would need to call 'returnsDefaultCC[i]' somehow and that would require
|
A thunk would need to call 'returnsDefaultCC[i]' somehow and that would require
|
||||||
an *additional* closure generation... Ok, not really, but it requires to pass
|
an *additional* closure generation... Ok, not really, but it requires to pass
|
||||||
the function to call. So we'd end up with 2 indirect calls instead of one.
|
the function to call. So we'd end up with 2 indirect calls instead of one.
|
||||||
Another much more severe problem which this solution is that it's not GC-safe
|
Another much more severe problem which this solution is that it's not GC-safe
|
||||||
to pass a proc pointer around via a generic ``ref`` type.
|
to pass a proc pointer around via a generic ``ref`` type.
|
||||||
|
|
||||||
|
|
||||||
Example code:
|
Example code:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
proc add(x: int): proc (y: int): int {.closure.} =
|
proc add(x: int): proc (y: int): int {.closure.} =
|
||||||
return proc (y: int): int =
|
return proc (y: int): int =
|
||||||
return x + y
|
return x + y
|
||||||
|
|
||||||
var add2 = add(2)
|
var add2 = add(2)
|
||||||
|
|
@ -458,16 +458,16 @@ This should produce roughly this code:
|
||||||
type
|
type
|
||||||
PEnv = ref object
|
PEnv = ref object
|
||||||
x: int # data
|
x: int # data
|
||||||
|
|
||||||
proc anon(y: int, c: PClosure): int =
|
proc anon(y: int, c: PClosure): int =
|
||||||
return y + c.x
|
return y + c.x
|
||||||
|
|
||||||
proc add(x: int): tuple[prc, data] =
|
proc add(x: int): tuple[prc, data] =
|
||||||
var env: PEnv
|
var env: PEnv
|
||||||
new env
|
new env
|
||||||
env.x = x
|
env.x = x
|
||||||
result = (anon, env)
|
result = (anon, env)
|
||||||
|
|
||||||
var add2 = add(2)
|
var add2 = add(2)
|
||||||
let tmp = if add2.data == nil: add2.prc(5) else: add2.prc(5, add2.data)
|
let tmp = if add2.data == nil: add2.prc(5) else: add2.prc(5, add2.data)
|
||||||
echo tmp
|
echo tmp
|
||||||
|
|
@ -476,9 +476,9 @@ This should produce roughly this code:
|
||||||
Beware of nesting:
|
Beware of nesting:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
proc add(x: int): proc (y: int): proc (z: int): int {.closure.} {.closure.} =
|
proc add(x: int): proc (y: int): proc (z: int): int {.closure.} {.closure.} =
|
||||||
return lamba (y: int): proc (z: int): int {.closure.} =
|
return lamba (y: int): proc (z: int): int {.closure.} =
|
||||||
return lambda (z: int): int =
|
return lambda (z: int): int =
|
||||||
return x + y + z
|
return x + y + z
|
||||||
|
|
||||||
var add24 = add(2)(4)
|
var add24 = add(2)(4)
|
||||||
|
|
@ -490,26 +490,26 @@ This should produce roughly this code:
|
||||||
type
|
type
|
||||||
PEnvX = ref object
|
PEnvX = ref object
|
||||||
x: int # data
|
x: int # data
|
||||||
|
|
||||||
PEnvY = ref object
|
PEnvY = ref object
|
||||||
y: int
|
y: int
|
||||||
ex: PEnvX
|
ex: PEnvX
|
||||||
|
|
||||||
proc lambdaZ(z: int, ey: PEnvY): int =
|
proc lambdaZ(z: int, ey: PEnvY): int =
|
||||||
return ey.ex.x + ey.y + z
|
return ey.ex.x + ey.y + z
|
||||||
|
|
||||||
proc lambdaY(y: int, ex: PEnvX): tuple[prc, data: PEnvY] =
|
proc lambdaY(y: int, ex: PEnvX): tuple[prc, data: PEnvY] =
|
||||||
var ey: PEnvY
|
var ey: PEnvY
|
||||||
new ey
|
new ey
|
||||||
ey.y = y
|
ey.y = y
|
||||||
ey.ex = ex
|
ey.ex = ex
|
||||||
result = (lambdaZ, ey)
|
result = (lambdaZ, ey)
|
||||||
|
|
||||||
proc add(x: int): tuple[prc, data: PEnvX] =
|
proc add(x: int): tuple[prc, data: PEnvX] =
|
||||||
var ex: PEnvX
|
var ex: PEnvX
|
||||||
ex.x = x
|
ex.x = x
|
||||||
result = (labmdaY, ex)
|
result = (labmdaY, ex)
|
||||||
|
|
||||||
var tmp = add(2)
|
var tmp = add(2)
|
||||||
var tmp2 = tmp.fn(4, tmp.data)
|
var tmp2 = tmp.fn(4, tmp.data)
|
||||||
var add24 = tmp2.fn(4, tmp2.data)
|
var add24 = tmp2.fn(4, tmp2.data)
|
||||||
|
|
@ -517,14 +517,14 @@ This should produce roughly this code:
|
||||||
|
|
||||||
|
|
||||||
We could get rid of nesting environments by always inlining inner anon procs.
|
We could get rid of nesting environments by always inlining inner anon procs.
|
||||||
More useful is escape analysis and stack allocation of the environment,
|
More useful is escape analysis and stack allocation of the environment,
|
||||||
however.
|
however.
|
||||||
|
|
||||||
|
|
||||||
Alternative
|
Alternative
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
Process the closure of all inner procs in one pass and accumulate the
|
Process the closure of all inner procs in one pass and accumulate the
|
||||||
environments. This is however not always possible.
|
environments. This is however not always possible.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -532,21 +532,21 @@ Accumulator
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
proc getAccumulator(start: int): proc (): int {.closure} =
|
proc getAccumulator(start: int): proc (): int {.closure} =
|
||||||
var i = start
|
var i = start
|
||||||
return lambda: int =
|
return lambda: int =
|
||||||
inc i
|
inc i
|
||||||
return i
|
return i
|
||||||
|
|
||||||
proc p =
|
proc p =
|
||||||
var delta = 7
|
var delta = 7
|
||||||
proc accumulator(start: int): proc(): int =
|
proc accumulator(start: int): proc(): int =
|
||||||
var x = start-1
|
var x = start-1
|
||||||
result = proc (): int =
|
result = proc (): int =
|
||||||
x = x + delta
|
x = x + delta
|
||||||
inc delta
|
inc delta
|
||||||
return x
|
return x
|
||||||
|
|
||||||
var a = accumulator(3)
|
var a = accumulator(3)
|
||||||
var b = accumulator(4)
|
var b = accumulator(4)
|
||||||
echo a() + b()
|
echo a() + b()
|
||||||
|
|
@ -560,7 +560,7 @@ pass generates code to setup the environment and to pass it around. However,
|
||||||
this pass does not change the types! So we have some kind of mismatch here; on
|
this pass does not change the types! So we have some kind of mismatch here; on
|
||||||
the one hand the proc expression becomes an explicit tuple, on the other hand
|
the one hand the proc expression becomes an explicit tuple, on the other hand
|
||||||
the tyProc(ccClosure) type is not changed. For C code generation it's also
|
the tyProc(ccClosure) type is not changed. For C code generation it's also
|
||||||
important the hidden formal param is ``void*`` and not something more
|
important the hidden formal param is ``void*`` and not something more
|
||||||
specialized. However the more specialized env type needs to passed to the
|
specialized. However the more specialized env type needs to passed to the
|
||||||
backend somehow. We deal with this by modifying ``s.ast[paramPos]`` to contain
|
backend somehow. We deal with this by modifying ``s.ast[paramPos]`` to contain
|
||||||
the formal hidden parameter, but not ``s.typ``!
|
the formal hidden parameter, but not ``s.typ``!
|
||||||
|
|
|
||||||
|
|
@ -14,20 +14,20 @@ optional *a*. Parentheses may be used to group elements.
|
||||||
``&`` is the lookahead operator; ``&a`` means that an ``a`` is expected but
|
``&`` is the lookahead operator; ``&a`` means that an ``a`` is expected but
|
||||||
not consumed. It will be consumed in the following rule.
|
not consumed. It will be consumed in the following rule.
|
||||||
|
|
||||||
The ``|``, ``/`` symbols are used to mark alternatives and have the lowest
|
The ``|``, ``/`` symbols are used to mark alternatives and have the lowest
|
||||||
precedence. ``/`` is the ordered choice that requires the parser to try the
|
precedence. ``/`` is the ordered choice that requires the parser to try the
|
||||||
alternatives in the given order. ``/`` is often used to ensure the grammar
|
alternatives in the given order. ``/`` is often used to ensure the grammar
|
||||||
is not ambiguous.
|
is not ambiguous.
|
||||||
|
|
||||||
Non-terminals start with a lowercase letter, abstract terminal symbols are in
|
Non-terminals start with a lowercase letter, abstract terminal symbols are in
|
||||||
UPPERCASE. Verbatim terminal symbols (including keywords) are quoted
|
UPPERCASE. Verbatim terminal symbols (including keywords) are quoted
|
||||||
with ``'``. An example::
|
with ``'``. An example::
|
||||||
|
|
||||||
ifStmt = 'if' expr ':' stmts ('elif' expr ':' stmts)* ('else' stmts)?
|
ifStmt = 'if' expr ':' stmts ('elif' expr ':' stmts)* ('else' stmts)?
|
||||||
|
|
||||||
The binary ``^*`` operator is used as a shorthand for 0 or more occurrences
|
The binary ``^*`` operator is used as a shorthand for 0 or more occurrences
|
||||||
separated by its second argument; likewise ``^+`` means 1 or more
|
separated by its second argument; likewise ``^+`` means 1 or more
|
||||||
occurrences: ``a ^+ b`` is short for ``a (b a)*``
|
occurrences: ``a ^+ b`` is short for ``a (b a)*``
|
||||||
and ``a ^* b`` is short for ``(a (b a)*)?``. Example::
|
and ``a ^* b`` is short for ``(a (b a)*)?``. Example::
|
||||||
|
|
||||||
arrayConstructor = '[' expr ^* ',' ']'
|
arrayConstructor = '[' expr ^* ',' ']'
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,9 @@ program execution. Unless explicitly classified, an error is a static error.
|
||||||
|
|
||||||
A `checked runtime error`:idx: is an error that the implementation detects
|
A `checked runtime error`:idx: is an error that the implementation detects
|
||||||
and reports at runtime. The method for reporting such errors is via *raising
|
and reports at runtime. The method for reporting such errors is via *raising
|
||||||
exceptions* or *dying with a fatal error*. However, the implementation
|
exceptions* or *dying with a fatal error*. However, the implementation
|
||||||
provides a means to disable these runtime checks. See the section pragmas_
|
provides a means to disable these runtime checks. See the section pragmas_
|
||||||
for details.
|
for details.
|
||||||
|
|
||||||
Whether a checked runtime error results in an exception or in a fatal error at
|
Whether a checked runtime error results in an exception or in a fatal error at
|
||||||
runtime is implementation specific. Thus the following program is always
|
runtime is implementation specific. Thus the following program is always
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ Exception tracking
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
Nim supports exception tracking. The `raises`:idx: pragma can be used
|
Nim supports exception tracking. The `raises`:idx: pragma can be used
|
||||||
to explicitly define which exceptions a proc/iterator/method/converter is
|
to explicitly define which exceptions a proc/iterator/method/converter is
|
||||||
allowed to raise. The compiler verifies this:
|
allowed to raise. The compiler verifies this:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
@ -24,7 +24,7 @@ An empty ``raises`` list (``raises: []``) means that no exception may be raised:
|
||||||
result = false
|
result = false
|
||||||
|
|
||||||
|
|
||||||
A ``raises`` list can also be attached to a proc type. This affects type
|
A ``raises`` list can also be attached to a proc type. This affects type
|
||||||
compatibility:
|
compatibility:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
@ -35,7 +35,7 @@ compatibility:
|
||||||
|
|
||||||
proc p(x: string) =
|
proc p(x: string) =
|
||||||
raise newException(OSError, "OS")
|
raise newException(OSError, "OS")
|
||||||
|
|
||||||
c = p # type error
|
c = p # type error
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -46,30 +46,30 @@ possibly raised exceptions; the algorithm operates on ``p``'s call graph:
|
||||||
raise ``system.Exception`` (the base type of the exception hierarchy) and
|
raise ``system.Exception`` (the base type of the exception hierarchy) and
|
||||||
thus any exception unless ``T`` has an explicit ``raises`` list.
|
thus any exception unless ``T`` has an explicit ``raises`` list.
|
||||||
However if the call is of the form ``f(...)`` where ``f`` is a parameter
|
However if the call is of the form ``f(...)`` where ``f`` is a parameter
|
||||||
of the currently analysed routine it is ignored. The call is optimistically
|
of the currently analysed routine it is ignored. The call is optimistically
|
||||||
assumed to have no effect. Rule 2 compensates for this case.
|
assumed to have no effect. Rule 2 compensates for this case.
|
||||||
2. Every expression of some proc type within a call that is not a call
|
2. Every expression of some proc type within a call that is not a call
|
||||||
itself (and not nil) is assumed to be called indirectly somehow and thus
|
itself (and not nil) is assumed to be called indirectly somehow and thus
|
||||||
its raises list is added to ``p``'s raises list.
|
its raises list is added to ``p``'s raises list.
|
||||||
3. Every call to a proc ``q`` which has an unknown body (due to a forward
|
3. Every call to a proc ``q`` which has an unknown body (due to a forward
|
||||||
declaration or an ``importc`` pragma) is assumed to
|
declaration or an ``importc`` pragma) is assumed to
|
||||||
raise ``system.Exception`` unless ``q`` has an explicit ``raises`` list.
|
raise ``system.Exception`` unless ``q`` has an explicit ``raises`` list.
|
||||||
4. Every call to a method ``m`` is assumed to
|
4. Every call to a method ``m`` is assumed to
|
||||||
raise ``system.Exception`` unless ``m`` has an explicit ``raises`` list.
|
raise ``system.Exception`` unless ``m`` has an explicit ``raises`` list.
|
||||||
5. For every other call the analysis can determine an exact ``raises`` list.
|
5. For every other call the analysis can determine an exact ``raises`` list.
|
||||||
6. For determining a ``raises`` list, the ``raise`` and ``try`` statements
|
6. For determining a ``raises`` list, the ``raise`` and ``try`` statements
|
||||||
of ``p`` are taken into consideration.
|
of ``p`` are taken into consideration.
|
||||||
|
|
||||||
Rules 1-2 ensure the following works:
|
Rules 1-2 ensure the following works:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
proc noRaise(x: proc()) {.raises: [].} =
|
proc noRaise(x: proc()) {.raises: [].} =
|
||||||
# unknown call that might raise anything, but valid:
|
# unknown call that might raise anything, but valid:
|
||||||
x()
|
x()
|
||||||
|
|
||||||
proc doRaise() {.raises: [IOError].} =
|
proc doRaise() {.raises: [IOError].} =
|
||||||
raise newException(IOError, "IO")
|
raise newException(IOError, "IO")
|
||||||
|
|
||||||
proc use() {.raises: [].} =
|
proc use() {.raises: [].} =
|
||||||
# doesn't compile! Can raise IOError!
|
# doesn't compile! Can raise IOError!
|
||||||
noRaise(doRaise)
|
noRaise(doRaise)
|
||||||
|
|
@ -82,21 +82,21 @@ Tag tracking
|
||||||
------------
|
------------
|
||||||
|
|
||||||
The exception tracking is part of Nim's `effect system`:idx:. Raising an
|
The exception tracking is part of Nim's `effect system`:idx:. Raising an
|
||||||
exception is an *effect*. Other effects can also be defined. A user defined
|
exception is an *effect*. Other effects can also be defined. A user defined
|
||||||
effect is a means to *tag* a routine and to perform checks against this tag:
|
effect is a means to *tag* a routine and to perform checks against this tag:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
type IO = object ## input/output effect
|
type IO = object ## input/output effect
|
||||||
proc readLine(): string {.tags: [IO].}
|
proc readLine(): string {.tags: [IO].}
|
||||||
|
|
||||||
proc no_IO_please() {.tags: [].} =
|
proc no_IO_please() {.tags: [].} =
|
||||||
# the compiler prevents this:
|
# the compiler prevents this:
|
||||||
let x = readLine()
|
let x = readLine()
|
||||||
|
|
||||||
A tag has to be a type name. A ``tags`` list - like a ``raises`` list - can
|
A tag has to be a type name. A ``tags`` list - like a ``raises`` list - can
|
||||||
also be attached to a proc type. This affects type compatibility.
|
also be attached to a proc type. This affects type compatibility.
|
||||||
|
|
||||||
The inference for tag tracking is analogous to the inference for
|
The inference for tag tracking is analogous to the inference for
|
||||||
exception tracking.
|
exception tracking.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -105,7 +105,7 @@ Read/Write tracking
|
||||||
|
|
||||||
**Note**: Read/write tracking is not yet implemented!
|
**Note**: Read/write tracking is not yet implemented!
|
||||||
|
|
||||||
The inference for read/write tracking is analogous to the inference for
|
The inference for read/write tracking is analogous to the inference for
|
||||||
exception tracking.
|
exception tracking.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -164,7 +164,7 @@ Alternatively, the ``distinct`` type modifier can be applied to the type class
|
||||||
to allow each param matching the type class to bind to a different type.
|
to allow each param matching the type class to bind to a different type.
|
||||||
|
|
||||||
If a proc param doesn't have a type specified, Nim will use the
|
If a proc param doesn't have a type specified, Nim will use the
|
||||||
``distinct auto`` type class (also known as ``any``). Note this behavior is
|
``distinct auto`` type class (also known as ``any``). Note this behavior is
|
||||||
deprecated for procs; templates, however, support them:
|
deprecated for procs; templates, however, support them:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ With this notation we can now easily define the core of the grammar: A block of
|
||||||
statements (simplified example)::
|
statements (simplified example)::
|
||||||
|
|
||||||
ifStmt = 'if' expr ':' stmt
|
ifStmt = 'if' expr ':' stmt
|
||||||
(IND{=} 'elif' expr ':' stmt)*
|
(IND{=} 'elif' expr ':' stmt)*
|
||||||
(IND{=} 'else' ':' stmt)?
|
(IND{=} 'else' ':' stmt)?
|
||||||
|
|
||||||
simpleStmt = ifStmt / ...
|
simpleStmt = ifStmt / ...
|
||||||
|
|
@ -156,7 +156,7 @@ contain the following `escape sequences`:idx:\ :
|
||||||
================== ===================================================
|
================== ===================================================
|
||||||
|
|
||||||
|
|
||||||
Strings in Nim may contain any 8-bit value, even embedded zeros. However
|
Strings in Nim may contain any 8-bit value, even embedded zeros. However
|
||||||
some operations may interpret the first binary zero as a terminator.
|
some operations may interpret the first binary zero as a terminator.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -174,7 +174,7 @@ be whitespace between the opening ``"""`` and the newline),
|
||||||
the newline (and the preceding whitespace) is not included in the string. The
|
the newline (and the preceding whitespace) is not included in the string. The
|
||||||
ending of the string literal is defined by the pattern ``"""[^"]``, so this:
|
ending of the string literal is defined by the pattern ``"""[^"]``, so this:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
""""long string within quotes""""
|
""""long string within quotes""""
|
||||||
|
|
||||||
Produces::
|
Produces::
|
||||||
|
|
@ -187,9 +187,9 @@ Raw string literals
|
||||||
|
|
||||||
Terminal symbol in the grammar: ``RSTR_LIT``.
|
Terminal symbol in the grammar: ``RSTR_LIT``.
|
||||||
|
|
||||||
There are also raw string literals that are preceded with the
|
There are also raw string literals that are preceded with the
|
||||||
letter ``r`` (or ``R``) and are delimited by matching double quotes (just
|
letter ``r`` (or ``R``) and are delimited by matching double quotes (just
|
||||||
like ordinary string literals) and do not interpret the escape sequences.
|
like ordinary string literals) and do not interpret the escape sequences.
|
||||||
This is especially convenient for regular expressions or Windows paths:
|
This is especially convenient for regular expressions or Windows paths:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
@ -201,21 +201,21 @@ To produce a single ``"`` within a raw string literal, it has to be doubled:
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
||||||
r"a""b"
|
r"a""b"
|
||||||
|
|
||||||
Produces::
|
Produces::
|
||||||
|
|
||||||
a"b
|
a"b
|
||||||
|
|
||||||
``r""""`` is not possible with this notation, because the three leading
|
``r""""`` is not possible with this notation, because the three leading
|
||||||
quotes introduce a triple quoted string literal. ``r"""`` is the same
|
quotes introduce a triple quoted string literal. ``r"""`` is the same
|
||||||
as ``"""`` since triple quoted string literals do not interpret escape
|
as ``"""`` since triple quoted string literals do not interpret escape
|
||||||
sequences either.
|
sequences either.
|
||||||
|
|
||||||
|
|
||||||
Generalized raw string literals
|
Generalized raw string literals
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
Terminal symbols in the grammar: ``GENERALIZED_STR_LIT``,
|
Terminal symbols in the grammar: ``GENERALIZED_STR_LIT``,
|
||||||
``GENERALIZED_TRIPLESTR_LIT``.
|
``GENERALIZED_TRIPLESTR_LIT``.
|
||||||
|
|
||||||
The construct ``identifier"string literal"`` (without whitespace between the
|
The construct ``identifier"string literal"`` (without whitespace between the
|
||||||
|
|
@ -281,7 +281,7 @@ Numerical constants are of a single type and have the form::
|
||||||
DEC_LIT = digit ( ['_'] digit )*
|
DEC_LIT = digit ( ['_'] digit )*
|
||||||
OCT_LIT = '0' ('o' | 'c' | 'C') octdigit ( ['_'] octdigit )*
|
OCT_LIT = '0' ('o' | 'c' | 'C') octdigit ( ['_'] octdigit )*
|
||||||
BIN_LIT = '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )*
|
BIN_LIT = '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )*
|
||||||
|
|
||||||
INT_LIT = HEX_LIT
|
INT_LIT = HEX_LIT
|
||||||
| DEC_LIT
|
| DEC_LIT
|
||||||
| OCT_LIT
|
| OCT_LIT
|
||||||
|
|
@ -383,7 +383,7 @@ The following strings denote other tokens::
|
||||||
` ( ) { } [ ] , ; [. .] {. .} (. .)
|
` ( ) { } [ ] , ; [. .] {. .} (. .)
|
||||||
|
|
||||||
|
|
||||||
The `slice`:idx: operator `..`:tok: takes precedence over other tokens that
|
The `slice`:idx: operator `..`:tok: takes precedence over other tokens that
|
||||||
contain a dot: `{..}`:tok: are the three tokens `{`:tok:, `..`:tok:, `}`:tok:
|
contain a dot: `{..}`:tok: are the three tokens `{`:tok:, `..`:tok:, `}`:tok:
|
||||||
and not the two tokens `{.`:tok:, `.}`:tok:.
|
and not the two tokens `{.`:tok:, `.}`:tok:.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ model low level lockfree mechanisms:
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
var dummyLock {.compileTime.}: int
|
var dummyLock {.compileTime.}: int
|
||||||
var atomicCounter {.guard: dummyLock.}: int
|
var atomicCounter {.guard: dummyLock.}: int
|
||||||
|
|
||||||
template atomicRead(x): expr =
|
template atomicRead(x): expr =
|
||||||
{.locks: [dummyLock].}:
|
{.locks: [dummyLock].}:
|
||||||
memoryReadBarrier()
|
memoryReadBarrier()
|
||||||
|
|
@ -135,30 +135,30 @@ Lock levels are used to enforce a global locking order in order to prevent
|
||||||
deadlocks at compile-time. A lock level is an constant integer in the range
|
deadlocks at compile-time. A lock level is an constant integer in the range
|
||||||
0..1_000. Lock level 0 means that no lock is acquired at all.
|
0..1_000. Lock level 0 means that no lock is acquired at all.
|
||||||
|
|
||||||
If a section of code holds a lock of level ``M`` than it can also acquire any
|
If a section of code holds a lock of level ``M`` than it can also acquire any
|
||||||
lock of level ``N < M``. Another lock of level ``M`` cannot be acquired. Locks
|
lock of level ``N < M``. Another lock of level ``M`` cannot be acquired. Locks
|
||||||
of the same level can only be acquired *at the same time* within a
|
of the same level can only be acquired *at the same time* within a
|
||||||
single ``locks`` section:
|
single ``locks`` section:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
var a, b: TLock[2]
|
var a, b: TLock[2]
|
||||||
var x: TLock[1]
|
var x: TLock[1]
|
||||||
# invalid locking order: TLock[1] cannot be acquired before TLock[2]:
|
# invalid locking order: TLock[1] cannot be acquired before TLock[2]:
|
||||||
{.locks: [x].}:
|
{.locks: [x].}:
|
||||||
{.locks: [a].}:
|
{.locks: [a].}:
|
||||||
...
|
...
|
||||||
# valid locking order: TLock[2] acquired before TLock[1]:
|
# valid locking order: TLock[2] acquired before TLock[1]:
|
||||||
{.locks: [a].}:
|
{.locks: [a].}:
|
||||||
{.locks: [x].}:
|
{.locks: [x].}:
|
||||||
...
|
...
|
||||||
|
|
||||||
# invalid locking order: TLock[2] acquired before TLock[2]:
|
# invalid locking order: TLock[2] acquired before TLock[2]:
|
||||||
{.locks: [a].}:
|
{.locks: [a].}:
|
||||||
{.locks: [b].}:
|
{.locks: [b].}:
|
||||||
...
|
...
|
||||||
|
|
||||||
# valid locking order, locks of the same level acquired at the same time:
|
# valid locking order, locks of the same level acquired at the same time:
|
||||||
{.locks: [a, b].}:
|
{.locks: [a, b].}:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -187,7 +187,7 @@ level. This then means that the routine may acquire locks of up to this level.
|
||||||
This is essential so that procs can be called within a ``locks`` section:
|
This is essential so that procs can be called within a ``locks`` section:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
proc p() {.locks: 3.} = discard
|
proc p() {.locks: 3.} = discard
|
||||||
|
|
||||||
var a: TLock[4]
|
var a: TLock[4]
|
||||||
{.locks: [a].}:
|
{.locks: [a].}:
|
||||||
|
|
@ -195,6 +195,6 @@ This is essential so that procs can be called within a ``locks`` section:
|
||||||
p()
|
p()
|
||||||
|
|
||||||
|
|
||||||
As usual ``locks`` is an inferred effect and there is a subtype
|
As usual ``locks`` is an inferred effect and there is a subtype
|
||||||
relation: ``proc () {.locks: N.}`` is a subtype of ``proc () {.locks: M.}``
|
relation: ``proc () {.locks: N.}`` is a subtype of ``proc () {.locks: M.}``
|
||||||
iff (M <= N).
|
iff (M <= N).
|
||||||
|
|
|
||||||
|
|
@ -81,8 +81,8 @@ A module alias can be introduced via the ``as`` keyword:
|
||||||
|
|
||||||
echo su.format("$1", "lalelu")
|
echo su.format("$1", "lalelu")
|
||||||
|
|
||||||
The original module name is then not accessible. The
|
The original module name is then not accessible. The
|
||||||
notations ``path/to/module`` or ``path.to.module`` or ``"path/to/module"``
|
notations ``path/to/module`` or ``path.to.module`` or ``"path/to/module"``
|
||||||
can be used to refer to a module in subdirectories:
|
can be used to refer to a module in subdirectories:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
@ -104,7 +104,7 @@ Likewise the following does not make sense as the name is ``strutils`` already:
|
||||||
From import statement
|
From import statement
|
||||||
~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
After the ``from`` statement a module name follows followed by
|
After the ``from`` statement a module name follows followed by
|
||||||
an ``import`` to list the symbols one likes to use without explict
|
an ``import`` to list the symbols one likes to use without explict
|
||||||
full qualification:
|
full qualification:
|
||||||
|
|
||||||
|
|
@ -115,8 +115,8 @@ full qualification:
|
||||||
# always possible: full qualification:
|
# always possible: full qualification:
|
||||||
echo strutils.replace("abc", "a", "z")
|
echo strutils.replace("abc", "a", "z")
|
||||||
|
|
||||||
It's also possible to use ``from module import nil`` if one wants to import
|
It's also possible to use ``from module import nil`` if one wants to import
|
||||||
the module but wants to enforce fully qualified access to every symbol
|
the module but wants to enforce fully qualified access to every symbol
|
||||||
in ``module``.
|
in ``module``.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -134,15 +134,15 @@ modules don't need to import a module's dependencies:
|
||||||
# module A
|
# module A
|
||||||
import B
|
import B
|
||||||
export B.MyObject
|
export B.MyObject
|
||||||
|
|
||||||
proc `$`*(x: MyObject): string = "my object"
|
proc `$`*(x: MyObject): string = "my object"
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
# module C
|
# module C
|
||||||
import A
|
import A
|
||||||
|
|
||||||
# B.MyObject has been imported implicitly here:
|
# B.MyObject has been imported implicitly here:
|
||||||
var x: MyObject
|
var x: MyObject
|
||||||
echo($x)
|
echo($x)
|
||||||
|
|
||||||
|
|
@ -185,7 +185,7 @@ following places:
|
||||||
Module scope
|
Module scope
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
All identifiers of a module are valid from the point of declaration until
|
All identifiers of a module are valid from the point of declaration until
|
||||||
the end of the module. Identifiers from indirectly dependent modules are *not*
|
the end of the module. Identifiers from indirectly dependent modules are *not*
|
||||||
available. The `system`:idx: module is automatically imported in every module.
|
available. The `system`:idx: module is automatically imported in every module.
|
||||||
|
|
||||||
If a module imports an identifier by two different modules, each occurrence of
|
If a module imports an identifier by two different modules, each occurrence of
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@ current module:
|
||||||
Method call syntax
|
Method call syntax
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
For object oriented programming, the syntax ``obj.method(args)`` can be used
|
For object oriented programming, the syntax ``obj.method(args)`` can be used
|
||||||
instead of ``method(obj, args)``. The parentheses can be omitted if there are no
|
instead of ``method(obj, args)``. The parentheses can be omitted if there are no
|
||||||
remaining arguments: ``obj.len`` (instead of ``len(obj)``).
|
remaining arguments: ``obj.len`` (instead of ``len(obj)``).
|
||||||
|
|
||||||
|
|
@ -128,7 +128,7 @@ This method call syntax is not restricted to objects, it can be used
|
||||||
to supply any type of first argument for procedures:
|
to supply any type of first argument for procedures:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
||||||
echo("abc".len) # is the same as echo(len("abc"))
|
echo("abc".len) # is the same as echo(len("abc"))
|
||||||
echo("abc".toUpper())
|
echo("abc".toUpper())
|
||||||
echo({'a', 'b', 'c'}.card)
|
echo({'a', 'b', 'c'}.card)
|
||||||
|
|
@ -143,11 +143,11 @@ See also: `Limitations of the method call syntax`_.
|
||||||
Properties
|
Properties
|
||||||
----------
|
----------
|
||||||
Nim has no need for *get-properties*: Ordinary get-procedures that are called
|
Nim has no need for *get-properties*: Ordinary get-procedures that are called
|
||||||
with the *method call syntax* achieve the same. But setting a value is
|
with the *method call syntax* achieve the same. But setting a value is
|
||||||
different; for this a special setter syntax is needed:
|
different; for this a special setter syntax is needed:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
||||||
type
|
type
|
||||||
Socket* = ref object of RootObj
|
Socket* = ref object of RootObj
|
||||||
FHost: int # cannot be accessed from the outside of the module
|
FHost: int # cannot be accessed from the outside of the module
|
||||||
|
|
@ -157,7 +157,7 @@ different; for this a special setter syntax is needed:
|
||||||
proc `host=`*(s: var Socket, value: int) {.inline.} =
|
proc `host=`*(s: var Socket, value: int) {.inline.} =
|
||||||
## setter of hostAddr
|
## setter of hostAddr
|
||||||
s.FHost = value
|
s.FHost = value
|
||||||
|
|
||||||
proc host*(s: Socket): int {.inline.} =
|
proc host*(s: Socket): int {.inline.} =
|
||||||
## getter of hostAddr
|
## getter of hostAddr
|
||||||
s.FHost
|
s.FHost
|
||||||
|
|
@ -180,18 +180,18 @@ more argument in this case:
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
proc optarg(x: int, y: int = 0): int = x + y
|
proc optarg(x: int, y: int = 0): int = x + y
|
||||||
proc singlearg(x: int): int = 20*x
|
proc singlearg(x: int): int = 20*x
|
||||||
|
|
||||||
echo optarg 1, " ", singlearg 2 # prints "1 40"
|
echo optarg 1, " ", singlearg 2 # prints "1 40"
|
||||||
|
|
||||||
let fail = optarg 1, optarg 8 # Wrong. Too many arguments for a command call
|
let fail = optarg 1, optarg 8 # Wrong. Too many arguments for a command call
|
||||||
let x = optarg(1, optarg 8) # traditional procedure call with 2 arguments
|
let x = optarg(1, optarg 8) # traditional procedure call with 2 arguments
|
||||||
let y = 1.optarg optarg 8 # same thing as above, w/o the parenthesis
|
let y = 1.optarg optarg 8 # same thing as above, w/o the parenthesis
|
||||||
assert x == y
|
assert x == y
|
||||||
|
|
||||||
The command invocation syntax also can't have complex expressions as arguments.
|
The command invocation syntax also can't have complex expressions as arguments.
|
||||||
For example: (`anonymous procs`_), ``if``, ``case`` or ``try``. The (`do
|
For example: (`anonymous procs`_), ``if``, ``case`` or ``try``. The (`do
|
||||||
notation`_) is limited, but usable for a single proc (see the example in the
|
notation`_) is limited, but usable for a single proc (see the example in the
|
||||||
corresponding section). Function calls with no arguments still needs () to
|
corresponding section). Function calls with no arguments still needs () to
|
||||||
distinguish between a call and the function itself as a first class value.
|
distinguish between a call and the function itself as a first class value.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -221,7 +221,7 @@ the proc's name.
|
||||||
cmp(x.len, y.len))
|
cmp(x.len, y.len))
|
||||||
|
|
||||||
|
|
||||||
Procs as expressions can appear both as nested procs and inside top level
|
Procs as expressions can appear both as nested procs and inside top level
|
||||||
executable code.
|
executable code.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -237,10 +237,10 @@ calls can use the ``do`` keyword:
|
||||||
sort(cities) do (x,y: string) -> int:
|
sort(cities) do (x,y: string) -> int:
|
||||||
cmp(x.len, y.len)
|
cmp(x.len, y.len)
|
||||||
# Less parenthesis using the method plus command syntax:
|
# Less parenthesis using the method plus command syntax:
|
||||||
cities = cities.map do (x:string) -> string:
|
cities = cities.map do (x:string) -> string:
|
||||||
"City of " & x
|
"City of " & x
|
||||||
|
|
||||||
``do`` is written after the parentheses enclosing the regular proc params.
|
``do`` is written after the parentheses enclosing the regular proc params.
|
||||||
The proc expression represented by the do block is appended to them.
|
The proc expression represented by the do block is appended to them.
|
||||||
|
|
||||||
More than one ``do`` block can appear in a single call:
|
More than one ``do`` block can appear in a single call:
|
||||||
|
|
@ -261,11 +261,11 @@ Nonoverloadable builtins
|
||||||
The following builtin procs cannot be overloaded for reasons of implementation
|
The following builtin procs cannot be overloaded for reasons of implementation
|
||||||
simplicity (they require specialized semantic checking)::
|
simplicity (they require specialized semantic checking)::
|
||||||
|
|
||||||
declared, defined, definedInScope, compiles, low, high, sizeOf,
|
declared, defined, definedInScope, compiles, low, high, sizeOf,
|
||||||
is, of, shallowCopy, getAst, astToStr, spawn, procCall
|
is, of, shallowCopy, getAst, astToStr, spawn, procCall
|
||||||
|
|
||||||
Thus they act more like keywords than like ordinary identifiers; unlike a
|
Thus they act more like keywords than like ordinary identifiers; unlike a
|
||||||
keyword however, a redefinition may `shadow`:idx: the definition in
|
keyword however, a redefinition may `shadow`:idx: the definition in
|
||||||
the ``system`` module. From this list the following should not be written in dot
|
the ``system`` module. From this list the following should not be written in dot
|
||||||
notation ``x.f`` since ``x`` cannot be type checked before it gets passed
|
notation ``x.f`` since ``x`` cannot be type checked before it gets passed
|
||||||
to ``f``::
|
to ``f``::
|
||||||
|
|
@ -342,11 +342,11 @@ returned value is an l-value and can be modified by the caller:
|
||||||
|
|
||||||
proc WriteAccessToG(): var int =
|
proc WriteAccessToG(): var int =
|
||||||
result = g
|
result = g
|
||||||
|
|
||||||
WriteAccessToG() = 6
|
WriteAccessToG() = 6
|
||||||
assert g == 6
|
assert g == 6
|
||||||
|
|
||||||
It is a compile time error if the implicitly introduced pointer could be
|
It is a compile time error if the implicitly introduced pointer could be
|
||||||
used to access a location beyond its lifetime:
|
used to access a location beyond its lifetime:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
@ -354,7 +354,7 @@ used to access a location beyond its lifetime:
|
||||||
var g = 0
|
var g = 0
|
||||||
result = g # Error!
|
result = g # Error!
|
||||||
|
|
||||||
For iterators, a component of a tuple return type can have a ``var`` type too:
|
For iterators, a component of a tuple return type can have a ``var`` type too:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
iterator mpairs(a: var seq[string]): tuple[key: int, val: var string] =
|
iterator mpairs(a: var seq[string]): tuple[key: int, val: var string] =
|
||||||
|
|
@ -384,28 +384,28 @@ dispatch.
|
||||||
x: int
|
x: int
|
||||||
PlusExpr = ref object of Expression
|
PlusExpr = ref object of Expression
|
||||||
a, b: Expression
|
a, b: Expression
|
||||||
|
|
||||||
method eval(e: Expression): int =
|
method eval(e: Expression): int =
|
||||||
# override this base method
|
# override this base method
|
||||||
quit "to override!"
|
quit "to override!"
|
||||||
|
|
||||||
method eval(e: Literal): int = return e.x
|
method eval(e: Literal): int = return e.x
|
||||||
|
|
||||||
method eval(e: PlusExpr): int =
|
method eval(e: PlusExpr): int =
|
||||||
# watch out: relies on dynamic binding
|
# watch out: relies on dynamic binding
|
||||||
result = eval(e.a) + eval(e.b)
|
result = eval(e.a) + eval(e.b)
|
||||||
|
|
||||||
proc newLit(x: int): Literal =
|
proc newLit(x: int): Literal =
|
||||||
new(result)
|
new(result)
|
||||||
result.x = x
|
result.x = x
|
||||||
|
|
||||||
proc newPlus(a, b: Expression): PlusExpr =
|
proc newPlus(a, b: Expression): PlusExpr =
|
||||||
new(result)
|
new(result)
|
||||||
result.a = a
|
result.a = a
|
||||||
result.b = b
|
result.b = b
|
||||||
|
|
||||||
echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
|
echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
|
||||||
|
|
||||||
In the example the constructors ``newLit`` and ``newPlus`` are procs
|
In the example the constructors ``newLit`` and ``newPlus`` are procs
|
||||||
because they should use static binding, but ``eval`` is a method because it
|
because they should use static binding, but ``eval`` is a method because it
|
||||||
requires dynamic binding.
|
requires dynamic binding.
|
||||||
|
|
@ -418,24 +418,24 @@ dispatching:
|
||||||
Thing = ref object of RootObj
|
Thing = ref object of RootObj
|
||||||
Unit = ref object of Thing
|
Unit = ref object of Thing
|
||||||
x: int
|
x: int
|
||||||
|
|
||||||
method collide(a, b: Thing) {.inline.} =
|
method collide(a, b: Thing) {.inline.} =
|
||||||
quit "to override!"
|
quit "to override!"
|
||||||
|
|
||||||
method collide(a: Thing, b: Unit) {.inline.} =
|
method collide(a: Thing, b: Unit) {.inline.} =
|
||||||
echo "1"
|
echo "1"
|
||||||
|
|
||||||
method collide(a: Unit, b: Thing) {.inline.} =
|
method collide(a: Unit, b: Thing) {.inline.} =
|
||||||
echo "2"
|
echo "2"
|
||||||
|
|
||||||
var a, b: Unit
|
var a, b: Unit
|
||||||
new a
|
new a
|
||||||
new b
|
new b
|
||||||
collide(a, b) # output: 2
|
collide(a, b) # output: 2
|
||||||
|
|
||||||
|
|
||||||
Invocation of a multi-method cannot be ambiguous: collide 2 is preferred over
|
Invocation of a multi-method cannot be ambiguous: collide 2 is preferred over
|
||||||
collide 1 because the resolution works from left to right.
|
collide 1 because the resolution works from left to right.
|
||||||
In the example ``Unit, Thing`` is preferred over ``Thing, Unit``.
|
In the example ``Unit, Thing`` is preferred over ``Thing, Unit``.
|
||||||
|
|
||||||
**Performance note**: Nim does not produce a virtual method table, but
|
**Performance note**: Nim does not produce a virtual method table, but
|
||||||
|
|
@ -450,7 +450,7 @@ Iterators and the for statement
|
||||||
The `for`:idx: statement is an abstract mechanism to iterate over the elements
|
The `for`:idx: statement is an abstract mechanism to iterate over the elements
|
||||||
of a container. It relies on an `iterator`:idx: to do so. Like ``while``
|
of a container. It relies on an `iterator`:idx: to do so. Like ``while``
|
||||||
statements, ``for`` statements open an `implicit block`:idx:, so that they
|
statements, ``for`` statements open an `implicit block`:idx:, so that they
|
||||||
can be left with a ``break`` statement.
|
can be left with a ``break`` statement.
|
||||||
|
|
||||||
The ``for`` loop declares iteration variables - their scope reaches until the
|
The ``for`` loop declares iteration variables - their scope reaches until the
|
||||||
end of the loop body. The iteration variables' types are inferred by the
|
end of the loop body. The iteration variables' types are inferred by the
|
||||||
|
|
@ -486,7 +486,7 @@ The compiler generates code as if the programmer would have written this:
|
||||||
|
|
||||||
If the iterator yields a tuple, there can be as many iteration variables
|
If the iterator yields a tuple, there can be as many iteration variables
|
||||||
as there are components in the tuple. The i'th iteration variable's type is
|
as there are components in the tuple. The i'th iteration variable's type is
|
||||||
the type of the i'th component. In other words, implicit tuple unpacking in a
|
the type of the i'th component. In other words, implicit tuple unpacking in a
|
||||||
for loop context is supported.
|
for loop context is supported.
|
||||||
|
|
||||||
Implict items/pairs invocations
|
Implict items/pairs invocations
|
||||||
|
|
@ -498,11 +498,11 @@ ie. an ``items`` iterator is implicitly invoked:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
for x in [1,2,3]: echo x
|
for x in [1,2,3]: echo x
|
||||||
|
|
||||||
If the for loop has exactly 2 variables, a ``pairs`` iterator is implicitly
|
If the for loop has exactly 2 variables, a ``pairs`` iterator is implicitly
|
||||||
invoked.
|
invoked.
|
||||||
|
|
||||||
Symbol lookup of the identifiers ``items``/``pairs`` is performed after
|
Symbol lookup of the identifiers ``items``/``pairs`` is performed after
|
||||||
the rewriting step, so that all overloads of ``items``/``pairs`` are taken
|
the rewriting step, so that all overloads of ``items``/``pairs`` are taken
|
||||||
into account.
|
into account.
|
||||||
|
|
||||||
|
|
@ -511,7 +511,7 @@ First class iterators
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
There are 2 kinds of iterators in Nim: *inline* and *closure* iterators.
|
There are 2 kinds of iterators in Nim: *inline* and *closure* iterators.
|
||||||
An `inline iterator`:idx: is an iterator that's always inlined by the compiler
|
An `inline iterator`:idx: is an iterator that's always inlined by the compiler
|
||||||
leading to zero overhead for the abstraction, but may result in a heavy
|
leading to zero overhead for the abstraction, but may result in a heavy
|
||||||
increase in code size. Inline iterators are second class citizens;
|
increase in code size. Inline iterators are second class citizens;
|
||||||
They can be passed as parameters only to other inlining code facilities like
|
They can be passed as parameters only to other inlining code facilities like
|
||||||
|
|
@ -522,7 +522,7 @@ In contrast to that, a `closure iterator`:idx: can be passed around more freely:
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
iterator count0(): int {.closure.} =
|
iterator count0(): int {.closure.} =
|
||||||
yield 0
|
yield 0
|
||||||
|
|
||||||
iterator count2(): int {.closure.} =
|
iterator count2(): int {.closure.} =
|
||||||
var x = 1
|
var x = 1
|
||||||
yield x
|
yield x
|
||||||
|
|
@ -539,7 +539,7 @@ Closure iterators have other restrictions than inline iterators:
|
||||||
|
|
||||||
1. ``yield`` in a closure iterator can not occur in a ``try`` statement.
|
1. ``yield`` in a closure iterator can not occur in a ``try`` statement.
|
||||||
2. For now, a closure iterator cannot be evaluated at compile time.
|
2. For now, a closure iterator cannot be evaluated at compile time.
|
||||||
3. ``return`` is allowed in a closure iterator (but rarely useful) and ends
|
3. ``return`` is allowed in a closure iterator (but rarely useful) and ends
|
||||||
iteration.
|
iteration.
|
||||||
4. Neither inline nor closure iterators can be recursive.
|
4. Neither inline nor closure iterators can be recursive.
|
||||||
|
|
||||||
|
|
@ -547,7 +547,7 @@ Iterators that are neither marked ``{.closure.}`` nor ``{.inline.}`` explicitly
|
||||||
default to being inline, but this may change in future versions of the
|
default to being inline, but this may change in future versions of the
|
||||||
implementation.
|
implementation.
|
||||||
|
|
||||||
The ``iterator`` type is always of the calling convention ``closure``
|
The ``iterator`` type is always of the calling convention ``closure``
|
||||||
implicitly; the following example shows how to use iterators to implement
|
implicitly; the following example shows how to use iterators to implement
|
||||||
a `collaborative tasking`:idx: system:
|
a `collaborative tasking`:idx: system:
|
||||||
|
|
||||||
|
|
@ -650,7 +650,7 @@ parameters of an outer factory proc:
|
||||||
Converters
|
Converters
|
||||||
==========
|
==========
|
||||||
|
|
||||||
A converter is like an ordinary proc except that it enhances
|
A converter is like an ordinary proc except that it enhances
|
||||||
the "implicitly convertible" type relation (see `Convertible relation`_):
|
the "implicitly convertible" type relation (see `Convertible relation`_):
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ templates and macros), depending on the desired effect:
|
||||||
The following dot operators are available:
|
The following dot operators are available:
|
||||||
|
|
||||||
operator `.`
|
operator `.`
|
||||||
------------
|
------------
|
||||||
This operator will be matched against both field accesses and method calls.
|
This operator will be matched against both field accesses and method calls.
|
||||||
|
|
||||||
operator `.()`
|
operator `.()`
|
||||||
|
|
|
||||||
|
|
@ -176,8 +176,8 @@ The rules for compile-time computability are:
|
||||||
1. Literals are compile-time computable.
|
1. Literals are compile-time computable.
|
||||||
2. Type conversions are compile-time computable.
|
2. Type conversions are compile-time computable.
|
||||||
3. Procedure calls of the form ``p(X)`` are compile-time computable if
|
3. Procedure calls of the form ``p(X)`` are compile-time computable if
|
||||||
``p`` is a proc without side-effects (see the `noSideEffect pragma
|
``p`` is a proc without side-effects (see the `noSideEffect pragma
|
||||||
<#pragmas-nosideeffect-pragma>`_ for details) and if ``X`` is a
|
<#pragmas-nosideeffect-pragma>`_ for details) and if ``X`` is a
|
||||||
(possibly empty) list of compile-time computable arguments.
|
(possibly empty) list of compile-time computable arguments.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
Taint mode
|
Taint mode
|
||||||
==========
|
==========
|
||||||
|
|
||||||
The Nim compiler and most parts of the standard library support
|
The Nim compiler and most parts of the standard library support
|
||||||
a taint mode. Input strings are declared with the `TaintedString`:idx:
|
a taint mode. Input strings are declared with the `TaintedString`:idx:
|
||||||
string type declared in the ``system`` module.
|
string type declared in the ``system`` module.
|
||||||
|
|
||||||
If the taint mode is turned on (via the ``--taintMode:on`` command line
|
If the taint mode is turned on (via the ``--taintMode:on`` command line
|
||||||
option) it is a distinct string type which helps to detect input
|
option) it is a distinct string type which helps to detect input
|
||||||
validation errors:
|
validation errors:
|
||||||
|
|
||||||
|
|
@ -13,7 +13,7 @@ validation errors:
|
||||||
echo "your name: "
|
echo "your name: "
|
||||||
var name: TaintedString = stdin.readline
|
var name: TaintedString = stdin.readline
|
||||||
# it is safe here to output the name without any input validation, so
|
# it is safe here to output the name without any input validation, so
|
||||||
# we simply convert `name` to string to make the compiler happy:
|
# we simply convert `name` to string to make the compiler happy:
|
||||||
echo "hi, ", name.string
|
echo "hi, ", name.string
|
||||||
|
|
||||||
If the taint mode is turned off, ``TaintedString`` is simply an alias for
|
If the taint mode is turned off, ``TaintedString`` is simply an alias for
|
||||||
|
|
|
||||||
|
|
@ -16,17 +16,17 @@ Example:
|
||||||
|
|
||||||
assert(5 != 6) # the compiler rewrites that to: assert(not (5 == 6))
|
assert(5 != 6) # the compiler rewrites that to: assert(not (5 == 6))
|
||||||
|
|
||||||
The ``!=``, ``>``, ``>=``, ``in``, ``notin``, ``isnot`` operators are in fact
|
The ``!=``, ``>``, ``>=``, ``in``, ``notin``, ``isnot`` operators are in fact
|
||||||
templates:
|
templates:
|
||||||
|
|
||||||
| ``a > b`` is transformed into ``b < a``.
|
| ``a > b`` is transformed into ``b < a``.
|
||||||
| ``a in b`` is transformed into ``contains(b, a)``.
|
| ``a in b`` is transformed into ``contains(b, a)``.
|
||||||
| ``notin`` and ``isnot`` have the obvious meanings.
|
| ``notin`` and ``isnot`` have the obvious meanings.
|
||||||
|
|
||||||
The "types" of templates can be the symbols ``expr`` (stands for *expression*),
|
The "types" of templates can be the symbols ``expr`` (stands for *expression*),
|
||||||
``stmt`` (stands for *statement*) or ``typedesc`` (stands for *type
|
``stmt`` (stands for *statement*) or ``typedesc`` (stands for *type
|
||||||
description*). These are "meta types", they can only be used in certain
|
description*). These are "meta types", they can only be used in certain
|
||||||
contexts. Real types can be used too; this implies that expressions are
|
contexts. Real types can be used too; this implies that expressions are
|
||||||
expected.
|
expected.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -40,7 +40,7 @@ So ordinary templates cannot receive undeclared identifiers:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
||||||
template declareInt(x: expr) =
|
template declareInt(x: expr) =
|
||||||
var x: int
|
var x: int
|
||||||
|
|
||||||
declareInt(x) # error: unknown identifier: 'x'
|
declareInt(x) # error: unknown identifier: 'x'
|
||||||
|
|
@ -51,7 +51,7 @@ receive undeclared identifiers:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
||||||
template declareInt(x: expr) {.immediate.} =
|
template declareInt(x: expr) {.immediate.} =
|
||||||
var x: int
|
var x: int
|
||||||
|
|
||||||
declareInt(x) # valid
|
declareInt(x) # valid
|
||||||
|
|
@ -75,11 +75,11 @@ special ``:`` syntax:
|
||||||
close(f)
|
close(f)
|
||||||
else:
|
else:
|
||||||
quit("cannot open: " & fn)
|
quit("cannot open: " & fn)
|
||||||
|
|
||||||
withFile(txt, "ttempl3.txt", fmWrite):
|
withFile(txt, "ttempl3.txt", fmWrite):
|
||||||
txt.writeLine("line 1")
|
txt.writeLine("line 1")
|
||||||
txt.writeLine("line 2")
|
txt.writeLine("line 2")
|
||||||
|
|
||||||
In the example the two ``writeLine`` statements are bound to the ``actions``
|
In the example the two ``writeLine`` statements are bound to the ``actions``
|
||||||
parameter.
|
parameter.
|
||||||
|
|
||||||
|
|
@ -92,9 +92,9 @@ bound from the definition scope of the template:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
# Module A
|
# Module A
|
||||||
var
|
var
|
||||||
lastId = 0
|
lastId = 0
|
||||||
|
|
||||||
template genId*: expr =
|
template genId*: expr =
|
||||||
inc(lastId)
|
inc(lastId)
|
||||||
lastId
|
lastId
|
||||||
|
|
@ -102,10 +102,10 @@ bound from the definition scope of the template:
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
# Module B
|
# Module B
|
||||||
import A
|
import A
|
||||||
|
|
||||||
echo genId() # Works as 'lastId' has been bound in 'genId's defining scope
|
echo genId() # Works as 'lastId' has been bound in 'genId's defining scope
|
||||||
|
|
||||||
As in generics symbol binding can be influenced via ``mixin`` or ``bind``
|
As in generics symbol binding can be influenced via ``mixin`` or ``bind``
|
||||||
statements.
|
statements.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -117,11 +117,11 @@ In templates identifiers can be constructed with the backticks notation:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
||||||
template typedef(name: expr, typ: typedesc) {.immediate.} =
|
template typedef(name: expr, typ: typedesc) {.immediate.} =
|
||||||
type
|
type
|
||||||
`T name`* {.inject.} = typ
|
`T name`* {.inject.} = typ
|
||||||
`P name`* {.inject.} = ref `T name`
|
`P name`* {.inject.} = ref `T name`
|
||||||
|
|
||||||
typedef(myint, int)
|
typedef(myint, int)
|
||||||
var x: PMyInt
|
var x: PMyInt
|
||||||
|
|
||||||
|
|
@ -150,7 +150,7 @@ shadowed by the same argument name even when fully qualified:
|
||||||
|
|
||||||
tstLev(levA)
|
tstLev(levA)
|
||||||
# produces: 'levA levA'
|
# produces: 'levA levA'
|
||||||
|
|
||||||
But the global symbol can properly be captured by a ``bind`` statement:
|
But the global symbol can properly be captured by a ``bind`` statement:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
@ -177,14 +177,14 @@ Per default templates are `hygienic`:idx:\: Local identifiers declared in a
|
||||||
template cannot be accessed in the instantiation context:
|
template cannot be accessed in the instantiation context:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
||||||
template newException*(exceptn: typedesc, message: string): expr =
|
template newException*(exceptn: typedesc, message: string): expr =
|
||||||
var
|
var
|
||||||
e: ref exceptn # e is implicitly gensym'ed here
|
e: ref exceptn # e is implicitly gensym'ed here
|
||||||
new(e)
|
new(e)
|
||||||
e.msg = message
|
e.msg = message
|
||||||
e
|
e
|
||||||
|
|
||||||
# so this works:
|
# so this works:
|
||||||
let e = "message"
|
let e = "message"
|
||||||
raise newException(EIO, e)
|
raise newException(EIO, e)
|
||||||
|
|
@ -196,7 +196,7 @@ symbols are not exposed but inject'ed are.
|
||||||
|
|
||||||
The default for symbols of entity ``type``, ``var``, ``let`` and ``const``
|
The default for symbols of entity ``type``, ``var``, ``let`` and ``const``
|
||||||
is ``gensym`` and for ``proc``, ``iterator``, ``converter``, ``template``,
|
is ``gensym`` and for ``proc``, ``iterator``, ``converter``, ``template``,
|
||||||
``macro`` is ``inject``. However, if the name of the entity is passed as a
|
``macro`` is ``inject``. However, if the name of the entity is passed as a
|
||||||
template parameter, it is an inject'ed symbol:
|
template parameter, it is an inject'ed symbol:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
@ -204,7 +204,7 @@ template parameter, it is an inject'ed symbol:
|
||||||
block:
|
block:
|
||||||
var f: File # since 'f' is a template param, it's injected implicitly
|
var f: File # since 'f' is a template param, it's injected implicitly
|
||||||
...
|
...
|
||||||
|
|
||||||
withFile(txt, "ttempl3.txt", fmWrite):
|
withFile(txt, "ttempl3.txt", fmWrite):
|
||||||
txt.writeLine("line 1")
|
txt.writeLine("line 1")
|
||||||
txt.writeLine("line 2")
|
txt.writeLine("line 2")
|
||||||
|
|
@ -215,7 +215,7 @@ no semantics outside of a template definition and cannot be abstracted over:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
{.pragma myInject: inject.}
|
{.pragma myInject: inject.}
|
||||||
|
|
||||||
template t() =
|
template t() =
|
||||||
var x {.myInject.}: int # does NOT work
|
var x {.myInject.}: int # does NOT work
|
||||||
|
|
||||||
|
|
@ -334,9 +334,9 @@ BindSym
|
||||||
-------
|
-------
|
||||||
|
|
||||||
The above ``debug`` macro relies on the fact that ``write``, ``writeLine`` and
|
The above ``debug`` macro relies on the fact that ``write``, ``writeLine`` and
|
||||||
``stdout`` are declared in the system module and thus visible in the
|
``stdout`` are declared in the system module and thus visible in the
|
||||||
instantiating context. There is a way to use bound identifiers
|
instantiating context. There is a way to use bound identifiers
|
||||||
(aka `symbols`:idx:) instead of using unbound identifiers. The ``bindSym``
|
(aka `symbols`:idx:) instead of using unbound identifiers. The ``bindSym``
|
||||||
builtin can be used for that:
|
builtin can be used for that:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
@ -418,8 +418,8 @@ powerful programming construct that still suffices. So the "check list" is:
|
||||||
Macros as pragmas
|
Macros as pragmas
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
Whole routines (procs, iterators etc.) can also be passed to a template or
|
Whole routines (procs, iterators etc.) can also be passed to a template or
|
||||||
a macro via the pragma notation:
|
a macro via the pragma notation:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
template m(s: stmt) = discard
|
template m(s: stmt) = discard
|
||||||
|
|
|
||||||
|
|
@ -3,32 +3,32 @@ Term rewriting macros
|
||||||
|
|
||||||
Term rewriting macros are macros or templates that have not only
|
Term rewriting macros are macros or templates that have not only
|
||||||
a *name* but also a *pattern* that is searched for after the semantic checking
|
a *name* but also a *pattern* that is searched for after the semantic checking
|
||||||
phase of the compiler: This means they provide an easy way to enhance the
|
phase of the compiler: This means they provide an easy way to enhance the
|
||||||
compilation pipeline with user defined optimizations:
|
compilation pipeline with user defined optimizations:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
template optMul{`*`(a, 2)}(a: int): int = a+a
|
template optMul{`*`(a, 2)}(a: int): int = a+a
|
||||||
|
|
||||||
let x = 3
|
let x = 3
|
||||||
echo x * 2
|
echo x * 2
|
||||||
|
|
||||||
The compiler now rewrites ``x * 2`` as ``x + x``. The code inside the
|
The compiler now rewrites ``x * 2`` as ``x + x``. The code inside the
|
||||||
curlies is the pattern to match against. The operators ``*``, ``**``,
|
curlies is the pattern to match against. The operators ``*``, ``**``,
|
||||||
``|``, ``~`` have a special meaning in patterns if they are written in infix
|
``|``, ``~`` have a special meaning in patterns if they are written in infix
|
||||||
notation, so to match verbatim against ``*`` the ordinary function call syntax
|
notation, so to match verbatim against ``*`` the ordinary function call syntax
|
||||||
needs to be used.
|
needs to be used.
|
||||||
|
|
||||||
|
|
||||||
Unfortunately optimizations are hard to get right and even the tiny example
|
Unfortunately optimizations are hard to get right and even the tiny example
|
||||||
is **wrong**:
|
is **wrong**:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
template optMul{`*`(a, 2)}(a: int): int = a+a
|
template optMul{`*`(a, 2)}(a: int): int = a+a
|
||||||
|
|
||||||
proc f(): int =
|
proc f(): int =
|
||||||
echo "side effect!"
|
echo "side effect!"
|
||||||
result = 55
|
result = 55
|
||||||
|
|
||||||
echo f() * 2
|
echo f() * 2
|
||||||
|
|
||||||
We cannot duplicate 'a' if it denotes an expression that has a side effect!
|
We cannot duplicate 'a' if it denotes an expression that has a side effect!
|
||||||
|
|
@ -36,11 +36,11 @@ Fortunately Nim supports side effect analysis:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
template optMul{`*`(a, 2)}(a: int{noSideEffect}): int = a+a
|
template optMul{`*`(a, 2)}(a: int{noSideEffect}): int = a+a
|
||||||
|
|
||||||
proc f(): int =
|
proc f(): int =
|
||||||
echo "side effect!"
|
echo "side effect!"
|
||||||
result = 55
|
result = 55
|
||||||
|
|
||||||
echo f() * 2 # not optimized ;-)
|
echo f() * 2 # not optimized ;-)
|
||||||
|
|
||||||
You can make one overload matching with a constraint and one without, and the
|
You can make one overload matching with a constraint and one without, and the
|
||||||
|
|
@ -53,13 +53,13 @@ blindly:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
template mulIsCommutative{`*`(a, b)}(a, b: int): int = b*a
|
template mulIsCommutative{`*`(a, b)}(a, b: int): int = b*a
|
||||||
|
|
||||||
What optimizers really need to do is a *canonicalization*:
|
What optimizers really need to do is a *canonicalization*:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
template canonMul{`*`(a, b)}(a: int{lit}, b: int): int = b*a
|
template canonMul{`*`(a, b)}(a: int{lit}, b: int): int = b*a
|
||||||
|
|
||||||
The ``int{lit}`` parameter pattern matches against an expression of
|
The ``int{lit}`` parameter pattern matches against an expression of
|
||||||
type ``int``, but only if it's a literal.
|
type ``int``, but only if it's a literal.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -67,7 +67,7 @@ type ``int``, but only if it's a literal.
|
||||||
Parameter constraints
|
Parameter constraints
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
The `parameter constraint`:idx: expression can use the operators ``|`` (or),
|
The `parameter constraint`:idx: expression can use the operators ``|`` (or),
|
||||||
``&`` (and) and ``~`` (not) and the following predicates:
|
``&`` (and) and ``~`` (not) and the following predicates:
|
||||||
|
|
||||||
=================== =====================================================
|
=================== =====================================================
|
||||||
|
|
@ -75,7 +75,7 @@ Predicate Meaning
|
||||||
=================== =====================================================
|
=================== =====================================================
|
||||||
``atom`` The matching node has no children.
|
``atom`` The matching node has no children.
|
||||||
``lit`` The matching node is a literal like "abc", 12.
|
``lit`` The matching node is a literal like "abc", 12.
|
||||||
``sym`` The matching node must be a symbol (a bound
|
``sym`` The matching node must be a symbol (a bound
|
||||||
identifier).
|
identifier).
|
||||||
``ident`` The matching node must be an identifier (an unbound
|
``ident`` The matching node must be an identifier (an unbound
|
||||||
identifier).
|
identifier).
|
||||||
|
|
@ -101,15 +101,15 @@ Predicate Meaning
|
||||||
``enumfield`` A symbol which is a field in an enumeration.
|
``enumfield`` A symbol which is a field in an enumeration.
|
||||||
``forvar`` A for loop variable.
|
``forvar`` A for loop variable.
|
||||||
``label`` A label (used in ``block`` statements).
|
``label`` A label (used in ``block`` statements).
|
||||||
``nk*`` The matching AST must have the specified kind.
|
``nk*`` The matching AST must have the specified kind.
|
||||||
(Example: ``nkIfStmt`` denotes an ``if`` statement.)
|
(Example: ``nkIfStmt`` denotes an ``if`` statement.)
|
||||||
``alias`` States that the marked parameter needs to alias
|
``alias`` States that the marked parameter needs to alias
|
||||||
with *some* other parameter.
|
with *some* other parameter.
|
||||||
``noalias`` States that *every* other parameter must not alias
|
``noalias`` States that *every* other parameter must not alias
|
||||||
with the marked parameter.
|
with the marked parameter.
|
||||||
=================== =====================================================
|
=================== =====================================================
|
||||||
|
|
||||||
Predicates that share their name with a keyword have to be escaped with
|
Predicates that share their name with a keyword have to be escaped with
|
||||||
backticks: `` `const` ``.
|
backticks: `` `const` ``.
|
||||||
The ``alias`` and ``noalias`` predicates refer not only to the matching AST,
|
The ``alias`` and ``noalias`` predicates refer not only to the matching AST,
|
||||||
but also to every other bound parameter; syntactically they need to occur after
|
but also to every other bound parameter; syntactically they need to occur after
|
||||||
|
|
@ -151,14 +151,14 @@ constant folding, so the following does not work:
|
||||||
The reason is that the compiler already transformed the 1 into "1" for
|
The reason is that the compiler already transformed the 1 into "1" for
|
||||||
the ``echo`` statement. However, a term rewriting macro should not change the
|
the ``echo`` statement. However, a term rewriting macro should not change the
|
||||||
semantics anyway. In fact they can be deactivated with the ``--patterns:off``
|
semantics anyway. In fact they can be deactivated with the ``--patterns:off``
|
||||||
command line option or temporarily with the ``patterns`` pragma.
|
command line option or temporarily with the ``patterns`` pragma.
|
||||||
|
|
||||||
|
|
||||||
The ``{}`` operator
|
The ``{}`` operator
|
||||||
~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
A pattern expression can be bound to a pattern parameter via the ``expr{param}``
|
A pattern expression can be bound to a pattern parameter via the ``expr{param}``
|
||||||
notation:
|
notation:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
template t{(0|1|2){x}}(x: expr): expr = x+1
|
template t{(0|1|2){x}}(x: expr): expr = x+1
|
||||||
|
|
@ -176,7 +176,7 @@ The ``~`` operator is the **not** operator in patterns:
|
||||||
template t{x = (~x){y} and (~x){z}}(x, y, z: bool): stmt =
|
template t{x = (~x){y} and (~x){z}}(x, y, z: bool): stmt =
|
||||||
x = y
|
x = y
|
||||||
if x: x = z
|
if x: x = z
|
||||||
|
|
||||||
var
|
var
|
||||||
a = false
|
a = false
|
||||||
b = true
|
b = true
|
||||||
|
|
@ -189,12 +189,12 @@ The ``*`` operator
|
||||||
~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
The ``*`` operator can *flatten* a nested binary expression like ``a & b & c``
|
The ``*`` operator can *flatten* a nested binary expression like ``a & b & c``
|
||||||
to ``&(a, b, c)``:
|
to ``&(a, b, c)``:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
var
|
var
|
||||||
calls = 0
|
calls = 0
|
||||||
|
|
||||||
proc `&&`(s: varargs[string]): string =
|
proc `&&`(s: varargs[string]): string =
|
||||||
result = s[0]
|
result = s[0]
|
||||||
for i in 1..len(s)-1: result.add s[i]
|
for i in 1..len(s)-1: result.add s[i]
|
||||||
|
|
@ -211,8 +211,8 @@ to ``&(a, b, c)``:
|
||||||
|
|
||||||
The second operator of `*` must be a parameter; it is used to gather all the
|
The second operator of `*` must be a parameter; it is used to gather all the
|
||||||
arguments. The expression ``"my" && (space & "awe" && "some " ) && "concat"``
|
arguments. The expression ``"my" && (space & "awe" && "some " ) && "concat"``
|
||||||
is passed to ``optConc`` in ``a`` as a special list (of kind ``nkArgList``)
|
is passed to ``optConc`` in ``a`` as a special list (of kind ``nkArgList``)
|
||||||
which is flattened into a call expression; thus the invocation of ``optConc``
|
which is flattened into a call expression; thus the invocation of ``optConc``
|
||||||
produces:
|
produces:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
@ -245,7 +245,7 @@ all the arguments, but also the matched operators in reverse polish notation:
|
||||||
|
|
||||||
var x, y, z: Matrix
|
var x, y, z: Matrix
|
||||||
|
|
||||||
echo x + y * z - x
|
echo x + y * z - x
|
||||||
|
|
||||||
This passes the expression ``x + y * z - x`` to the ``optM`` macro as
|
This passes the expression ``x + y * z - x`` to the ``optM`` macro as
|
||||||
an ``nnkArgList`` node containing::
|
an ``nnkArgList`` node containing::
|
||||||
|
|
@ -265,7 +265,7 @@ an ``nnkArgList`` node containing::
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
|
|
||||||
Parameters in a pattern are type checked in the matching process. If a
|
Parameters in a pattern are type checked in the matching process. If a
|
||||||
parameter is of the type ``varargs`` it is treated specially and it can match
|
parameter is of the type ``varargs`` it is treated specially and it can match
|
||||||
0 or more arguments in the AST to be matched against:
|
0 or more arguments in the AST to be matched against:
|
||||||
|
|
||||||
|
|
@ -275,7 +275,7 @@ parameter is of the type ``varargs`` it is treated specially and it can match
|
||||||
((write|writeLine){w})(f, y)
|
((write|writeLine){w})(f, y)
|
||||||
}(x, y: varargs[expr], f: File, w: expr) =
|
}(x, y: varargs[expr], f: File, w: expr) =
|
||||||
w(f, x, y)
|
w(f, x, y)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Example: Partial evaluation
|
Example: Partial evaluation
|
||||||
|
|
@ -310,7 +310,7 @@ The following example shows how some form of hoisting can be implemented:
|
||||||
|
|
||||||
The ``optPeg`` template optimizes the case of a peg constructor with a string
|
The ``optPeg`` template optimizes the case of a peg constructor with a string
|
||||||
literal, so that the pattern will only be parsed once at program startup and
|
literal, so that the pattern will only be parsed once at program startup and
|
||||||
stored in a global ``gl`` which is then re-used. This optimization is called
|
stored in a global ``gl`` which is then re-used. This optimization is called
|
||||||
hoisting because it is comparable to classical loop hoisting.
|
hoisting because it is comparable to classical loop hoisting.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -343,7 +343,7 @@ ordinary routines.
|
||||||
Move optimization
|
Move optimization
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
The ``call`` constraint is particularly useful to implement a move
|
The ``call`` constraint is particularly useful to implement a move
|
||||||
optimization for types that have copying semantics:
|
optimization for types that have copying semantics:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
|
||||||
|
|
@ -301,7 +301,7 @@ matches better than just ``T`` then.
|
||||||
proc sayHi(x: var int): string =
|
proc sayHi(x: var int): string =
|
||||||
# matches a var int
|
# matches a var int
|
||||||
result = $(x + 10)
|
result = $(x + 10)
|
||||||
|
|
||||||
proc sayHello(x: int) =
|
proc sayHello(x: int) =
|
||||||
var m = x # a mutable version of x
|
var m = x # a mutable version of x
|
||||||
echo sayHi(x) # matches the non-var version of sayHi
|
echo sayHi(x) # matches the non-var version of sayHi
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,6 @@ Example:
|
||||||
A type section begins with the ``type`` keyword. It contains multiple
|
A type section begins with the ``type`` keyword. It contains multiple
|
||||||
type definitions. A type definition binds a type to a name. Type definitions
|
type definitions. A type definition binds a type to a name. Type definitions
|
||||||
can be recursive or even mutually recursive. Mutually recursive types are only
|
can be recursive or even mutually recursive. Mutually recursive types are only
|
||||||
possible within a single ``type`` section. Nominal types like ``objects``
|
possible within a single ``type`` section. Nominal types like ``objects``
|
||||||
or ``enums`` can only be defined in a ``type`` section.
|
or ``enums`` can only be defined in a ``type`` section.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ Note that there can be exceptions to these rules. Nim being as flexible as it
|
||||||
is, there will be parts of this style guide that don't make sense in certain
|
is, there will be parts of this style guide that don't make sense in certain
|
||||||
contexts. Furthermore, just as
|
contexts. Furthermore, just as
|
||||||
`Python's style guide<http://legacy.python.org/dev/peps/pep-0008/>`_ changes
|
`Python's style guide<http://legacy.python.org/dev/peps/pep-0008/>`_ changes
|
||||||
over time, this style guide will too.
|
over time, this style guide will too.
|
||||||
|
|
||||||
These rules will only be enforced for contributions to the Nim
|
These rules will only be enforced for contributions to the Nim
|
||||||
codebase and official projects, such as the Nim compiler, the standard library,
|
codebase and official projects, such as the Nim compiler, the standard library,
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
=====================
|
=====================
|
||||||
Nimfix User Guide
|
Nimfix User Guide
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
:Author: Andreas Rumpf
|
:Author: Andreas Rumpf
|
||||||
:Version: |nimversion|
|
:Version: |nimversion|
|
||||||
|
|
||||||
**WARNING**: Nimfix is currently beta-quality.
|
**WARNING**: Nimfix is currently beta-quality.
|
||||||
|
|
||||||
Nimfix is a tool to help you upgrade from Nimrod (<= version 0.9.6) to
|
Nimfix is a tool to help you upgrade from Nimrod (<= version 0.9.6) to
|
||||||
Nim (=> version 0.10.0).
|
Nim (=> version 0.10.0).
|
||||||
|
|
||||||
|
|
@ -48,7 +48,7 @@ Options:
|
||||||
--wholeProject overwrite every processed file.
|
--wholeProject overwrite every processed file.
|
||||||
--checkExtern:on|off style check also extern names
|
--checkExtern:on|off style check also extern names
|
||||||
--styleCheck:on|off|auto performs style checking for identifiers
|
--styleCheck:on|off|auto performs style checking for identifiers
|
||||||
and suggests an alternative spelling;
|
and suggests an alternative spelling;
|
||||||
'auto' corrects the spelling.
|
'auto' corrects the spelling.
|
||||||
|
|
||||||
In addition, all command line options of Nim are supported.
|
In addition, all command line options of Nim are supported.
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,11 @@
|
||||||
|
|
||||||
|
|
||||||
Nimgrep is a command line tool for search&replace tasks. It can search for
|
Nimgrep is a command line tool for search&replace tasks. It can search for
|
||||||
regex or peg patterns and can search whole directories at once. User
|
regex or peg patterns and can search whole directories at once. User
|
||||||
confirmation for every single replace operation can be requested.
|
confirmation for every single replace operation can be requested.
|
||||||
|
|
||||||
Nimgrep has particularly good support for Nim's
|
Nimgrep has particularly good support for Nim's
|
||||||
eccentric *style insensitivity*. Apart from that it is a generic text
|
eccentric *style insensitivity*. Apart from that it is a generic text
|
||||||
manipulation tool.
|
manipulation tool.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -34,10 +34,10 @@ Options:
|
||||||
--find, -f find the pattern (default)
|
--find, -f find the pattern (default)
|
||||||
--replace, -r replace the pattern
|
--replace, -r replace the pattern
|
||||||
--peg pattern is a peg
|
--peg pattern is a peg
|
||||||
--re pattern is a regular expression (default); extended
|
--re pattern is a regular expression (default); extended
|
||||||
syntax for the regular expression is always turned on
|
syntax for the regular expression is always turned on
|
||||||
--recursive process directories recursively
|
--recursive process directories recursively
|
||||||
--confirm confirm each occurrence/replacement; there is a chance
|
--confirm confirm each occurrence/replacement; there is a chance
|
||||||
to abort any time without touching the file
|
to abort any time without touching the file
|
||||||
--stdin read pattern from stdin (to avoid the shell's confusing
|
--stdin read pattern from stdin (to avoid the shell's confusing
|
||||||
quoting rules)
|
quoting rules)
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,12 @@ Introduction
|
||||||
============
|
============
|
||||||
|
|
||||||
niminst is a tool to generate an installer for a Nim program. Currently
|
niminst is a tool to generate an installer for a Nim program. Currently
|
||||||
it can create an installer for Windows
|
it can create an installer for Windows
|
||||||
via `Inno Setup <http://www.jrsoftware.org/isinfo.php>`_ as well as
|
via `Inno Setup <http://www.jrsoftware.org/isinfo.php>`_ as well as
|
||||||
installation/deinstallation scripts for UNIX. Later versions will support
|
installation/deinstallation scripts for UNIX. Later versions will support
|
||||||
Linux' package management systems.
|
Linux' package management systems.
|
||||||
|
|
||||||
niminst works by reading a configuration file that contains all the
|
niminst works by reading a configuration file that contains all the
|
||||||
information that it needs to generate an installer for the different operating
|
information that it needs to generate an installer for the different operating
|
||||||
systems.
|
systems.
|
||||||
|
|
||||||
|
|
@ -24,7 +24,7 @@ systems.
|
||||||
Configuration file
|
Configuration file
|
||||||
==================
|
==================
|
||||||
|
|
||||||
niminst uses the Nim `parsecfg <parsecfg.html>`_ module to parse the
|
niminst uses the Nim `parsecfg <parsecfg.html>`_ module to parse the
|
||||||
configuration file. Here's an example of how the syntax looks like:
|
configuration file. Here's an example of how the syntax looks like:
|
||||||
|
|
||||||
.. include:: doc/mytest.cfg
|
.. include:: doc/mytest.cfg
|
||||||
|
|
@ -54,10 +54,10 @@ Key description
|
||||||
``OS`` the OSes to generate C code for; for example:
|
``OS`` the OSes to generate C code for; for example:
|
||||||
``"windows;linux;macosx"``
|
``"windows;linux;macosx"``
|
||||||
``CPU`` the CPUs to generate C code for; for example:
|
``CPU`` the CPUs to generate C code for; for example:
|
||||||
``"i386;amd64;powerpc"``
|
``"i386;amd64;powerpc"``
|
||||||
``Authors`` the project's authors
|
``Authors`` the project's authors
|
||||||
``Description`` the project's description
|
``Description`` the project's description
|
||||||
``App`` the application's type: "Console" or "GUI". If
|
``App`` the application's type: "Console" or "GUI". If
|
||||||
"Console", niminst generates a special batch file
|
"Console", niminst generates a special batch file
|
||||||
for Windows to open up the command line shell.
|
for Windows to open up the command line shell.
|
||||||
``License`` the filename of the application's license
|
``License`` the filename of the application's license
|
||||||
|
|
@ -69,7 +69,7 @@ Key description
|
||||||
|
|
||||||
Many sections support the ``files`` key. Listed filenames
|
Many sections support the ``files`` key. Listed filenames
|
||||||
can be separated by semicolon or the ``files`` key can be repeated. Wildcards
|
can be separated by semicolon or the ``files`` key can be repeated. Wildcards
|
||||||
in filenames are supported. If it is a directory name, all files in the
|
in filenames are supported. If it is a directory name, all files in the
|
||||||
directory are used::
|
directory are used::
|
||||||
|
|
||||||
[Config]
|
[Config]
|
||||||
|
|
@ -87,35 +87,35 @@ will be installed into the OS's configuration directory.
|
||||||
Documentation section
|
Documentation section
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
The ``documentation`` section supports the ``files`` key.
|
The ``documentation`` section supports the ``files`` key.
|
||||||
Listed files will be installed into the OS's native documentation directory
|
Listed files will be installed into the OS's native documentation directory
|
||||||
(which might be ``$appdir/doc``).
|
(which might be ``$appdir/doc``).
|
||||||
|
|
||||||
There is a ``start`` key which determines whether the Windows installer
|
There is a ``start`` key which determines whether the Windows installer
|
||||||
generates a link to e.g. the ``index.html`` of your documentation.
|
generates a link to e.g. the ``index.html`` of your documentation.
|
||||||
|
|
||||||
|
|
||||||
Other section
|
Other section
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
The ``other`` section currently only supports the ``files`` key.
|
The ``other`` section currently only supports the ``files`` key.
|
||||||
Listed files will be installed into the application installation directory
|
Listed files will be installed into the application installation directory
|
||||||
(``$appdir``).
|
(``$appdir``).
|
||||||
|
|
||||||
|
|
||||||
Lib section
|
Lib section
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
The ``lib`` section currently only supports the ``files`` key.
|
The ``lib`` section currently only supports the ``files`` key.
|
||||||
Listed files will be installed into the OS's native library directory
|
Listed files will be installed into the OS's native library directory
|
||||||
(which might be ``$appdir/lib``).
|
(which might be ``$appdir/lib``).
|
||||||
|
|
||||||
|
|
||||||
Windows section
|
Windows section
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
The ``windows`` section supports the ``files`` key for Windows specific files.
|
The ``windows`` section supports the ``files`` key for Windows specific files.
|
||||||
Listed files will be installed into the application installation directory
|
Listed files will be installed into the application installation directory
|
||||||
(``$appdir``).
|
(``$appdir``).
|
||||||
|
|
||||||
Other possible options are:
|
Other possible options are:
|
||||||
|
|
@ -133,8 +133,8 @@ Key description
|
||||||
UnixBin section
|
UnixBin section
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
The ``UnixBin`` section currently only supports the ``files`` key.
|
The ``UnixBin`` section currently only supports the ``files`` key.
|
||||||
Listed files will be installed into the OS's native bin directory
|
Listed files will be installed into the OS's native bin directory
|
||||||
(e.g. ``/usr/local/bin``). The exact location depends on the
|
(e.g. ``/usr/local/bin``). The exact location depends on the
|
||||||
installation path the user specifies when running the ``install.sh`` script.
|
installation path the user specifies when running the ``install.sh`` script.
|
||||||
|
|
||||||
|
|
@ -150,7 +150,7 @@ Key description
|
||||||
``InstallScript`` boolean flag whether an installation shell script
|
``InstallScript`` boolean flag whether an installation shell script
|
||||||
should be generated. Example: ``InstallScript: "Yes"``
|
should be generated. Example: ``InstallScript: "Yes"``
|
||||||
``UninstallScript`` boolean flag whether a deinstallation shell script
|
``UninstallScript`` boolean flag whether a deinstallation shell script
|
||||||
should be generated.
|
should be generated.
|
||||||
Example: ``UninstallScript: "Yes"``
|
Example: ``UninstallScript: "Yes"``
|
||||||
==================== =======================================================
|
==================== =======================================================
|
||||||
|
|
||||||
|
|
@ -163,7 +163,7 @@ Possible options are:
|
||||||
==================== =======================================================
|
==================== =======================================================
|
||||||
Key description
|
Key description
|
||||||
==================== =======================================================
|
==================== =======================================================
|
||||||
``path`` Path to Inno Setup.
|
``path`` Path to Inno Setup.
|
||||||
Example: ``path = r"c:\inno setup 5\iscc.exe"``
|
Example: ``path = r"c:\inno setup 5\iscc.exe"``
|
||||||
``flags`` Flags to pass to Inno Setup.
|
``flags`` Flags to pass to Inno Setup.
|
||||||
Example: ``flags = "/Q"``
|
Example: ``flags = "/Q"``
|
||||||
|
|
@ -178,7 +178,7 @@ Possible options are:
|
||||||
==================== =======================================================
|
==================== =======================================================
|
||||||
Key description
|
Key description
|
||||||
==================== =======================================================
|
==================== =======================================================
|
||||||
``path`` Path to the C compiler.
|
``path`` Path to the C compiler.
|
||||||
``flags`` Flags to pass to the C Compiler.
|
``flags`` Flags to pass to the C Compiler.
|
||||||
Example: ``flags = "-w"``
|
Example: ``flags = "-w"``
|
||||||
==================== =======================================================
|
==================== =======================================================
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ notation meaning
|
||||||
order, to the text ahead, until one of them succeeds and
|
order, to the text ahead, until one of them succeeds and
|
||||||
possibly consumes some text. Indicate success if one of
|
possibly consumes some text. Indicate success if one of
|
||||||
expressions succeeded. Otherwise do not consume any text
|
expressions succeeded. Otherwise do not consume any text
|
||||||
and indicate failure.
|
and indicate failure.
|
||||||
``A ... Z`` Sequence: Apply expressions `A`, ..., `Z`, in this order,
|
``A ... Z`` Sequence: Apply expressions `A`, ..., `Z`, in this order,
|
||||||
to consume consecutive portions of the text ahead, as long
|
to consume consecutive portions of the text ahead, as long
|
||||||
as they succeed. Indicate success if all succeeded.
|
as they succeed. Indicate success if all succeeded.
|
||||||
|
|
@ -27,11 +27,11 @@ notation meaning
|
||||||
``{E}`` Capture: Apply expression `E` and store the substring
|
``{E}`` Capture: Apply expression `E` and store the substring
|
||||||
that matched `E` into a *capture* that can be accessed
|
that matched `E` into a *capture* that can be accessed
|
||||||
after the matching process.
|
after the matching process.
|
||||||
``$i`` Back reference to the ``i``th capture. ``i`` counts from 1.
|
``$i`` Back reference to the ``i``th capture. ``i`` counts from 1.
|
||||||
``$`` Anchor: Matches at the end of the input. No character
|
``$`` Anchor: Matches at the end of the input. No character
|
||||||
is consumed. Same as ``!.``.
|
is consumed. Same as ``!.``.
|
||||||
``^`` Anchor: Matches at the start of the input. No character
|
``^`` Anchor: Matches at the start of the input. No character
|
||||||
is consumed.
|
is consumed.
|
||||||
``&E`` And predicate: Indicate success if expression `E` matches
|
``&E`` And predicate: Indicate success if expression `E` matches
|
||||||
the text ahead; otherwise indicate failure. Do not consume
|
the text ahead; otherwise indicate failure. Do not consume
|
||||||
any text.
|
any text.
|
||||||
|
|
@ -41,15 +41,15 @@ notation meaning
|
||||||
``E+`` One or more: Apply expression `E` repeatedly to match
|
``E+`` One or more: Apply expression `E` repeatedly to match
|
||||||
the text ahead, as long as it succeeds. Consume the matched
|
the text ahead, as long as it succeeds. Consume the matched
|
||||||
text (if any) and indicate success if there was at least
|
text (if any) and indicate success if there was at least
|
||||||
one match. Otherwise indicate failure.
|
one match. Otherwise indicate failure.
|
||||||
``E*`` Zero or more: Apply expression `E` repeatedly to match
|
``E*`` Zero or more: Apply expression `E` repeatedly to match
|
||||||
the text ahead, as long as it succeeds. Consume the matched
|
the text ahead, as long as it succeeds. Consume the matched
|
||||||
text (if any). Always indicate success.
|
text (if any). Always indicate success.
|
||||||
``E?`` Zero or one: If expression `E` matches the text ahead,
|
``E?`` Zero or one: If expression `E` matches the text ahead,
|
||||||
consume it. Always indicate success.
|
consume it. Always indicate success.
|
||||||
``[s]`` Character class: If the character ahead appears in the
|
``[s]`` Character class: If the character ahead appears in the
|
||||||
string `s`, consume it and indicate success. Otherwise
|
string `s`, consume it and indicate success. Otherwise
|
||||||
indicate failure.
|
indicate failure.
|
||||||
``[a-b]`` Character range: If the character ahead is one from the
|
``[a-b]`` Character range: If the character ahead is one from the
|
||||||
range `a` through `b`, consume it and indicate success.
|
range `a` through `b`, consume it and indicate success.
|
||||||
Otherwise indicate failure.
|
Otherwise indicate failure.
|
||||||
|
|
@ -70,7 +70,7 @@ notation meaning
|
||||||
failure.
|
failure.
|
||||||
``@E`` Search: Shorthand for ``(!E .)* E``. (Search loop for the
|
``@E`` Search: Shorthand for ``(!E .)* E``. (Search loop for the
|
||||||
pattern `E`.)
|
pattern `E`.)
|
||||||
``{@} E`` Captured Search: Shorthand for ``{(!E .)*} E``. (Search
|
``{@} E`` Captured Search: Shorthand for ``{(!E .)*} E``. (Search
|
||||||
loop for the pattern `E`.) Everything until and exluding
|
loop for the pattern `E`.) Everything until and exluding
|
||||||
`E` is captured.
|
`E` is captured.
|
||||||
``@@ E`` Same as ``{@} E``.
|
``@@ E`` Same as ``{@} E``.
|
||||||
|
|
@ -79,7 +79,7 @@ notation meaning
|
||||||
matching engine.**
|
matching engine.**
|
||||||
``\identifier`` Built-in macro for a longer expression.
|
``\identifier`` Built-in macro for a longer expression.
|
||||||
``\ddd`` Character with decimal code *ddd*.
|
``\ddd`` Character with decimal code *ddd*.
|
||||||
``\"``, etc Literal ``"``, etc.
|
``\"``, etc Literal ``"``, etc.
|
||||||
=============== ============================================================
|
=============== ============================================================
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -101,9 +101,9 @@ macro meaning
|
||||||
``\n`` any newline combination: ``\10 / \13\10 / \13``
|
``\n`` any newline combination: ``\10 / \13\10 / \13``
|
||||||
``\i`` ignore case for matching; use this at the start of the PEG
|
``\i`` ignore case for matching; use this at the start of the PEG
|
||||||
``\y`` ignore style for matching; use this at the start of the PEG
|
``\y`` ignore style for matching; use this at the start of the PEG
|
||||||
``\skip`` pat skip pattern *pat* before trying to match other tokens;
|
``\skip`` pat skip pattern *pat* before trying to match other tokens;
|
||||||
this is useful for whitespace skipping, for example:
|
this is useful for whitespace skipping, for example:
|
||||||
``\skip(\s*) {\ident} ':' {\ident}`` matches key value
|
``\skip(\s*) {\ident} ':' {\ident}`` matches key value
|
||||||
pairs ignoring whitespace around the ``':'``.
|
pairs ignoring whitespace around the ``':'``.
|
||||||
``\ident`` a standard ASCII identifier: ``[a-zA-Z_][a-zA-Z_0-9]*``
|
``\ident`` a standard ASCII identifier: ``[a-zA-Z_][a-zA-Z_0-9]*``
|
||||||
``\letter`` any Unicode letter
|
``\letter`` any Unicode letter
|
||||||
|
|
@ -133,42 +133,42 @@ The PEG parser implements this grammar (written in PEG syntax)::
|
||||||
# Example grammar of PEG in PEG syntax.
|
# Example grammar of PEG in PEG syntax.
|
||||||
# Comments start with '#'.
|
# Comments start with '#'.
|
||||||
# First symbol is the start symbol.
|
# First symbol is the start symbol.
|
||||||
|
|
||||||
grammar <- rule* / expr
|
grammar <- rule* / expr
|
||||||
|
|
||||||
identifier <- [A-Za-z][A-Za-z0-9_]*
|
identifier <- [A-Za-z][A-Za-z0-9_]*
|
||||||
charsetchar <- "\\" . / [^\]]
|
charsetchar <- "\\" . / [^\]]
|
||||||
charset <- "[" "^"? (charsetchar ("-" charsetchar)?)+ "]"
|
charset <- "[" "^"? (charsetchar ("-" charsetchar)?)+ "]"
|
||||||
stringlit <- identifier? ("\"" ("\\" . / [^"])* "\"" /
|
stringlit <- identifier? ("\"" ("\\" . / [^"])* "\"" /
|
||||||
"'" ("\\" . / [^'])* "'")
|
"'" ("\\" . / [^'])* "'")
|
||||||
builtin <- "\\" identifier / [^\13\10]
|
builtin <- "\\" identifier / [^\13\10]
|
||||||
|
|
||||||
comment <- '#' @ \n
|
comment <- '#' @ \n
|
||||||
ig <- (\s / comment)* # things to ignore
|
ig <- (\s / comment)* # things to ignore
|
||||||
|
|
||||||
rule <- identifier \s* "<-" expr ig
|
rule <- identifier \s* "<-" expr ig
|
||||||
identNoArrow <- identifier !(\s* "<-")
|
identNoArrow <- identifier !(\s* "<-")
|
||||||
prefixOpr <- ig '&' / ig '!' / ig '@' / ig '{@}' / ig '@@'
|
prefixOpr <- ig '&' / ig '!' / ig '@' / ig '{@}' / ig '@@'
|
||||||
literal <- ig identifier? '$' [0-9]+ / '$' / '^' /
|
literal <- ig identifier? '$' [0-9]+ / '$' / '^' /
|
||||||
ig identNoArrow /
|
ig identNoArrow /
|
||||||
ig charset /
|
ig charset /
|
||||||
ig stringlit /
|
ig stringlit /
|
||||||
ig builtin /
|
ig builtin /
|
||||||
ig '.' /
|
ig '.' /
|
||||||
ig '_' /
|
ig '_' /
|
||||||
(ig "(" expr ig ")")
|
(ig "(" expr ig ")")
|
||||||
postfixOpr <- ig '?' / ig '*' / ig '+'
|
postfixOpr <- ig '?' / ig '*' / ig '+'
|
||||||
primary <- prefixOpr* (literal postfixOpr*)
|
primary <- prefixOpr* (literal postfixOpr*)
|
||||||
|
|
||||||
# Concatenation has higher priority than choice:
|
# Concatenation has higher priority than choice:
|
||||||
# ``a b / c`` means ``(a b) / c``
|
# ``a b / c`` means ``(a b) / c``
|
||||||
|
|
||||||
seqExpr <- primary+
|
seqExpr <- primary+
|
||||||
expr <- seqExpr (ig "/" expr)*
|
expr <- seqExpr (ig "/" expr)*
|
||||||
|
|
||||||
|
|
||||||
**Note**: As a special syntactic extension if the whole PEG is only a single
|
**Note**: As a special syntactic extension if the whole PEG is only a single
|
||||||
expression, identifiers are not interpreted as non-terminals, but are
|
expression, identifiers are not interpreted as non-terminals, but are
|
||||||
interpreted as verbatim string:
|
interpreted as verbatim string:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
@ -185,7 +185,7 @@ Check if `s` matches Nim's "while" keyword:
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
s =~ peg" y'while'"
|
s =~ peg" y'while'"
|
||||||
|
|
||||||
Exchange (key, val)-pairs:
|
Exchange (key, val)-pairs:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
"key: val; key2: val2".replacef(peg"{\ident} \s* ':' \s* {\ident}", "$2: $1")
|
"key: val; key2: val2".replacef(peg"{\ident} \s* ':' \s* {\ident}", "$2: $1")
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
============================
|
============================
|
||||||
Nim's documenation system
|
Nim's documenation system
|
||||||
============================
|
============================
|
||||||
|
|
||||||
This folder contains Nim's documentation. The documentation
|
This folder contains Nim's documentation. The documentation
|
||||||
is written in a format called *reStructuredText*, a markup language that reads
|
is written in a format called *reStructuredText*, a markup language that reads
|
||||||
like ASCII and can be converted to HTML automatically!
|
like ASCII and can be converted to HTML automatically!
|
||||||
|
|
|
||||||
|
|
@ -175,17 +175,17 @@ for themselves. For example:
|
||||||
example meaning
|
example meaning
|
||||||
============== ============================================================
|
============== ============================================================
|
||||||
``\040`` is another way of writing a space
|
``\040`` is another way of writing a space
|
||||||
``\40`` is the same, provided there are fewer than 40 previous
|
``\40`` is the same, provided there are fewer than 40 previous
|
||||||
capturing subpatterns
|
capturing subpatterns
|
||||||
``\7`` is always a back reference
|
``\7`` is always a back reference
|
||||||
``\11`` might be a back reference, or another way of writing a tab
|
``\11`` might be a back reference, or another way of writing a tab
|
||||||
``\011`` is always a tab
|
``\011`` is always a tab
|
||||||
``\0113`` is a tab followed by the character "3"
|
``\0113`` is a tab followed by the character "3"
|
||||||
``\113`` might be a back reference, otherwise the character with
|
``\113`` might be a back reference, otherwise the character with
|
||||||
octal code 113
|
octal code 113
|
||||||
``\377`` might be a back reference, otherwise the byte consisting
|
``\377`` might be a back reference, otherwise the byte consisting
|
||||||
entirely of 1 bits
|
entirely of 1 bits
|
||||||
``\81`` is either a back reference, or a binary zero followed by
|
``\81`` is either a back reference, or a binary zero followed by
|
||||||
the two characters "8" and "1"
|
the two characters "8" and "1"
|
||||||
============== ============================================================
|
============== ============================================================
|
||||||
|
|
||||||
|
|
@ -240,7 +240,7 @@ even when Unicode character property support is available.
|
||||||
|
|
||||||
Simple assertions
|
Simple assertions
|
||||||
-----------------
|
-----------------
|
||||||
The fourth use of backslash is for certain `simple assertions`:idx:. An
|
The fourth use of backslash is for certain `simple assertions`:idx:. An
|
||||||
assertion specifies a condition that has to be met at a particular point in
|
assertion specifies a condition that has to be met at a particular point in
|
||||||
a match, without consuming any characters from the subject string. The use of
|
a match, without consuming any characters from the subject string. The use of
|
||||||
subpatterns for more complicated assertions is described below. The
|
subpatterns for more complicated assertions is described below. The
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ basetype can only be an ordinal type of a certain size, namely:
|
||||||
* ``uint8``/``byte``-``uint16``
|
* ``uint8``/``byte``-``uint16``
|
||||||
* ``char``
|
* ``char``
|
||||||
* ``enum``
|
* ``enum``
|
||||||
or equivalent. The reason is that sets are implemented as high
|
or equivalent. The reason is that sets are implemented as high
|
||||||
performance bit vectors. Attempting to declare a set with a larger type will
|
performance bit vectors. Attempting to declare a set with a larger type will
|
||||||
result in an error:
|
result in an error:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
Parallel & Spawn
|
Parallel & Spawn
|
||||||
==========================================================
|
==========================================================
|
||||||
|
|
||||||
Nim has two flavors of parallelism:
|
Nim has two flavors of parallelism:
|
||||||
1) `Structured`:idx parallelism via the ``parallel`` statement.
|
1) `Structured`:idx parallelism via the ``parallel`` statement.
|
||||||
2) `Unstructured`:idx: parallelism via the standalone ``spawn`` statement.
|
2) `Unstructured`:idx: parallelism via the standalone ``spawn`` statement.
|
||||||
|
|
||||||
|
|
@ -30,7 +30,7 @@ variables at the same time:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
import threadpool, ...
|
import threadpool, ...
|
||||||
|
|
||||||
# wait until 2 out of 3 servers received the update:
|
# wait until 2 out of 3 servers received the update:
|
||||||
proc main =
|
proc main =
|
||||||
var responses = newSeq[FlowVarBase](3)
|
var responses = newSeq[FlowVarBase](3)
|
||||||
|
|
@ -89,7 +89,7 @@ restrictions / changes:
|
||||||
the ``parallel`` section. This is called the *immutability check*. Currently
|
the ``parallel`` section. This is called the *immutability check*. Currently
|
||||||
it is not specified what exactly "complex location" means. We need to make
|
it is not specified what exactly "complex location" means. We need to make
|
||||||
this an optimization!
|
this an optimization!
|
||||||
* Every array access has to be provably within bounds. This is called
|
* Every array access has to be provably within bounds. This is called
|
||||||
the *bounds check*.
|
the *bounds check*.
|
||||||
* Slices are optimized so that no copy is performed. This optimization is not
|
* Slices are optimized so that no copy is performed. This optimization is not
|
||||||
yet performed for ordinary slices outside of a ``parallel`` section. Slices
|
yet performed for ordinary slices outside of a ``parallel`` section. Slices
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
Substitution Expressions (subex)
|
Substitution Expressions (subex)
|
||||||
================================
|
================================
|
||||||
|
|
||||||
A *subex* (*Substitution Expression*) represents an advanted string
|
A *subex* (*Substitution Expression*) represents an advanted string
|
||||||
substitution. In contrast to a `regex`:idx: which deals with string analysis, a
|
substitution. In contrast to a `regex`:idx: which deals with string analysis, a
|
||||||
*subex* deals with string synthesis.
|
*subex* deals with string synthesis.
|
||||||
|
|
||||||
Thanks to its conditional construct ``$[0|1|2|else]`` it supports
|
Thanks to its conditional construct ``$[0|1|2|else]`` it supports
|
||||||
`internationalization`:idx: of format string literals quite well.
|
`internationalization`:idx: of format string literals quite well.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -37,7 +37,7 @@ Notation meaning
|
||||||
``$[zero|one|def]1`` use ``X = parseInt(arg[1])`` to determine which
|
``$[zero|one|def]1`` use ``X = parseInt(arg[1])`` to determine which
|
||||||
branch to use. If ``X == 0`` the 'zero' branch is
|
branch to use. If ``X == 0`` the 'zero' branch is
|
||||||
selected, if ``X == 1`` the 'one' branch is
|
selected, if ``X == 1`` the 'one' branch is
|
||||||
selected, etc. Otherwise the 'def' branch is
|
selected, etc. Otherwise the 'def' branch is
|
||||||
selected. ``$x`` is interpreted in branches too.
|
selected. ``$x`` is interpreted in branches too.
|
||||||
If a branch needs to contain ``|``, ``]`` put
|
If a branch needs to contain ``|``, ``]`` put
|
||||||
them in single quotes. To produce a verbatim single
|
them in single quotes. To produce a verbatim single
|
||||||
|
|
@ -50,12 +50,12 @@ Examples
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
||||||
subex"$1($', '{2..})" % ["f", "a", "b", "c"] == "f(a, b, c)"
|
subex"$1($', '{2..})" % ["f", "a", "b", "c"] == "f(a, b, c)"
|
||||||
|
|
||||||
subex"$1 $[files|file|files]{1} copied" % ["1"] == "1 file copied"
|
subex"$1 $[files|file|files]{1} copied" % ["1"] == "1 file copied"
|
||||||
|
|
||||||
subex"$['''|'|''''|']']#" % "0" == "'|"
|
subex"$['''|'|''''|']']#" % "0" == "'|"
|
||||||
|
|
||||||
subex("type\n TEnum = enum\n $', '40c'\n '{..}") % [
|
subex("type\n TEnum = enum\n $', '40c'\n '{..}") % [
|
||||||
"fieldNameA", "fieldNameB", "fieldNameC", "fieldNameD"]
|
"fieldNameA", "fieldNameB", "fieldNameC", "fieldNameD"]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
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.
|
type does not matter.
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
var
|
var
|
||||||
fruits: seq[string] # reference to a sequence of strings that is initialized with 'nil'
|
fruits: seq[string] # reference to a sequence of strings that is initialized with 'nil'
|
||||||
capitals: array[3, string] # array of strings with a fixed size
|
capitals: array[3, string] # array of strings with a fixed size
|
||||||
|
|
||||||
fruits = @[] # creates an empty sequence on the heap that will be referenced by 'fruits'
|
fruits = @[] # creates an empty sequence on the heap that will be referenced by 'fruits'
|
||||||
|
|
||||||
capitals = ["New York", "London", "Berlin"] # array 'capitals' allows only assignment of three elements
|
capitals = ["New York", "London", "Berlin"] # array 'capitals' allows only assignment of three elements
|
||||||
fruits.add("Banana") # sequence 'fruits' is dynamically expandable during runtime
|
fruits.add("Banana") # sequence 'fruits' is dynamically expandable during runtime
|
||||||
fruits.add("Mango")
|
fruits.add("Mango")
|
||||||
|
|
||||||
proc openArraySize(oa: openArray[string]): int =
|
proc openArraySize(oa: openArray[string]): int =
|
||||||
oa.len
|
oa.len
|
||||||
|
|
||||||
assert openArraySize(fruits) == 2 # procedure accepts a sequence as parameter
|
assert openArraySize(fruits) == 2 # procedure accepts a sequence as parameter
|
||||||
assert openArraySize(capitals) == 3 # but also an array type
|
assert openArraySize(capitals) == 3 # but also an array type
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,9 @@ template any(container, cond: expr): expr {.immediate.} =
|
||||||
break
|
break
|
||||||
result
|
result
|
||||||
|
|
||||||
if all("mystring", {'a'..'z'}.contains) and any("myohmy", 'y'.`==`):
|
if all("mystring", {'a'..'z'}.contains) and any("myohmy", 'y'.`==`):
|
||||||
echo "works"
|
echo "works"
|
||||||
else:
|
else:
|
||||||
echo "does not work"
|
echo "does not work"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,13 @@ type
|
||||||
TDimension2d {.final, header: irr, importc: "dimension2d".} = object
|
TDimension2d {.final, header: irr, importc: "dimension2d".} = object
|
||||||
Tvector3df {.final, header: irr, importc: "vector3df".} = object
|
Tvector3df {.final, header: irr, importc: "vector3df".} = object
|
||||||
TColor {.final, header: irr, importc: "SColor".} = object
|
TColor {.final, header: irr, importc: "SColor".} = object
|
||||||
|
|
||||||
TIrrlichtDevice {.final, header: irr, importc: "IrrlichtDevice".} = object
|
TIrrlichtDevice {.final, header: irr, importc: "IrrlichtDevice".} = object
|
||||||
TIVideoDriver {.final, header: irr, importc: "IVideoDriver".} = object
|
TIVideoDriver {.final, header: irr, importc: "IVideoDriver".} = object
|
||||||
TISceneManager {.final, header: irr, importc: "ISceneManager".} = object
|
TISceneManager {.final, header: irr, importc: "ISceneManager".} = object
|
||||||
TIGUIEnvironment {.final, header: irr, importc: "IGUIEnvironment".} = object
|
TIGUIEnvironment {.final, header: irr, importc: "IGUIEnvironment".} = object
|
||||||
TIAnimatedMesh {.final, header: irr, importc: "IAnimatedMesh".} = object
|
TIAnimatedMesh {.final, header: irr, importc: "IAnimatedMesh".} = object
|
||||||
TIAnimatedMeshSceneNode {.final, header: irr,
|
TIAnimatedMeshSceneNode {.final, header: irr,
|
||||||
importc: "IAnimatedMeshSceneNode".} = object
|
importc: "IAnimatedMeshSceneNode".} = object
|
||||||
TITexture {.final, header: irr, importc: "ITexture".} = object
|
TITexture {.final, header: irr, importc: "ITexture".} = object
|
||||||
|
|
||||||
|
|
@ -100,7 +100,7 @@ var node = smgr.addAnimatedMeshSceneNode(mesh)
|
||||||
if node != nil:
|
if node != nil:
|
||||||
#node->setMaterialFlag(EMF_LIGHTING, false)
|
#node->setMaterialFlag(EMF_LIGHTING, false)
|
||||||
#node->setMD2Animation(scene::EMAT_STAND)
|
#node->setMD2Animation(scene::EMAT_STAND)
|
||||||
node.setMaterialTexture(0,
|
node.setMaterialTexture(0,
|
||||||
driver.getTexture(
|
driver.getTexture(
|
||||||
"/home/andreas/download/irrlicht-1.7.2/media/media/sydney.bmp"))
|
"/home/andreas/download/irrlicht-1.7.2/media/media/sydney.bmp"))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
This example demonstrates how to use Nim with Lazarus. The GUI is generated
|
This example demonstrates how to use Nim with Lazarus. The GUI is generated
|
||||||
with Lazarus, while the "backend" is written in Nim. To compile the example,
|
with Lazarus, while the "backend" is written in Nim. To compile the example,
|
||||||
use this command:
|
use this command:
|
||||||
|
|
||||||
nim c --app:gui --no_main --no_linking backend.nim
|
nim c --app:gui --no_main --no_linking backend.nim
|
||||||
|
|
||||||
Open the ``nimlaz.lpi`` file in Lazarus and run the program.
|
Open the ``nimlaz.lpi`` file in Lazarus and run the program.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# Backend for the different user interfaces.
|
# Backend for the different user interfaces.
|
||||||
|
|
||||||
proc myAdd*(x, y: int): int {.cdecl, exportc.} =
|
proc myAdd*(x, y: int): int {.cdecl, exportc.} =
|
||||||
result = x + y
|
result = x + y
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
The cross platform calculator illustrates how to use Nim to create a backend
|
The cross platform calculator illustrates how to use Nim to create a backend
|
||||||
called by different native user interfaces.
|
called by different native user interfaces.
|
||||||
|
|
||||||
Since the purpose of the example is to show how the cross platform code
|
Since the purpose of the example is to show how the cross platform code
|
||||||
interacts with Nimrod the actual backend code is just a simple addition proc.
|
interacts with Nimrod the actual backend code is just a simple addition proc.
|
||||||
By keeping your program logic in Nim you can easily reuse it in different
|
By keeping your program logic in Nim you can easily reuse it in different
|
||||||
platforms.
|
platforms.
|
||||||
|
|
||||||
To avoid duplication of code, the backend code lies in a separate directory and
|
To avoid duplication of code, the backend code lies in a separate directory and
|
||||||
each platform compiles it with a different custom build process, usually
|
each platform compiles it with a different custom build process, usually
|
||||||
generating C code in a temporary build directory.
|
generating C code in a temporary build directory.
|
||||||
|
|
||||||
For a more elaborate and useful example see the cross_todo example.
|
For a more elaborate and useful example see the cross_todo example.
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
This directory contains the nim backend code for the todo cross platform
|
This directory contains the nim backend code for the todo cross platform
|
||||||
example.
|
example.
|
||||||
|
|
||||||
Unlike the cross platform calculator example, this backend features more code,
|
Unlike the cross platform calculator example, this backend features more code,
|
||||||
using an sqlite database for storage. Also a basic test module is provided, not
|
using an sqlite database for storage. Also a basic test module is provided, not
|
||||||
to be included with the final program but to test the exported functionality.
|
to be included with the final program but to test the exported functionality.
|
||||||
The test is not embedded directly in the backend.nim file to avoid being able
|
The test is not embedded directly in the backend.nim file to avoid being able
|
||||||
to access internal data types and procs not exported and replicate the
|
to access internal data types and procs not exported and replicate the
|
||||||
environment of client code.
|
environment of client code.
|
||||||
|
|
||||||
In a bigger project with several people you could run `nim doc backend.nim`
|
In a bigger project with several people you could run `nim doc backend.nim`
|
||||||
(or use the doc2 command for a whole project) and provide the generated html
|
(or use the doc2 command for a whole project) and provide the generated html
|
||||||
documentation to another programer for her to implement an interface without
|
documentation to another programer for her to implement an interface without
|
||||||
having to look at the source code.
|
having to look at the source code.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
The cross platform todo illustrates how to use Nim to create a backend
|
The cross platform todo illustrates how to use Nim to create a backend
|
||||||
called by different native user interfaces.
|
called by different native user interfaces.
|
||||||
|
|
||||||
This example builds on the knowledge learned from the cross_calculator example.
|
This example builds on the knowledge learned from the cross_calculator example.
|
||||||
Check it out first to learn how to set up Nim on different platforms.
|
Check it out first to learn how to set up Nim on different platforms.
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import
|
import
|
||||||
libcurl
|
libcurl
|
||||||
|
|
||||||
var hCurl = easy_init()
|
var hCurl = easy_init()
|
||||||
if hCurl != nil:
|
if hCurl != nil:
|
||||||
discard easy_setopt(hCurl, OPT_VERBOSE, true)
|
discard easy_setopt(hCurl, OPT_VERBOSE, true)
|
||||||
discard easy_setopt(hCurl, OPT_URL, "http://nim-lang.org/")
|
discard easy_setopt(hCurl, OPT_URL, "http://nim-lang.org/")
|
||||||
discard easy_perform(hCurl)
|
discard easy_perform(hCurl)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#! stdtmpl | standard
|
#! stdtmpl | standard
|
||||||
#proc generateHTMLPage(title, currentTab, content: string,
|
#proc generateHTMLPage(title, currentTab, content: string,
|
||||||
# tabs: openArray[string]): string =
|
# tabs: openArray[string]): string =
|
||||||
# result = ""
|
# result = ""
|
||||||
<head><title>$title</title></head>
|
<head><title>$title</title></head>
|
||||||
<body>
|
<body>
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
<ul>
|
<ul>
|
||||||
#for tab in items(tabs):
|
#for tab in items(tabs):
|
||||||
#if currentTab == tab:
|
#if currentTab == tab:
|
||||||
<li><a id="selected"
|
<li><a id="selected"
|
||||||
#else:
|
#else:
|
||||||
<li><a
|
<li><a
|
||||||
#end if
|
#end if
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,11 @@
|
||||||
|
|
||||||
import os, streams, parsexml, strutils
|
import os, streams, parsexml, strutils
|
||||||
|
|
||||||
proc `=?=` (a, b: string): bool =
|
proc `=?=` (a, b: string): bool =
|
||||||
# little trick: define our own comparator that ignores case
|
# little trick: define our own comparator that ignores case
|
||||||
return cmpIgnoreCase(a, b) == 0
|
return cmpIgnoreCase(a, b) == 0
|
||||||
|
|
||||||
if paramCount() < 1:
|
if paramCount() < 1:
|
||||||
quit("Usage: htmlrefs filename[.html]")
|
quit("Usage: htmlrefs filename[.html]")
|
||||||
|
|
||||||
var links = 0 # count the number of links
|
var links = 0 # count the number of links
|
||||||
|
|
@ -21,17 +21,17 @@ next(x) # get first event
|
||||||
block mainLoop:
|
block mainLoop:
|
||||||
while true:
|
while true:
|
||||||
case x.kind
|
case x.kind
|
||||||
of xmlElementOpen:
|
of xmlElementOpen:
|
||||||
# the <a href = "xyz"> tag we are interested in always has an attribute,
|
# the <a href = "xyz"> tag we are interested in always has an attribute,
|
||||||
# thus we search for ``xmlElementOpen`` and not for ``xmlElementStart``
|
# thus we search for ``xmlElementOpen`` and not for ``xmlElementStart``
|
||||||
if x.elementName =?= "a":
|
if x.elementName =?= "a":
|
||||||
x.next()
|
x.next()
|
||||||
if x.kind == xmlAttribute:
|
if x.kind == xmlAttribute:
|
||||||
if x.attrKey =?= "href":
|
if x.attrKey =?= "href":
|
||||||
var link = x.attrValue
|
var link = x.attrValue
|
||||||
inc(links)
|
inc(links)
|
||||||
# skip until we have an ``xmlElementClose`` event
|
# skip until we have an ``xmlElementClose`` event
|
||||||
while true:
|
while true:
|
||||||
x.next()
|
x.next()
|
||||||
case x.kind
|
case x.kind
|
||||||
of xmlEof: break mainLoop
|
of xmlEof: break mainLoop
|
||||||
|
|
@ -40,14 +40,14 @@ block mainLoop:
|
||||||
x.next() # skip ``xmlElementClose``
|
x.next() # skip ``xmlElementClose``
|
||||||
# now we have the description for the ``a`` element
|
# now we have the description for the ``a`` element
|
||||||
var desc = ""
|
var desc = ""
|
||||||
while x.kind == xmlCharData:
|
while x.kind == xmlCharData:
|
||||||
desc.add(x.charData)
|
desc.add(x.charData)
|
||||||
x.next()
|
x.next()
|
||||||
echo(desc & ": " & link)
|
echo(desc & ": " & link)
|
||||||
else:
|
else:
|
||||||
x.next()
|
x.next()
|
||||||
of xmlEof: break # end of file reached
|
of xmlEof: break # end of file reached
|
||||||
of xmlError:
|
of xmlError:
|
||||||
echo(errorMsg(x))
|
echo(errorMsg(x))
|
||||||
x.next()
|
x.next()
|
||||||
else: x.next() # skip other events
|
else: x.next() # skip other events
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,11 @@ open(x, s, filename)
|
||||||
while true:
|
while true:
|
||||||
x.next()
|
x.next()
|
||||||
case x.kind
|
case x.kind
|
||||||
of xmlElementStart:
|
of xmlElementStart:
|
||||||
if cmpIgnoreCase(x.elementName, "title") == 0:
|
if cmpIgnoreCase(x.elementName, "title") == 0:
|
||||||
var title = ""
|
var title = ""
|
||||||
x.next() # skip "<title>"
|
x.next() # skip "<title>"
|
||||||
while x.kind == xmlCharData:
|
while x.kind == xmlCharData:
|
||||||
title.add(x.charData)
|
title.add(x.charData)
|
||||||
x.next()
|
x.next()
|
||||||
if x.kind == xmlElementEnd and cmpIgnoreCase(x.elementName, "title") == 0:
|
if x.kind == xmlElementEnd and cmpIgnoreCase(x.elementName, "title") == 0:
|
||||||
|
|
@ -27,7 +27,7 @@ while true:
|
||||||
quit(0) # Success!
|
quit(0) # Success!
|
||||||
else:
|
else:
|
||||||
echo(x.errorMsgExpected("/title"))
|
echo(x.errorMsgExpected("/title"))
|
||||||
|
|
||||||
of xmlEof: break # end of file reached
|
of xmlEof: break # end of file reached
|
||||||
else: discard # ignore other events
|
else: discard # ignore other events
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ proc serveFile(client: Socket, filename: string) =
|
||||||
|
|
||||||
# ------------------ CGI execution -------------------------------------------
|
# ------------------ CGI execution -------------------------------------------
|
||||||
|
|
||||||
proc executeCgi(server: var TServer, client: Socket, path, query: string,
|
proc executeCgi(server: var TServer, client: Socket, path, query: string,
|
||||||
meth: TRequestMethod) =
|
meth: TRequestMethod) =
|
||||||
var env = newStringTable(modeCaseInsensitive)
|
var env = newStringTable(modeCaseInsensitive)
|
||||||
var contentLength = -1
|
var contentLength = -1
|
||||||
|
|
@ -123,12 +123,12 @@ proc executeCgi(server: var TServer, client: Socket, path, query: string,
|
||||||
send(client, "HTTP/1.0 200 OK" & wwwNL)
|
send(client, "HTTP/1.0 200 OK" & wwwNL)
|
||||||
|
|
||||||
var process = startProcess(command=path, env=env)
|
var process = startProcess(command=path, env=env)
|
||||||
|
|
||||||
var job: TJob
|
var job: TJob
|
||||||
job.process = process
|
job.process = process
|
||||||
job.client = client
|
job.client = client
|
||||||
server.job.add(job)
|
server.job.add(job)
|
||||||
|
|
||||||
if meth == reqPost:
|
if meth == reqPost:
|
||||||
# get from client and post to CGI program:
|
# get from client and post to CGI program:
|
||||||
var buf = alloc(contentLength)
|
var buf = alloc(contentLength)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Test high level features
|
# Test high level features
|
||||||
|
|
||||||
import strutils
|
import strutils
|
||||||
|
|
||||||
echo "Give a list of numbers (separated by spaces): "
|
echo "Give a list of numbers (separated by spaces): "
|
||||||
stdin.readLine.split.map(parseInt).max.`$`.echo(" is the maximum!")
|
stdin.readLine.split.map(parseInt).max.`$`.echo(" is the maximum!")
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
os, parsecfg, strutils, streams
|
os, parsecfg, strutils, streams
|
||||||
|
|
||||||
var f = newFileStream(paramStr(1), fmRead)
|
var f = newFileStream(paramStr(1), fmRead)
|
||||||
if f != nil:
|
if f != nil:
|
||||||
var p: CfgParser
|
var p: CfgParser
|
||||||
|
|
@ -9,7 +9,7 @@ if f != nil:
|
||||||
while true:
|
while true:
|
||||||
var e = next(p)
|
var e = next(p)
|
||||||
case e.kind
|
case e.kind
|
||||||
of cfgEof:
|
of cfgEof:
|
||||||
echo("EOF!")
|
echo("EOF!")
|
||||||
break
|
break
|
||||||
of cfgSectionStart: ## a ``[section]`` has been parsed
|
of cfgSectionStart: ## a ``[section]`` has been parsed
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
In this directory you will find several examples for how to use the Nimrod
|
In this directory you will find several examples for how to use the Nimrod
|
||||||
library.
|
library.
|
||||||
|
|
||||||
Copyright (c) 2004-2012 Andreas Rumpf.
|
Copyright (c) 2004-2012 Andreas Rumpf.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
|
||||||
|
|
@ -1,52 +1,52 @@
|
||||||
# Test the SDL interface:
|
# Test the SDL interface:
|
||||||
|
|
||||||
import
|
import
|
||||||
sdl, sdl_image, colors
|
sdl, sdl_image, colors
|
||||||
|
|
||||||
var
|
var
|
||||||
screen, greeting: PSurface
|
screen, greeting: PSurface
|
||||||
r: TRect
|
r: TRect
|
||||||
event: TEvent
|
event: TEvent
|
||||||
bgColor = colChocolate.int32
|
bgColor = colChocolate.int32
|
||||||
|
|
||||||
if init(INIT_VIDEO) != 0:
|
if init(INIT_VIDEO) != 0:
|
||||||
quit "SDL failed to initialize!"
|
quit "SDL failed to initialize!"
|
||||||
|
|
||||||
screen = setVideoMode(640, 480, 16, SWSURFACE or ANYFORMAT)
|
screen = setVideoMode(640, 480, 16, SWSURFACE or ANYFORMAT)
|
||||||
if screen.isNil:
|
if screen.isNil:
|
||||||
quit($sdl.getError())
|
quit($sdl.getError())
|
||||||
|
|
||||||
greeting = imgLoad("tux.png")
|
greeting = imgLoad("tux.png")
|
||||||
if greeting.isNil:
|
if greeting.isNil:
|
||||||
echo "Failed to load tux.png"
|
echo "Failed to load tux.png"
|
||||||
else:
|
else:
|
||||||
## convert the image to alpha and free the old one
|
## convert the image to alpha and free the old one
|
||||||
var s = greeting.displayFormatAlpha()
|
var s = greeting.displayFormatAlpha()
|
||||||
swap(greeting, s)
|
swap(greeting, s)
|
||||||
s.freeSurface()
|
s.freeSurface()
|
||||||
|
|
||||||
r.x = 0
|
r.x = 0
|
||||||
r.y = 0
|
r.y = 0
|
||||||
|
|
||||||
block game_loop:
|
block game_loop:
|
||||||
while true:
|
while true:
|
||||||
|
|
||||||
while pollEvent(addr event) > 0:
|
while pollEvent(addr event) > 0:
|
||||||
case event.kind
|
case event.kind
|
||||||
of QUITEV:
|
of QUITEV:
|
||||||
break game_loop
|
break game_loop
|
||||||
of KEYDOWN:
|
of KEYDOWN:
|
||||||
if evKeyboard(addr event).keysym.sym == K_ESCAPE:
|
if evKeyboard(addr event).keysym.sym == K_ESCAPE:
|
||||||
break game_loop
|
break game_loop
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
discard fillRect(screen, nil, bgColor)
|
discard fillRect(screen, nil, bgColor)
|
||||||
discard blitSurface(greeting, nil, screen, addr r)
|
discard blitSurface(greeting, nil, screen, addr r)
|
||||||
discard flip(screen)
|
discard flip(screen)
|
||||||
|
|
||||||
greeting.freeSurface()
|
greeting.freeSurface()
|
||||||
screen.freeSurface()
|
screen.freeSurface()
|
||||||
sdl.quit()
|
sdl.quit()
|
||||||
|
|
||||||
## fowl wuz here 10/2012
|
## fowl wuz here 10/2012
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ while readRow(x):
|
||||||
for i in 0..x.row.len-1: header[i] = "Col " & $(i+1)
|
for i in 0..x.row.len-1: header[i] = "Col " & $(i+1)
|
||||||
else:
|
else:
|
||||||
# data line:
|
# data line:
|
||||||
for i in 0..x.row.len-1:
|
for i in 0..x.row.len-1:
|
||||||
push(res[i], parseFloat(x.row[i]))
|
push(res[i], parseFloat(x.row[i]))
|
||||||
x.close()
|
x.close()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
template htmlTag(tag: expr) {.immediate.} =
|
template htmlTag(tag: expr) {.immediate.} =
|
||||||
proc tag(): string = "<" & astToStr(tag) & ">"
|
proc tag(): string = "<" & astToStr(tag) & ">"
|
||||||
|
|
||||||
htmlTag(br)
|
htmlTag(br)
|
||||||
htmlTag(html)
|
htmlTag(html)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,47 +1,47 @@
|
||||||
import
|
import
|
||||||
unittest, macros
|
unittest, macros
|
||||||
|
|
||||||
var
|
var
|
||||||
a = 1
|
a = 1
|
||||||
b = 22
|
b = 22
|
||||||
c = 1
|
c = 1
|
||||||
d = 3
|
d = 3
|
||||||
|
|
||||||
suite "my suite":
|
suite "my suite":
|
||||||
setup:
|
setup:
|
||||||
echo "suite setup"
|
echo "suite setup"
|
||||||
var testVar = "from setup"
|
var testVar = "from setup"
|
||||||
|
|
||||||
teardown:
|
teardown:
|
||||||
echo "suite teardown"
|
echo "suite teardown"
|
||||||
|
|
||||||
test "first suite test":
|
test "first suite test":
|
||||||
testVar = "modified"
|
testVar = "modified"
|
||||||
echo "test var: " & testVar
|
echo "test var: " & testVar
|
||||||
check a > b
|
check a > b
|
||||||
|
|
||||||
test "second suite test":
|
test "second suite test":
|
||||||
echo "test var: " & testVar
|
echo "test var: " & testVar
|
||||||
|
|
||||||
proc foo: bool =
|
proc foo: bool =
|
||||||
echo "running foo"
|
echo "running foo"
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc err =
|
proc err =
|
||||||
raise newException(EArithmetic, "some exception")
|
raise newException(EArithmetic, "some exception")
|
||||||
|
|
||||||
test "final test":
|
test "final test":
|
||||||
echo "inside suite-less test"
|
echo "inside suite-less test"
|
||||||
|
|
||||||
check:
|
check:
|
||||||
a == c
|
a == c
|
||||||
foo()
|
foo()
|
||||||
d > 10
|
d > 10
|
||||||
|
|
||||||
test "arithmetic failure":
|
test "arithmetic failure":
|
||||||
expect(EArithmetic):
|
expect(EArithmetic):
|
||||||
err()
|
err()
|
||||||
|
|
||||||
expect(EArithmetic, ESystem):
|
expect(EArithmetic, ESystem):
|
||||||
discard foo()
|
discard foo()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## A higher level `mySQL`:idx: database wrapper. The same interface is
|
## A higher level `mySQL`:idx: database wrapper. The same interface is
|
||||||
## implemented for other databases too.
|
## implemented for other databases too.
|
||||||
|
|
||||||
import strutils, mysql
|
import strutils, mysql
|
||||||
|
|
@ -29,22 +29,22 @@ type
|
||||||
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
|
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
|
||||||
|
|
||||||
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
|
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
|
||||||
## constructs a SqlQuery from the string `query`. This is supposed to be
|
## constructs a SqlQuery from the string `query`. This is supposed to be
|
||||||
## used as a raw-string-literal modifier:
|
## used as a raw-string-literal modifier:
|
||||||
## ``sql"update user set counter = counter + 1"``
|
## ``sql"update user set counter = counter + 1"``
|
||||||
##
|
##
|
||||||
## If assertions are turned off, it does nothing. If assertions are turned
|
## If assertions are turned off, it does nothing. If assertions are turned
|
||||||
## on, later versions will check the string for valid syntax.
|
## on, later versions will check the string for valid syntax.
|
||||||
result = SqlQuery(query)
|
result = SqlQuery(query)
|
||||||
|
|
||||||
proc dbError(db: DbConn) {.noreturn.} =
|
proc dbError(db: DbConn) {.noreturn.} =
|
||||||
## raises an EDb exception.
|
## raises an EDb exception.
|
||||||
var e: ref EDb
|
var e: ref EDb
|
||||||
new(e)
|
new(e)
|
||||||
e.msg = $mysql.error(db)
|
e.msg = $mysql.error(db)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
proc dbError*(msg: string) {.noreturn.} =
|
proc dbError*(msg: string) {.noreturn.} =
|
||||||
## raises an EDb exception with message `msg`.
|
## raises an EDb exception with message `msg`.
|
||||||
var e: ref EDb
|
var e: ref EDb
|
||||||
new(e)
|
new(e)
|
||||||
|
|
@ -55,9 +55,9 @@ when false:
|
||||||
proc dbQueryOpt*(db: DbConn, query: string, args: varargs[string, `$`]) =
|
proc dbQueryOpt*(db: DbConn, query: string, args: varargs[string, `$`]) =
|
||||||
var stmt = mysql_stmt_init(db)
|
var stmt = mysql_stmt_init(db)
|
||||||
if stmt == nil: dbError(db)
|
if stmt == nil: dbError(db)
|
||||||
if mysql_stmt_prepare(stmt, query, len(query)) != 0:
|
if mysql_stmt_prepare(stmt, query, len(query)) != 0:
|
||||||
dbError(db)
|
dbError(db)
|
||||||
var
|
var
|
||||||
binding: seq[MYSQL_BIND]
|
binding: seq[MYSQL_BIND]
|
||||||
discard mysql_stmt_close(stmt)
|
discard mysql_stmt_close(stmt)
|
||||||
|
|
||||||
|
|
@ -79,9 +79,9 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
|
||||||
else:
|
else:
|
||||||
add(result, dbQuote(args[a]))
|
add(result, dbQuote(args[a]))
|
||||||
inc(a)
|
inc(a)
|
||||||
else:
|
else:
|
||||||
add(result, c)
|
add(result, c)
|
||||||
|
|
||||||
proc tryExec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {.
|
proc tryExec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {.
|
||||||
tags: [FReadDB, FWriteDb].} =
|
tags: [FReadDB, FWriteDb].} =
|
||||||
## tries to execute the query and returns true if successful, false otherwise.
|
## tries to execute the query and returns true if successful, false otherwise.
|
||||||
|
|
@ -97,19 +97,19 @@ proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
|
||||||
## executes the query and raises EDB if not successful.
|
## executes the query and raises EDB if not successful.
|
||||||
var q = dbFormat(query, args)
|
var q = dbFormat(query, args)
|
||||||
if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db)
|
if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db)
|
||||||
|
|
||||||
proc newRow(L: int): Row =
|
proc newRow(L: int): Row =
|
||||||
newSeq(result, L)
|
newSeq(result, L)
|
||||||
for i in 0..L-1: result[i] = ""
|
for i in 0..L-1: result[i] = ""
|
||||||
|
|
||||||
proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) =
|
proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) =
|
||||||
if row != nil:
|
if row != nil:
|
||||||
while mysql.fetchRow(sqlres) != nil: discard
|
while mysql.fetchRow(sqlres) != nil: discard
|
||||||
mysql.freeResult(sqlres)
|
mysql.freeResult(sqlres)
|
||||||
|
|
||||||
iterator fastRows*(db: DbConn, query: SqlQuery,
|
iterator fastRows*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
||||||
## executes the query and iterates over the result dataset. This is very
|
## executes the query and iterates over the result dataset. This is very
|
||||||
## fast, but potenially dangerous: If the for-loop-body executes another
|
## fast, but potenially dangerous: If the for-loop-body executes another
|
||||||
## query, the results can be undefined. For MySQL this is the case!.
|
## query, the results can be undefined. For MySQL this is the case!.
|
||||||
rawExec(db, query, args)
|
rawExec(db, query, args)
|
||||||
|
|
@ -121,7 +121,7 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
|
||||||
while true:
|
while true:
|
||||||
row = mysql.fetchRow(sqlres)
|
row = mysql.fetchRow(sqlres)
|
||||||
if row == nil: break
|
if row == nil: break
|
||||||
for i in 0..L-1:
|
for i in 0..L-1:
|
||||||
setLen(result[i], 0)
|
setLen(result[i], 0)
|
||||||
if row[i] == nil:
|
if row[i] == nil:
|
||||||
result[i] = nil
|
result[i] = nil
|
||||||
|
|
@ -164,8 +164,8 @@ proc getRow*(db: DbConn, query: SqlQuery,
|
||||||
var L = int(mysql.numFields(sqlres))
|
var L = int(mysql.numFields(sqlres))
|
||||||
result = newRow(L)
|
result = newRow(L)
|
||||||
var row = mysql.fetchRow(sqlres)
|
var row = mysql.fetchRow(sqlres)
|
||||||
if row != nil:
|
if row != nil:
|
||||||
for i in 0..L-1:
|
for i in 0..L-1:
|
||||||
setLen(result[i], 0)
|
setLen(result[i], 0)
|
||||||
if row[i] == nil:
|
if row[i] == nil:
|
||||||
result[i] = nil
|
result[i] = nil
|
||||||
|
|
@ -173,7 +173,7 @@ proc getRow*(db: DbConn, query: SqlQuery,
|
||||||
add(result[i], row[i])
|
add(result[i], row[i])
|
||||||
properFreeResult(sqlres, row)
|
properFreeResult(sqlres, row)
|
||||||
|
|
||||||
proc getAllRows*(db: DbConn, query: SqlQuery,
|
proc getAllRows*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): seq[Row] {.tags: [FReadDB].} =
|
args: varargs[string, `$`]): seq[Row] {.tags: [FReadDB].} =
|
||||||
## executes the query and returns the whole result dataset.
|
## executes the query and returns the whole result dataset.
|
||||||
result = @[]
|
result = @[]
|
||||||
|
|
@ -196,44 +196,44 @@ proc getAllRows*(db: DbConn, query: SqlQuery,
|
||||||
inc(j)
|
inc(j)
|
||||||
mysql.freeResult(sqlres)
|
mysql.freeResult(sqlres)
|
||||||
|
|
||||||
iterator rows*(db: DbConn, query: SqlQuery,
|
iterator rows*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
||||||
## same as `fastRows`, but slower and safe.
|
## same as `fastRows`, but slower and safe.
|
||||||
for r in items(getAllRows(db, query, args)): yield r
|
for r in items(getAllRows(db, query, args)): yield r
|
||||||
|
|
||||||
proc getValue*(db: DbConn, query: SqlQuery,
|
proc getValue*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): string {.tags: [FReadDB].} =
|
args: varargs[string, `$`]): string {.tags: [FReadDB].} =
|
||||||
## executes the query and returns the first column of the first row of the
|
## executes the query and returns the first column of the first row of the
|
||||||
## result dataset. Returns "" if the dataset contains no rows or the database
|
## result dataset. Returns "" if the dataset contains no rows or the database
|
||||||
## value is NULL.
|
## value is NULL.
|
||||||
result = getRow(db, query, args)[0]
|
result = getRow(db, query, args)[0]
|
||||||
|
|
||||||
proc tryInsertId*(db: DbConn, query: SqlQuery,
|
proc tryInsertId*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
||||||
## executes the query (typically "INSERT") and returns the
|
## executes the query (typically "INSERT") and returns the
|
||||||
## generated ID for the row or -1 in case of an error.
|
## generated ID for the row or -1 in case of an error.
|
||||||
var q = dbFormat(query, args)
|
var q = dbFormat(query, args)
|
||||||
if mysql.realQuery(db, q, q.len) != 0'i32:
|
if mysql.realQuery(db, q, q.len) != 0'i32:
|
||||||
result = -1'i64
|
result = -1'i64
|
||||||
else:
|
else:
|
||||||
result = mysql.insertId(db)
|
result = mysql.insertId(db)
|
||||||
|
|
||||||
proc insertId*(db: DbConn, query: SqlQuery,
|
proc insertId*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
||||||
## executes the query (typically "INSERT") and returns the
|
## executes the query (typically "INSERT") and returns the
|
||||||
## generated ID for the row.
|
## generated ID for the row.
|
||||||
result = tryInsertID(db, query, args)
|
result = tryInsertID(db, query, args)
|
||||||
if result < 0: dbError(db)
|
if result < 0: dbError(db)
|
||||||
|
|
||||||
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): int64 {.
|
args: varargs[string, `$`]): int64 {.
|
||||||
tags: [FReadDB, FWriteDb].} =
|
tags: [FReadDB, FWriteDb].} =
|
||||||
## runs the query (typically "UPDATE") and returns the
|
## runs the query (typically "UPDATE") and returns the
|
||||||
## number of affected rows
|
## number of affected rows
|
||||||
rawExec(db, query, args)
|
rawExec(db, query, args)
|
||||||
result = mysql.affectedRows(db)
|
result = mysql.affectedRows(db)
|
||||||
|
|
||||||
proc close*(db: DbConn) {.tags: [FDb].} =
|
proc close*(db: DbConn) {.tags: [FDb].} =
|
||||||
## closes the database connection.
|
## closes the database connection.
|
||||||
if db != nil: mysql.close(db)
|
if db != nil: mysql.close(db)
|
||||||
|
|
||||||
|
|
@ -242,14 +242,14 @@ proc open*(connection, user, password, database: string): DbConn {.
|
||||||
## opens a database connection. Raises `EDb` if the connection could not
|
## opens a database connection. Raises `EDb` if the connection could not
|
||||||
## be established.
|
## be established.
|
||||||
result = mysql.init(nil)
|
result = mysql.init(nil)
|
||||||
if result == nil: dbError("could not open database connection")
|
if result == nil: dbError("could not open database connection")
|
||||||
let
|
let
|
||||||
colonPos = connection.find(':')
|
colonPos = connection.find(':')
|
||||||
host = if colonPos < 0: connection
|
host = if colonPos < 0: connection
|
||||||
else: substr(connection, 0, colonPos-1)
|
else: substr(connection, 0, colonPos-1)
|
||||||
port: int32 = if colonPos < 0: 0'i32
|
port: int32 = if colonPos < 0: 0'i32
|
||||||
else: substr(connection, colonPos+1).parseInt.int32
|
else: substr(connection, colonPos+1).parseInt.int32
|
||||||
if mysql.realConnect(result, host, user, password, database,
|
if mysql.realConnect(result, host, user, password, database,
|
||||||
port, nil, 0) == nil:
|
port, nil, 0) == nil:
|
||||||
var errmsg = $mysql.error(result)
|
var errmsg = $mysql.error(result)
|
||||||
db_mysql.close(result)
|
db_mysql.close(result)
|
||||||
|
|
@ -257,6 +257,6 @@ proc open*(connection, user, password, database: string): DbConn {.
|
||||||
|
|
||||||
proc setEncoding*(connection: DbConn, encoding: string): bool {.
|
proc setEncoding*(connection: DbConn, encoding: string): bool {.
|
||||||
tags: [FDb].} =
|
tags: [FDb].} =
|
||||||
## sets the encoding of a database connection, returns true for
|
## sets the encoding of a database connection, returns true for
|
||||||
## success, false for failure.
|
## success, false for failure.
|
||||||
result = mysql.set_character_set(connection, encoding) == 0
|
result = mysql.set_character_set(connection, encoding) == 0
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## A higher level `PostgreSQL`:idx: database wrapper. This interface
|
## A higher level `PostgreSQL`:idx: database wrapper. This interface
|
||||||
## is implemented for other databases too.
|
## is implemented for other databases too.
|
||||||
|
|
||||||
import strutils, postgres
|
import strutils, postgres
|
||||||
|
|
@ -20,7 +20,7 @@ type
|
||||||
## used to get a row's
|
## used to get a row's
|
||||||
## column text on demand
|
## column text on demand
|
||||||
EDb* = object of IOError ## exception that is raised if a database error occurs
|
EDb* = object of IOError ## exception that is raised if a database error occurs
|
||||||
|
|
||||||
SqlQuery* = distinct string ## an SQL query string
|
SqlQuery* = distinct string ## an SQL query string
|
||||||
SqlPrepared* = distinct string ## a identifier for the prepared queries
|
SqlPrepared* = distinct string ## a identifier for the prepared queries
|
||||||
|
|
||||||
|
|
@ -30,15 +30,15 @@ type
|
||||||
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn,
|
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn,
|
||||||
TSqlPrepared: SqlPrepared].}
|
TSqlPrepared: SqlPrepared].}
|
||||||
|
|
||||||
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
|
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
|
||||||
## constructs a SqlQuery from the string `query`. This is supposed to be
|
## constructs a SqlQuery from the string `query`. This is supposed to be
|
||||||
## used as a raw-string-literal modifier:
|
## used as a raw-string-literal modifier:
|
||||||
## ``sql"update user set counter = counter + 1"``
|
## ``sql"update user set counter = counter + 1"``
|
||||||
##
|
##
|
||||||
## If assertions are turned off, it does nothing. If assertions are turned
|
## If assertions are turned off, it does nothing. If assertions are turned
|
||||||
## on, later versions will check the string for valid syntax.
|
## on, later versions will check the string for valid syntax.
|
||||||
result = SqlQuery(query)
|
result = SqlQuery(query)
|
||||||
|
|
||||||
proc dbError*(db: DbConn) {.noreturn.} =
|
proc dbError*(db: DbConn) {.noreturn.} =
|
||||||
## raises an EDb exception.
|
## raises an EDb exception.
|
||||||
var e: ref EDb
|
var e: ref EDb
|
||||||
|
|
@ -73,7 +73,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
|
||||||
inc(a)
|
inc(a)
|
||||||
else:
|
else:
|
||||||
add(result, c)
|
add(result, c)
|
||||||
|
|
||||||
proc tryExec*(db: DbConn, query: SqlQuery,
|
proc tryExec*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): bool {.tags: [FReadDB, FWriteDb].} =
|
args: varargs[string, `$`]): bool {.tags: [FReadDB, FWriteDb].} =
|
||||||
## tries to execute the query and returns true if successful, false otherwise.
|
## tries to execute the query and returns true if successful, false otherwise.
|
||||||
|
|
@ -106,7 +106,7 @@ proc exec*(db: DbConn, stmtName: SqlPrepared,
|
||||||
proc newRow(L: int): Row =
|
proc newRow(L: int): Row =
|
||||||
newSeq(result, L)
|
newSeq(result, L)
|
||||||
for i in 0..L-1: result[i] = ""
|
for i in 0..L-1: result[i] = ""
|
||||||
|
|
||||||
proc setupQuery(db: DbConn, query: SqlQuery,
|
proc setupQuery(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string]): PPGresult =
|
args: varargs[string]): PPGresult =
|
||||||
var arr = allocCStringArray(args)
|
var arr = allocCStringArray(args)
|
||||||
|
|
@ -128,7 +128,7 @@ proc prepare*(db: DbConn; stmtName: string, query: SqlQuery;
|
||||||
var res = pqprepare(db, stmtName, query.string, int32(nParams), nil)
|
var res = pqprepare(db, stmtName, query.string, int32(nParams), nil)
|
||||||
if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db)
|
if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db)
|
||||||
return SqlPrepared(stmtName)
|
return SqlPrepared(stmtName)
|
||||||
|
|
||||||
proc setRow(res: PPGresult, r: var Row, line, cols: int32) =
|
proc setRow(res: PPGresult, r: var Row, line, cols: int32) =
|
||||||
for col in 0..cols-1:
|
for col in 0..cols-1:
|
||||||
setLen(r[col], 0)
|
setLen(r[col], 0)
|
||||||
|
|
@ -140,7 +140,7 @@ proc setRow(res: PPGresult, r: var Row, line, cols: int32) =
|
||||||
|
|
||||||
iterator fastRows*(db: DbConn, query: SqlQuery,
|
iterator fastRows*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
||||||
## executes the query and iterates over the result dataset. This is very
|
## executes the query and iterates over the result dataset. This is very
|
||||||
## fast, but potenially dangerous: If the for-loop-body executes another
|
## fast, but potenially dangerous: If the for-loop-body executes another
|
||||||
## query, the results can be undefined. For Postgres it is safe though.
|
## query, the results can be undefined. For Postgres it is safe though.
|
||||||
var res = setupQuery(db, query, args)
|
var res = setupQuery(db, query, args)
|
||||||
|
|
@ -224,14 +224,14 @@ proc getValue*(db: DbConn, query: SqlQuery,
|
||||||
## value is NULL.
|
## value is NULL.
|
||||||
var x = pqgetvalue(setupQuery(db, query, args), 0, 0)
|
var x = pqgetvalue(setupQuery(db, query, args), 0, 0)
|
||||||
result = if isNil(x): "" else: $x
|
result = if isNil(x): "" else: $x
|
||||||
|
|
||||||
proc tryInsertID*(db: DbConn, query: SqlQuery,
|
proc tryInsertID*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].}=
|
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].}=
|
||||||
## executes the query (typically "INSERT") and returns the
|
## executes the query (typically "INSERT") and returns the
|
||||||
## generated ID for the row or -1 in case of an error. For Postgre this adds
|
## generated ID for the row or -1 in case of an error. For Postgre this adds
|
||||||
## ``RETURNING id`` to the query, so it only works if your primary key is
|
## ``RETURNING id`` to the query, so it only works if your primary key is
|
||||||
## named ``id``.
|
## named ``id``.
|
||||||
var x = pqgetvalue(setupQuery(db, SqlQuery(string(query) & " RETURNING id"),
|
var x = pqgetvalue(setupQuery(db, SqlQuery(string(query) & " RETURNING id"),
|
||||||
args), 0, 0)
|
args), 0, 0)
|
||||||
if not isNil(x):
|
if not isNil(x):
|
||||||
result = parseBiggestInt($x)
|
result = parseBiggestInt($x)
|
||||||
|
|
@ -240,13 +240,13 @@ proc tryInsertID*(db: DbConn, query: SqlQuery,
|
||||||
|
|
||||||
proc insertID*(db: DbConn, query: SqlQuery,
|
proc insertID*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
||||||
## executes the query (typically "INSERT") and returns the
|
## executes the query (typically "INSERT") and returns the
|
||||||
## generated ID for the row. For Postgre this adds
|
## generated ID for the row. For Postgre this adds
|
||||||
## ``RETURNING id`` to the query, so it only works if your primary key is
|
## ``RETURNING id`` to the query, so it only works if your primary key is
|
||||||
## named ``id``.
|
## named ``id``.
|
||||||
result = tryInsertID(db, query, args)
|
result = tryInsertID(db, query, args)
|
||||||
if result < 0: dbError(db)
|
if result < 0: dbError(db)
|
||||||
|
|
||||||
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): int64 {.tags: [
|
args: varargs[string, `$`]): int64 {.tags: [
|
||||||
FReadDB, FWriteDb].} =
|
FReadDB, FWriteDb].} =
|
||||||
|
|
@ -286,6 +286,6 @@ proc open*(connection, user, password, database: string): DbConn {.
|
||||||
|
|
||||||
proc setEncoding*(connection: DbConn, encoding: string): bool {.
|
proc setEncoding*(connection: DbConn, encoding: string): bool {.
|
||||||
tags: [FDb].} =
|
tags: [FDb].} =
|
||||||
## sets the encoding of a database connection, returns true for
|
## sets the encoding of a database connection, returns true for
|
||||||
## success, false for failure.
|
## success, false for failure.
|
||||||
return pqsetClientEncoding(connection, encoding) == 0
|
return pqsetClientEncoding(connection, encoding) == 0
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## A higher level `SQLite`:idx: database wrapper. This interface
|
## A higher level `SQLite`:idx: database wrapper. This interface
|
||||||
## is implemented for other databases too.
|
## is implemented for other databases too.
|
||||||
|
|
||||||
import strutils, sqlite3
|
import strutils, sqlite3
|
||||||
|
|
@ -19,31 +19,31 @@ type
|
||||||
InstantRow* = Pstmt ## a handle that can be used to get a row's column
|
InstantRow* = Pstmt ## a handle that can be used to get a row's column
|
||||||
## text on demand
|
## text on demand
|
||||||
EDb* = object of IOError ## exception that is raised if a database error occurs
|
EDb* = object of IOError ## exception that is raised if a database error occurs
|
||||||
|
|
||||||
SqlQuery* = distinct string ## an SQL query string
|
SqlQuery* = distinct string ## an SQL query string
|
||||||
|
|
||||||
FDb* = object of IOEffect ## effect that denotes a database operation
|
FDb* = object of IOEffect ## effect that denotes a database operation
|
||||||
FReadDb* = object of FDb ## effect that denotes a read operation
|
FReadDb* = object of FDb ## effect that denotes a read operation
|
||||||
FWriteDb* = object of FDb ## effect that denotes a write operation
|
FWriteDb* = object of FDb ## effect that denotes a write operation
|
||||||
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
|
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
|
||||||
|
|
||||||
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
|
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
|
||||||
## constructs a SqlQuery from the string `query`. This is supposed to be
|
## constructs a SqlQuery from the string `query`. This is supposed to be
|
||||||
## used as a raw-string-literal modifier:
|
## used as a raw-string-literal modifier:
|
||||||
## ``sql"update user set counter = counter + 1"``
|
## ``sql"update user set counter = counter + 1"``
|
||||||
##
|
##
|
||||||
## If assertions are turned off, it does nothing. If assertions are turned
|
## If assertions are turned off, it does nothing. If assertions are turned
|
||||||
## on, later versions will check the string for valid syntax.
|
## on, later versions will check the string for valid syntax.
|
||||||
result = SqlQuery(query)
|
result = SqlQuery(query)
|
||||||
|
|
||||||
proc dbError(db: DbConn) {.noreturn.} =
|
proc dbError(db: DbConn) {.noreturn.} =
|
||||||
## raises an EDb exception.
|
## raises an EDb exception.
|
||||||
var e: ref EDb
|
var e: ref EDb
|
||||||
new(e)
|
new(e)
|
||||||
e.msg = $sqlite3.errmsg(db)
|
e.msg = $sqlite3.errmsg(db)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
proc dbError*(msg: string) {.noreturn.} =
|
proc dbError*(msg: string) {.noreturn.} =
|
||||||
## raises an EDb exception with message `msg`.
|
## raises an EDb exception with message `msg`.
|
||||||
var e: ref EDb
|
var e: ref EDb
|
||||||
new(e)
|
new(e)
|
||||||
|
|
@ -67,8 +67,8 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
|
||||||
inc(a)
|
inc(a)
|
||||||
else:
|
else:
|
||||||
add(result, c)
|
add(result, c)
|
||||||
|
|
||||||
proc tryExec*(db: DbConn, query: SqlQuery,
|
proc tryExec*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): bool {.tags: [FReadDb, FWriteDb].} =
|
args: varargs[string, `$`]): bool {.tags: [FReadDb, FWriteDb].} =
|
||||||
## tries to execute the query and returns true if successful, false otherwise.
|
## tries to execute the query and returns true if successful, false otherwise.
|
||||||
var q = dbFormat(query, args)
|
var q = dbFormat(query, args)
|
||||||
|
|
@ -81,32 +81,32 @@ proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
|
||||||
tags: [FReadDb, FWriteDb].} =
|
tags: [FReadDb, FWriteDb].} =
|
||||||
## executes the query and raises EDB if not successful.
|
## executes the query and raises EDB if not successful.
|
||||||
if not tryExec(db, query, args): dbError(db)
|
if not tryExec(db, query, args): dbError(db)
|
||||||
|
|
||||||
proc newRow(L: int): Row =
|
proc newRow(L: int): Row =
|
||||||
newSeq(result, L)
|
newSeq(result, L)
|
||||||
for i in 0..L-1: result[i] = ""
|
for i in 0..L-1: result[i] = ""
|
||||||
|
|
||||||
proc setupQuery(db: DbConn, query: SqlQuery,
|
proc setupQuery(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string]): Pstmt =
|
args: varargs[string]): Pstmt =
|
||||||
var q = dbFormat(query, args)
|
var q = dbFormat(query, args)
|
||||||
if prepare_v2(db, q, q.len.cint, result, nil) != SQLITE_OK: dbError(db)
|
if prepare_v2(db, q, q.len.cint, result, nil) != SQLITE_OK: dbError(db)
|
||||||
|
|
||||||
proc setRow(stmt: Pstmt, r: var Row, cols: cint) =
|
proc setRow(stmt: Pstmt, r: var Row, cols: cint) =
|
||||||
for col in 0..cols-1:
|
for col in 0..cols-1:
|
||||||
setLen(r[col], column_bytes(stmt, col)) # set capacity
|
setLen(r[col], column_bytes(stmt, col)) # set capacity
|
||||||
setLen(r[col], 0)
|
setLen(r[col], 0)
|
||||||
let x = column_text(stmt, col)
|
let x = column_text(stmt, col)
|
||||||
if not isNil(x): add(r[col], x)
|
if not isNil(x): add(r[col], x)
|
||||||
|
|
||||||
iterator fastRows*(db: DbConn, query: SqlQuery,
|
iterator fastRows*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
|
args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
|
||||||
## executes the query and iterates over the result dataset. This is very
|
## executes the query and iterates over the result dataset. This is very
|
||||||
## fast, but potenially dangerous: If the for-loop-body executes another
|
## fast, but potenially dangerous: If the for-loop-body executes another
|
||||||
## query, the results can be undefined. For Sqlite it is safe though.
|
## query, the results can be undefined. For Sqlite it is safe though.
|
||||||
var stmt = setupQuery(db, query, args)
|
var stmt = setupQuery(db, query, args)
|
||||||
var L = (column_count(stmt))
|
var L = (column_count(stmt))
|
||||||
var result = newRow(L)
|
var result = newRow(L)
|
||||||
while step(stmt) == SQLITE_ROW:
|
while step(stmt) == SQLITE_ROW:
|
||||||
setRow(stmt, result, L)
|
setRow(stmt, result, L)
|
||||||
yield result
|
yield result
|
||||||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||||
|
|
@ -136,31 +136,31 @@ proc getRow*(db: DbConn, query: SqlQuery,
|
||||||
var stmt = setupQuery(db, query, args)
|
var stmt = setupQuery(db, query, args)
|
||||||
var L = (column_count(stmt))
|
var L = (column_count(stmt))
|
||||||
result = newRow(L)
|
result = newRow(L)
|
||||||
if step(stmt) == SQLITE_ROW:
|
if step(stmt) == SQLITE_ROW:
|
||||||
setRow(stmt, result, L)
|
setRow(stmt, result, L)
|
||||||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||||
|
|
||||||
proc getAllRows*(db: DbConn, query: SqlQuery,
|
proc getAllRows*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): seq[Row] {.tags: [FReadDb].} =
|
args: varargs[string, `$`]): seq[Row] {.tags: [FReadDb].} =
|
||||||
## executes the query and returns the whole result dataset.
|
## executes the query and returns the whole result dataset.
|
||||||
result = @[]
|
result = @[]
|
||||||
for r in fastRows(db, query, args):
|
for r in fastRows(db, query, args):
|
||||||
result.add(r)
|
result.add(r)
|
||||||
|
|
||||||
iterator rows*(db: DbConn, query: SqlQuery,
|
iterator rows*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
|
args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
|
||||||
## same as `FastRows`, but slower and safe.
|
## same as `FastRows`, but slower and safe.
|
||||||
for r in fastRows(db, query, args): yield r
|
for r in fastRows(db, query, args): yield r
|
||||||
|
|
||||||
proc getValue*(db: DbConn, query: SqlQuery,
|
proc getValue*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): string {.tags: [FReadDb].} =
|
args: varargs[string, `$`]): string {.tags: [FReadDb].} =
|
||||||
## executes the query and returns the first column of the first row of the
|
## executes the query and returns the first column of the first row of the
|
||||||
## result dataset. Returns "" if the dataset contains no rows or the database
|
## result dataset. Returns "" if the dataset contains no rows or the database
|
||||||
## value is NULL.
|
## value is NULL.
|
||||||
var stmt = setupQuery(db, query, args)
|
var stmt = setupQuery(db, query, args)
|
||||||
if step(stmt) == SQLITE_ROW:
|
if step(stmt) == SQLITE_ROW:
|
||||||
let cb = column_bytes(stmt, 0)
|
let cb = column_bytes(stmt, 0)
|
||||||
if cb == 0:
|
if cb == 0:
|
||||||
result = ""
|
result = ""
|
||||||
else:
|
else:
|
||||||
result = newStringOfCap(cb)
|
result = newStringOfCap(cb)
|
||||||
|
|
@ -168,12 +168,12 @@ proc getValue*(db: DbConn, query: SqlQuery,
|
||||||
else:
|
else:
|
||||||
result = ""
|
result = ""
|
||||||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||||
|
|
||||||
proc tryInsertID*(db: DbConn, query: SqlQuery,
|
proc tryInsertID*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): int64
|
args: varargs[string, `$`]): int64
|
||||||
{.tags: [FWriteDb], raises: [].} =
|
{.tags: [FWriteDb], raises: [].} =
|
||||||
## executes the query (typically "INSERT") and returns the
|
## executes the query (typically "INSERT") and returns the
|
||||||
## generated ID for the row or -1 in case of an error.
|
## generated ID for the row or -1 in case of an error.
|
||||||
var q = dbFormat(query, args)
|
var q = dbFormat(query, args)
|
||||||
var stmt: sqlite3.Pstmt
|
var stmt: sqlite3.Pstmt
|
||||||
result = -1
|
result = -1
|
||||||
|
|
@ -183,27 +183,27 @@ proc tryInsertID*(db: DbConn, query: SqlQuery,
|
||||||
if finalize(stmt) != SQLITE_OK:
|
if finalize(stmt) != SQLITE_OK:
|
||||||
result = -1
|
result = -1
|
||||||
|
|
||||||
proc insertID*(db: DbConn, query: SqlQuery,
|
proc insertID*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
||||||
## executes the query (typically "INSERT") and returns the
|
## executes the query (typically "INSERT") and returns the
|
||||||
## generated ID for the row. For Postgre this adds
|
## generated ID for the row. For Postgre this adds
|
||||||
## ``RETURNING id`` to the query, so it only works if your primary key is
|
## ``RETURNING id`` to the query, so it only works if your primary key is
|
||||||
## named ``id``.
|
## named ``id``.
|
||||||
result = tryInsertID(db, query, args)
|
result = tryInsertID(db, query, args)
|
||||||
if result < 0: dbError(db)
|
if result < 0: dbError(db)
|
||||||
|
|
||||||
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
||||||
args: varargs[string, `$`]): int64 {.
|
args: varargs[string, `$`]): int64 {.
|
||||||
tags: [FReadDb, FWriteDb].} =
|
tags: [FReadDb, FWriteDb].} =
|
||||||
## executes the query (typically "UPDATE") and returns the
|
## executes the query (typically "UPDATE") and returns the
|
||||||
## number of affected rows.
|
## number of affected rows.
|
||||||
exec(db, query, args)
|
exec(db, query, args)
|
||||||
result = changes(db)
|
result = changes(db)
|
||||||
|
|
||||||
proc close*(db: DbConn) {.tags: [FDb].} =
|
proc close*(db: DbConn) {.tags: [FDb].} =
|
||||||
## closes the database connection.
|
## closes the database connection.
|
||||||
if sqlite3.close(db) != SQLITE_OK: dbError(db)
|
if sqlite3.close(db) != SQLITE_OK: dbError(db)
|
||||||
|
|
||||||
proc open*(connection, user, password, database: string): DbConn {.
|
proc open*(connection, user, password, database: string): DbConn {.
|
||||||
tags: [FDb].} =
|
tags: [FDb].} =
|
||||||
## opens a database connection. Raises `EDb` if the connection could not
|
## opens a database connection. Raises `EDb` if the connection could not
|
||||||
|
|
@ -216,12 +216,12 @@ proc open*(connection, user, password, database: string): DbConn {.
|
||||||
|
|
||||||
proc setEncoding*(connection: DbConn, encoding: string): bool {.
|
proc setEncoding*(connection: DbConn, encoding: string): bool {.
|
||||||
tags: [FDb].} =
|
tags: [FDb].} =
|
||||||
## sets the encoding of a database connection, returns true for
|
## sets the encoding of a database connection, returns true for
|
||||||
## success, false for failure.
|
## success, false for failure.
|
||||||
##
|
##
|
||||||
## Note that the encoding cannot be changed once it's been set.
|
## Note that the encoding cannot be changed once it's been set.
|
||||||
## According to SQLite3 documentation, any attempt to change
|
## According to SQLite3 documentation, any attempt to change
|
||||||
## the encoding after the database is created will be silently
|
## the encoding after the database is created will be silently
|
||||||
## ignored.
|
## ignored.
|
||||||
exec(connection, sql"PRAGMA encoding = ?", [encoding])
|
exec(connection, sql"PRAGMA encoding = ?", [encoding])
|
||||||
result = connection.getValue(sql"PRAGMA encoding") == encoding
|
result = connection.getValue(sql"PRAGMA encoding") == encoding
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
## This module implements graphical output for Nim; the current
|
## This module implements graphical output for Nim; the current
|
||||||
## implementation uses SDL but the interface is meant to support multiple
|
## implementation uses SDL but the interface is meant to support multiple
|
||||||
## backends some day. There is no need to init SDL as this module does that
|
## backends some day. There is no need to init SDL as this module does that
|
||||||
## implicitly.
|
## implicitly.
|
||||||
|
|
||||||
import colors, math
|
import colors, math
|
||||||
|
|
@ -24,7 +24,7 @@ type
|
||||||
Surface* {.pure, final.} = object
|
Surface* {.pure, final.} = object
|
||||||
w*, h*: Natural
|
w*, h*: Natural
|
||||||
s*: sdl.PSurface
|
s*: sdl.PSurface
|
||||||
|
|
||||||
EGraphics* = object of IOError
|
EGraphics* = object of IOError
|
||||||
|
|
||||||
Font {.pure, final.} = object
|
Font {.pure, final.} = object
|
||||||
|
|
@ -35,7 +35,7 @@ type
|
||||||
|
|
||||||
proc toSdlColor*(c: Color): sdl.Color =
|
proc toSdlColor*(c: Color): sdl.Color =
|
||||||
## Convert colors.Color to sdl.Color
|
## Convert colors.Color to sdl.Color
|
||||||
var x = c.extractRGB
|
var x = c.extractRGB
|
||||||
result.r = x.r and 0xff
|
result.r = x.r and 0xff
|
||||||
result.g = x.g and 0xff
|
result.g = x.g and 0xff
|
||||||
result.b = x.b and 0xff
|
result.b = x.b and 0xff
|
||||||
|
|
@ -43,7 +43,7 @@ proc toSdlColor*(c: Color): sdl.Color =
|
||||||
proc createSdlColor*(sur: PSurface, c: Color, alpha: int = 0): int32 =
|
proc createSdlColor*(sur: PSurface, c: Color, alpha: int = 0): int32 =
|
||||||
## Creates a color using ``sdl.MapRGBA``.
|
## Creates a color using ``sdl.MapRGBA``.
|
||||||
var x = c.extractRGB
|
var x = c.extractRGB
|
||||||
return sdl.mapRGBA(sur.s.format, x.r and 0xff, x.g and 0xff,
|
return sdl.mapRGBA(sur.s.format, x.r and 0xff, x.g and 0xff,
|
||||||
x.b and 0xff, alpha and 0xff)
|
x.b and 0xff, alpha and 0xff)
|
||||||
|
|
||||||
proc toSdlRect*(r: Rect): sdl.Rect =
|
proc toSdlRect*(r: Rect): sdl.Rect =
|
||||||
|
|
@ -53,26 +53,26 @@ proc toSdlRect*(r: Rect): sdl.Rect =
|
||||||
result.w = uint16(r.width)
|
result.w = uint16(r.width)
|
||||||
result.h = uint16(r.height)
|
result.h = uint16(r.height)
|
||||||
|
|
||||||
proc raiseEGraphics =
|
proc raiseEGraphics =
|
||||||
raise newException(EGraphics, $sdl.getError())
|
raise newException(EGraphics, $sdl.getError())
|
||||||
|
|
||||||
proc surfaceFinalizer(s: PSurface) = sdl.freeSurface(s.s)
|
proc surfaceFinalizer(s: PSurface) = sdl.freeSurface(s.s)
|
||||||
|
|
||||||
proc newSurface*(width, height: int): PSurface =
|
proc newSurface*(width, height: int): PSurface =
|
||||||
## creates a new surface.
|
## creates a new surface.
|
||||||
new(result, surfaceFinalizer)
|
new(result, surfaceFinalizer)
|
||||||
result.w = width
|
result.w = width
|
||||||
result.h = height
|
result.h = height
|
||||||
result.s = sdl.createRGBSurface(sdl.SWSURFACE, width, height,
|
result.s = sdl.createRGBSurface(sdl.SWSURFACE, width, height,
|
||||||
32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0)
|
32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0)
|
||||||
if result.s == nil:
|
if result.s == nil:
|
||||||
raiseEGraphics()
|
raiseEGraphics()
|
||||||
|
|
||||||
assert(not sdl.mustLock(result.s))
|
assert(not sdl.mustLock(result.s))
|
||||||
|
|
||||||
proc fontFinalizer(f: PFont) = closeFont(f.f)
|
proc fontFinalizer(f: PFont) = closeFont(f.f)
|
||||||
|
|
||||||
proc newFont*(name = "VeraMono.ttf", size = 9, color = colBlack): PFont =
|
proc newFont*(name = "VeraMono.ttf", size = 9, color = colBlack): PFont =
|
||||||
## Creates a new font object. Raises ``EIO`` if the font cannot be loaded.
|
## Creates a new font object. Raises ``EIO`` if the font cannot be loaded.
|
||||||
new(result, fontFinalizer)
|
new(result, fontFinalizer)
|
||||||
result.f = openFont(name, size.cint)
|
result.f = openFont(name, size.cint)
|
||||||
|
|
@ -84,7 +84,7 @@ var
|
||||||
defaultFont*: PFont ## default font that is used; this needs to initialized
|
defaultFont*: PFont ## default font that is used; this needs to initialized
|
||||||
## by the client!
|
## by the client!
|
||||||
|
|
||||||
proc initDefaultFont*(name = "VeraMono.ttf", size = 9, color = colBlack) =
|
proc initDefaultFont*(name = "VeraMono.ttf", size = 9, color = colBlack) =
|
||||||
## initializes the `defaultFont` var.
|
## initializes the `defaultFont` var.
|
||||||
defaultFont = newFont(name, size, color)
|
defaultFont = newFont(name, size, color)
|
||||||
|
|
||||||
|
|
@ -98,7 +98,7 @@ proc newScreenSurface*(width, height: int): PSurface =
|
||||||
raiseEGraphics()
|
raiseEGraphics()
|
||||||
|
|
||||||
proc writeToBMP*(sur: PSurface, filename: string) =
|
proc writeToBMP*(sur: PSurface, filename: string) =
|
||||||
## Saves the contents of the surface `sur` to the file `filename` as a
|
## Saves the contents of the surface `sur` to the file `filename` as a
|
||||||
## BMP file.
|
## BMP file.
|
||||||
if sdl.saveBMP(sur.s, filename) != 0:
|
if sdl.saveBMP(sur.s, filename) != 0:
|
||||||
raise newException(IOError, "cannot write: " & filename)
|
raise newException(IOError, "cannot write: " & filename)
|
||||||
|
|
@ -111,7 +111,7 @@ type
|
||||||
template setPix(video, pitch, x, y, col: expr): stmt =
|
template setPix(video, pitch, x, y, col: expr): stmt =
|
||||||
video[y * pitch + x] = int32(col)
|
video[y * pitch + x] = int32(col)
|
||||||
|
|
||||||
template getPix(video, pitch, x, y: expr): expr =
|
template getPix(video, pitch, x, y: expr): expr =
|
||||||
colors.Color(video[y * pitch + x])
|
colors.Color(video[y * pitch + x])
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|
@ -120,7 +120,7 @@ const
|
||||||
proc getPixel(sur: PSurface, x, y: Natural): colors.Color {.inline.} =
|
proc getPixel(sur: PSurface, x, y: Natural): colors.Color {.inline.} =
|
||||||
assert x <% sur.w
|
assert x <% sur.w
|
||||||
assert y <% sur.h
|
assert y <% sur.h
|
||||||
result = getPix(cast[PPixels](sur.s.pixels), sur.s.pitch.int div ColSize,
|
result = getPix(cast[PPixels](sur.s.pixels), sur.s.pitch.int div ColSize,
|
||||||
x, y)
|
x, y)
|
||||||
|
|
||||||
proc setPixel(sur: PSurface, x, y: Natural, col: colors.Color) {.inline.} =
|
proc setPixel(sur: PSurface, x, y: Natural, col: colors.Color) {.inline.} =
|
||||||
|
|
@ -146,7 +146,7 @@ proc `[]=`*(sur: PSurface, x, y: int, col: Color) =
|
||||||
## set the pixel at position ``(x, y)``. No range checking is done!
|
## set the pixel at position ``(x, y)``. No range checking is done!
|
||||||
setPixel(sur, x, y, col)
|
setPixel(sur, x, y, col)
|
||||||
|
|
||||||
proc blit*(destSurf: PSurface, destRect: Rect, srcSurf: PSurface,
|
proc blit*(destSurf: PSurface, destRect: Rect, srcSurf: PSurface,
|
||||||
srcRect: Rect) =
|
srcRect: Rect) =
|
||||||
## Copies ``srcSurf`` into ``destSurf``
|
## Copies ``srcSurf`` into ``destSurf``
|
||||||
var destTRect, srcTRect: sdl.Rect
|
var destTRect, srcTRect: sdl.Rect
|
||||||
|
|
@ -175,7 +175,7 @@ proc drawText*(sur: PSurface, p: Point, text: string, font = defaultFont) =
|
||||||
## font.
|
## font.
|
||||||
var textSur: PSurface # This surface will have the text drawn on it
|
var textSur: PSurface # This surface will have the text drawn on it
|
||||||
new(textSur, surfaceFinalizer)
|
new(textSur, surfaceFinalizer)
|
||||||
|
|
||||||
# Render the text
|
# Render the text
|
||||||
textSur.s = sdl_ttf.renderTextBlended(font.f, text, font.color)
|
textSur.s = sdl_ttf.renderTextBlended(font.f, text, font.color)
|
||||||
# Merge the text surface with sur
|
# Merge the text surface with sur
|
||||||
|
|
@ -183,14 +183,14 @@ proc drawText*(sur: PSurface, p: Point, text: string, font = defaultFont) =
|
||||||
|
|
||||||
proc drawText*(sur: PSurface, p: Point, text: string,
|
proc drawText*(sur: PSurface, p: Point, text: string,
|
||||||
bg: Color, font = defaultFont) =
|
bg: Color, font = defaultFont) =
|
||||||
## Draws text, at location ``p`` with font ``font``. ``bg``
|
## Draws text, at location ``p`` with font ``font``. ``bg``
|
||||||
## is the background color.
|
## is the background color.
|
||||||
var textSur: PSurface # This surface will have the text drawn on it
|
var textSur: PSurface # This surface will have the text drawn on it
|
||||||
new(textSur, surfaceFinalizer)
|
new(textSur, surfaceFinalizer)
|
||||||
textSur.s = sdl_ttf.renderTextShaded(font.f, text, font.color, toSdlColor(bg))
|
textSur.s = sdl_ttf.renderTextShaded(font.f, text, font.color, toSdlColor(bg))
|
||||||
# Merge the text surface with sur
|
# Merge the text surface with sur
|
||||||
sur.blit((p.x, p.y, sur.w, sur.h), textSur, (0, 0, sur.w, sur.h))
|
sur.blit((p.x, p.y, sur.w, sur.h), textSur, (0, 0, sur.w, sur.h))
|
||||||
|
|
||||||
proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) =
|
proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) =
|
||||||
## draws a circle with center `p` and radius `r` with the given color
|
## draws a circle with center `p` and radius `r` with the given color
|
||||||
## onto the surface `sur`.
|
## onto the surface `sur`.
|
||||||
|
|
@ -205,7 +205,7 @@ proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) =
|
||||||
if x+px <% sur.w:
|
if x+px <% sur.w:
|
||||||
if y+py <% sur.h: setPix(video, pitch, x+px, y+py, color)
|
if y+py <% sur.h: setPix(video, pitch, x+px, y+py, color)
|
||||||
if y-py <% sur.h: setPix(video, pitch, x+px, y-py, color)
|
if y-py <% sur.h: setPix(video, pitch, x+px, y-py, color)
|
||||||
|
|
||||||
if x-px <% sur.w:
|
if x-px <% sur.w:
|
||||||
if y+py <% sur.h: setPix(video, pitch, x-px, y+py, color)
|
if y+py <% sur.h: setPix(video, pitch, x-px, y+py, color)
|
||||||
if y-py <% sur.h: setPix(video, pitch, x-px, y-py, color)
|
if y-py <% sur.h: setPix(video, pitch, x-px, y-py, color)
|
||||||
|
|
@ -213,7 +213,7 @@ proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) =
|
||||||
if x+py <% sur.w:
|
if x+py <% sur.w:
|
||||||
if y+px <% sur.h: setPix(video, pitch, x+py, y+px, color)
|
if y+px <% sur.h: setPix(video, pitch, x+py, y+px, color)
|
||||||
if y-px <% sur.h: setPix(video, pitch, x+py, y-px, color)
|
if y-px <% sur.h: setPix(video, pitch, x+py, y-px, color)
|
||||||
|
|
||||||
if x-py <% sur.w:
|
if x-py <% sur.w:
|
||||||
if y+px <% sur.h: setPix(video, pitch, x-py, y+px, color)
|
if y+px <% sur.h: setPix(video, pitch, x-py, y+px, color)
|
||||||
if y-px <% sur.h: setPix(video, pitch, x-py, y-px, color)
|
if y-px <% sur.h: setPix(video, pitch, x-py, y-px, color)
|
||||||
|
|
@ -225,10 +225,10 @@ proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) =
|
||||||
py = py - 1
|
py = py - 1
|
||||||
px = px + 1
|
px = px + 1
|
||||||
|
|
||||||
proc `>-<`(val: int, s: PSurface): int {.inline.} =
|
proc `>-<`(val: int, s: PSurface): int {.inline.} =
|
||||||
return if val < 0: 0 elif val >= s.w: s.w-1 else: val
|
return if val < 0: 0 elif val >= s.w: s.w-1 else: val
|
||||||
|
|
||||||
proc `>|<`(val: int, s: PSurface): int {.inline.} =
|
proc `>|<`(val: int, s: PSurface): int {.inline.} =
|
||||||
return if val < 0: 0 elif val >= s.h: s.h-1 else: val
|
return if val < 0: 0 elif val >= s.h: s.h-1 else: val
|
||||||
|
|
||||||
proc drawLine*(sur: PSurface, p1, p2: Point, color: Color) =
|
proc drawLine*(sur: PSurface, p1, p2: Point, color: Color) =
|
||||||
|
|
@ -242,7 +242,7 @@ proc drawLine*(sur: PSurface, p1, p2: Point, color: Color) =
|
||||||
var dy = y1 - y0
|
var dy = y1 - y0
|
||||||
var dx = x1 - x0
|
var dx = x1 - x0
|
||||||
if dy < 0:
|
if dy < 0:
|
||||||
dy = -dy
|
dy = -dy
|
||||||
stepy = -1
|
stepy = -1
|
||||||
else:
|
else:
|
||||||
stepy = 1
|
stepy = 1
|
||||||
|
|
@ -251,7 +251,7 @@ proc drawLine*(sur: PSurface, p1, p2: Point, color: Color) =
|
||||||
stepx = -1
|
stepx = -1
|
||||||
else:
|
else:
|
||||||
stepx = 1
|
stepx = 1
|
||||||
dy = dy * 2
|
dy = dy * 2
|
||||||
dx = dx * 2
|
dx = dx * 2
|
||||||
var video = cast[PPixels](sur.s.pixels)
|
var video = cast[PPixels](sur.s.pixels)
|
||||||
var pitch = sur.s.pitch.int div ColSize
|
var pitch = sur.s.pitch.int div ColSize
|
||||||
|
|
@ -328,17 +328,17 @@ proc drawRect*(sur: PSurface, r: Rect, color: Color) =
|
||||||
if (r.x >= 0 and r.x <= sur.s.w) and (r.y >= 0 and r.y <= sur.s.h):
|
if (r.x >= 0 and r.x <= sur.s.w) and (r.y >= 0 and r.y <= sur.s.h):
|
||||||
var minW = min(sur.s.w - r.x, r.width)
|
var minW = min(sur.s.w - r.x, r.width)
|
||||||
var minH = min(sur.s.h - r.y, r.height)
|
var minH = min(sur.s.h - r.y, r.height)
|
||||||
|
|
||||||
# Draw Top
|
# Draw Top
|
||||||
for i in 0 .. minW - 1:
|
for i in 0 .. minW - 1:
|
||||||
setPix(video, pitch, r.x + i, r.y, color)
|
setPix(video, pitch, r.x + i, r.y, color)
|
||||||
setPix(video, pitch, r.x + i, r.y + minH - 1, color) # Draw bottom
|
setPix(video, pitch, r.x + i, r.y + minH - 1, color) # Draw bottom
|
||||||
|
|
||||||
# Draw left side
|
# Draw left side
|
||||||
for i in 0 .. minH - 1:
|
for i in 0 .. minH - 1:
|
||||||
setPix(video, pitch, r.x, r.y + i, color)
|
setPix(video, pitch, r.x, r.y + i, color)
|
||||||
setPix(video, pitch, r.x + minW - 1, r.y + i, color) # Draw right side
|
setPix(video, pitch, r.x + minW - 1, r.y + i, color) # Draw right side
|
||||||
|
|
||||||
proc fillRect*(sur: PSurface, r: Rect, col: Color) =
|
proc fillRect*(sur: PSurface, r: Rect, col: Color) =
|
||||||
## Fills a rectangle using sdl's ``FillRect`` function.
|
## Fills a rectangle using sdl's ``FillRect`` function.
|
||||||
var rect = toSdlRect(r)
|
var rect = toSdlRect(r)
|
||||||
|
|
@ -350,23 +350,23 @@ proc plot4EllipsePoints(sur: PSurface, cx, cy, x, y: Natural, col: Color) =
|
||||||
var pitch = sur.s.pitch.int div ColSize
|
var pitch = sur.s.pitch.int div ColSize
|
||||||
if cx+x <= sur.s.w-1:
|
if cx+x <= sur.s.w-1:
|
||||||
if cy+y <= sur.s.h-1: setPix(video, pitch, cx+x, cy+y, col)
|
if cy+y <= sur.s.h-1: setPix(video, pitch, cx+x, cy+y, col)
|
||||||
if cy-y <= sur.s.h-1: setPix(video, pitch, cx+x, cy-y, col)
|
if cy-y <= sur.s.h-1: setPix(video, pitch, cx+x, cy-y, col)
|
||||||
if cx-x <= sur.s.w-1:
|
if cx-x <= sur.s.w-1:
|
||||||
if cy+y <= sur.s.h-1: setPix(video, pitch, cx-x, cy+y, col)
|
if cy+y <= sur.s.h-1: setPix(video, pitch, cx-x, cy+y, col)
|
||||||
if cy-y <= sur.s.h-1: setPix(video, pitch, cx-x, cy-y, col)
|
if cy-y <= sur.s.h-1: setPix(video, pitch, cx-x, cy-y, col)
|
||||||
|
|
||||||
proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural,
|
proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural,
|
||||||
col: Color) =
|
col: Color) =
|
||||||
## Draws an ellipse, ``CX`` and ``CY`` specify the center X and Y of the
|
## Draws an ellipse, ``CX`` and ``CY`` specify the center X and Y of the
|
||||||
## ellipse, ``XRadius`` and ``YRadius`` specify half the width and height
|
## ellipse, ``XRadius`` and ``YRadius`` specify half the width and height
|
||||||
## of the ellipse.
|
## of the ellipse.
|
||||||
var
|
var
|
||||||
x, y: Natural
|
x, y: Natural
|
||||||
xChange, yChange: int
|
xChange, yChange: int
|
||||||
ellipseError: Natural
|
ellipseError: Natural
|
||||||
twoASquare, twoBSquare: Natural
|
twoASquare, twoBSquare: Natural
|
||||||
stoppingX, stoppingY: Natural
|
stoppingX, stoppingY: Natural
|
||||||
|
|
||||||
twoASquare = 2 * xRadius * xRadius
|
twoASquare = 2 * xRadius * xRadius
|
||||||
twoBSquare = 2 * yRadius * yRadius
|
twoBSquare = 2 * yRadius * yRadius
|
||||||
x = xRadius
|
x = xRadius
|
||||||
|
|
@ -376,7 +376,7 @@ proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural,
|
||||||
ellipseError = 0
|
ellipseError = 0
|
||||||
stoppingX = twoBSquare * xRadius
|
stoppingX = twoBSquare * xRadius
|
||||||
stoppingY = 0
|
stoppingY = 0
|
||||||
|
|
||||||
while stoppingX >= stoppingY: # 1st set of points, y` > - 1
|
while stoppingX >= stoppingY: # 1st set of points, y` > - 1
|
||||||
sur.plot4EllipsePoints(cx, cy, x, y, col)
|
sur.plot4EllipsePoints(cx, cy, x, y, col)
|
||||||
inc(y)
|
inc(y)
|
||||||
|
|
@ -388,7 +388,7 @@ proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural,
|
||||||
dec(stoppingX, twoBSquare)
|
dec(stoppingX, twoBSquare)
|
||||||
inc(ellipseError, xChange)
|
inc(ellipseError, xChange)
|
||||||
inc(xChange, twoBSquare)
|
inc(xChange, twoBSquare)
|
||||||
|
|
||||||
# 1st point set is done; start the 2nd set of points
|
# 1st point set is done; start the 2nd set of points
|
||||||
x = 0
|
x = 0
|
||||||
y = yRadius
|
y = yRadius
|
||||||
|
|
@ -408,7 +408,7 @@ proc drawEllipse*(sur: PSurface, cx, cy, xRadius, yRadius: Natural,
|
||||||
dec(stoppingY, twoASquare)
|
dec(stoppingY, twoASquare)
|
||||||
inc(ellipseError, yChange)
|
inc(ellipseError, yChange)
|
||||||
inc(yChange,twoASquare)
|
inc(yChange,twoASquare)
|
||||||
|
|
||||||
|
|
||||||
proc plotAA(sur: PSurface, x, y: int, c: float, color: Color) =
|
proc plotAA(sur: PSurface, x, y: int, c: float, color: Color) =
|
||||||
if (x > 0 and x < sur.s.w) and (y > 0 and y < sur.s.h):
|
if (x > 0 and x < sur.s.w) and (y > 0 and y < sur.s.h):
|
||||||
|
|
@ -419,43 +419,43 @@ proc plotAA(sur: PSurface, x, y: int, c: float, color: Color) =
|
||||||
|
|
||||||
setPix(video, pitch, x, y,
|
setPix(video, pitch, x, y,
|
||||||
pixColor.intensity(1.0 - c) + color.intensity(c))
|
pixColor.intensity(1.0 - c) + color.intensity(c))
|
||||||
|
|
||||||
|
|
||||||
template ipart(x: expr): expr = floor(x)
|
|
||||||
|
template ipart(x: expr): expr = floor(x)
|
||||||
template cround(x: expr): expr = ipart(x + 0.5)
|
template cround(x: expr): expr = ipart(x + 0.5)
|
||||||
template fpart(x: expr): expr = x - ipart(x)
|
template fpart(x: expr): expr = x - ipart(x)
|
||||||
template rfpart(x: expr): expr = 1.0 - fpart(x)
|
template rfpart(x: expr): expr = 1.0 - fpart(x)
|
||||||
|
|
||||||
proc drawLineAA*(sur: PSurface, p1, p2: Point, color: Color) =
|
proc drawLineAA*(sur: PSurface, p1, p2: Point, color: Color) =
|
||||||
## Draws a anti-aliased line from ``p1`` to ``p2``, using Xiaolin Wu's
|
## Draws a anti-aliased line from ``p1`` to ``p2``, using Xiaolin Wu's
|
||||||
## line algorithm
|
## line algorithm
|
||||||
var (x1, x2, y1, y2) = (p1.x.toFloat(), p2.x.toFloat(),
|
var (x1, x2, y1, y2) = (p1.x.toFloat(), p2.x.toFloat(),
|
||||||
p1.y.toFloat(), p2.y.toFloat())
|
p1.y.toFloat(), p2.y.toFloat())
|
||||||
var dx = x2 - x1
|
var dx = x2 - x1
|
||||||
var dy = y2 - y1
|
var dy = y2 - y1
|
||||||
|
|
||||||
var ax = dx
|
var ax = dx
|
||||||
if ax < 0'f64:
|
if ax < 0'f64:
|
||||||
ax = 0'f64 - ax
|
ax = 0'f64 - ax
|
||||||
var ay = dy
|
var ay = dy
|
||||||
if ay < 0'f64:
|
if ay < 0'f64:
|
||||||
ay = 0'f64 - ay
|
ay = 0'f64 - ay
|
||||||
|
|
||||||
if ax < ay:
|
if ax < ay:
|
||||||
swap(x1, y1)
|
swap(x1, y1)
|
||||||
swap(x2, y2)
|
swap(x2, y2)
|
||||||
swap(dx, dy)
|
swap(dx, dy)
|
||||||
|
|
||||||
template doPlot(x, y: int, c: float, color: Color): stmt =
|
template doPlot(x, y: int, c: float, color: Color): stmt =
|
||||||
if ax < ay:
|
if ax < ay:
|
||||||
sur.plotAA(y, x, c, color)
|
sur.plotAA(y, x, c, color)
|
||||||
else:
|
else:
|
||||||
sur.plotAA(x, y, c, color)
|
sur.plotAA(x, y, c, color)
|
||||||
|
|
||||||
if x2 < x1:
|
if x2 < x1:
|
||||||
swap(x1, x2)
|
swap(x1, x2)
|
||||||
swap(y1, y2)
|
swap(y1, y2)
|
||||||
|
|
||||||
var gradient = dy / dx
|
var gradient = dy / dx
|
||||||
# handle first endpoint
|
# handle first endpoint
|
||||||
var xend = cround(x1)
|
var xend = cround(x1)
|
||||||
|
|
@ -509,19 +509,19 @@ when not defined(testing) and isMainModule:
|
||||||
# Draw the shapes
|
# Draw the shapes
|
||||||
surf.drawLineAA((150, 170), (400, 471), colTan)
|
surf.drawLineAA((150, 170), (400, 471), colTan)
|
||||||
surf.drawLine((100, 170), (400, 471), colRed)
|
surf.drawLine((100, 170), (400, 471), colRed)
|
||||||
|
|
||||||
surf.drawEllipse(200, 300, 200, 30, colSeaGreen)
|
surf.drawEllipse(200, 300, 200, 30, colSeaGreen)
|
||||||
surf.drawHorLine(1, 300, 400, colViolet)
|
surf.drawHorLine(1, 300, 400, colViolet)
|
||||||
# Check if the ellipse is the size it's suppose to be.
|
# Check if the ellipse is the size it's suppose to be.
|
||||||
surf.drawVerLine(200, 300 - 30 + 1, 60, colViolet) # ^^ | i suppose it is
|
surf.drawVerLine(200, 300 - 30 + 1, 60, colViolet) # ^^ | i suppose it is
|
||||||
|
|
||||||
surf.drawEllipse(400, 300, 300, 300, colOrange)
|
surf.drawEllipse(400, 300, 300, 300, colOrange)
|
||||||
surf.drawEllipse(5, 5, 5, 5, colGreen)
|
surf.drawEllipse(5, 5, 5, 5, colGreen)
|
||||||
|
|
||||||
surf.drawHorLine(5, 5, 900, colRed)
|
surf.drawHorLine(5, 5, 900, colRed)
|
||||||
surf.drawVerLine(5, 60, 800, colRed)
|
surf.drawVerLine(5, 60, 800, colRed)
|
||||||
surf.drawCircle((600, 500), 60, colRed)
|
surf.drawCircle((600, 500), 60, colRed)
|
||||||
|
|
||||||
surf.fillRect((50, 50, 100, 100), colFuchsia)
|
surf.fillRect((50, 50, 100, 100), colFuchsia)
|
||||||
surf.fillRect((150, 50, 100, 100), colGreen)
|
surf.fillRect((150, 50, 100, 100), colGreen)
|
||||||
surf.drawRect((50, 150, 100, 100), colGreen)
|
surf.drawRect((50, 150, 100, 100), colGreen)
|
||||||
|
|
@ -530,12 +530,12 @@ when not defined(testing) and isMainModule:
|
||||||
surf.drawHorLine(250, 150, 100, colRed)
|
surf.drawHorLine(250, 150, 100, colRed)
|
||||||
|
|
||||||
surf.drawLineAA((592, 160), (592, 280), colPurple)
|
surf.drawLineAA((592, 160), (592, 280), colPurple)
|
||||||
|
|
||||||
#surf.drawText((300, 300), "TEST", colMidnightBlue)
|
#surf.drawText((300, 300), "TEST", colMidnightBlue)
|
||||||
#var textSize = textBounds("TEST")
|
#var textSize = textBounds("TEST")
|
||||||
#surf.drawText((300, 300 + textSize.height), $textSize.width & ", " &
|
#surf.drawText((300, 300 + textSize.height), $textSize.width & ", " &
|
||||||
# $textSize.height, colDarkGreen)
|
# $textSize.height, colDarkGreen)
|
||||||
|
|
||||||
var mouseStartX = -1
|
var mouseStartX = -1
|
||||||
var mouseStartY = -1
|
var mouseStartY = -1
|
||||||
withEvents(surf, event):
|
withEvents(surf, event):
|
||||||
|
|
@ -561,17 +561,17 @@ when not defined(testing) and isMainModule:
|
||||||
surf.drawLineAA((mouseStartX, mouseStartY), (int(mbd.x), int(mbd.y)), colPurple)
|
surf.drawLineAA((mouseStartX, mouseStartY), (int(mbd.x), int(mbd.y)), colPurple)
|
||||||
mouseStartX = -1
|
mouseStartX = -1
|
||||||
mouseStartY = -1
|
mouseStartY = -1
|
||||||
|
|
||||||
of sdl.MOUSEMOTION:
|
of sdl.MOUSEMOTION:
|
||||||
var mm = sdl.evMouseMotion(eventp)
|
var mm = sdl.evMouseMotion(eventp)
|
||||||
if mouseStartX != -1 and mouseStartY != -1:
|
if mouseStartX != -1 and mouseStartY != -1:
|
||||||
surf.drawLineAA((mouseStartX, mouseStartY), (int(mm.x), int(mm.y)), colPurple)
|
surf.drawLineAA((mouseStartX, mouseStartY), (int(mm.x), int(mm.y)), colPurple)
|
||||||
#echo(mm.x, " ", mm.y, " ", mm.yrel)
|
#echo(mm.x, " ", mm.y, " ", mm.yrel)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
discard "echo(event.kind)"
|
discard "echo(event.kind)"
|
||||||
|
|
||||||
sdl.updateRect(surf.s, 0, 0, 800, 600)
|
sdl.updateRect(surf.s, 0, 0, 800, 600)
|
||||||
|
|
||||||
surf.writeToBMP("test.bmp")
|
surf.writeToBMP("test.bmp")
|
||||||
sdl.quit()
|
sdl.quit()
|
||||||
|
|
|
||||||
|
|
@ -41,11 +41,11 @@ type
|
||||||
reExtended = 3, ## ignore whitespace and ``#`` comments
|
reExtended = 3, ## ignore whitespace and ``#`` comments
|
||||||
reStudy = 4 ## study the expression (may be omitted if the
|
reStudy = 4 ## study the expression (may be omitted if the
|
||||||
## expression will be used only once)
|
## expression will be used only once)
|
||||||
|
|
||||||
RegexDesc = object
|
RegexDesc = object
|
||||||
h: ptr Pcre
|
h: ptr Pcre
|
||||||
e: ptr ExtraData
|
e: ptr ExtraData
|
||||||
|
|
||||||
Regex* {.deprecated.} = ref RegexDesc ## a compiled regular expression
|
Regex* {.deprecated.} = ref RegexDesc ## a compiled regular expression
|
||||||
|
|
||||||
RegexError* = object of ValueError
|
RegexError* = object of ValueError
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## This module provides an easy to use sockets-style
|
## This module provides an easy to use sockets-style
|
||||||
## nim interface to the OpenSSL library.
|
## nim interface to the OpenSSL library.
|
||||||
|
|
||||||
{.deprecated.}
|
{.deprecated.}
|
||||||
|
|
@ -20,37 +20,37 @@ type
|
||||||
bio: BIO
|
bio: BIO
|
||||||
{.deprecated: [TSecureSocket: SecureSocket].}
|
{.deprecated: [TSecureSocket: SecureSocket].}
|
||||||
|
|
||||||
proc connect*(sock: var SecureSocket, address: string,
|
proc connect*(sock: var SecureSocket, address: string,
|
||||||
port: int): int =
|
port: int): int =
|
||||||
## Connects to the specified `address` on the specified `port`.
|
## Connects to the specified `address` on the specified `port`.
|
||||||
## Returns the result of the certificate validation.
|
## Returns the result of the certificate validation.
|
||||||
SslLoadErrorStrings()
|
SslLoadErrorStrings()
|
||||||
ERR_load_BIO_strings()
|
ERR_load_BIO_strings()
|
||||||
|
|
||||||
if SSL_library_init() != 1:
|
if SSL_library_init() != 1:
|
||||||
raiseOSError(osLastError())
|
raiseOSError(osLastError())
|
||||||
|
|
||||||
var ctx = SSL_CTX_new(SSLv23_client_method())
|
var ctx = SSL_CTX_new(SSLv23_client_method())
|
||||||
if ctx == nil:
|
if ctx == nil:
|
||||||
ERR_print_errors_fp(stderr)
|
ERR_print_errors_fp(stderr)
|
||||||
raiseOSError(osLastError())
|
raiseOSError(osLastError())
|
||||||
|
|
||||||
#if SSL_CTX_load_verify_locations(ctx,
|
#if SSL_CTX_load_verify_locations(ctx,
|
||||||
# "/tmp/openssl-0.9.8e/certs/vsign1.pem", NIL) == 0:
|
# "/tmp/openssl-0.9.8e/certs/vsign1.pem", NIL) == 0:
|
||||||
# echo("Failed load verify locations")
|
# echo("Failed load verify locations")
|
||||||
# ERR_print_errors_fp(stderr)
|
# ERR_print_errors_fp(stderr)
|
||||||
|
|
||||||
sock.bio = BIO_new_ssl_connect(ctx)
|
sock.bio = BIO_new_ssl_connect(ctx)
|
||||||
if BIO_get_ssl(sock.bio, addr(sock.ssl)) == 0:
|
if BIO_get_ssl(sock.bio, addr(sock.ssl)) == 0:
|
||||||
raiseOSError(osLastError())
|
raiseOSError(osLastError())
|
||||||
|
|
||||||
if BIO_set_conn_hostname(sock.bio, address & ":" & $port) != 1:
|
if BIO_set_conn_hostname(sock.bio, address & ":" & $port) != 1:
|
||||||
raiseOSError(osLastError())
|
raiseOSError(osLastError())
|
||||||
|
|
||||||
if BIO_do_connect(sock.bio) <= 0:
|
if BIO_do_connect(sock.bio) <= 0:
|
||||||
ERR_print_errors_fp(stderr)
|
ERR_print_errors_fp(stderr)
|
||||||
raiseOSError(osLastError())
|
raiseOSError(osLastError())
|
||||||
|
|
||||||
result = SSL_get_verify_result(sock.ssl)
|
result = SSL_get_verify_result(sock.ssl)
|
||||||
|
|
||||||
proc recvLine*(sock: SecureSocket, line: var TaintedString): bool =
|
proc recvLine*(sock: SecureSocket, line: var TaintedString): bool =
|
||||||
|
|
@ -86,12 +86,12 @@ proc close*(sock: SecureSocket) =
|
||||||
when not defined(testing) and isMainModule:
|
when not defined(testing) and isMainModule:
|
||||||
var s: SecureSocket
|
var s: SecureSocket
|
||||||
echo connect(s, "smtp.gmail.com", 465)
|
echo connect(s, "smtp.gmail.com", 465)
|
||||||
|
|
||||||
#var buffer: array[0..255, char]
|
#var buffer: array[0..255, char]
|
||||||
#echo BIO_read(bio, buffer, buffer.len)
|
#echo BIO_read(bio, buffer, buffer.len)
|
||||||
var buffer: string = ""
|
var buffer: string = ""
|
||||||
|
|
||||||
echo s.recvLine(buffer)
|
echo s.recvLine(buffer)
|
||||||
echo buffer
|
echo buffer
|
||||||
echo buffer.len
|
echo buffer.len
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## Main file to generate a DLL from the standard library.
|
## Main file to generate a DLL from the standard library.
|
||||||
## The default Nimrtl does not only contain the ``system`` module, but these
|
## The default Nimrtl does not only contain the ``system`` module, but these
|
||||||
## too:
|
## too:
|
||||||
##
|
##
|
||||||
## * parseutils
|
## * parseutils
|
||||||
|
|
@ -22,12 +22,12 @@
|
||||||
## * unicode
|
## * unicode
|
||||||
## * pegs
|
## * pegs
|
||||||
## * ropes
|
## * ropes
|
||||||
##
|
##
|
||||||
|
|
||||||
when system.appType != "lib":
|
when system.appType != "lib":
|
||||||
{.error: "This file has to be compiled as a library!".}
|
{.error: "This file has to be compiled as a library!".}
|
||||||
|
|
||||||
when not defined(createNimRtl):
|
when not defined(createNimRtl):
|
||||||
{.error: "This file has to be compiled with '-d:createNimRtl'".}
|
{.error: "This file has to be compiled with '-d:createNimRtl'".}
|
||||||
|
|
||||||
import
|
import
|
||||||
|
|
|
||||||
|
|
@ -29,34 +29,34 @@ const
|
||||||
|
|
||||||
# Valid opcodes ( "op" parameter ) to issue to epoll_ctl().
|
# Valid opcodes ( "op" parameter ) to issue to epoll_ctl().
|
||||||
|
|
||||||
const
|
const
|
||||||
EPOLL_CTL_ADD* = 1 # Add a file descriptor to the interface.
|
EPOLL_CTL_ADD* = 1 # Add a file descriptor to the interface.
|
||||||
EPOLL_CTL_DEL* = 2 # Remove a file descriptor from the interface.
|
EPOLL_CTL_DEL* = 2 # Remove a file descriptor from the interface.
|
||||||
EPOLL_CTL_MOD* = 3 # Change file descriptor epoll_event structure.
|
EPOLL_CTL_MOD* = 3 # Change file descriptor epoll_event structure.
|
||||||
|
|
||||||
type
|
type
|
||||||
epoll_data* {.importc: "union epoll_data",
|
epoll_data* {.importc: "union epoll_data",
|
||||||
header: "<sys/epoll.h>", pure, final.} = object # TODO: This is actually a union.
|
header: "<sys/epoll.h>", pure, final.} = object # TODO: This is actually a union.
|
||||||
#thePtr* {.importc: "ptr".}: pointer
|
#thePtr* {.importc: "ptr".}: pointer
|
||||||
fd* {.importc: "fd".}: cint # \
|
fd* {.importc: "fd".}: cint # \
|
||||||
#u32*: uint32
|
#u32*: uint32
|
||||||
#u64*: uint64
|
#u64*: uint64
|
||||||
|
|
||||||
epoll_event* {.importc: "struct epoll_event", header: "<sys/epoll.h>", pure, final.} = object
|
epoll_event* {.importc: "struct epoll_event", header: "<sys/epoll.h>", pure, final.} = object
|
||||||
events*: uint32 # Epoll events
|
events*: uint32 # Epoll events
|
||||||
data*: epoll_data # User data variable
|
data*: epoll_data # User data variable
|
||||||
|
|
||||||
proc epoll_create*(size: cint): cint {.importc: "epoll_create",
|
proc epoll_create*(size: cint): cint {.importc: "epoll_create",
|
||||||
header: "<sys/epoll.h>".}
|
header: "<sys/epoll.h>".}
|
||||||
## Creates an epoll instance. Returns an fd for the new instance.
|
## Creates an epoll instance. Returns an fd for the new instance.
|
||||||
## The "size" parameter is a hint specifying the number of file
|
## The "size" parameter is a hint specifying the number of file
|
||||||
## descriptors to be associated with the new instance. The fd
|
## descriptors to be associated with the new instance. The fd
|
||||||
## returned by epoll_create() should be closed with close().
|
## returned by epoll_create() should be closed with close().
|
||||||
|
|
||||||
proc epoll_create1*(flags: cint): cint {.importc: "epoll_create1",
|
proc epoll_create1*(flags: cint): cint {.importc: "epoll_create1",
|
||||||
header: "<sys/epoll.h>".}
|
header: "<sys/epoll.h>".}
|
||||||
## Same as epoll_create but with an FLAGS parameter. The unused SIZE
|
## Same as epoll_create but with an FLAGS parameter. The unused SIZE
|
||||||
## parameter has been dropped.
|
## parameter has been dropped.
|
||||||
|
|
||||||
proc epoll_ctl*(epfd: cint; op: cint; fd: cint | SocketHandle; event: ptr epoll_event): cint {.
|
proc epoll_ctl*(epfd: cint; op: cint; fd: cint | SocketHandle; event: ptr epoll_event): cint {.
|
||||||
importc: "epoll_ctl", header: "<sys/epoll.h>".}
|
importc: "epoll_ctl", header: "<sys/epoll.h>".}
|
||||||
|
|
@ -65,10 +65,10 @@ proc epoll_ctl*(epfd: cint; op: cint; fd: cint | SocketHandle; event: ptr epoll_
|
||||||
## specific error code ) The "op" parameter is one of the EPOLL_CTL_*
|
## specific error code ) The "op" parameter is one of the EPOLL_CTL_*
|
||||||
## constants defined above. The "fd" parameter is the target of the
|
## constants defined above. The "fd" parameter is the target of the
|
||||||
## operation. The "event" parameter describes which events the caller
|
## operation. The "event" parameter describes which events the caller
|
||||||
## is interested in and any associated user data.
|
## is interested in and any associated user data.
|
||||||
|
|
||||||
proc epoll_wait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
|
proc epoll_wait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
|
||||||
timeout: cint): cint {.importc: "epoll_wait",
|
timeout: cint): cint {.importc: "epoll_wait",
|
||||||
header: "<sys/epoll.h>".}
|
header: "<sys/epoll.h>".}
|
||||||
## Wait for events on an epoll instance "epfd". Returns the number of
|
## Wait for events on an epoll instance "epfd". Returns the number of
|
||||||
## triggered events returned in "events" buffer. Or -1 in case of
|
## triggered events returned in "events" buffer. Or -1 in case of
|
||||||
|
|
@ -82,11 +82,11 @@ proc epoll_wait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
|
||||||
## __THROW.
|
## __THROW.
|
||||||
|
|
||||||
|
|
||||||
#proc epoll_pwait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
|
#proc epoll_pwait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
|
||||||
# timeout: cint; ss: ptr sigset_t): cint {.
|
# timeout: cint; ss: ptr sigset_t): cint {.
|
||||||
# importc: "epoll_pwait", header: "<sys/epoll.h>".}
|
# importc: "epoll_pwait", header: "<sys/epoll.h>".}
|
||||||
# Same as epoll_wait, but the thread's signal mask is temporarily
|
# Same as epoll_wait, but the thread's signal mask is temporarily
|
||||||
# and atomically replaced with the one provided as parameter.
|
# and atomically replaced with the one provided as parameter.
|
||||||
#
|
#
|
||||||
# This function is a cancellation point and therefore not marked with
|
# This function is a cancellation point and therefore not marked with
|
||||||
# __THROW.
|
# __THROW.
|
||||||
|
|
|
||||||
|
|
@ -9,65 +9,65 @@
|
||||||
|
|
||||||
{.deadCodeElim:on.}
|
{.deadCodeElim:on.}
|
||||||
|
|
||||||
# Get the platform-dependent flags.
|
# Get the platform-dependent flags.
|
||||||
# Structure describing an inotify event.
|
# Structure describing an inotify event.
|
||||||
type
|
type
|
||||||
InotifyEvent*{.pure, final, importc: "struct inotify_event",
|
InotifyEvent*{.pure, final, importc: "struct inotify_event",
|
||||||
header: "<sys/inotify.h>".} = object
|
header: "<sys/inotify.h>".} = object
|
||||||
wd*{.importc: "wd".}: cint # Watch descriptor.
|
wd*{.importc: "wd".}: cint # Watch descriptor.
|
||||||
mask*{.importc: "mask".}: uint32 # Watch mask.
|
mask*{.importc: "mask".}: uint32 # Watch mask.
|
||||||
cookie*{.importc: "cookie".}: uint32 # Cookie to synchronize two events.
|
cookie*{.importc: "cookie".}: uint32 # Cookie to synchronize two events.
|
||||||
len*{.importc: "len".}: uint32 # Length (including NULs) of name.
|
len*{.importc: "len".}: uint32 # Length (including NULs) of name.
|
||||||
name*{.importc: "name".}: char # Name.
|
name*{.importc: "name".}: char # Name.
|
||||||
{.deprecated: [Tinotify_event: InotifyEvent].}
|
{.deprecated: [Tinotify_event: InotifyEvent].}
|
||||||
|
|
||||||
# Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH.
|
# Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH.
|
||||||
const
|
const
|
||||||
IN_ACCESS* = 0x00000001 # File was accessed.
|
IN_ACCESS* = 0x00000001 # File was accessed.
|
||||||
IN_MODIFY* = 0x00000002 # File was modified.
|
IN_MODIFY* = 0x00000002 # File was modified.
|
||||||
IN_ATTRIB* = 0x00000004 # Metadata changed.
|
IN_ATTRIB* = 0x00000004 # Metadata changed.
|
||||||
IN_CLOSE_WRITE* = 0x00000008 # Writtable file was closed.
|
IN_CLOSE_WRITE* = 0x00000008 # Writtable file was closed.
|
||||||
IN_CLOSE_NOWRITE* = 0x00000010 # Unwrittable file closed.
|
IN_CLOSE_NOWRITE* = 0x00000010 # Unwrittable file closed.
|
||||||
IN_CLOSE* = (IN_CLOSE_WRITE or IN_CLOSE_NOWRITE) # Close.
|
IN_CLOSE* = (IN_CLOSE_WRITE or IN_CLOSE_NOWRITE) # Close.
|
||||||
IN_OPEN* = 0x00000020 # File was opened.
|
IN_OPEN* = 0x00000020 # File was opened.
|
||||||
IN_MOVED_FROM* = 0x00000040 # File was moved from X.
|
IN_MOVED_FROM* = 0x00000040 # File was moved from X.
|
||||||
IN_MOVED_TO* = 0x00000080 # File was moved to Y.
|
IN_MOVED_TO* = 0x00000080 # File was moved to Y.
|
||||||
IN_MOVE* = (IN_MOVED_FROM or IN_MOVED_TO) # Moves.
|
IN_MOVE* = (IN_MOVED_FROM or IN_MOVED_TO) # Moves.
|
||||||
IN_CREATE* = 0x00000100 # Subfile was created.
|
IN_CREATE* = 0x00000100 # Subfile was created.
|
||||||
IN_DELETE* = 0x00000200 # Subfile was deleted.
|
IN_DELETE* = 0x00000200 # Subfile was deleted.
|
||||||
IN_DELETE_SELF* = 0x00000400 # Self was deleted.
|
IN_DELETE_SELF* = 0x00000400 # Self was deleted.
|
||||||
IN_MOVE_SELF* = 0x00000800 # Self was moved.
|
IN_MOVE_SELF* = 0x00000800 # Self was moved.
|
||||||
# Events sent by the kernel.
|
# Events sent by the kernel.
|
||||||
const
|
const
|
||||||
IN_UNMOUNT* = 0x00002000 # Backing fs was unmounted.
|
IN_UNMOUNT* = 0x00002000 # Backing fs was unmounted.
|
||||||
IN_Q_OVERFLOW* = 0x00004000 # Event queued overflowed.
|
IN_Q_OVERFLOW* = 0x00004000 # Event queued overflowed.
|
||||||
IN_IGNORED* = 0x00008000 # File was ignored.
|
IN_IGNORED* = 0x00008000 # File was ignored.
|
||||||
# Special flags.
|
# Special flags.
|
||||||
const
|
const
|
||||||
IN_ONLYDIR* = 0x01000000 # Only watch the path if it is a
|
IN_ONLYDIR* = 0x01000000 # Only watch the path if it is a
|
||||||
# directory.
|
# directory.
|
||||||
IN_DONT_FOLLOW* = 0x02000000 # Do not follow a sym link.
|
IN_DONT_FOLLOW* = 0x02000000 # Do not follow a sym link.
|
||||||
IN_EXCL_UNLINK* = 0x04000000 # Exclude events on unlinked
|
IN_EXCL_UNLINK* = 0x04000000 # Exclude events on unlinked
|
||||||
# objects.
|
# objects.
|
||||||
IN_MASK_ADD* = 0x20000000 # Add to the mask of an already
|
IN_MASK_ADD* = 0x20000000 # Add to the mask of an already
|
||||||
# existing watch.
|
# existing watch.
|
||||||
IN_ISDIR* = 0x40000000 # Event occurred against dir.
|
IN_ISDIR* = 0x40000000 # Event occurred against dir.
|
||||||
IN_ONESHOT* = 0x80000000 # Only send event once.
|
IN_ONESHOT* = 0x80000000 # Only send event once.
|
||||||
# All events which a program can wait on.
|
# All events which a program can wait on.
|
||||||
const
|
const
|
||||||
IN_ALL_EVENTS* = (IN_ACCESS or IN_MODIFY or IN_ATTRIB or IN_CLOSE_WRITE or
|
IN_ALL_EVENTS* = (IN_ACCESS or IN_MODIFY or IN_ATTRIB or IN_CLOSE_WRITE or
|
||||||
IN_CLOSE_NOWRITE or IN_OPEN or IN_MOVED_FROM or IN_MOVED_TO or
|
IN_CLOSE_NOWRITE or IN_OPEN or IN_MOVED_FROM or IN_MOVED_TO or
|
||||||
IN_CREATE or IN_DELETE or IN_DELETE_SELF or IN_MOVE_SELF)
|
IN_CREATE or IN_DELETE or IN_DELETE_SELF or IN_MOVE_SELF)
|
||||||
# Create and initialize inotify instance.
|
# Create and initialize inotify instance.
|
||||||
proc inotify_init*(): cint{.cdecl, importc: "inotify_init",
|
proc inotify_init*(): cint{.cdecl, importc: "inotify_init",
|
||||||
header: "<sys/inotify.h>".}
|
header: "<sys/inotify.h>".}
|
||||||
# Create and initialize inotify instance.
|
# Create and initialize inotify instance.
|
||||||
proc inotify_init1*(flags: cint): cint{.cdecl, importc: "inotify_init1",
|
proc inotify_init1*(flags: cint): cint{.cdecl, importc: "inotify_init1",
|
||||||
header: "<sys/inotify.h>".}
|
header: "<sys/inotify.h>".}
|
||||||
# Add watch of object NAME to inotify instance FD. Notify about
|
# Add watch of object NAME to inotify instance FD. Notify about
|
||||||
# events specified by MASK.
|
# events specified by MASK.
|
||||||
proc inotify_add_watch*(fd: cint; name: cstring; mask: uint32): cint{.
|
proc inotify_add_watch*(fd: cint; name: cstring; mask: uint32): cint{.
|
||||||
cdecl, importc: "inotify_add_watch", header: "<sys/inotify.h>".}
|
cdecl, importc: "inotify_add_watch", header: "<sys/inotify.h>".}
|
||||||
# Remove the watch specified by WD from the inotify instance FD.
|
# Remove the watch specified by WD from the inotify instance FD.
|
||||||
proc inotify_rm_watch*(fd: cint; wd: cint): cint{.cdecl,
|
proc inotify_rm_watch*(fd: cint; wd: cint): cint{.cdecl,
|
||||||
importc: "inotify_rm_watch", header: "<sys/inotify.h>".}
|
importc: "inotify_rm_watch", header: "<sys/inotify.h>".}
|
||||||
|
|
|
||||||
|
|
@ -24,5 +24,5 @@ const
|
||||||
|
|
||||||
# fn should be of type proc (a2: pointer): void {.cdecl.}
|
# fn should be of type proc (a2: pointer): void {.cdecl.}
|
||||||
proc clone*(fn: pointer; child_stack: pointer; flags: cint;
|
proc clone*(fn: pointer; child_stack: pointer; flags: cint;
|
||||||
arg: pointer; ptid: ptr Pid; tls: pointer;
|
arg: pointer; ptid: ptr Pid; tls: pointer;
|
||||||
ctid: ptr Pid): cint {.importc, header: "<sched.h>".}
|
ctid: ptr Pid): cint {.importc, header: "<sched.h>".}
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ type
|
||||||
Actor[In, Out] = object{.pure, final.}
|
Actor[In, Out] = object{.pure, final.}
|
||||||
i: Channel[Task[In, Out]]
|
i: Channel[Task[In, Out]]
|
||||||
t: TThread[ptr Actor[In, Out]]
|
t: TThread[ptr Actor[In, Out]]
|
||||||
|
|
||||||
PActor*[In, Out] = ptr Actor[In, Out] ## an actor
|
PActor*[In, Out] = ptr Actor[In, Out] ## an actor
|
||||||
{.deprecated: [TTask: Task, TActor: Actor].}
|
{.deprecated: [TTask: Task, TActor: Actor].}
|
||||||
|
|
||||||
|
|
@ -83,7 +83,7 @@ proc send*[In, Out, X, Y](receiver: PActor[In, Out], msg: In,
|
||||||
shallowCopy(t.data, msg)
|
shallowCopy(t.data, msg)
|
||||||
send(receiver.i, t)
|
send(receiver.i, t)
|
||||||
|
|
||||||
proc send*[In, Out](receiver: PActor[In, Out], msg: In,
|
proc send*[In, Out](receiver: PActor[In, Out], msg: In,
|
||||||
sender: ptr Channel[Out] = nil) =
|
sender: ptr Channel[Out] = nil) =
|
||||||
## sends a message to `receiver`'s inbox.
|
## sends a message to `receiver`'s inbox.
|
||||||
var t: Task[In, Out]
|
var t: Task[In, Out]
|
||||||
|
|
@ -138,7 +138,7 @@ proc createActorPool*[In, Out](a: var ActorPool[In, Out], poolSize = 4) =
|
||||||
|
|
||||||
proc sync*[In, Out](a: var ActorPool[In, Out], polling=50) =
|
proc sync*[In, Out](a: var ActorPool[In, Out], polling=50) =
|
||||||
## waits for every actor of `a` to finish with its work. Currently this is
|
## waits for every actor of `a` to finish with its work. Currently this is
|
||||||
## implemented as polling every `polling` ms and has a slight chance
|
## implemented as polling every `polling` ms and has a slight chance
|
||||||
## of failing since we check for every actor to be in `ready` state and not
|
## of failing since we check for every actor to be in `ready` state and not
|
||||||
## for messages still in ether. This will change in a later
|
## for messages still in ether. This will change in a later
|
||||||
## version, however.
|
## version, however.
|
||||||
|
|
@ -146,7 +146,7 @@ proc sync*[In, Out](a: var ActorPool[In, Out], polling=50) =
|
||||||
while true:
|
while true:
|
||||||
var wait = false
|
var wait = false
|
||||||
for i in 0..high(a.actors):
|
for i in 0..high(a.actors):
|
||||||
if not a.actors[i].i.ready:
|
if not a.actors[i].i.ready:
|
||||||
wait = true
|
wait = true
|
||||||
allReadyCount = 0
|
allReadyCount = 0
|
||||||
break
|
break
|
||||||
|
|
@ -222,7 +222,7 @@ proc spawn*[In](p: var ActorPool[In, void], input: In,
|
||||||
var t: Task[In, void]
|
var t: Task[In, void]
|
||||||
setupTask()
|
setupTask()
|
||||||
schedule()
|
schedule()
|
||||||
|
|
||||||
when not defined(testing) and isMainModule:
|
when not defined(testing) and isMainModule:
|
||||||
var
|
var
|
||||||
a: ActorPool[int, void]
|
a: ActorPool[int, void]
|
||||||
|
|
|
||||||
|
|
@ -195,10 +195,10 @@ proc read*(f: AsyncFile, size: int): Future[string] =
|
||||||
readBuffer.setLen(res)
|
readBuffer.setLen(res)
|
||||||
f.offset.inc(res)
|
f.offset.inc(res)
|
||||||
retFuture.complete(readBuffer)
|
retFuture.complete(readBuffer)
|
||||||
|
|
||||||
if not cb(f.fd):
|
if not cb(f.fd):
|
||||||
addRead(f.fd, cb)
|
addRead(f.fd, cb)
|
||||||
|
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
proc readLine*(f: AsyncFile): Future[string] {.async.} =
|
proc readLine*(f: AsyncFile): Future[string] {.async.} =
|
||||||
|
|
@ -222,7 +222,7 @@ proc getFilePos*(f: AsyncFile): int64 =
|
||||||
|
|
||||||
proc setFilePos*(f: AsyncFile, pos: int64) =
|
proc setFilePos*(f: AsyncFile, pos: int64) =
|
||||||
## Sets the position of the file pointer that is used for read/write
|
## Sets the position of the file pointer that is used for read/write
|
||||||
## operations. The file's first byte has the index zero.
|
## operations. The file's first byte has the index zero.
|
||||||
f.offset = pos
|
f.offset = pos
|
||||||
when not defined(windows) and not defined(nimdoc):
|
when not defined(windows) and not defined(nimdoc):
|
||||||
let ret = lseek(f.fd.cint, pos, SEEK_SET)
|
let ret = lseek(f.fd.cint, pos, SEEK_SET)
|
||||||
|
|
@ -291,7 +291,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
|
||||||
retFuture.complete()
|
retFuture.complete()
|
||||||
else:
|
else:
|
||||||
var written = 0
|
var written = 0
|
||||||
|
|
||||||
proc cb(fd: AsyncFD): bool =
|
proc cb(fd: AsyncFD): bool =
|
||||||
result = true
|
result = true
|
||||||
let remainderSize = data.len-written
|
let remainderSize = data.len-written
|
||||||
|
|
@ -309,7 +309,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
|
||||||
result = false # We still have data to write.
|
result = false # We still have data to write.
|
||||||
else:
|
else:
|
||||||
retFuture.complete()
|
retFuture.complete()
|
||||||
|
|
||||||
if not cb(f.fd):
|
if not cb(f.fd):
|
||||||
addWrite(f.fd, cb)
|
addWrite(f.fd, cb)
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue