mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-18 05:20:31 +00:00
Merge branch 'devel' into gogc
This commit is contained in:
0
bootstrap.sh
Normal file → Executable file
0
bootstrap.sh
Normal file → Executable file
@@ -1,6 +1,6 @@
|
||||
[Package]
|
||||
name = "compiler"
|
||||
version = "0.10.3"
|
||||
version = "0.11.3"
|
||||
author = "Andreas Rumpf"
|
||||
description = "Compiler package providing the compiler sources as a library."
|
||||
license = "MIT"
|
||||
@@ -8,4 +8,4 @@ license = "MIT"
|
||||
InstallDirs = "doc, compiler"
|
||||
|
||||
[Deps]
|
||||
Requires: "nim >= 0.10.3"
|
||||
Requires: "nim >= 0.11.3"
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
# abstract syntax tree + symbol table
|
||||
|
||||
import
|
||||
msgs, hashes, nversion, options, strutils, crc, ropes, idents, lists,
|
||||
msgs, hashes, nversion, options, strutils, securehash, ropes, idents, lists,
|
||||
intsets, idgen
|
||||
|
||||
type
|
||||
@@ -423,6 +423,7 @@ type
|
||||
# but unfortunately it has measurable impact for compilation
|
||||
# efficiency
|
||||
nfTransf, # node has been transformed
|
||||
nfNoRewrite # node should not be transformed anymore
|
||||
nfSem # node has been checked for semantics
|
||||
nfLL # node has gone through lambda lifting
|
||||
nfDotField # the call can use a dot operator
|
||||
@@ -538,22 +539,21 @@ type
|
||||
mIncl, mExcl, mCard, mChr,
|
||||
mGCref, mGCunref,
|
||||
|
||||
mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64,
|
||||
mDivI64, mModI64, mSucc, mPred,
|
||||
mAddI, mSubI, mMulI, mDivI, mModI,
|
||||
mSucc, mPred,
|
||||
mAddF64, mSubF64, mMulF64, mDivF64,
|
||||
|
||||
mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI,
|
||||
mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64,
|
||||
mMinF64, mMaxF64, mAddU, mSubU, mMulU,
|
||||
mDivU, mModU, mEqI, mLeI,
|
||||
mLtI,
|
||||
mEqI64, mLeI64, mLtI64, mEqF64, mLeF64, mLtF64,
|
||||
mEqF64, mLeF64, mLtF64,
|
||||
mLeU, mLtU, mLeU64, mLtU64,
|
||||
mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef,
|
||||
mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mEqProc, mUnaryMinusI,
|
||||
mUnaryMinusI64, mAbsI, mAbsI64, mNot,
|
||||
mUnaryMinusI64, mAbsI, mNot,
|
||||
mUnaryPlusI, mBitnotI,
|
||||
mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64,
|
||||
mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64,
|
||||
mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32,
|
||||
mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr,
|
||||
mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr,
|
||||
@@ -593,20 +593,19 @@ const
|
||||
mPred, mInc, mDec, mOrd, mLengthOpenArray,
|
||||
mLengthStr, mLengthArray, mLengthSeq, mXLenStr, mXLenSeq,
|
||||
mIncl, mExcl, mCard, mChr,
|
||||
mAddI, mSubI, mMulI, mDivI, mModI, mAddI64, mSubI64, mMulI64,
|
||||
mDivI64, mModI64, mAddF64, mSubF64, mMulF64, mDivF64,
|
||||
mAddI, mSubI, mMulI, mDivI, mModI,
|
||||
mAddF64, mSubF64, mMulF64, mDivF64,
|
||||
mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI,
|
||||
mShrI64, mShlI64, mBitandI64, mBitorI64, mBitxorI64,
|
||||
mMinF64, mMaxF64, mAddU, mSubU, mMulU,
|
||||
mDivU, mModU, mEqI, mLeI,
|
||||
mLtI,
|
||||
mEqI64, mLeI64, mLtI64, mEqF64, mLeF64, mLtF64,
|
||||
mEqF64, mLeF64, mLtF64,
|
||||
mLeU, mLtU, mLeU64, mLtU64,
|
||||
mEqEnum, mLeEnum, mLtEnum, mEqCh, mLeCh, mLtCh, mEqB, mLeB, mLtB, mEqRef,
|
||||
mEqProc, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mUnaryMinusI,
|
||||
mUnaryMinusI64, mAbsI, mAbsI64, mNot,
|
||||
mUnaryMinusI64, mAbsI, mNot,
|
||||
mUnaryPlusI, mBitnotI,
|
||||
mBitnotI64, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64,
|
||||
mUnaryPlusF64, mUnaryMinusF64, mAbsF64, mZe8ToI, mZe8ToI64,
|
||||
mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32,
|
||||
mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr,
|
||||
mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr,
|
||||
@@ -842,7 +841,7 @@ type
|
||||
data*: TIdNodePairSeq
|
||||
|
||||
TNodePair* = object
|
||||
h*: THash # because it is expensive to compute!
|
||||
h*: Hash # because it is expensive to compute!
|
||||
key*: PNode
|
||||
val*: int
|
||||
|
||||
@@ -947,6 +946,9 @@ proc add*(father, son: PNode) =
|
||||
proc `[]`*(n: PNode, i: int): PNode {.inline.} =
|
||||
result = n.sons[i]
|
||||
|
||||
template `-|`*(b, s: expr): expr =
|
||||
(if b >= 0: b else: s.len + b)
|
||||
|
||||
# son access operators with support for negative indices
|
||||
template `{}`*(n: PNode, i: int): expr = n[i -| n]
|
||||
template `{}=`*(n: PNode, i: int, s: PNode): stmt =
|
||||
@@ -1345,7 +1347,7 @@ proc propagateToOwner*(owner, elem: PType) =
|
||||
owner.flags.incl tfHasAsgn
|
||||
|
||||
if owner.kind notin {tyProc, tyGenericInst, tyGenericBody,
|
||||
tyGenericInvocation}:
|
||||
tyGenericInvocation, tyPtr}:
|
||||
let elemB = elem.skipTypes({tyGenericInst})
|
||||
if elemB.isGCedMem or tfHasGCedMem in elemB.flags:
|
||||
# for simplicity, we propagate this flag even to generics. We then
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
import
|
||||
ast, hashes, intsets, strutils, options, msgs, ropes, idents, rodutils
|
||||
|
||||
proc hashNode*(p: RootRef): THash
|
||||
proc hashNode*(p: RootRef): Hash
|
||||
proc treeToYaml*(n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope
|
||||
# Convert a tree into its YAML representation; this is used by the
|
||||
# YAML code generator and it is invaluable for debugging purposes.
|
||||
@@ -49,7 +49,7 @@ proc strTableGet*(t: TStrTable, name: PIdent): PSym
|
||||
|
||||
type
|
||||
TTabIter*{.final.} = object # consider all fields here private
|
||||
h*: THash # current hash
|
||||
h*: Hash # current hash
|
||||
|
||||
proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym
|
||||
proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym
|
||||
@@ -65,7 +65,7 @@ proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym
|
||||
|
||||
type
|
||||
TIdentIter*{.final.} = object # iterator over all syms with same identifier
|
||||
h*: THash # current hash
|
||||
h*: Hash # current hash
|
||||
name*: PIdent
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ proc getSymFromList*(list: PNode, ident: PIdent, start: int = 0): PSym
|
||||
proc lookupInRecord*(n: PNode, field: PIdent): PSym
|
||||
proc getModule*(s: PSym): PSym
|
||||
proc mustRehash*(length, counter: int): bool
|
||||
proc nextTry*(h, maxHash: THash): THash {.inline.}
|
||||
proc nextTry*(h, maxHash: Hash): Hash {.inline.}
|
||||
|
||||
# ------------- table[int, int] ---------------------------------------------
|
||||
const
|
||||
@@ -196,7 +196,7 @@ proc getSymFromList(list: PNode, ident: PIdent, start: int = 0): PSym =
|
||||
else: internalError(list.info, "getSymFromList")
|
||||
result = nil
|
||||
|
||||
proc hashNode(p: RootRef): THash =
|
||||
proc hashNode(p: RootRef): Hash =
|
||||
result = hash(cast[pointer](p))
|
||||
|
||||
proc mustRehash(length, counter: int): bool =
|
||||
@@ -466,7 +466,7 @@ proc debug(n: PNode) =
|
||||
const
|
||||
EmptySeq = @[]
|
||||
|
||||
proc nextTry(h, maxHash: THash): THash =
|
||||
proc nextTry(h, maxHash: Hash): Hash =
|
||||
result = ((5 * h) + 1) and maxHash
|
||||
# For any initial h in range(maxHash), repeating that maxHash times
|
||||
# generates each int in range(maxHash) exactly once (see any text on
|
||||
@@ -474,7 +474,7 @@ proc nextTry(h, maxHash: THash): THash =
|
||||
|
||||
proc objectSetContains(t: TObjectSet, obj: RootRef): bool =
|
||||
# returns true whether n is in t
|
||||
var h: THash = hashNode(obj) and high(t.data) # start with real hash value
|
||||
var h: Hash = hashNode(obj) and high(t.data) # start with real hash value
|
||||
while t.data[h] != nil:
|
||||
if t.data[h] == obj:
|
||||
return true
|
||||
@@ -482,7 +482,7 @@ proc objectSetContains(t: TObjectSet, obj: RootRef): bool =
|
||||
result = false
|
||||
|
||||
proc objectSetRawInsert(data: var TObjectSeq, obj: RootRef) =
|
||||
var h: THash = hashNode(obj) and high(data)
|
||||
var h: Hash = hashNode(obj) and high(data)
|
||||
while data[h] != nil:
|
||||
assert(data[h] != obj)
|
||||
h = nextTry(h, high(data))
|
||||
@@ -503,7 +503,7 @@ proc objectSetIncl(t: var TObjectSet, obj: RootRef) =
|
||||
|
||||
proc objectSetContainsOrIncl(t: var TObjectSet, obj: RootRef): bool =
|
||||
# returns true if obj is already in the string table:
|
||||
var h: THash = hashNode(obj) and high(t.data)
|
||||
var h: Hash = hashNode(obj) and high(t.data)
|
||||
while true:
|
||||
var it = t.data[h]
|
||||
if it == nil: break
|
||||
@@ -520,7 +520,7 @@ proc objectSetContainsOrIncl(t: var TObjectSet, obj: RootRef): bool =
|
||||
result = false
|
||||
|
||||
proc tableRawGet(t: TTable, key: RootRef): int =
|
||||
var h: THash = hashNode(key) and high(t.data) # start with real hash value
|
||||
var h: Hash = hashNode(key) and high(t.data) # start with real hash value
|
||||
while t.data[h].key != nil:
|
||||
if t.data[h].key == key:
|
||||
return h
|
||||
@@ -529,7 +529,7 @@ proc tableRawGet(t: TTable, key: RootRef): int =
|
||||
|
||||
proc tableSearch(t: TTable, key, closure: RootRef,
|
||||
comparator: TCmpProc): RootRef =
|
||||
var h: THash = hashNode(key) and high(t.data) # start with real hash value
|
||||
var h: Hash = hashNode(key) and high(t.data) # start with real hash value
|
||||
while t.data[h].key != nil:
|
||||
if t.data[h].key == key:
|
||||
if comparator(t.data[h].val, closure):
|
||||
@@ -544,7 +544,7 @@ proc tableGet(t: TTable, key: RootRef): RootRef =
|
||||
else: result = nil
|
||||
|
||||
proc tableRawInsert(data: var TPairSeq, key, val: RootRef) =
|
||||
var h: THash = hashNode(key) and high(data)
|
||||
var h: Hash = hashNode(key) and high(data)
|
||||
while data[h].key != nil:
|
||||
assert(data[h].key != key)
|
||||
h = nextTry(h, high(data))
|
||||
@@ -569,7 +569,7 @@ proc tablePut(t: var TTable, key, val: RootRef) =
|
||||
inc(t.counter)
|
||||
|
||||
proc strTableContains(t: TStrTable, n: PSym): bool =
|
||||
var h: THash = n.name.h and high(t.data) # start with real hash value
|
||||
var h: Hash = n.name.h and high(t.data) # start with real hash value
|
||||
while t.data[h] != nil:
|
||||
if (t.data[h] == n):
|
||||
return true
|
||||
@@ -577,7 +577,7 @@ proc strTableContains(t: TStrTable, n: PSym): bool =
|
||||
result = false
|
||||
|
||||
proc strTableRawInsert(data: var TSymSeq, n: PSym) =
|
||||
var h: THash = n.name.h and high(data)
|
||||
var h: Hash = n.name.h and high(data)
|
||||
if sfImmediate notin n.flags:
|
||||
# fast path:
|
||||
while data[h] != nil:
|
||||
@@ -606,7 +606,7 @@ proc strTableRawInsert(data: var TSymSeq, n: PSym) =
|
||||
|
||||
proc symTabReplaceRaw(data: var TSymSeq, prevSym: PSym, newSym: PSym) =
|
||||
assert prevSym.name.h == newSym.name.h
|
||||
var h: THash = prevSym.name.h and high(data)
|
||||
var h: Hash = prevSym.name.h and high(data)
|
||||
while data[h] != nil:
|
||||
if data[h] == prevSym:
|
||||
data[h] = newSym
|
||||
@@ -640,7 +640,7 @@ proc strTableIncl*(t: var TStrTable, n: PSym): bool {.discardable.} =
|
||||
# It is essential that `n` is written nevertheless!
|
||||
# This way the newest redefinition is picked by the semantic analyses!
|
||||
assert n.name != nil
|
||||
var h: THash = n.name.h and high(t.data)
|
||||
var h: Hash = n.name.h and high(t.data)
|
||||
var replaceSlot = -1
|
||||
while true:
|
||||
var it = t.data[h]
|
||||
@@ -666,7 +666,7 @@ proc strTableIncl*(t: var TStrTable, n: PSym): bool {.discardable.} =
|
||||
result = false
|
||||
|
||||
proc strTableGet(t: TStrTable, name: PIdent): PSym =
|
||||
var h: THash = name.h and high(t.data)
|
||||
var h: Hash = name.h and high(t.data)
|
||||
while true:
|
||||
result = t.data[h]
|
||||
if result == nil: break
|
||||
@@ -694,7 +694,7 @@ proc nextIdentIter(ti: var TIdentIter, tab: TStrTable): PSym =
|
||||
|
||||
proc nextIdentExcluding*(ti: var TIdentIter, tab: TStrTable,
|
||||
excluding: IntSet): PSym =
|
||||
var h: THash = ti.h and high(tab.data)
|
||||
var h: Hash = ti.h and high(tab.data)
|
||||
var start = h
|
||||
result = tab.data[h]
|
||||
while result != nil:
|
||||
@@ -743,7 +743,7 @@ proc hasEmptySlot(data: TIdPairSeq): bool =
|
||||
result = false
|
||||
|
||||
proc idTableRawGet(t: TIdTable, key: int): int =
|
||||
var h: THash
|
||||
var h: Hash
|
||||
h = key and high(t.data) # start with real hash value
|
||||
while t.data[h].key != nil:
|
||||
if t.data[h].key.id == key:
|
||||
@@ -772,7 +772,7 @@ iterator pairs*(t: TIdTable): tuple[key: int, value: RootRef] =
|
||||
yield (t.data[i].key.id, t.data[i].val)
|
||||
|
||||
proc idTableRawInsert(data: var TIdPairSeq, key: PIdObj, val: RootRef) =
|
||||
var h: THash
|
||||
var h: Hash
|
||||
h = key.id and high(data)
|
||||
while data[h].key != nil:
|
||||
assert(data[h].key.id != key.id)
|
||||
@@ -805,7 +805,7 @@ iterator idTablePairs*(t: TIdTable): tuple[key: PIdObj, val: RootRef] =
|
||||
if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val)
|
||||
|
||||
proc idNodeTableRawGet(t: TIdNodeTable, key: PIdObj): int =
|
||||
var h: THash
|
||||
var h: Hash
|
||||
h = key.id and high(t.data) # start with real hash value
|
||||
while t.data[h].key != nil:
|
||||
if t.data[h].key.id == key.id:
|
||||
@@ -824,7 +824,7 @@ proc idNodeTableGetLazy*(t: TIdNodeTable, key: PIdObj): PNode =
|
||||
result = idNodeTableGet(t, key)
|
||||
|
||||
proc idNodeTableRawInsert(data: var TIdNodePairSeq, key: PIdObj, val: PNode) =
|
||||
var h: THash
|
||||
var h: Hash
|
||||
h = key.id and high(data)
|
||||
while data[h].key != nil:
|
||||
assert(data[h].key.id != key.id)
|
||||
@@ -863,7 +863,7 @@ proc initIITable(x: var TIITable) =
|
||||
for i in countup(0, StartSize - 1): x.data[i].key = InvalidKey
|
||||
|
||||
proc iiTableRawGet(t: TIITable, key: int): int =
|
||||
var h: THash
|
||||
var h: Hash
|
||||
h = key and high(t.data) # start with real hash value
|
||||
while t.data[h].key != InvalidKey:
|
||||
if t.data[h].key == key: return h
|
||||
@@ -876,7 +876,7 @@ proc iiTableGet(t: TIITable, key: int): int =
|
||||
else: result = InvalidKey
|
||||
|
||||
proc iiTableRawInsert(data: var TIIPairSeq, key, val: int) =
|
||||
var h: THash
|
||||
var h: Hash
|
||||
h = key and high(data)
|
||||
while data[h].key != InvalidKey:
|
||||
assert(data[h].key != key)
|
||||
|
||||
@@ -503,13 +503,13 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
||||
"$# = #addInt($#, $#);$n", "$# = #subInt($#, $#);$n",
|
||||
"$# = #mulInt($#, $#);$n", "$# = #divInt($#, $#);$n",
|
||||
"$# = #modInt($#, $#);$n",
|
||||
"$# = #addInt($#, $#);$n", "$# = #subInt($#, $#);$n"]
|
||||
prc64: array[mAddI..mPred, string] = [
|
||||
"$# = #addInt64($#, $#);$n", "$# = #subInt64($#, $#);$n",
|
||||
"$# = #mulInt64($#, $#);$n", "$# = #divInt64($#, $#);$n",
|
||||
"$# = #modInt64($#, $#);$n",
|
||||
"$# = #addInt($#, $#);$n", "$# = #subInt($#, $#);$n"]
|
||||
"$# = #addInt64($#, $#);$n", "$# = #subInt64($#, $#);$n"]
|
||||
opr: array[mAddI..mPred, string] = [
|
||||
"($#)($# + $#)", "($#)($# - $#)", "($#)($# * $#)",
|
||||
"($#)($# / $#)", "($#)($# % $#)",
|
||||
"($#)($# + $#)", "($#)($# - $#)", "($#)($# * $#)",
|
||||
"($#)($# / $#)", "($#)($# % $#)",
|
||||
"($#)($# + $#)", "($#)($# - $#)"]
|
||||
@@ -525,16 +525,16 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
||||
let res = opr[m] % [getTypeDesc(p.module, t), rdLoc(a), rdLoc(b)]
|
||||
putIntoDest(p, d, e.typ, res)
|
||||
else:
|
||||
let res = binaryArithOverflowRaw(p, t, a, b, prc[m])
|
||||
let res = binaryArithOverflowRaw(p, t, a, b,
|
||||
if t.kind == tyInt64: prc64[m] else: prc[m])
|
||||
putIntoDest(p, d, e.typ, "($#)($#)" % [getTypeDesc(p.module, t), res])
|
||||
|
||||
proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
|
||||
const
|
||||
opr: array[mUnaryMinusI..mAbsI64, string] = [
|
||||
opr: array[mUnaryMinusI..mAbsI, string] = [
|
||||
mUnaryMinusI: "((NI$2)-($1))",
|
||||
mUnaryMinusI64: "-($1)",
|
||||
mAbsI: "($1 > 0? ($1) : -($1))",
|
||||
mAbsI64: "($1 > 0? ($1) : -($1))"]
|
||||
mAbsI: "($1 > 0? ($1) : -($1))"]
|
||||
var
|
||||
a: TLoc
|
||||
t: PType
|
||||
@@ -561,11 +561,6 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
"($4)($1 ^ $2)", # BitxorI
|
||||
"(($1 <= $2) ? $1 : $2)", # MinI
|
||||
"(($1 >= $2) ? $1 : $2)", # MaxI
|
||||
"($4)((NU64)($1) >> (NU64)($2))", # ShrI64
|
||||
"($4)((NU64)($1) << (NU64)($2))", # ShlI64
|
||||
"($4)($1 & $2)", # BitandI64
|
||||
"($4)($1 | $2)", # BitorI64
|
||||
"($4)($1 ^ $2)", # BitxorI64
|
||||
"(($1 <= $2) ? $1 : $2)", # MinF64
|
||||
"(($1 >= $2) ? $1 : $2)", # MaxF64
|
||||
"($4)((NU$3)($1) + (NU$3)($2))", # AddU
|
||||
@@ -576,9 +571,6 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
"($1 == $2)", # EqI
|
||||
"($1 <= $2)", # LeI
|
||||
"($1 < $2)", # LtI
|
||||
"($1 == $2)", # EqI64
|
||||
"($1 <= $2)", # LeI64
|
||||
"($1 < $2)", # LtI64
|
||||
"($1 == $2)", # EqF64
|
||||
"($1 <= $2)", # LeF64
|
||||
"($1 < $2)", # LtF64
|
||||
@@ -638,7 +630,6 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
unArithTab: array[mNot..mToBiggestInt, string] = ["!($1)", # Not
|
||||
"$1", # UnaryPlusI
|
||||
"($3)((NU$2) ~($1))", # BitnotI
|
||||
"($3)((NU$2) ~($1))", # BitnotI64
|
||||
"$1", # UnaryPlusF64
|
||||
"-($1)", # UnaryMinusF64
|
||||
"($1 > 0? ($1) : -($1))", # AbsF64; BUGFIX: fabs() makes problems
|
||||
@@ -1661,7 +1652,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
case op
|
||||
of mOr, mAnd: genAndOr(p, e, d, op)
|
||||
of mNot..mToBiggestInt: unaryArith(p, e, d, op)
|
||||
of mUnaryMinusI..mAbsI64: unaryArithOverflow(p, e, d, op)
|
||||
of mUnaryMinusI..mAbsI: unaryArithOverflow(p, e, d, op)
|
||||
of mAddF64..mDivF64: binaryFloatArith(p, e, d, op)
|
||||
of mShrI..mXor: binaryArith(p, e, d, op)
|
||||
of mEqProc: genEqProc(p, e, d)
|
||||
@@ -2150,7 +2141,7 @@ proc genNamedConstExpr(p: BProc, n: PNode): Rope =
|
||||
proc genConstSimpleList(p: BProc, n: PNode): Rope =
|
||||
var length = sonsLen(n)
|
||||
result = rope("{")
|
||||
for i in countup(0, length - 2):
|
||||
for i in countup(ord(n.kind == nkObjConstr), length - 2):
|
||||
addf(result, "$1,$n", [genNamedConstExpr(p, n.sons[i])])
|
||||
if length > 0: add(result, genNamedConstExpr(p, n.sons[length - 1]))
|
||||
addf(result, "}$n", [])
|
||||
|
||||
@@ -10,12 +10,11 @@
|
||||
## This module implements the C code generator.
|
||||
|
||||
import
|
||||
ast, astalgo, hashes, trees, platform, magicsys, extccomp,
|
||||
options, intsets,
|
||||
nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os,
|
||||
ropes, math, passes, rodread, wordrecg, treetab, cgmeth, condsyms,
|
||||
rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, lowerings,
|
||||
semparallel
|
||||
ast, astalgo, hashes, trees, platform, magicsys, extccomp, options, intsets,
|
||||
nversion, nimsets, msgs, securehash, bitsets, idents, lists, types,
|
||||
ccgutils, os, ropes, math, passes, rodread, wordrecg, treetab, cgmeth,
|
||||
condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases,
|
||||
lowerings, semparallel
|
||||
|
||||
import strutils except `%` # collides with ropes.`%`
|
||||
|
||||
@@ -721,6 +720,8 @@ proc genProcPrototype(m: BModule, sym: PSym) =
|
||||
getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym)))
|
||||
elif not containsOrIncl(m.declaredProtos, sym.id):
|
||||
var header = genProcHeader(m, sym)
|
||||
if sfNoReturn in sym.flags and hasDeclspec in extccomp.CC[cCompiler].props:
|
||||
header = "__declspec(noreturn) " & header
|
||||
if sym.typ.callConv != ccInline and crossesCppBoundary(m, sym):
|
||||
header = "extern \"C\" " & header
|
||||
if sfPure in sym.flags and hasAttribute in CC[cCompiler].props:
|
||||
|
||||
@@ -537,6 +537,9 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
|
||||
of "genscript":
|
||||
expectNoArg(switch, arg, pass, info)
|
||||
incl(gGlobalOptions, optGenScript)
|
||||
of "usecolors":
|
||||
expectNoArg(switch, arg, pass, info)
|
||||
incl(gGlobalOptions, optUseColors)
|
||||
of "lib":
|
||||
expectArg(switch, arg, pass, info)
|
||||
libpath = processPath(arg, notRelativeToProj=true)
|
||||
|
||||
@@ -88,3 +88,4 @@ proc initDefines*() =
|
||||
defineSymbol("nimalias")
|
||||
defineSymbol("nimlocks")
|
||||
defineSymbol("nimnode")
|
||||
defineSymbol("nimnomagic64")
|
||||
|
||||
147
compiler/crc.nim
147
compiler/crc.nim
@@ -1,147 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
import
|
||||
strutils
|
||||
|
||||
type
|
||||
TCrc32* = int32
|
||||
|
||||
const
|
||||
InitCrc32* = TCrc32(- 1)
|
||||
InitAdler32* = int32(1)
|
||||
|
||||
proc updateCrc32*(val: int8, crc: TCrc32): TCrc32 {.inline.}
|
||||
proc updateCrc32*(val: char, crc: TCrc32): TCrc32 {.inline.}
|
||||
proc crcFromBuf*(buf: pointer, length: int): TCrc32
|
||||
proc strCrc32*(s: string): TCrc32
|
||||
proc crcFromFile*(filename: string): TCrc32
|
||||
proc updateAdler32*(adler: int32, buf: pointer, length: int): int32
|
||||
# implementation
|
||||
|
||||
type
|
||||
TCRC_TabEntry = int
|
||||
|
||||
const
|
||||
crc32table: array[0..255, TCRC_TabEntry] = [0, 1996959894, - 301047508,
|
||||
- 1727442502, 124634137, 1886057615, - 379345611, - 1637575261, 249268274,
|
||||
2044508324, - 522852066, - 1747789432, 162941995, 2125561021, - 407360249,
|
||||
- 1866523247, 498536548, 1789927666, - 205950648, - 2067906082, 450548861,
|
||||
1843258603, - 187386543, - 2083289657, 325883990, 1684777152, - 43845254,
|
||||
- 1973040660, 335633487, 1661365465, - 99664541, - 1928851979, 997073096,
|
||||
1281953886, - 715111964, - 1570279054, 1006888145, 1258607687, - 770865667,
|
||||
- 1526024853, 901097722, 1119000684, - 608450090, - 1396901568, 853044451,
|
||||
1172266101, - 589951537, - 1412350631, 651767980, 1373503546, - 925412992,
|
||||
- 1076862698, 565507253, 1454621731, - 809855591, - 1195530993, 671266974,
|
||||
1594198024, - 972236366, - 1324619484, 795835527, 1483230225, - 1050600021,
|
||||
- 1234817731, 1994146192, 31158534, - 1731059524, - 271249366, 1907459465,
|
||||
112637215, - 1614814043, - 390540237, 2013776290, 251722036, - 1777751922,
|
||||
- 519137256, 2137656763, 141376813, - 1855689577, - 429695999, 1802195444,
|
||||
476864866, - 2056965928, - 228458418, 1812370925, 453092731, - 2113342271,
|
||||
- 183516073, 1706088902, 314042704, - 1950435094, - 54949764, 1658658271,
|
||||
366619977, - 1932296973, - 69972891, 1303535960, 984961486, - 1547960204,
|
||||
- 725929758, 1256170817, 1037604311, - 1529756563, - 740887301, 1131014506,
|
||||
879679996, - 1385723834, - 631195440, 1141124467, 855842277, - 1442165665,
|
||||
- 586318647, 1342533948, 654459306, - 1106571248, - 921952122, 1466479909,
|
||||
544179635, - 1184443383, - 832445281, 1591671054, 702138776, - 1328506846,
|
||||
- 942167884, 1504918807, 783551873, - 1212326853, - 1061524307, - 306674912,
|
||||
- 1698712650, 62317068, 1957810842, - 355121351, - 1647151185, 81470997,
|
||||
1943803523, - 480048366, - 1805370492, 225274430, 2053790376, - 468791541,
|
||||
- 1828061283, 167816743, 2097651377, - 267414716, - 2029476910, 503444072,
|
||||
1762050814, - 144550051, - 2140837941, 426522225, 1852507879, - 19653770,
|
||||
- 1982649376, 282753626, 1742555852, - 105259153, - 1900089351, 397917763,
|
||||
1622183637, - 690576408, - 1580100738, 953729732, 1340076626, - 776247311,
|
||||
- 1497606297, 1068828381, 1219638859, - 670225446, - 1358292148, 906185462,
|
||||
1090812512, - 547295293, - 1469587627, 829329135, 1181335161, - 882789492,
|
||||
- 1134132454, 628085408, 1382605366, - 871598187, - 1156888829, 570562233,
|
||||
1426400815, - 977650754, - 1296233688, 733239954, 1555261956, - 1026031705,
|
||||
- 1244606671, 752459403, 1541320221, - 1687895376, - 328994266, 1969922972,
|
||||
40735498, - 1677130071, - 351390145, 1913087877, 83908371, - 1782625662,
|
||||
- 491226604, 2075208622, 213261112, - 1831694693, - 438977011, 2094854071,
|
||||
198958881, - 2032938284, - 237706686, 1759359992, 534414190, - 2118248755,
|
||||
- 155638181, 1873836001, 414664567, - 2012718362, - 15766928, 1711684554,
|
||||
285281116, - 1889165569, - 127750551, 1634467795, 376229701, - 1609899400,
|
||||
- 686959890, 1308918612, 956543938, - 1486412191, - 799009033, 1231636301,
|
||||
1047427035, - 1362007478, - 640263460, 1088359270, 936918000, - 1447252397,
|
||||
- 558129467, 1202900863, 817233897, - 1111625188, - 893730166, 1404277552,
|
||||
615818150, - 1160759803, - 841546093, 1423857449, 601450431, - 1285129682,
|
||||
- 1000256840, 1567103746, 711928724, - 1274298825, - 1022587231, 1510334235,
|
||||
755167117]
|
||||
|
||||
proc updateCrc32(val: int8, crc: TCrc32): TCrc32 =
|
||||
result = TCrc32(crc32table[(int(crc) xor (int(val) and 0x000000FF)) and
|
||||
0x000000FF]) xor (crc shr TCrc32(8))
|
||||
|
||||
proc updateCrc32(val: char, crc: TCrc32): TCrc32 =
|
||||
result = updateCrc32(toU8(ord(val)), crc)
|
||||
|
||||
proc strCrc32(s: string): TCrc32 =
|
||||
result = InitCrc32
|
||||
for i in countup(0, len(s) - 1): result = updateCrc32(s[i], result)
|
||||
|
||||
proc `><`*(c: TCrc32, s: string): TCrc32 =
|
||||
result = c
|
||||
for i in 0..len(s)-1: result = updateCrc32(s[i], result)
|
||||
|
||||
type
|
||||
TByteArray = array[0..10000000, int8]
|
||||
PByteArray = ref TByteArray
|
||||
|
||||
proc crcFromBuf(buf: pointer, length: int): TCrc32 =
|
||||
var p = cast[PByteArray](buf)
|
||||
result = InitCrc32
|
||||
for i in countup(0, length - 1): result = updateCrc32(p[i], result)
|
||||
|
||||
proc crcFromFile(filename: string): TCrc32 =
|
||||
const
|
||||
bufSize = 8000 # don't use 8K for the memory allocator!
|
||||
var
|
||||
bin: File
|
||||
result = InitCrc32
|
||||
if not open(bin, filename):
|
||||
return # not equal if file does not exist
|
||||
var buf = alloc(bufSize)
|
||||
var p = cast[PByteArray](buf)
|
||||
while true:
|
||||
var readBytes = readBuffer(bin, buf, bufSize)
|
||||
for i in countup(0, readBytes - 1): result = updateCrc32(p[i], result)
|
||||
if readBytes != bufSize: break
|
||||
dealloc(buf)
|
||||
close(bin)
|
||||
|
||||
const
|
||||
base = int32(65521) # largest prime smaller than 65536
|
||||
# NMAX = 5552; original code with unsigned 32 bit integer
|
||||
# NMAX is the largest n
|
||||
# such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1
|
||||
nmax = 3854 # code with signed 32 bit integer
|
||||
# NMAX is the largest n such that
|
||||
# 255n(n+1)/2 + (n+1)(BASE-1) <= 2^31-1
|
||||
# The penalty is the time loss in the extra MOD-calls.
|
||||
|
||||
proc updateAdler32(adler: int32, buf: pointer, length: int): int32 =
|
||||
var
|
||||
s1, s2: int32
|
||||
L, k, b: int
|
||||
s1 = adler and int32(0x0000FFFF)
|
||||
s2 = (adler shr int32(16)) and int32(0x0000FFFF)
|
||||
L = length
|
||||
b = 0
|
||||
while (L > 0):
|
||||
if L < nmax: k = L
|
||||
else: k = nmax
|
||||
dec(L, k)
|
||||
while (k > 0):
|
||||
s1 = s1 +% int32((cast[cstring](buf))[b])
|
||||
s2 = s2 +% s1
|
||||
inc(b)
|
||||
dec(k)
|
||||
s1 = `%%`(s1, base)
|
||||
s2 = `%%`(s2, base)
|
||||
result = (s2 shl int32(16)) or s1
|
||||
@@ -18,7 +18,7 @@ import
|
||||
|
||||
type
|
||||
TSections = array[TSymKind, Rope]
|
||||
TDocumentor = object of rstgen.TRstGenerator
|
||||
TDocumentor = object of rstgen.RstGenerator
|
||||
modDesc: Rope # module description
|
||||
id: int # for generating IDs
|
||||
toc, section: TSections
|
||||
@@ -29,7 +29,7 @@ type
|
||||
PDoc* = ref TDocumentor ## Alias to type less.
|
||||
|
||||
proc compilerMsgHandler(filename: string, line, col: int,
|
||||
msgKind: rst.TMsgKind, arg: string) {.procvar.} =
|
||||
msgKind: rst.MsgKind, arg: string) {.procvar.} =
|
||||
# translate msg kind:
|
||||
var k: msgs.TMsgKind
|
||||
case msgKind
|
||||
@@ -53,7 +53,7 @@ proc docgenFindFile(s: string): string {.procvar.} =
|
||||
|
||||
proc parseRst(text, filename: string,
|
||||
line, column: int, hasToc: var bool,
|
||||
rstOptions: TRstParseOptions): PRstNode =
|
||||
rstOptions: RstParseOptions): PRstNode =
|
||||
result = rstParse(text, filename, line, column, hasToc, rstOptions,
|
||||
docgenFindFile, compilerMsgHandler)
|
||||
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
# nim files.
|
||||
|
||||
import
|
||||
lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs, crc
|
||||
lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs,
|
||||
securehash
|
||||
|
||||
type
|
||||
TSystemCC* = enum
|
||||
@@ -572,26 +573,24 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
|
||||
"nim", quoteShell(getPrefixDir()),
|
||||
"lib", quoteShell(libpath)])
|
||||
|
||||
proc footprint(filename: string): TCrc32 =
|
||||
# note, '><' further modifies a crc value with a string.
|
||||
result = crcFromFile(filename) ><
|
||||
platform.OS[targetOS].name ><
|
||||
platform.CPU[targetCPU].name ><
|
||||
extccomp.CC[extccomp.cCompiler].name ><
|
||||
getCompileCFileCmd(filename, true)
|
||||
proc footprint(filename: string): SecureHash =
|
||||
result = secureHash(
|
||||
$secureHashFile(filename) &
|
||||
platform.OS[targetOS].name &
|
||||
platform.CPU[targetCPU].name &
|
||||
extccomp.CC[extccomp.cCompiler].name &
|
||||
getCompileCFileCmd(filename, true))
|
||||
|
||||
proc externalFileChanged(filename: string): bool =
|
||||
if gCmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}:
|
||||
return false
|
||||
|
||||
var crcFile = toGeneratedFile(filename.withPackageName, "crc")
|
||||
var currentCrc = int(footprint(filename))
|
||||
var currentCrc = footprint(filename)
|
||||
var f: File
|
||||
if open(f, crcFile, fmRead):
|
||||
var line = newStringOfCap(40)
|
||||
if not f.readLine(line): line = "0"
|
||||
let oldCrc = parseSecureHash(f.readLine())
|
||||
close(f)
|
||||
var oldCrc = parseInt(line)
|
||||
result = oldCrc != currentCrc
|
||||
else:
|
||||
result = true
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
import ast, astalgo
|
||||
|
||||
const
|
||||
someCmp = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
|
||||
mEqUntracedRef, mLeI, mLeI64, mLeF64, mLeU, mLeU64, mLeEnum,
|
||||
mLeCh, mLeB, mLePtr, mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum,
|
||||
someCmp = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
|
||||
mEqUntracedRef, mLeI, mLeF64, mLeU, mLeU64, mLeEnum,
|
||||
mLeCh, mLeB, mLePtr, mLtI, mLtF64, mLtU, mLtU64, mLtEnum,
|
||||
mLtCh, mLtB, mLtPtr}
|
||||
|
||||
proc isCounter(s: PSym): bool {.inline.} =
|
||||
|
||||
@@ -13,13 +13,13 @@ import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents,
|
||||
saturate
|
||||
|
||||
const
|
||||
someEq = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
|
||||
someEq = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
|
||||
mEqUntracedRef, mEqStr, mEqSet, mEqCString}
|
||||
|
||||
# set excluded here as the semantics are vastly different:
|
||||
someLe = {mLeI, mLeI64, mLeF64, mLeU, mLeU64, mLeEnum,
|
||||
someLe = {mLeI, mLeF64, mLeU, mLeU64, mLeEnum,
|
||||
mLeCh, mLeB, mLePtr, mLeStr}
|
||||
someLt = {mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum,
|
||||
someLt = {mLtI, mLtF64, mLtU, mLtU64, mLtEnum,
|
||||
mLtCh, mLtB, mLtPtr, mLtStr}
|
||||
|
||||
someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq,
|
||||
@@ -30,11 +30,11 @@ const
|
||||
someHigh = {mHigh}
|
||||
# we don't list unsigned here because wrap around semantics suck for
|
||||
# proving anything:
|
||||
someAdd = {mAddI, mAddI64, mAddF64, mSucc}
|
||||
someSub = {mSubI, mSubI64, mSubF64, mPred}
|
||||
someMul = {mMulI, mMulI64, mMulF64}
|
||||
someDiv = {mDivI, mDivI64, mDivF64}
|
||||
someMod = {mModI, mModI64}
|
||||
someAdd = {mAddI, mAddF64, mSucc}
|
||||
someSub = {mSubI, mSubF64, mPred}
|
||||
someMul = {mMulI, mMulF64}
|
||||
someDiv = {mDivI, mDivF64}
|
||||
someMod = {mModI}
|
||||
someMax = {mMaxI, mMaxF64}
|
||||
someMin = {mMinI, mMinF64}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# id. This module is essential for the compiler's performance.
|
||||
|
||||
import
|
||||
hashes, strutils
|
||||
hashes, strutils, etcpriv
|
||||
|
||||
type
|
||||
TIdObj* = object of RootObj
|
||||
@@ -23,7 +23,7 @@ type
|
||||
TIdent*{.acyclic.} = object of TIdObj
|
||||
s*: string
|
||||
next*: PIdent # for hash-table chaining
|
||||
h*: THash # hash value of s
|
||||
h*: Hash # hash value of s
|
||||
|
||||
var firstCharIsCS*: bool = true
|
||||
var buckets*: array[0..4096 * 2 - 1, PIdent]
|
||||
@@ -37,6 +37,8 @@ proc cmpIgnoreStyle(a, b: cstring, blen: int): int =
|
||||
while j < blen:
|
||||
while a[i] == '_': inc(i)
|
||||
while b[j] == '_': inc(j)
|
||||
while isMagicIdentSeparatorRune(a, i): inc(i, magicIdentSeparatorRuneByteWidth)
|
||||
while isMagicIdentSeparatorRune(b, j): inc(j, magicIdentSeparatorRuneByteWidth)
|
||||
# tolower inlined:
|
||||
var aa = a[i]
|
||||
var bb = b[j]
|
||||
@@ -65,7 +67,7 @@ proc cmpExact(a, b: cstring, blen: int): int =
|
||||
|
||||
var wordCounter = 1
|
||||
|
||||
proc getIdent*(identifier: cstring, length: int, h: THash): PIdent =
|
||||
proc getIdent*(identifier: cstring, length: int, h: Hash): PIdent =
|
||||
var idx = h and high(buckets)
|
||||
result = buckets[idx]
|
||||
var last: PIdent = nil
|
||||
@@ -99,7 +101,7 @@ proc getIdent*(identifier: string): PIdent =
|
||||
result = getIdent(cstring(identifier), len(identifier),
|
||||
hashIgnoreStyle(identifier))
|
||||
|
||||
proc getIdent*(identifier: string, h: THash): PIdent =
|
||||
proc getIdent*(identifier: string, h: Hash): PIdent =
|
||||
result = getIdent(cstring(identifier), len(identifier), h)
|
||||
|
||||
proc identEq*(id: PIdent, name: string): bool =
|
||||
|
||||
@@ -99,6 +99,7 @@ Files: "lib/pure/concurrency/*.nim"
|
||||
Files: "lib/pure/unidecode/*.nim"
|
||||
Files: "lib/pure/concurrency/*.cfg"
|
||||
Files: "lib/impure/*.nim"
|
||||
Files: "lib/impure/nre/private/*.nim"
|
||||
Files: "lib/wrappers/*.nim"
|
||||
|
||||
Files: "lib/wrappers/readline/*.nim"
|
||||
|
||||
@@ -30,8 +30,8 @@ implements the required case distinction.
|
||||
|
||||
|
||||
import
|
||||
ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp,
|
||||
options, nversion, nimsets, msgs, crc, bitsets, idents, lists, types, os,
|
||||
ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options,
|
||||
nversion, nimsets, msgs, securehash, bitsets, idents, lists, types, os,
|
||||
times, ropes, math, passes, ccgutils, wordrecg, renderer, rodread, rodutils,
|
||||
intsets, cgmeth, lowerings
|
||||
|
||||
@@ -258,11 +258,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
|
||||
["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI
|
||||
["divInt", "", "divInt($1, $2)", "Math.floor($1 / $2)"], # DivI
|
||||
["modInt", "", "modInt($1, $2)", "Math.floor($1 % $2)"], # ModI
|
||||
["addInt64", "", "addInt64($1, $2)", "($1 + $2)"], # AddI64
|
||||
["subInt64", "", "subInt64($1, $2)", "($1 - $2)"], # SubI64
|
||||
["mulInt64", "", "mulInt64($1, $2)", "($1 * $2)"], # MulI64
|
||||
["divInt64", "", "divInt64($1, $2)", "Math.floor($1 / $2)"], # DivI64
|
||||
["modInt64", "", "modInt64($1, $2)", "Math.floor($1 % $2)"], # ModI64
|
||||
["addInt", "", "addInt($1, $2)", "($1 + $2)"], # Succ
|
||||
["subInt", "", "subInt($1, $2)", "($1 - $2)"], # Pred
|
||||
["", "", "($1 + $2)", "($1 + $2)"], # AddF64
|
||||
@@ -276,11 +271,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
|
||||
["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI
|
||||
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinI
|
||||
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI
|
||||
["", "", "($1 >>> $2)", "($1 >>> $2)"], # ShrI64
|
||||
["", "", "($1 << $2)", "($1 << $2)"], # ShlI64
|
||||
["", "", "($1 & $2)", "($1 & $2)"], # BitandI64
|
||||
["", "", "($1 | $2)", "($1 | $2)"], # BitorI64
|
||||
["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI64
|
||||
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64
|
||||
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64
|
||||
["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU
|
||||
@@ -291,9 +281,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
|
||||
["", "", "($1 == $2)", "($1 == $2)"], # EqI
|
||||
["", "", "($1 <= $2)", "($1 <= $2)"], # LeI
|
||||
["", "", "($1 < $2)", "($1 < $2)"], # LtI
|
||||
["", "", "($1 == $2)", "($1 == $2)"], # EqI64
|
||||
["", "", "($1 <= $2)", "($1 <= $2)"], # LeI64
|
||||
["", "", "($1 < $2)", "($1 < $2)"], # LtI64
|
||||
["", "", "($1 == $2)", "($1 == $2)"], # EqF64
|
||||
["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64
|
||||
["", "", "($1 < $2)", "($1 < $2)"], # LtF64
|
||||
@@ -320,11 +307,9 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
|
||||
["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI
|
||||
["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64
|
||||
["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI
|
||||
["absInt64", "", "absInt64($1)", "Math.abs($1)"], # AbsI64
|
||||
["", "", "!($1)", "!($1)"], # Not
|
||||
["", "", "+($1)", "+($1)"], # UnaryPlusI
|
||||
["", "", "~($1)", "~($1)"], # BitnotI
|
||||
["", "", "~($1)", "~($1)"], # BitnotI64
|
||||
["", "", "+($1)", "+($1)"], # UnaryPlusF64
|
||||
["", "", "-($1)", "-($1)"], # UnaryMinusF64
|
||||
["", "", "Math.abs($1)", "Math.abs($1)"], # AbsF64
|
||||
@@ -357,11 +342,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
|
||||
["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI
|
||||
["divInt", "", "divInt($1, $2)", "Math.floor($1 / $2)"], # DivI
|
||||
["modInt", "", "modInt($1, $2)", "Math.floor($1 % $2)"], # ModI
|
||||
["addInt64", "", "addInt64($1, $2)", "($1 + $2)"], # AddI64
|
||||
["subInt64", "", "subInt64($1, $2)", "($1 - $2)"], # SubI64
|
||||
["mulInt64", "", "mulInt64($1, $2)", "($1 * $2)"], # MulI64
|
||||
["divInt64", "", "divInt64($1, $2)", "Math.floor($1 / $2)"], # DivI64
|
||||
["modInt64", "", "modInt64($1, $2)", "Math.floor($1 % $2)"], # ModI64
|
||||
["addInt", "", "addInt($1, $2)", "($1 + $2)"], # Succ
|
||||
["subInt", "", "subInt($1, $2)", "($1 - $2)"], # Pred
|
||||
["", "", "($1 + $2)", "($1 + $2)"], # AddF64
|
||||
@@ -375,11 +355,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
|
||||
["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI
|
||||
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinI
|
||||
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI
|
||||
["", "", "($1 >>> $2)", "($1 >>> $2)"], # ShrI64
|
||||
["", "", "($1 << $2)", "($1 << $2)"], # ShlI64
|
||||
["", "", "($1 & $2)", "($1 & $2)"], # BitandI64
|
||||
["", "", "($1 | $2)", "($1 | $2)"], # BitorI64
|
||||
["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI64
|
||||
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64
|
||||
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64
|
||||
["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU
|
||||
@@ -390,9 +365,6 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
|
||||
["", "", "($1 == $2)", "($1 == $2)"], # EqI
|
||||
["", "", "($1 <= $2)", "($1 <= $2)"], # LeI
|
||||
["", "", "($1 < $2)", "($1 < $2)"], # LtI
|
||||
["", "", "($1 == $2)", "($1 == $2)"], # EqI64
|
||||
["", "", "($1 <= $2)", "($1 <= $2)"], # LeI64
|
||||
["", "", "($1 < $2)", "($1 < $2)"], # LtI64
|
||||
["", "", "($1 == $2)", "($1 == $2)"], # EqF64
|
||||
["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64
|
||||
["", "", "($1 < $2)", "($1 < $2)"], # LtF64
|
||||
@@ -419,11 +391,9 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
|
||||
["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI
|
||||
["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64
|
||||
["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI
|
||||
["absInt64", "", "absInt64($1)", "Math.abs($1)"], # AbsI64
|
||||
["", "", "not ($1)", "not ($1)"], # Not
|
||||
["", "", "+($1)", "+($1)"], # UnaryPlusI
|
||||
["", "", "~($1)", "~($1)"], # BitnotI
|
||||
["", "", "~($1)", "~($1)"], # BitnotI64
|
||||
["", "", "+($1)", "+($1)"], # UnaryPlusF64
|
||||
["", "", "-($1)", "-($1)"], # UnaryMinusF64
|
||||
["", "", "Math.abs($1)", "Math.abs($1)"], # AbsF64
|
||||
@@ -817,7 +787,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
|
||||
addf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
|
||||
else:
|
||||
useMagic(p, "nimCopy")
|
||||
addf(p.body, "$1 = nimCopy($2, $3);$n",
|
||||
addf(p.body, "$1 = nimCopy($1, $2, $3);$n",
|
||||
[a.res, b.res, genTypeInfo(p, y.typ)])
|
||||
of etyBaseIndex:
|
||||
if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
|
||||
@@ -976,16 +946,28 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
genFieldAccess(p, n.sons[0], r)
|
||||
of nkBracketExpr:
|
||||
var ty = skipTypes(n.sons[0].typ, abstractVarRange)
|
||||
if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange)
|
||||
case ty.kind
|
||||
of tyArray, tyArrayConstr, tyOpenArray, tySequence, tyString, tyCString,
|
||||
tyVarargs, tyChar:
|
||||
genArrayAddr(p, n.sons[0], r)
|
||||
of tyTuple:
|
||||
genFieldAddr(p, n.sons[0], r)
|
||||
else: internalError(n.sons[0].info, "expr(nkBracketExpr, " & $ty.kind & ')')
|
||||
if ty.kind in MappedToObject:
|
||||
gen(p, n.sons[0], r)
|
||||
else:
|
||||
let kindOfIndexedExpr = skipTypes(n.sons[0].sons[0].typ, abstractVarRange).kind
|
||||
case kindOfIndexedExpr
|
||||
of tyArray, tyArrayConstr, tyOpenArray, tySequence, tyString, tyCString,
|
||||
tyVarargs:
|
||||
genArrayAddr(p, n.sons[0], r)
|
||||
of tyTuple:
|
||||
genFieldAddr(p, n.sons[0], r)
|
||||
else: internalError(n.sons[0].info, "expr(nkBracketExpr, " & $kindOfIndexedExpr & ')')
|
||||
else: internalError(n.sons[0].info, "genAddr")
|
||||
|
||||
proc genProcForSymIfNeeded(p: PProc, s: PSym) =
|
||||
if not p.g.generatedSyms.containsOrIncl(s.id):
|
||||
let newp = genProc(p, s)
|
||||
var owner = p
|
||||
while owner != nil and owner.prc != s.owner:
|
||||
owner = owner.up
|
||||
if owner != nil: add(owner.locals, newp)
|
||||
else: add(p.g.code, newp)
|
||||
|
||||
proc genSym(p: PProc, n: PNode, r: var TCompRes) =
|
||||
var s = n.sym
|
||||
case s.kind
|
||||
@@ -1021,13 +1003,8 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
|
||||
discard
|
||||
elif sfForward in s.flags:
|
||||
p.g.forwarded.add(s)
|
||||
elif not p.g.generatedSyms.containsOrIncl(s.id):
|
||||
let newp = genProc(p, s)
|
||||
var owner = p
|
||||
while owner != nil and owner.prc != s.owner:
|
||||
owner = owner.up
|
||||
if owner != nil: add(owner.locals, newp)
|
||||
else: add(p.g.code, newp)
|
||||
else:
|
||||
genProcForSymIfNeeded(p, s)
|
||||
else:
|
||||
if s.loc.r == nil:
|
||||
internalError(n.info, "symbol has no generated name: " & s.name.s)
|
||||
@@ -1202,7 +1179,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
|
||||
s = a.res
|
||||
else:
|
||||
useMagic(p, "nimCopy")
|
||||
s = "nimCopy($1, $2)" % [a.res, genTypeInfo(p, n.typ)]
|
||||
s = "nimCopy(null, $1, $2)" % [a.res, genTypeInfo(p, n.typ)]
|
||||
of etyBaseIndex:
|
||||
if (a.typ != etyBaseIndex): internalError(n.info, "genVarInit")
|
||||
if {sfAddrTaken, sfGlobal} * v.flags != {}:
|
||||
@@ -1394,6 +1371,9 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
of mCopyStrLast: ternaryExpr(p, n, r, "", "($1.slice($2, ($3)+1).concat(0))")
|
||||
of mNewString: unaryExpr(p, n, r, "mnewString", "mnewString($1)")
|
||||
of mNewStringOfCap: unaryExpr(p, n, r, "mnewString", "mnewString(0)")
|
||||
of mDotDot:
|
||||
genProcForSymIfNeeded(p, n.sons[0].sym)
|
||||
genCall(p, n, r)
|
||||
else:
|
||||
genCall(p, n, r)
|
||||
#else internalError(e.info, 'genMagic: ' + magicToStr[op]);
|
||||
|
||||
@@ -946,7 +946,11 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode =
|
||||
proc liftLambdas*(fn: PSym, body: PNode): PNode =
|
||||
# XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs
|
||||
# the transformation even when compiling to JS ...
|
||||
if body.kind == nkEmpty or gCmd == cmdCompileToJS or
|
||||
|
||||
# However we can do lifting for the stuff which is *only* compiletime.
|
||||
let isCompileTime = sfCompileTime in fn.flags or fn.kind == skMacro
|
||||
|
||||
if body.kind == nkEmpty or (gCmd == cmdCompileToJS and not isCompileTime) or
|
||||
fn.skipGenericOwner.kind != skModule:
|
||||
# ignore forward declaration:
|
||||
result = body
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
import
|
||||
hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream,
|
||||
wordrecg
|
||||
wordrecg, etcpriv
|
||||
|
||||
const
|
||||
MaxLineLength* = 80 # lines longer than this lead to a warning
|
||||
@@ -140,10 +140,12 @@ proc isKeyword*(kind: TTokType): bool =
|
||||
proc isNimIdentifier*(s: string): bool =
|
||||
if s[0] in SymStartChars:
|
||||
var i = 1
|
||||
while i < s.len:
|
||||
var sLen = s.len
|
||||
while i < sLen:
|
||||
if s[i] == '_':
|
||||
inc(i)
|
||||
if s[i] notin SymChars: return
|
||||
elif isMagicIdentSeparatorRune(cstring s, i):
|
||||
inc(i, magicIdentSeparatorRuneByteWidth)
|
||||
if s[i] notin SymChars: return
|
||||
inc(i)
|
||||
result = true
|
||||
@@ -229,23 +231,6 @@ proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") =
|
||||
var info = newLineInfo(L.fileIdx, L.lineNumber, pos - L.lineStart)
|
||||
L.dispMessage(info, msg, arg)
|
||||
|
||||
proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: set[char]) =
|
||||
var pos = L.bufpos # use registers for pos, buf
|
||||
var buf = L.buf
|
||||
while true:
|
||||
if buf[pos] in chars:
|
||||
add(tok.literal, buf[pos])
|
||||
inc(pos)
|
||||
else:
|
||||
break
|
||||
if buf[pos] == '_':
|
||||
if buf[pos+1] notin chars:
|
||||
lexMessage(L, errInvalidToken, "_")
|
||||
break
|
||||
add(tok.literal, '_')
|
||||
inc(pos)
|
||||
L.bufpos = pos
|
||||
|
||||
proc matchTwoChars(L: TLexer, first: char, second: set[char]): bool =
|
||||
result = (L.buf[L.bufpos] == first) and (L.buf[L.bufpos + 1] in second)
|
||||
|
||||
@@ -268,136 +253,200 @@ proc unsafeParseUInt(s: string, b: var BiggestInt, start = 0): int =
|
||||
result = i - start
|
||||
{.pop.} # overflowChecks
|
||||
|
||||
|
||||
template eatChar(L: var TLexer, t: var TToken, replacementChar: char) =
|
||||
add(t.literal, replacementChar)
|
||||
inc(L.bufpos)
|
||||
|
||||
template eatChar(L: var TLexer, t: var TToken) =
|
||||
add(t.literal, L.buf[L.bufpos])
|
||||
inc(L.bufpos)
|
||||
|
||||
proc getNumber(L: var TLexer): TToken =
|
||||
proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: set[char]) =
|
||||
var pos = L.bufpos # use registers for pos, buf
|
||||
var buf = L.buf
|
||||
while true:
|
||||
if buf[pos] in chars:
|
||||
add(tok.literal, buf[pos])
|
||||
inc(pos)
|
||||
else:
|
||||
break
|
||||
if buf[pos] == '_':
|
||||
if buf[pos+1] notin chars:
|
||||
lexMessage(L, errInvalidToken, "_")
|
||||
break
|
||||
add(tok.literal, '_')
|
||||
inc(pos)
|
||||
L.bufpos = pos
|
||||
|
||||
proc matchChars(L: var TLexer, tok: var TToken, chars: set[char]) =
|
||||
var pos = L.bufpos # use registers for pos, buf
|
||||
var buf = L.buf
|
||||
while buf[pos] in chars:
|
||||
add(tok.literal, buf[pos])
|
||||
inc(pos)
|
||||
L.bufpos = pos
|
||||
|
||||
proc lexMessageLitNum(L: var TLexer, msg: TMsgKind, startpos: int) =
|
||||
# Used to get slightly human friendlier err messages.
|
||||
# Note: the erroneous 'O' char in the character set is intentional
|
||||
const literalishChars = {'A'..'F', 'a'..'f', '0'..'9', 'X', 'x', 'o', 'O',
|
||||
'c', 'C', 'b', 'B', '_', '.', '\'', 'd', 'i', 'u'}
|
||||
var msgPos = L.bufpos
|
||||
var t: TToken
|
||||
t.literal = ""
|
||||
L.bufpos = startpos # Use L.bufpos as pos because of matchChars
|
||||
matchChars(L, t, literalishChars)
|
||||
# We must verify +/- specifically so that we're not past the literal
|
||||
if L.buf[L.bufpos] in {'+', '-'} and
|
||||
L.buf[L.bufpos - 1] in {'e', 'E'}:
|
||||
add(t.literal, L.buf[L.bufpos])
|
||||
inc(L.bufpos)
|
||||
matchChars(L, t, literalishChars)
|
||||
if L.buf[L.bufpos] in {'\'', 'f', 'F', 'd', 'D', 'i', 'I', 'u', 'U'}:
|
||||
inc(L.bufpos)
|
||||
add(t.literal, L.buf[L.bufpos])
|
||||
matchChars(L, t, {'0'..'9'})
|
||||
L.bufpos = msgPos
|
||||
lexMessage(L, msg, t.literal)
|
||||
|
||||
var
|
||||
pos, endpos: int
|
||||
startpos, endpos: int
|
||||
xi: BiggestInt
|
||||
# get the base:
|
||||
isBase10 = true
|
||||
const
|
||||
baseCodeChars = {'X', 'x', 'o', 'c', 'C', 'b', 'B'}
|
||||
literalishChars = baseCodeChars + {'A'..'F', 'a'..'f', '0'..'9', '_', '\''}
|
||||
floatTypes = {tkFloatLit, tkFloat32Lit, tkFloat64Lit, tkFloat128Lit}
|
||||
result.tokType = tkIntLit # int literal until we know better
|
||||
result.literal = ""
|
||||
result.base = base10 # BUGFIX
|
||||
pos = L.bufpos # make sure the literal is correct for error messages:
|
||||
var eallowed = false
|
||||
if L.buf[pos] == '0' and L.buf[pos+1] in {'X', 'x'}:
|
||||
matchUnderscoreChars(L, result, {'A'..'F', 'a'..'f', '0'..'9', 'X', 'x'})
|
||||
result.base = base10
|
||||
startpos = L.bufpos
|
||||
|
||||
# First stage: find out base, make verifications, build token literal string
|
||||
if L.buf[L.bufpos] == '0' and L.buf[L.bufpos + 1] in baseCodeChars + {'O'}:
|
||||
isBase10 = false
|
||||
eatChar(L, result, '0')
|
||||
case L.buf[L.bufpos]
|
||||
of 'O':
|
||||
lexMessageLitNum(L, errInvalidNumberOctalCode, startpos)
|
||||
of 'x', 'X':
|
||||
eatChar(L, result, 'x')
|
||||
matchUnderscoreChars(L, result, {'0'..'9', 'a'..'f', 'A'..'F'})
|
||||
of 'o', 'c', 'C':
|
||||
eatChar(L, result, 'c')
|
||||
matchUnderscoreChars(L, result, {'0'..'7'})
|
||||
of 'b', 'B':
|
||||
eatChar(L, result, 'b')
|
||||
matchUnderscoreChars(L, result, {'0'..'1'})
|
||||
else:
|
||||
internalError(getLineInfo(L), "getNumber")
|
||||
else:
|
||||
matchUnderscoreChars(L, result, {'0'..'9', 'b', 'B', 'o', 'c', 'C'})
|
||||
eallowed = true
|
||||
if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}):
|
||||
add(result.literal, '.')
|
||||
inc(L.bufpos)
|
||||
matchUnderscoreChars(L, result, {'0'..'9'})
|
||||
eallowed = true
|
||||
if eallowed and L.buf[L.bufpos] in {'e', 'E'}:
|
||||
add(result.literal, 'e')
|
||||
inc(L.bufpos)
|
||||
if L.buf[L.bufpos] in {'+', '-'}:
|
||||
add(result.literal, L.buf[L.bufpos])
|
||||
inc(L.bufpos)
|
||||
matchUnderscoreChars(L, result, {'0'..'9'})
|
||||
if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}):
|
||||
result.tokType = tkFloat64Lit
|
||||
eatChar(L, result, '.')
|
||||
matchUnderscoreChars(L, result, {'0'..'9'})
|
||||
if L.buf[L.bufpos] in {'e', 'E'}:
|
||||
result.tokType = tkFloat64Lit
|
||||
eatChar(L, result, 'e')
|
||||
if L.buf[L.bufpos] in {'+', '-'}:
|
||||
eatChar(L, result)
|
||||
matchUnderscoreChars(L, result, {'0'..'9'})
|
||||
endpos = L.bufpos
|
||||
if L.buf[endpos] in {'\'', 'f', 'F', 'i', 'I', 'u', 'U'}:
|
||||
if L.buf[endpos] == '\'': inc(endpos)
|
||||
L.bufpos = pos # restore position
|
||||
case L.buf[endpos]
|
||||
|
||||
# Second stage, find out if there's a datatype suffix and handle it
|
||||
var postPos = endpos
|
||||
if L.buf[postPos] in {'\'', 'f', 'F', 'd', 'D', 'i', 'I', 'u', 'U'}:
|
||||
if L.buf[postPos] == '\'':
|
||||
inc(postPos)
|
||||
|
||||
case L.buf[postPos]
|
||||
of 'f', 'F':
|
||||
inc(endpos)
|
||||
if (L.buf[endpos] == '3') and (L.buf[endpos + 1] == '2'):
|
||||
inc(postPos)
|
||||
if (L.buf[postPos] == '3') and (L.buf[postPos + 1] == '2'):
|
||||
result.tokType = tkFloat32Lit
|
||||
inc(endpos, 2)
|
||||
elif (L.buf[endpos] == '6') and (L.buf[endpos + 1] == '4'):
|
||||
inc(postPos, 2)
|
||||
elif (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'):
|
||||
result.tokType = tkFloat64Lit
|
||||
inc(endpos, 2)
|
||||
elif (L.buf[endpos] == '1') and
|
||||
(L.buf[endpos + 1] == '2') and
|
||||
(L.buf[endpos + 2] == '8'):
|
||||
inc(postPos, 2)
|
||||
elif (L.buf[postPos] == '1') and
|
||||
(L.buf[postPos + 1] == '2') and
|
||||
(L.buf[postPos + 2] == '8'):
|
||||
result.tokType = tkFloat128Lit
|
||||
inc(endpos, 3)
|
||||
else:
|
||||
lexMessage(L, errInvalidNumber, result.literal & "'f" & L.buf[endpos])
|
||||
inc(postPos, 3)
|
||||
else: # "f" alone defaults to float32
|
||||
result.tokType = tkFloat32Lit
|
||||
of 'd', 'D': # ad hoc convenience shortcut for f64
|
||||
inc(postPos)
|
||||
result.tokType = tkFloat64Lit
|
||||
of 'i', 'I':
|
||||
inc(endpos)
|
||||
if (L.buf[endpos] == '6') and (L.buf[endpos + 1] == '4'):
|
||||
inc(postPos)
|
||||
if (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'):
|
||||
result.tokType = tkInt64Lit
|
||||
inc(endpos, 2)
|
||||
elif (L.buf[endpos] == '3') and (L.buf[endpos + 1] == '2'):
|
||||
inc(postPos, 2)
|
||||
elif (L.buf[postPos] == '3') and (L.buf[postPos + 1] == '2'):
|
||||
result.tokType = tkInt32Lit
|
||||
inc(endpos, 2)
|
||||
elif (L.buf[endpos] == '1') and (L.buf[endpos + 1] == '6'):
|
||||
inc(postPos, 2)
|
||||
elif (L.buf[postPos] == '1') and (L.buf[postPos + 1] == '6'):
|
||||
result.tokType = tkInt16Lit
|
||||
inc(endpos, 2)
|
||||
elif (L.buf[endpos] == '8'):
|
||||
inc(postPos, 2)
|
||||
elif (L.buf[postPos] == '8'):
|
||||
result.tokType = tkInt8Lit
|
||||
inc(endpos)
|
||||
inc(postPos)
|
||||
else:
|
||||
lexMessage(L, errInvalidNumber, result.literal & "'i" & L.buf[endpos])
|
||||
lexMessageLitNum(L, errInvalidNumber, startpos)
|
||||
of 'u', 'U':
|
||||
inc(endpos)
|
||||
if (L.buf[endpos] == '6') and (L.buf[endpos + 1] == '4'):
|
||||
inc(postPos)
|
||||
if (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'):
|
||||
result.tokType = tkUInt64Lit
|
||||
inc(endpos, 2)
|
||||
elif (L.buf[endpos] == '3') and (L.buf[endpos + 1] == '2'):
|
||||
inc(postPos, 2)
|
||||
elif (L.buf[postPos] == '3') and (L.buf[postPos + 1] == '2'):
|
||||
result.tokType = tkUInt32Lit
|
||||
inc(endpos, 2)
|
||||
elif (L.buf[endpos] == '1') and (L.buf[endpos + 1] == '6'):
|
||||
inc(postPos, 2)
|
||||
elif (L.buf[postPos] == '1') and (L.buf[postPos + 1] == '6'):
|
||||
result.tokType = tkUInt16Lit
|
||||
inc(endpos, 2)
|
||||
elif (L.buf[endpos] == '8'):
|
||||
inc(postPos, 2)
|
||||
elif (L.buf[postPos] == '8'):
|
||||
result.tokType = tkUInt8Lit
|
||||
inc(endpos)
|
||||
inc(postPos)
|
||||
else:
|
||||
result.tokType = tkUIntLit
|
||||
else: lexMessage(L, errInvalidNumber, result.literal & "'" & L.buf[endpos])
|
||||
else:
|
||||
L.bufpos = pos # restore position
|
||||
else:
|
||||
lexMessageLitNum(L, errInvalidNumber, startpos)
|
||||
|
||||
# Is there still a literalish char awaiting? Then it's an error!
|
||||
if L.buf[postPos] in literalishChars or
|
||||
(L.buf[postPos] == '.' and L.buf[postPos + 1] in {'0'..'9'}):
|
||||
lexMessageLitNum(L, errInvalidNumber, startpos)
|
||||
|
||||
# Third stage, extract actual number
|
||||
L.bufpos = startpos # restore position
|
||||
var pos: int = startpos
|
||||
try:
|
||||
if (L.buf[pos] == '0') and
|
||||
(L.buf[pos + 1] in {'x', 'X', 'b', 'B', 'o', 'O', 'c', 'C'}):
|
||||
if (L.buf[pos] == '0') and (L.buf[pos + 1] in baseCodeChars):
|
||||
inc(pos, 2)
|
||||
xi = 0 # it may be a base prefix
|
||||
case L.buf[pos - 1] # now look at the optional type suffix:
|
||||
xi = 0 # it is a base prefix
|
||||
|
||||
case L.buf[pos - 1]
|
||||
of 'b', 'B':
|
||||
result.base = base2
|
||||
while true:
|
||||
case L.buf[pos]
|
||||
of '2'..'9', '.':
|
||||
lexMessage(L, errInvalidNumber, result.literal)
|
||||
inc(pos)
|
||||
of '_':
|
||||
if L.buf[pos+1] notin {'0'..'1'}:
|
||||
lexMessage(L, errInvalidToken, "_")
|
||||
break
|
||||
inc(pos)
|
||||
of '0', '1':
|
||||
while pos < endpos:
|
||||
if L.buf[pos] != '_':
|
||||
xi = `shl`(xi, 1) or (ord(L.buf[pos]) - ord('0'))
|
||||
inc(pos)
|
||||
else: break
|
||||
inc(pos)
|
||||
of 'o', 'c', 'C':
|
||||
result.base = base8
|
||||
while true:
|
||||
case L.buf[pos]
|
||||
of '8'..'9', '.':
|
||||
lexMessage(L, errInvalidNumber, result.literal)
|
||||
inc(pos)
|
||||
of '_':
|
||||
if L.buf[pos+1] notin {'0'..'7'}:
|
||||
lexMessage(L, errInvalidToken, "_")
|
||||
break
|
||||
inc(pos)
|
||||
of '0'..'7':
|
||||
while pos < endpos:
|
||||
if L.buf[pos] != '_':
|
||||
xi = `shl`(xi, 3) or (ord(L.buf[pos]) - ord('0'))
|
||||
inc(pos)
|
||||
else: break
|
||||
of 'O':
|
||||
lexMessage(L, errInvalidNumber, result.literal)
|
||||
inc(pos)
|
||||
of 'x', 'X':
|
||||
result.base = base16
|
||||
while true:
|
||||
while pos < endpos:
|
||||
case L.buf[pos]
|
||||
of '_':
|
||||
if L.buf[pos+1] notin {'0'..'9', 'a'..'f', 'A'..'F'}:
|
||||
lexMessage(L, errInvalidToken, "_")
|
||||
break
|
||||
inc(pos)
|
||||
of '0'..'9':
|
||||
xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('0'))
|
||||
@@ -408,51 +457,81 @@ proc getNumber(L: var TLexer): TToken =
|
||||
of 'A'..'F':
|
||||
xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('A') + 10)
|
||||
inc(pos)
|
||||
else: break
|
||||
else: internalError(getLineInfo(L), "getNumber")
|
||||
else:
|
||||
break
|
||||
else:
|
||||
internalError(getLineInfo(L), "getNumber")
|
||||
|
||||
case result.tokType
|
||||
of tkIntLit, tkInt64Lit: result.iNumber = xi
|
||||
of tkInt8Lit: result.iNumber = BiggestInt(int8(toU8(int(xi))))
|
||||
of tkInt16Lit: result.iNumber = BiggestInt(toU16(int(xi)))
|
||||
of tkInt32Lit: result.iNumber = BiggestInt(toU32(xi))
|
||||
of tkInt16Lit: result.iNumber = BiggestInt(int16(toU16(int(xi))))
|
||||
of tkInt32Lit: result.iNumber = BiggestInt(int32(toU32(int64(xi))))
|
||||
of tkUIntLit, tkUInt64Lit: result.iNumber = xi
|
||||
of tkUInt8Lit: result.iNumber = BiggestInt(int8(toU8(int(xi))))
|
||||
of tkUInt16Lit: result.iNumber = BiggestInt(toU16(int(xi)))
|
||||
of tkUInt32Lit: result.iNumber = BiggestInt(toU32(xi))
|
||||
of tkUInt8Lit: result.iNumber = BiggestInt(uint8(toU8(int(xi))))
|
||||
of tkUInt16Lit: result.iNumber = BiggestInt(uint16(toU16(int(xi))))
|
||||
of tkUInt32Lit: result.iNumber = BiggestInt(uint32(toU32(int64(xi))))
|
||||
of tkFloat32Lit:
|
||||
result.fNumber = (cast[PFloat32](addr(xi)))[]
|
||||
# note: this code is endian neutral!
|
||||
# XXX: Test this on big endian machine!
|
||||
of tkFloat64Lit: result.fNumber = (cast[PFloat64](addr(xi)))[]
|
||||
else: internalError(getLineInfo(L), "getNumber")
|
||||
elif isFloatLiteral(result.literal) or (result.tokType == tkFloat32Lit) or
|
||||
(result.tokType == tkFloat64Lit):
|
||||
result.fNumber = parseFloat(result.literal)
|
||||
if result.tokType == tkIntLit: result.tokType = tkFloatLit
|
||||
elif result.tokType == tkUint64Lit:
|
||||
xi = 0
|
||||
let len = unsafeParseUInt(result.literal, xi)
|
||||
if len != result.literal.len or len == 0:
|
||||
raise newException(ValueError, "invalid integer: " & $xi)
|
||||
result.iNumber = xi
|
||||
|
||||
# Bounds checks. Non decimal literals are allowed to overflow the range of
|
||||
# the datatype as long as their pattern don't overflow _bitwise_, hence
|
||||
# below checks of signed sizes against uint*.high is deliberate:
|
||||
# (0x80'u8 = 128, 0x80'i8 = -128, etc == OK)
|
||||
if result.tokType notin floatTypes:
|
||||
let outOfRange = case result.tokType:
|
||||
of tkUInt8Lit, tkUInt16Lit, tkUInt32Lit: result.iNumber != xi
|
||||
of tkInt8Lit: (xi > BiggestInt(uint8.high))
|
||||
of tkInt16Lit: (xi > BiggestInt(uint16.high))
|
||||
of tkInt32Lit: (xi > BiggestInt(uint32.high))
|
||||
else: false
|
||||
|
||||
if outOfRange:
|
||||
#echo "out of range num: ", result.iNumber, " vs ", xi
|
||||
lexMessageLitNum(L, errNumberOutOfRange, startpos)
|
||||
|
||||
else:
|
||||
result.iNumber = parseBiggestInt(result.literal)
|
||||
case result.tokType
|
||||
of floatTypes:
|
||||
result.fNumber = parseFloat(result.literal)
|
||||
of tkUint64Lit:
|
||||
xi = 0
|
||||
let len = unsafeParseUInt(result.literal, xi)
|
||||
if len != result.literal.len or len == 0:
|
||||
raise newException(ValueError, "invalid integer: " & $xi)
|
||||
result.iNumber = xi
|
||||
else:
|
||||
result.iNumber = parseBiggestInt(result.literal)
|
||||
|
||||
# Explicit bounds checks
|
||||
let outOfRange = case result.tokType:
|
||||
of tkInt8Lit: (result.iNumber < int8.low or result.iNumber > int8.high)
|
||||
of tkUInt8Lit: (result.iNumber < BiggestInt(uint8.low) or
|
||||
result.iNumber > BiggestInt(uint8.high))
|
||||
of tkInt16Lit: (result.iNumber < int16.low or result.iNumber > int16.high)
|
||||
of tkUInt16Lit: (result.iNumber < BiggestInt(uint16.low) or
|
||||
result.iNumber > BiggestInt(uint16.high))
|
||||
of tkInt32Lit: (result.iNumber < int32.low or result.iNumber > int32.high)
|
||||
of tkUInt32Lit: (result.iNumber < BiggestInt(uint32.low) or
|
||||
result.iNumber > BiggestInt(uint32.high))
|
||||
else: false
|
||||
|
||||
if outOfRange: lexMessageLitNum(L, errNumberOutOfRange, startpos)
|
||||
|
||||
# Promote int literal to int64? Not always necessary, but more consistent
|
||||
if result.tokType == tkIntLit:
|
||||
if (result.iNumber < low(int32)) or (result.iNumber > high(int32)):
|
||||
if result.tokType == tkIntLit:
|
||||
result.tokType = tkInt64Lit
|
||||
elif result.tokType in {tkInt8Lit, tkInt16Lit, tkInt32Lit}:
|
||||
lexMessage(L, errNumberOutOfRange, result.literal)
|
||||
elif result.tokType == tkInt8Lit and
|
||||
(result.iNumber < int8.low or result.iNumber > int8.high):
|
||||
lexMessage(L, errNumberOutOfRange, result.literal)
|
||||
elif result.tokType == tkInt16Lit and
|
||||
(result.iNumber < int16.low or result.iNumber > int16.high):
|
||||
lexMessage(L, errNumberOutOfRange, result.literal)
|
||||
result.tokType = tkInt64Lit
|
||||
|
||||
except ValueError:
|
||||
lexMessage(L, errInvalidNumber, result.literal)
|
||||
lexMessageLitNum(L, errInvalidNumber, startpos)
|
||||
except OverflowError, RangeError:
|
||||
lexMessage(L, errNumberOutOfRange, result.literal)
|
||||
L.bufpos = endpos
|
||||
lexMessageLitNum(L, errNumberOutOfRange, startpos)
|
||||
L.bufpos = postPos
|
||||
|
||||
proc handleHexChar(L: var TLexer, xi: var int) =
|
||||
case L.buf[L.bufpos]
|
||||
@@ -625,23 +704,34 @@ proc getCharacter(L: var TLexer, tok: var TToken) =
|
||||
inc(L.bufpos) # skip '
|
||||
|
||||
proc getSymbol(L: var TLexer, tok: var TToken) =
|
||||
var h: THash = 0
|
||||
var h: Hash = 0
|
||||
var pos = L.bufpos
|
||||
var buf = L.buf
|
||||
while true:
|
||||
var c = buf[pos]
|
||||
case c
|
||||
of 'a'..'z', '0'..'9', '\x80'..'\xFF':
|
||||
h = h !& ord(c)
|
||||
if c == '\226' and
|
||||
buf[pos+1] == '\128' and
|
||||
buf[pos+2] == '\147': # It's a 'magic separator' en-dash Unicode
|
||||
if buf[pos + magicIdentSeparatorRuneByteWidth] notin SymChars:
|
||||
lexMessage(L, errInvalidToken, "–")
|
||||
break
|
||||
inc(pos, magicIdentSeparatorRuneByteWidth)
|
||||
else:
|
||||
h = h !& ord(c)
|
||||
inc(pos)
|
||||
of 'A'..'Z':
|
||||
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
|
||||
h = h !& ord(c)
|
||||
inc(pos)
|
||||
of '_':
|
||||
if buf[pos+1] notin SymChars:
|
||||
lexMessage(L, errInvalidToken, "_")
|
||||
break
|
||||
inc(pos)
|
||||
|
||||
else: break
|
||||
inc(pos)
|
||||
h = !$h
|
||||
tok.ident = getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
|
||||
L.bufpos = pos
|
||||
@@ -652,7 +742,7 @@ proc getSymbol(L: var TLexer, tok: var TToken) =
|
||||
tok.tokType = TTokType(tok.ident.id + ord(tkSymbol))
|
||||
|
||||
proc endOperator(L: var TLexer, tok: var TToken, pos: int,
|
||||
hash: THash) {.inline.} =
|
||||
hash: Hash) {.inline.} =
|
||||
var h = !$hash
|
||||
tok.ident = getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
|
||||
if (tok.ident.id < oprLow) or (tok.ident.id > oprHigh): tok.tokType = tkOpr
|
||||
@@ -662,7 +752,7 @@ proc endOperator(L: var TLexer, tok: var TToken, pos: int,
|
||||
proc getOperator(L: var TLexer, tok: var TToken) =
|
||||
var pos = L.bufpos
|
||||
var buf = L.buf
|
||||
var h: THash = 0
|
||||
var h: Hash = 0
|
||||
while true:
|
||||
var c = buf[pos]
|
||||
if c notin OpChars: break
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
## implements the module handling
|
||||
|
||||
import
|
||||
ast, astalgo, magicsys, crc, rodread, msgs, cgendata, sigmatch, options,
|
||||
ast, astalgo, magicsys, securehash, rodread, msgs, cgendata, sigmatch, options,
|
||||
idents, os, lexer, idgen, passes, syntaxes, llstream
|
||||
|
||||
type
|
||||
@@ -19,7 +19,7 @@ type
|
||||
|
||||
TModuleInMemory* = object
|
||||
compiledAt*: float
|
||||
crc*: TCrc32
|
||||
crc*: SecureHash
|
||||
deps*: seq[int32] ## XXX: slurped files are currently not tracked
|
||||
needsRecompile*: TNeedRecompile
|
||||
crcStatus*: TCrcStatus
|
||||
@@ -51,19 +51,19 @@ proc crcChanged(fileIdx: int32): bool =
|
||||
of crcNotChanged:
|
||||
result = false
|
||||
of crcCached:
|
||||
let newCrc = crcFromFile(fileIdx.toFilename)
|
||||
let newCrc = secureHashFile(fileIdx.toFullPath)
|
||||
result = newCrc != gMemCacheData[fileIdx].crc
|
||||
gMemCacheData[fileIdx].crc = newCrc
|
||||
updateStatus()
|
||||
of crcNotTaken:
|
||||
gMemCacheData[fileIdx].crc = crcFromFile(fileIdx.toFilename)
|
||||
gMemCacheData[fileIdx].crc = secureHashFile(fileIdx.toFullPath)
|
||||
result = true
|
||||
updateStatus()
|
||||
|
||||
proc doCRC(fileIdx: int32) =
|
||||
if gMemCacheData[fileIdx].crcStatus == crcNotTaken:
|
||||
# echo "FIRST CRC: ", fileIdx.ToFilename
|
||||
gMemCacheData[fileIdx].crc = crcFromFile(fileIdx.toFilename)
|
||||
gMemCacheData[fileIdx].crc = secureHashFile(fileIdx.toFullPath)
|
||||
|
||||
proc addDep(x: PSym, dep: int32) =
|
||||
growCache gMemCacheData, dep
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#
|
||||
|
||||
import
|
||||
options, strutils, os, tables, ropes, platform
|
||||
options, strutils, os, tables, ropes, platform, terminal
|
||||
|
||||
type
|
||||
TMsgKind* = enum
|
||||
@@ -17,10 +17,9 @@ type
|
||||
errIntLiteralExpected, errInvalidCharacterConstant,
|
||||
errClosingTripleQuoteExpected, errClosingQuoteExpected,
|
||||
errTabulatorsAreNotAllowed, errInvalidToken, errLineTooLong,
|
||||
errInvalidNumber, errNumberOutOfRange, errNnotAllowedInCharacter,
|
||||
errClosingBracketExpected, errMissingFinalQuote, errIdentifierExpected,
|
||||
errNewlineExpected,
|
||||
errInvalidModuleName,
|
||||
errInvalidNumber, errInvalidNumberOctalCode, errNumberOutOfRange,
|
||||
errNnotAllowedInCharacter, errClosingBracketExpected, errMissingFinalQuote,
|
||||
errIdentifierExpected, errNewlineExpected, errInvalidModuleName,
|
||||
errOperatorExpected, errTokenExpected, errStringAfterIncludeExpected,
|
||||
errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected,
|
||||
errInvalidPragma, errUnknownPragma, errInvalidDirectiveX,
|
||||
@@ -35,7 +34,9 @@ type
|
||||
errNoneSpeedOrSizeExpectedButXFound, errGuiConsoleOrLibExpectedButXFound,
|
||||
errUnknownOS, errUnknownCPU, errGenOutExpectedButXFound,
|
||||
errArgsNeedRunOption, errInvalidMultipleAsgn, errColonOrEqualsExpected,
|
||||
errExprExpected, errUndeclaredIdentifier, errUseQualifier, errTypeExpected,
|
||||
errExprExpected, errUndeclaredIdentifier, errUndeclaredField,
|
||||
errUndeclaredRoutine, errUseQualifier,
|
||||
errTypeExpected,
|
||||
errSystemNeeds, errExecutionOfProgramFailed, errNotOverloadable,
|
||||
errInvalidArgForX, errStmtHasNoEffect, errXExpectsTypeOrValue,
|
||||
errXExpectsArrayType, errIteratorCannotBeInstantiated, errExprXAmbiguous,
|
||||
@@ -143,6 +144,7 @@ const
|
||||
errInvalidToken: "invalid token: $1",
|
||||
errLineTooLong: "line too long",
|
||||
errInvalidNumber: "$1 is not a valid number",
|
||||
errInvalidNumberOctalCode: "$1 is not a valid number; did you mean octal? Then use one of '0o', '0c' or '0C'.",
|
||||
errNumberOutOfRange: "number $1 out of valid range",
|
||||
errNnotAllowedInCharacter: "\\n not allowed in character literal",
|
||||
errClosingBracketExpected: "closing ']' expected, but end of file reached",
|
||||
@@ -190,6 +192,8 @@ const
|
||||
errColonOrEqualsExpected: "\':\' or \'=\' expected, but found \'$1\'",
|
||||
errExprExpected: "expression expected, but found \'$1\'",
|
||||
errUndeclaredIdentifier: "undeclared identifier: \'$1\'",
|
||||
errUndeclaredField: "undeclared field: \'$1\'",
|
||||
errUndeclaredRoutine: "attempting to call undeclared routine: \'$1\'",
|
||||
errUseQualifier: "ambiguous identifier: \'$1\' -- use a qualifier",
|
||||
errTypeExpected: "type expected",
|
||||
errSystemNeeds: "system module needs \'$1\'",
|
||||
@@ -601,13 +605,13 @@ proc suggestQuit*() =
|
||||
# this format is understood by many text editors: it is the same that
|
||||
# Borland and Freepascal use
|
||||
const
|
||||
PosErrorFormat* = "$1($2, $3) Error: $4"
|
||||
PosWarningFormat* = "$1($2, $3) Warning: $4"
|
||||
PosHintFormat* = "$1($2, $3) Hint: $4"
|
||||
PosContextFormat = "$1($2, $3) Info: $4"
|
||||
RawErrorFormat* = "Error: $1"
|
||||
RawWarningFormat* = "Warning: $1"
|
||||
RawHintFormat* = "Hint: $1"
|
||||
PosErrorFormat* = "$1($2, $3) Error: "
|
||||
PosWarningFormat* = "$1($2, $3) Warning: "
|
||||
PosHintFormat* = "$1($2, $3) Hint: "
|
||||
PosContextFormat = "$1($2, $3) Info: "
|
||||
RawError* = "Error: "
|
||||
RawWarning* = "Warning: "
|
||||
RawHint* = "Hint: "
|
||||
|
||||
proc getInfoContextLen*(): int = return msgContext.len
|
||||
proc setInfoContextLen*(L: int) = setLen(msgContext, L)
|
||||
@@ -682,17 +686,27 @@ proc outWriteln*(s: string) =
|
||||
## Writes to stdout. Always.
|
||||
if eStdOut in errorOutputs: writeln(stdout, s)
|
||||
|
||||
proc msgWriteln*(s: string) =
|
||||
## Writes to stdout. If --stdout option is given, writes to stderr instead.
|
||||
proc msgWriteln*(s: string, color: ForegroundColor = fgWhite, coloredText: string = "") =
|
||||
## Writes to stdout. If --stderr option is given, writes to stderr instead.
|
||||
|
||||
#if gCmd == cmdIdeTools and optCDebug notin gGlobalOptions: return
|
||||
|
||||
var hasColor = optUseColors in gGlobalOptions
|
||||
if not isNil(writelnHook):
|
||||
writelnHook(s)
|
||||
elif optStdout in gGlobalOptions:
|
||||
if eStdErr in errorOutputs: writeln(stderr, s)
|
||||
writelnHook(coloredText & s)
|
||||
else:
|
||||
if eStdOut in errorOutputs: writeln(stdout, s)
|
||||
if optStdout in gGlobalOptions:
|
||||
if eStdErr in errorOutputs:
|
||||
if hasColor: setForegroundColor(color)
|
||||
write(stderr, coloredText)
|
||||
if hasColor: resetAttributes()
|
||||
writeln(stderr, s)
|
||||
else:
|
||||
if eStdOut in errorOutputs:
|
||||
if hasColor: setForegroundColor(color)
|
||||
write(stdout, coloredText)
|
||||
if hasColor: resetAttributes()
|
||||
writeln(stdout, s)
|
||||
|
||||
proc coordToStr(coord: int): string =
|
||||
if coord == -1: result = "???"
|
||||
@@ -714,7 +728,7 @@ proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) =
|
||||
if stackTraceAvailable() and isNil(writelnHook):
|
||||
writeStackTrace()
|
||||
else:
|
||||
msgWriteln("No stack traceback available\nTo create a stacktrace, rerun compilation with ./koch temp " & options.command & " <file>")
|
||||
msgWriteln("", fgRed, "No stack traceback available\nTo create a stacktrace, rerun compilation with ./koch temp " & options.command & " <file>")
|
||||
quit 1
|
||||
|
||||
if msg >= fatalMin and msg <= fatalMax:
|
||||
@@ -737,34 +751,39 @@ proc writeContext(lastinfo: TLineInfo) =
|
||||
for i in countup(0, len(msgContext) - 1):
|
||||
if msgContext[i] != lastinfo and msgContext[i] != info:
|
||||
msgWriteln(PosContextFormat % [toMsgFilename(msgContext[i]),
|
||||
coordToStr(msgContext[i].line),
|
||||
coordToStr(msgContext[i].col+1),
|
||||
getMessageStr(errInstantiationFrom, "")])
|
||||
coordToStr(msgContext[i].line),
|
||||
coordToStr(msgContext[i].col+1),
|
||||
getMessageStr(errInstantiationFrom, "")])
|
||||
info = msgContext[i]
|
||||
|
||||
proc ignoreMsgBecauseOfIdeTools(msg: TMsgKind): bool =
|
||||
msg >= errGenerated and gCmd == cmdIdeTools and optIdeDebug notin gGlobalOptions
|
||||
|
||||
proc rawMessage*(msg: TMsgKind, args: openArray[string]) =
|
||||
var frmt: string
|
||||
var
|
||||
frmt: string
|
||||
color: ForegroundColor
|
||||
case msg
|
||||
of errMin..errMax:
|
||||
writeContext(unknownLineInfo())
|
||||
frmt = RawErrorFormat
|
||||
frmt = RawError
|
||||
color = fgRed
|
||||
of warnMin..warnMax:
|
||||
if optWarns notin gOptions: return
|
||||
if msg notin gNotes: return
|
||||
writeContext(unknownLineInfo())
|
||||
frmt = RawWarningFormat
|
||||
frmt = RawWarning
|
||||
inc(gWarnCounter)
|
||||
color = fgYellow
|
||||
of hintMin..hintMax:
|
||||
if optHints notin gOptions: return
|
||||
if msg notin gNotes: return
|
||||
frmt = RawHintFormat
|
||||
frmt = RawHint
|
||||
inc(gHintCounter)
|
||||
let s = `%`(frmt, `%`(msgKindToString(msg), args))
|
||||
color = fgGreen
|
||||
let s = `%`(msgKindToString(msg), args)
|
||||
if not ignoreMsgBecauseOfIdeTools(msg):
|
||||
msgWriteln(s)
|
||||
msgWriteln(s, color, frmt)
|
||||
handleError(msg, doAbort, s)
|
||||
|
||||
proc rawMessage*(msg: TMsgKind, arg: string) =
|
||||
@@ -785,8 +804,10 @@ proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string =
|
||||
|
||||
proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
|
||||
eh: TErrorHandling) =
|
||||
var frmt: string
|
||||
var ignoreMsg = false
|
||||
var
|
||||
frmt: string
|
||||
ignoreMsg = false
|
||||
color: ForegroundColor
|
||||
case msg
|
||||
of errMin..errMax:
|
||||
writeContext(info)
|
||||
@@ -795,22 +816,26 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
|
||||
# in the same file and line are produced:
|
||||
#ignoreMsg = lastError == info and eh != doAbort
|
||||
lastError = info
|
||||
color = fgRed
|
||||
of warnMin..warnMax:
|
||||
ignoreMsg = optWarns notin gOptions or msg notin gNotes
|
||||
if not ignoreMsg: writeContext(info)
|
||||
frmt = PosWarningFormat
|
||||
inc(gWarnCounter)
|
||||
color = fgYellow
|
||||
of hintMin..hintMax:
|
||||
ignoreMsg = optHints notin gOptions or msg notin gNotes
|
||||
frmt = PosHintFormat
|
||||
inc(gHintCounter)
|
||||
color = fgGreen
|
||||
# NOTE: currently line info line numbers start with 1,
|
||||
# but column numbers start with 0, however most editors expect
|
||||
# first column to be 1, so we need to +1 here
|
||||
let s = frmt % [toMsgFilename(info), coordToStr(info.line),
|
||||
coordToStr(info.col+1), getMessageStr(msg, arg)]
|
||||
let x = frmt % [toMsgFilename(info), coordToStr(info.line),
|
||||
coordToStr(info.col+1)]
|
||||
let s = getMessageStr(msg, arg)
|
||||
if not ignoreMsg and not ignoreMsgBecauseOfIdeTools(msg):
|
||||
msgWriteln(s)
|
||||
msgWriteln(s, color, x)
|
||||
if optPrintSurroundingSrc and msg in errMin..errMax:
|
||||
info.writeSurroundingSrc
|
||||
handleError(msg, eh, s)
|
||||
|
||||
@@ -10,8 +10,10 @@
|
||||
## Nimfix is a tool that helps to convert old-style Nimrod code to Nim code.
|
||||
|
||||
import strutils, os, parseopt
|
||||
import options, commands, modules, sem, passes, passaux, pretty, msgs, nimconf,
|
||||
extccomp, condsyms, lists
|
||||
import compiler/options, compiler/commands, compiler/modules, compiler/sem,
|
||||
compiler/passes, compiler/passaux, compiler/nimfix/pretty,
|
||||
compiler/msgs, compiler/nimconf,
|
||||
compiler/extccomp, compiler/condsyms, compiler/lists
|
||||
|
||||
const Usage = """
|
||||
Nimfix - Tool to patch Nim code
|
||||
@@ -24,7 +26,7 @@ Options:
|
||||
--wholeProject overwrite every processed file.
|
||||
--checkExtern:on|off style check also extern names
|
||||
--styleCheck:on|off|auto performs style checking for identifiers
|
||||
and suggests an alternative spelling;
|
||||
and suggests an alternative spelling;
|
||||
'auto' corrects the spelling.
|
||||
--bestEffort try to fix the code even when there
|
||||
are errors.
|
||||
@@ -48,11 +50,11 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
|
||||
var p = parseopt.initOptParser(cmd)
|
||||
var argsCount = 0
|
||||
gOnlyMainfile = true
|
||||
while true:
|
||||
while true:
|
||||
parseopt.next(p)
|
||||
case p.kind
|
||||
of cmdEnd: break
|
||||
of cmdLongoption, cmdShortOption:
|
||||
of cmdEnd: break
|
||||
of cmdLongoption, cmdShortOption:
|
||||
case p.key.normalize
|
||||
of "overwritefiles":
|
||||
case p.val.normalize
|
||||
|
||||
@@ -10,9 +10,11 @@
|
||||
## This module implements the code "prettifier". This is part of the toolchain
|
||||
## to convert Nim code into a consistent style.
|
||||
|
||||
import
|
||||
strutils, os, options, ast, astalgo, msgs, ropes, idents,
|
||||
intsets, strtabs, semdata, prettybase
|
||||
import
|
||||
strutils, os, intsets, strtabs
|
||||
|
||||
import compiler/options, compiler/ast, compiler/astalgo, compiler/msgs,
|
||||
compiler/semdata, compiler/nimfix/prettybase, compiler/ropes, compiler/idents
|
||||
|
||||
type
|
||||
StyleCheck* {.pure.} = enum None, Warn, Auto
|
||||
@@ -92,7 +94,7 @@ proc beautifyName(s: string, k: TSymKind): string =
|
||||
|
||||
proc replaceInFile(info: TLineInfo; newName: string) =
|
||||
loadFile(info)
|
||||
|
||||
|
||||
let line = gSourceFiles[info.fileIndex].lines[info.line-1]
|
||||
var first = min(info.col.int, line.len)
|
||||
if first < 0: return
|
||||
@@ -100,18 +102,18 @@ proc replaceInFile(info: TLineInfo; newName: string) =
|
||||
while first > 0 and line[first-1] in prettybase.Letters: dec first
|
||||
if first < 0: return
|
||||
if line[first] == '`': inc first
|
||||
|
||||
|
||||
let last = first+identLen(line, first)-1
|
||||
if differ(line, first, last, newName):
|
||||
# last-first+1 != newName.len or
|
||||
var x = line.substr(0, first-1) & newName & line.substr(last+1)
|
||||
# last-first+1 != newName.len or
|
||||
var x = line.substr(0, first-1) & newName & line.substr(last+1)
|
||||
system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x)
|
||||
gSourceFiles[info.fileIndex].dirty = true
|
||||
|
||||
proc checkStyle(info: TLineInfo, s: string, k: TSymKind; sym: PSym) =
|
||||
let beau = beautifyName(s, k)
|
||||
if s != beau:
|
||||
if gStyleCheck == StyleCheck.Auto:
|
||||
if gStyleCheck == StyleCheck.Auto:
|
||||
sym.name = getIdent(beau)
|
||||
replaceInFile(info, beau)
|
||||
else:
|
||||
@@ -137,7 +139,7 @@ proc styleCheckUseImpl(info: TLineInfo; s: PSym) =
|
||||
if info.fileIndex < 0: return
|
||||
# we simply convert it to what it looks like in the definition
|
||||
# for consistency
|
||||
|
||||
|
||||
# operators stay as they are:
|
||||
if s.kind in {skResult, skTemp} or s.name.s[0] notin prettybase.Letters:
|
||||
return
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
import ast, msgs, strutils, idents, lexbase, streams
|
||||
import strutils, lexbase, streams
|
||||
import compiler/ast, compiler/msgs, compiler/idents
|
||||
from os import splitFile
|
||||
|
||||
type
|
||||
@@ -39,7 +40,7 @@ proc loadFile*(info: TLineInfo) =
|
||||
var pos = lex.bufpos
|
||||
while true:
|
||||
case lex.buf[pos]
|
||||
of '\c':
|
||||
of '\c':
|
||||
gSourceFiles[i].newline = "\c\L"
|
||||
break
|
||||
of '\L', '\0':
|
||||
@@ -70,7 +71,7 @@ proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) =
|
||||
while first > 0 and line[first-1] in Letters: dec first
|
||||
if first < 0: return
|
||||
if line[first] == '`': inc first
|
||||
|
||||
|
||||
let last = first+identLen(line, first)-1
|
||||
if cmpIgnoreStyle(line[first..last], oldSym.s) == 0:
|
||||
var x = line.substr(0, first-1) & newSym.s & line.substr(last+1)
|
||||
|
||||
@@ -7,330 +7,6 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Nimsuggest is a tool that helps to give editors IDE like capabilities.
|
||||
## Nimsuggest has been moved to https://github.com/nim-lang/nimsuggest
|
||||
|
||||
import strutils, os, parseopt, parseutils, sequtils, net
|
||||
# Do NOT import suggest. It will lead to wierd bugs with
|
||||
# suggestionResultHook, because suggest.nim is included by sigmatch.
|
||||
# So we import that one instead.
|
||||
import options, commands, modules, sem, passes, passaux, msgs, nimconf,
|
||||
extccomp, condsyms, lists, net, rdstdin, sexp, sigmatch, ast
|
||||
|
||||
when defined(windows):
|
||||
import winlean
|
||||
else:
|
||||
import posix
|
||||
|
||||
const Usage = """
|
||||
Nimsuggest - Tool to give every editor IDE like capabilities for Nim
|
||||
Usage:
|
||||
nimsuggest [options] projectfile.nim
|
||||
|
||||
Options:
|
||||
--port:PORT port, by default 6000
|
||||
--address:HOST binds to that address, by default ""
|
||||
--stdin read commands from stdin and write results to
|
||||
stdout instead of using sockets
|
||||
--epc use emacs epc mode
|
||||
|
||||
The server then listens to the connection and takes line-based commands.
|
||||
|
||||
In addition, all command line options of Nim that do not affect code generation
|
||||
are supported.
|
||||
"""
|
||||
type
|
||||
Mode = enum mstdin, mtcp, mepc
|
||||
|
||||
var
|
||||
gPort = 6000.Port
|
||||
gAddress = ""
|
||||
gMode: Mode
|
||||
|
||||
const
|
||||
seps = {':', ';', ' ', '\t'}
|
||||
Help = "usage: sug|con|def|use file.nim[;dirtyfile.nim]:line:col\n"&
|
||||
"type 'quit' to quit\n" &
|
||||
"type 'debug' to toggle debug mode on/off\n" &
|
||||
"type 'terse' to toggle terse mode on/off"
|
||||
|
||||
type
|
||||
EUnexpectedCommand = object of Exception
|
||||
|
||||
proc parseQuoted(cmd: string; outp: var string; start: int): int =
|
||||
var i = start
|
||||
i += skipWhitespace(cmd, i)
|
||||
if cmd[i] == '"':
|
||||
i += parseUntil(cmd, outp, '"', i+1)+2
|
||||
else:
|
||||
i += parseUntil(cmd, outp, seps, i)
|
||||
result = i
|
||||
|
||||
proc sexp(s: IdeCmd): SexpNode = sexp($s)
|
||||
|
||||
proc sexp(s: TSymKind): SexpNode = sexp($s)
|
||||
|
||||
proc sexp(s: Suggest): SexpNode =
|
||||
# If you change the oder here, make sure to change it over in
|
||||
# nim-mode.el too.
|
||||
result = convertSexp([
|
||||
s.section,
|
||||
s.symkind,
|
||||
s.qualifiedPath.map(newSString),
|
||||
s.filePath,
|
||||
s.forth,
|
||||
s.line,
|
||||
s.column,
|
||||
s.doc
|
||||
])
|
||||
|
||||
proc sexp(s: seq[Suggest]): SexpNode =
|
||||
result = newSList()
|
||||
for sug in s:
|
||||
result.add(sexp(sug))
|
||||
|
||||
proc listEPC(): SexpNode =
|
||||
let
|
||||
argspecs = sexp("file line column dirtyfile".split(" ").map(newSSymbol))
|
||||
docstring = sexp("line starts at 1, column at 0, dirtyfile is optional")
|
||||
result = newSList()
|
||||
for command in ["sug", "con", "def", "use"]:
|
||||
let
|
||||
cmd = sexp(command)
|
||||
methodDesc = newSList()
|
||||
methodDesc.add(cmd)
|
||||
methodDesc.add(argspecs)
|
||||
methodDesc.add(docstring)
|
||||
result.add(methodDesc)
|
||||
|
||||
proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int) =
|
||||
gIdeCmd = cmd
|
||||
if cmd == ideUse:
|
||||
modules.resetAllModules()
|
||||
var isKnownFile = true
|
||||
let dirtyIdx = file.fileInfoIdx(isKnownFile)
|
||||
|
||||
if dirtyfile.len != 0: msgs.setDirtyFile(dirtyIdx, dirtyfile)
|
||||
else: msgs.setDirtyFile(dirtyIdx, nil)
|
||||
|
||||
resetModule dirtyIdx
|
||||
if dirtyIdx != gProjectMainIdx:
|
||||
resetModule gProjectMainIdx
|
||||
|
||||
gTrackPos = newLineInfo(dirtyIdx, line, col)
|
||||
gErrorCounter = 0
|
||||
if not isKnownFile:
|
||||
compileProject()
|
||||
compileProject(dirtyIdx)
|
||||
|
||||
proc executeEPC(cmd: IdeCmd, args: SexpNode) =
|
||||
let
|
||||
file = args[0].getStr
|
||||
line = args[1].getNum
|
||||
column = args[2].getNum
|
||||
var dirtyfile = ""
|
||||
if len(args) > 3:
|
||||
dirtyfile = args[3].getStr(nil)
|
||||
execute(cmd, file, dirtyfile, int(line), int(column))
|
||||
|
||||
proc returnEPC(socket: var Socket, uid: BiggestInt, s: SexpNode, return_symbol = "return") =
|
||||
let response = $convertSexp([newSSymbol(return_symbol), uid, s])
|
||||
socket.send(toHex(len(response), 6))
|
||||
socket.send(response)
|
||||
|
||||
proc connectToNextFreePort(server: Socket, host: string, start = 30000): int =
|
||||
result = start
|
||||
while true:
|
||||
try:
|
||||
server.bindaddr(Port(result), host)
|
||||
return
|
||||
except OsError:
|
||||
when defined(windows):
|
||||
let checkFor = WSAEADDRINUSE.OSErrorCode
|
||||
else:
|
||||
let checkFor = EADDRINUSE.OSErrorCode
|
||||
if osLastError() != checkFor:
|
||||
raise getCurrentException()
|
||||
else:
|
||||
result += 1
|
||||
|
||||
proc parseCmdLine(cmd: string) =
|
||||
template toggle(sw) =
|
||||
if sw in gGlobalOptions:
|
||||
excl(gGlobalOptions, sw)
|
||||
else:
|
||||
incl(gGlobalOptions, sw)
|
||||
return
|
||||
|
||||
template err() =
|
||||
echo Help
|
||||
return
|
||||
|
||||
var opc = ""
|
||||
var i = parseIdent(cmd, opc, 0)
|
||||
case opc.normalize
|
||||
of "sug": gIdeCmd = ideSug
|
||||
of "con": gIdeCmd = ideCon
|
||||
of "def": gIdeCmd = ideDef
|
||||
of "use": gIdeCmd = ideUse
|
||||
of "quit": quit()
|
||||
of "debug": toggle optIdeDebug
|
||||
of "terse": toggle optIdeTerse
|
||||
else: err()
|
||||
var dirtyfile = ""
|
||||
var orig = ""
|
||||
i = parseQuoted(cmd, orig, i)
|
||||
if cmd[i] == ';':
|
||||
i = parseQuoted(cmd, dirtyfile, i+1)
|
||||
i += skipWhile(cmd, seps, i)
|
||||
var line = -1
|
||||
var col = 0
|
||||
i += parseInt(cmd, line, i)
|
||||
i += skipWhile(cmd, seps, i)
|
||||
i += parseInt(cmd, col, i)
|
||||
|
||||
execute(gIdeCmd, orig, dirtyfile, line, col-1)
|
||||
|
||||
proc serve() =
|
||||
case gMode:
|
||||
of mstdin:
|
||||
echo Help
|
||||
var line = ""
|
||||
while readLineFromStdin("> ", line):
|
||||
parseCmdLine line
|
||||
echo ""
|
||||
flushFile(stdout)
|
||||
of mtcp:
|
||||
var server = newSocket()
|
||||
server.bindAddr(gPort, gAddress)
|
||||
var inp = "".TaintedString
|
||||
server.listen()
|
||||
|
||||
while true:
|
||||
var stdoutSocket = newSocket()
|
||||
msgs.writelnHook = proc (line: string) =
|
||||
stdoutSocket.send(line & "\c\L")
|
||||
|
||||
accept(server, stdoutSocket)
|
||||
|
||||
stdoutSocket.readLine(inp)
|
||||
parseCmdLine inp.string
|
||||
|
||||
stdoutSocket.send("\c\L")
|
||||
stdoutSocket.close()
|
||||
of mepc:
|
||||
var server = newSocket()
|
||||
let port = connectToNextFreePort(server, "localhost")
|
||||
var inp = "".TaintedString
|
||||
server.listen()
|
||||
echo(port)
|
||||
var client = newSocket()
|
||||
# Wait for connection
|
||||
accept(server, client)
|
||||
while true:
|
||||
var sizeHex = ""
|
||||
if client.recv(sizeHex, 6) != 6:
|
||||
raise newException(ValueError, "didn't get all the hexbytes")
|
||||
var size = 0
|
||||
if parseHex(sizeHex, size) == 0:
|
||||
raise newException(ValueError, "invalid size hex: " & $sizeHex)
|
||||
var messageBuffer = ""
|
||||
if client.recv(messageBuffer, size) != size:
|
||||
raise newException(ValueError, "didn't get all the bytes")
|
||||
let
|
||||
message = parseSexp($messageBuffer)
|
||||
messageType = message[0].getSymbol
|
||||
case messageType:
|
||||
of "call":
|
||||
var results: seq[Suggest] = @[]
|
||||
suggestionResultHook = proc (s: Suggest) =
|
||||
results.add(s)
|
||||
|
||||
let
|
||||
uid = message[1].getNum
|
||||
cmd = parseIdeCmd(message[2].getSymbol)
|
||||
args = message[3]
|
||||
executeEPC(cmd, args)
|
||||
returnEPC(client, uid, sexp(results))
|
||||
of "return":
|
||||
raise newException(EUnexpectedCommand, "no return expected")
|
||||
of "return-error":
|
||||
raise newException(EUnexpectedCommand, "no return expected")
|
||||
of "epc-error":
|
||||
stderr.writeln("recieved epc error: " & $messageBuffer)
|
||||
raise newException(IOError, "epc error")
|
||||
of "methods":
|
||||
returnEPC(client, message[1].getNum, listEPC())
|
||||
else:
|
||||
raise newException(EUnexpectedCommand, "unexpected call: " & messageType)
|
||||
|
||||
proc mainCommand =
|
||||
registerPass verbosePass
|
||||
registerPass semPass
|
||||
gCmd = cmdIdeTools
|
||||
incl gGlobalOptions, optCaasEnabled
|
||||
isServing = true
|
||||
wantMainModule()
|
||||
appendStr(searchPaths, options.libpath)
|
||||
if gProjectFull.len != 0:
|
||||
# current path is always looked first for modules
|
||||
prependStr(searchPaths, gProjectPath)
|
||||
|
||||
# do not stop after the first error:
|
||||
msgs.gErrorMax = high(int)
|
||||
compileProject()
|
||||
serve()
|
||||
|
||||
proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
|
||||
var p = parseopt.initOptParser(cmd)
|
||||
while true:
|
||||
parseopt.next(p)
|
||||
case p.kind
|
||||
of cmdEnd: break
|
||||
of cmdLongoption, cmdShortOption:
|
||||
case p.key.normalize
|
||||
of "port":
|
||||
gPort = parseInt(p.val).Port
|
||||
gMode = mtcp
|
||||
of "address":
|
||||
gAddress = p.val
|
||||
gMode = mtcp
|
||||
of "stdin": gMode = mstdin
|
||||
of "epc":
|
||||
gMode = mepc
|
||||
gVerbosity = 0 # Port number gotta be first.
|
||||
else: processSwitch(pass, p)
|
||||
of cmdArgument:
|
||||
options.gProjectName = unixToNativePath(p.key)
|
||||
# if processArgument(pass, p, argsCount): break
|
||||
|
||||
proc handleCmdLine() =
|
||||
if paramCount() == 0:
|
||||
stdout.writeln(Usage)
|
||||
else:
|
||||
processCmdLine(passCmd1, "")
|
||||
if gProjectName != "":
|
||||
try:
|
||||
gProjectFull = canonicalizePath(gProjectName)
|
||||
except OSError:
|
||||
gProjectFull = gProjectName
|
||||
var p = splitFile(gProjectFull)
|
||||
gProjectPath = p.dir
|
||||
gProjectName = p.name
|
||||
else:
|
||||
gProjectPath = getCurrentDir()
|
||||
loadConfigs(DefaultConfig) # load all config files
|
||||
# now process command line arguments again, because some options in the
|
||||
# command line can overwite the config file's settings
|
||||
extccomp.initVars()
|
||||
processCmdLine(passCmd2, "")
|
||||
mainCommand()
|
||||
|
||||
when false:
|
||||
proc quitCalled() {.noconv.} =
|
||||
writeStackTrace()
|
||||
|
||||
addQuitProc(quitCalled)
|
||||
|
||||
condsyms.initDefines()
|
||||
defineSymbol "nimsuggest"
|
||||
handleCmdline()
|
||||
{.error: "This project has moved to the following repo: https://github.com/nim-lang/nimsuggest".}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
# Special configuration file for the Nim project
|
||||
|
||||
gc:markAndSweep
|
||||
|
||||
hint[XDeclaredButNotUsed]:off
|
||||
path:"$projectPath/../.."
|
||||
|
||||
path:"$lib/packages/docutils"
|
||||
path:"../../compiler"
|
||||
|
||||
define:useStdoutAsStdmsg
|
||||
define:nimsuggest
|
||||
|
||||
cs:partial
|
||||
#define:useNodeIds
|
||||
define:booting
|
||||
#define:noDocgen
|
||||
@@ -4,3 +4,5 @@ proc findNodeJs*(): string =
|
||||
result = findExe("nodejs")
|
||||
if result == "":
|
||||
result = findExe("node")
|
||||
if result == "":
|
||||
result = findExe("iojs")
|
||||
|
||||
@@ -54,6 +54,7 @@ type # please make sure we have under 32 options
|
||||
optSkipUserConfigFile, # skip the users's config file
|
||||
optSkipParentConfigFiles, # skip parent dir's config files
|
||||
optNoMain, # do not generate a "main" proc
|
||||
optUseColors, # use colors for hints, warnings, and errors
|
||||
optThreads, # support for multi-threading
|
||||
optStdout, # output to stdout
|
||||
optThreadAnalysis, # thread analysis pass
|
||||
@@ -145,6 +146,7 @@ const
|
||||
var
|
||||
gConfigVars* = newStringTable(modeStyleInsensitive)
|
||||
gDllOverrides = newStringTable(modeCaseInsensitive)
|
||||
gPrefixDir* = "" # Overrides the default prefix dir in getPrefixDir proc.
|
||||
libpath* = ""
|
||||
gProjectName* = "" # holds a name like 'nimrod'
|
||||
gProjectPath* = "" # holds a path like /home/alice/projects/nimrod/compiler/
|
||||
@@ -183,8 +185,13 @@ proc getOutFile*(filename, ext: string): string =
|
||||
else: result = changeFileExt(filename, ext)
|
||||
|
||||
proc getPrefixDir*(): string =
|
||||
## gets the application directory
|
||||
result = splitPath(getAppDir()).head
|
||||
## Gets the prefix dir, usually the parent directory where the binary resides.
|
||||
##
|
||||
## This is overrided by some tools (namely nimsuggest) via the ``gPrefixDir``
|
||||
## global.
|
||||
if gPrefixDir != "": result = gPrefixDir
|
||||
else:
|
||||
result = splitPath(getAppDir()).head
|
||||
|
||||
proc canonicalizePath*(path: string): string =
|
||||
when not FileSystemCaseSensitive: result = path.expandFilename.toLower
|
||||
|
||||
@@ -64,6 +64,7 @@ proc setBaseFlags*(n: PNode, base: TNumericalBase)
|
||||
proc parseSymbol*(p: var TParser, allowNil = false): PNode
|
||||
proc parseTry(p: var TParser; isExpr: bool): PNode
|
||||
proc parseCase(p: var TParser): PNode
|
||||
proc parseStmtPragma(p: var TParser): PNode
|
||||
# implementation
|
||||
|
||||
proc getTok(p: var TParser) =
|
||||
@@ -499,10 +500,13 @@ proc parsePar(p: var TParser): PNode =
|
||||
#| parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try'
|
||||
#| | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let'
|
||||
#| | 'when' | 'var' | 'mixin'
|
||||
#| par = '(' optInd (&parKeyw complexOrSimpleStmt ^+ ';'
|
||||
#| | simpleExpr ('=' expr (';' complexOrSimpleStmt ^+ ';' )? )?
|
||||
#| | (':' expr)? (',' (exprColonEqExpr comma?)*)? )?
|
||||
#| optPar ')'
|
||||
#| par = '(' optInd
|
||||
#| ( &parKeyw complexOrSimpleStmt ^+ ';'
|
||||
#| | ';' complexOrSimpleStmt ^+ ';'
|
||||
#| | pragmaStmt
|
||||
#| | simpleExpr ( ('=' expr (';' complexOrSimpleStmt ^+ ';' )? )
|
||||
#| | (':' expr (',' exprColonEqExpr ^+ ',' )? ) ) )
|
||||
#| optPar ')'
|
||||
#
|
||||
# unfortunately it's ambiguous: (expr: expr) vs (exprStmt); however a
|
||||
# leading ';' could be used to enforce a 'stmt' context ...
|
||||
@@ -521,6 +525,8 @@ proc parsePar(p: var TParser): PNode =
|
||||
getTok(p)
|
||||
optInd(p, result)
|
||||
semiStmtList(p, result)
|
||||
elif p.tok.tokType == tkCurlyDotLe:
|
||||
result.add(parseStmtPragma(p))
|
||||
elif p.tok.tokType != tkParRi:
|
||||
var a = simpleExpr(p)
|
||||
if p.tok.tokType == tkEquals:
|
||||
|
||||
@@ -130,7 +130,9 @@ proc matchNested(c: PPatternContext, p, n: PNode, rpn: bool): bool =
|
||||
|
||||
proc matches(c: PPatternContext, p, n: PNode): bool =
|
||||
# hidden conversions (?)
|
||||
if isPatternParam(c, p):
|
||||
if nfNoRewrite in n.flags:
|
||||
result = false
|
||||
elif isPatternParam(c, p):
|
||||
result = bindOrCheck(c, p.sym, n)
|
||||
elif n.kind == nkSym and p.kind == nkIdent:
|
||||
result = p.ident.id == n.sym.name.id
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
|
||||
## The builtin 'system.locals' implemented as a plugin.
|
||||
|
||||
import plugins, ast, astalgo, magicsys, lookups, semdata, lowerings
|
||||
import compiler/plugins, compiler/ast, compiler/astalgo, compiler/magicsys,
|
||||
compiler/lookups, compiler/semdata, compiler/lowerings
|
||||
|
||||
proc semLocals(c: PContext, n: PNode): PNode =
|
||||
var counter = 0
|
||||
|
||||
@@ -37,7 +37,7 @@ const
|
||||
wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
|
||||
wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises,
|
||||
wTags, wLocks, wGcSafe}
|
||||
exprPragmas* = {wLine, wLocks}
|
||||
exprPragmas* = {wLine, wLocks, wNoRewrite}
|
||||
stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks,
|
||||
wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
|
||||
wLinedir, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError,
|
||||
@@ -859,6 +859,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||
c.module.flags.incl sfExperimental
|
||||
else:
|
||||
localError(it.info, "'experimental' pragma only valid as toplevel statement")
|
||||
of wNoRewrite:
|
||||
noVal(it)
|
||||
else: invalidPragma(it)
|
||||
else: invalidPragma(it)
|
||||
else: processNote(c, it)
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
|
||||
import
|
||||
os, options, strutils, nversion, ast, astalgo, msgs, platform, condsyms,
|
||||
ropes, idents, crc, idgen, types, rodutils, memfiles
|
||||
ropes, idents, securehash, idgen, types, rodutils, memfiles
|
||||
|
||||
type
|
||||
TReasonForRecompile* = enum ## all the reasons that can trigger recompilation
|
||||
@@ -538,10 +538,11 @@ proc cmdChangeTriggersRecompilation(old, new: TCommands): bool =
|
||||
# else: trigger recompilation:
|
||||
result = true
|
||||
|
||||
proc processRodFile(r: PRodReader, crc: TCrc32) =
|
||||
proc processRodFile(r: PRodReader, crc: SecureHash) =
|
||||
var
|
||||
w: string
|
||||
d, inclCrc: int
|
||||
d: int
|
||||
var inclCrc: SecureHash
|
||||
while r.s[r.pos] != '\0':
|
||||
var section = rdWord(r)
|
||||
if r.reason != rrNone:
|
||||
@@ -549,7 +550,8 @@ proc processRodFile(r: PRodReader, crc: TCrc32) =
|
||||
case section
|
||||
of "CRC":
|
||||
inc(r.pos) # skip ':'
|
||||
if int(crc) != decodeVInt(r.s, r.pos): r.reason = rrCrcChange
|
||||
if crc != parseSecureHash(decodeStr(r.s, r.pos)):
|
||||
r.reason = rrCrcChange
|
||||
of "ID":
|
||||
inc(r.pos) # skip ':'
|
||||
r.moduleID = decodeVInt(r.s, r.pos)
|
||||
@@ -596,9 +598,9 @@ proc processRodFile(r: PRodReader, crc: TCrc32) =
|
||||
while r.s[r.pos] != ')':
|
||||
w = r.files[decodeVInt(r.s, r.pos)].toFullPath
|
||||
inc(r.pos) # skip ' '
|
||||
inclCrc = decodeVInt(r.s, r.pos)
|
||||
inclCrc = parseSecureHash(decodeStr(r.s, r.pos))
|
||||
if r.reason == rrNone:
|
||||
if not existsFile(w) or (inclCrc != int(crcFromFile(w))):
|
||||
if not existsFile(w) or (inclCrc != secureHashFile(w)):
|
||||
r.reason = rrInclDeps
|
||||
if r.s[r.pos] == '\x0A':
|
||||
inc(r.pos)
|
||||
@@ -649,7 +651,7 @@ proc startsWith(buf: cstring, token: string, pos = 0): bool =
|
||||
while s < token.len and buf[pos+s] == token[s]: inc s
|
||||
result = s == token.len
|
||||
|
||||
proc newRodReader(modfilename: string, crc: TCrc32,
|
||||
proc newRodReader(modfilename: string, crc: SecureHash,
|
||||
readerIndex: int): PRodReader =
|
||||
new(result)
|
||||
try:
|
||||
@@ -701,7 +703,7 @@ type
|
||||
filename*: string
|
||||
reason*: TReasonForRecompile
|
||||
rd*: PRodReader
|
||||
crc*: TCrc32
|
||||
crc*: SecureHash
|
||||
crcDone*: bool
|
||||
|
||||
TFileModuleMap = seq[TFileModuleRec]
|
||||
@@ -794,13 +796,13 @@ proc loadMethods(r: PRodReader) =
|
||||
r.methods.add(rrGetSym(r, d, unknownLineInfo()))
|
||||
if r.s[r.pos] == ' ': inc(r.pos)
|
||||
|
||||
proc getCRC*(fileIdx: int32): TCrc32 =
|
||||
proc getCRC*(fileIdx: int32): SecureHash =
|
||||
internalAssert fileIdx >= 0 and fileIdx < gMods.len
|
||||
|
||||
if gMods[fileIdx].crcDone:
|
||||
return gMods[fileIdx].crc
|
||||
|
||||
result = crcFromFile(fileIdx.toFilename)
|
||||
result = secureHashFile(fileIdx.toFilename)
|
||||
gMods[fileIdx].crc = result
|
||||
|
||||
template growCache*(cache, pos) =
|
||||
@@ -1017,7 +1019,7 @@ proc writeType(f: File; t: PType) =
|
||||
f.write("]\n")
|
||||
|
||||
proc viewFile(rodfile: string) =
|
||||
var r = newRodReader(rodfile, 0, 0)
|
||||
var r = newRodReader(rodfile, secureHash(""), 0)
|
||||
if r == nil:
|
||||
rawMessage(errGenerated, "cannot open file (or maybe wrong version):" &
|
||||
rodfile)
|
||||
|
||||
@@ -13,14 +13,15 @@
|
||||
|
||||
import
|
||||
intsets, os, options, strutils, nversion, ast, astalgo, msgs, platform,
|
||||
condsyms, ropes, idents, crc, rodread, passes, importer, idgen, rodutils
|
||||
condsyms, ropes, idents, securehash, rodread, passes, importer, idgen,
|
||||
rodutils
|
||||
|
||||
# implementation
|
||||
|
||||
type
|
||||
TRodWriter = object of TPassContext
|
||||
module: PSym
|
||||
crc: TCrc32
|
||||
crc: SecureHash
|
||||
options: TOptions
|
||||
defines: string
|
||||
inclDeps: string
|
||||
@@ -38,7 +39,7 @@ type
|
||||
|
||||
PRodWriter = ref TRodWriter
|
||||
|
||||
proc newRodWriter(crc: TCrc32, module: PSym): PRodWriter
|
||||
proc newRodWriter(crc: SecureHash, module: PSym): PRodWriter
|
||||
proc addModDep(w: PRodWriter, dep: string)
|
||||
proc addInclDep(w: PRodWriter, dep: string)
|
||||
proc addInterfaceSym(w: PRodWriter, s: PSym)
|
||||
@@ -62,7 +63,7 @@ proc fileIdx(w: PRodWriter, filename: string): int =
|
||||
template filename*(w: PRodWriter): string =
|
||||
w.module.filename
|
||||
|
||||
proc newRodWriter(crc: TCrc32, module: PSym): PRodWriter =
|
||||
proc newRodWriter(crc: SecureHash, module: PSym): PRodWriter =
|
||||
new(result)
|
||||
result.sstack = @[]
|
||||
result.tstack = @[]
|
||||
@@ -96,7 +97,7 @@ proc addInclDep(w: PRodWriter, dep: string) =
|
||||
var resolved = dep.findModule(w.module.info.toFullPath)
|
||||
encodeVInt(fileIdx(w, dep), w.inclDeps)
|
||||
add(w.inclDeps, " ")
|
||||
encodeVInt(crcFromFile(resolved), w.inclDeps)
|
||||
encodeStr($secureHashFile(resolved), w.inclDeps)
|
||||
add(w.inclDeps, rodNL)
|
||||
|
||||
proc pushType(w: PRodWriter, t: PType) =
|
||||
@@ -440,7 +441,7 @@ proc writeRod(w: PRodWriter) =
|
||||
f.write(rodNL)
|
||||
|
||||
var crc = "CRC:"
|
||||
encodeVInt(w.crc, crc)
|
||||
encodeStr($w.crc, crc)
|
||||
f.write(crc)
|
||||
f.write(rodNL)
|
||||
|
||||
|
||||
199
compiler/securehash.nim
Normal file
199
compiler/securehash.nim
Normal file
@@ -0,0 +1,199 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2015 Nim Contributers
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
import
|
||||
strutils, unsigned
|
||||
|
||||
const Sha1DigestSize = 20
|
||||
|
||||
type
|
||||
Sha1Digest = array[0 .. Sha1DigestSize-1, uint8]
|
||||
SecureHash* = distinct Sha1Digest
|
||||
|
||||
proc sha1(src: string) : Sha1Digest
|
||||
|
||||
proc secureHash*(str: string): SecureHash = SecureHash(sha1(str))
|
||||
proc secureHashFile*(filename: string): SecureHash = secureHash(readFile(filename))
|
||||
proc `$`*(self: SecureHash): string =
|
||||
result = ""
|
||||
for v in Sha1Digest(self):
|
||||
result.add(toHex(int(v), 2))
|
||||
|
||||
proc parseSecureHash*(hash: string): SecureHash =
|
||||
for i in 0.. <Sha1DigestSize:
|
||||
Sha1Digest(result)[i] = uint8(parseHexInt(hash[i*2] & hash[i*2 + 1]))
|
||||
|
||||
proc `==`*(a, b: SecureHash): bool =
|
||||
# Not a constant-time comparison, but that's acceptable in this context
|
||||
Sha1Digest(a) == Sha1Digest(b)
|
||||
|
||||
|
||||
when isMainModule:
|
||||
let hash1 = secureHash("a93tgj0p34jagp9[agjp98ajrhp9aej]")
|
||||
doAssert hash1 == hash1
|
||||
doAssert parseSecureHash($hash1) == hash1
|
||||
|
||||
|
||||
# Copyright (c) 2011, Micael Hildenborg
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# * Neither the name of Micael Hildenborg nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY
|
||||
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY
|
||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
# Ported to Nim by Erik O'Leary
|
||||
|
||||
type
|
||||
Sha1State = array[0 .. 5-1, uint32]
|
||||
Sha1Buffer = array[0 .. 80-1, uint32]
|
||||
|
||||
template clearBuffer(w: Sha1Buffer, len = 16) =
|
||||
zeroMem(addr(w), len * sizeof(uint32))
|
||||
|
||||
proc init(result: var Sha1State) =
|
||||
result[0] = 0x67452301'u32
|
||||
result[1] = 0xefcdab89'u32
|
||||
result[2] = 0x98badcfe'u32
|
||||
result[3] = 0x10325476'u32
|
||||
result[4] = 0xc3d2e1f0'u32
|
||||
|
||||
proc innerHash(state: var Sha1State, w: var Sha1Buffer) =
|
||||
var
|
||||
a = state[0]
|
||||
b = state[1]
|
||||
c = state[2]
|
||||
d = state[3]
|
||||
e = state[4]
|
||||
|
||||
var round = 0
|
||||
|
||||
template rot(value, bits: uint32): uint32 {.immediate.} =
|
||||
(value shl bits) or (value shr (32 - bits))
|
||||
|
||||
template sha1(fun, val: uint32): stmt =
|
||||
let t = rot(a, 5) + fun + e + val + w[round]
|
||||
e = d
|
||||
d = c
|
||||
c = rot(b, 30)
|
||||
b = a
|
||||
a = t
|
||||
|
||||
template process(body: stmt): stmt =
|
||||
w[round] = rot(w[round - 3] xor w[round - 8] xor w[round - 14] xor w[round - 16], 1)
|
||||
body
|
||||
inc(round)
|
||||
|
||||
template wrap(dest, value: expr): stmt {.immediate.} =
|
||||
let v = dest + value
|
||||
dest = v
|
||||
|
||||
while round < 16:
|
||||
sha1((b and c) or (not b and d), 0x5a827999'u32)
|
||||
inc(round)
|
||||
|
||||
while round < 20:
|
||||
process:
|
||||
sha1((b and c) or (not b and d), 0x5a827999'u32)
|
||||
|
||||
while round < 40:
|
||||
process:
|
||||
sha1(b xor c xor d, 0x6ed9eba1'u32)
|
||||
|
||||
while round < 60:
|
||||
process:
|
||||
sha1((b and c) or (b and d) or (c and d), 0x8f1bbcdc'u32)
|
||||
|
||||
while round < 80:
|
||||
process:
|
||||
sha1(b xor c xor d, 0xca62c1d6'u32)
|
||||
|
||||
wrap state[0], a
|
||||
wrap state[1], b
|
||||
wrap state[2], c
|
||||
wrap state[3], d
|
||||
wrap state[4], e
|
||||
|
||||
template computeInternal(src: expr): stmt {.immediate.} =
|
||||
#Initialize state
|
||||
var state: Sha1State
|
||||
init(state)
|
||||
|
||||
#Create w buffer
|
||||
var w: Sha1Buffer
|
||||
|
||||
#Loop through all complete 64byte blocks.
|
||||
let byteLen = src.len
|
||||
let endOfFullBlocks = byteLen - 64
|
||||
var endCurrentBlock = 0
|
||||
var currentBlock = 0
|
||||
|
||||
while currentBlock <= endOfFullBlocks:
|
||||
endCurrentBlock = currentBlock + 64
|
||||
|
||||
var i = 0
|
||||
while currentBlock < endCurrentBlock:
|
||||
w[i] = uint32(src[currentBlock+3]) or
|
||||
uint32(src[currentBlock+2]) shl 8'u32 or
|
||||
uint32(src[currentBlock+1]) shl 16'u32 or
|
||||
uint32(src[currentBlock]) shl 24'u32
|
||||
currentBlock += 4
|
||||
inc(i)
|
||||
|
||||
innerHash(state, w)
|
||||
|
||||
#Handle last and not full 64 byte block if existing
|
||||
endCurrentBlock = byteLen - currentBlock
|
||||
clearBuffer(w)
|
||||
var lastBlockBytes = 0
|
||||
|
||||
while lastBlockBytes < endCurrentBlock:
|
||||
|
||||
var value = uint32(src[lastBlockBytes + currentBlock]) shl
|
||||
((3'u32 - (lastBlockBytes and 3)) shl 3)
|
||||
|
||||
w[lastBlockBytes shr 2] = w[lastBlockBytes shr 2] or value
|
||||
inc(lastBlockBytes)
|
||||
|
||||
w[lastBlockBytes shr 2] = w[lastBlockBytes shr 2] or (
|
||||
0x80'u32 shl ((3'u32 - (lastBlockBytes and 3)) shl 3)
|
||||
)
|
||||
|
||||
if endCurrentBlock >= 56:
|
||||
innerHash(state, w)
|
||||
clearBuffer(w)
|
||||
|
||||
w[15] = uint32(byteLen) shl 3
|
||||
innerHash(state, w)
|
||||
|
||||
# Store hash in result pointer, and make sure we get in in the correct order
|
||||
# on both endian models.
|
||||
for i in 0 .. Sha1DigestSize-1:
|
||||
result[i] = uint8((int(state[i shr 2]) shr ((3-(i and 3)) * 8)) and 255)
|
||||
|
||||
proc sha1(src: string) : Sha1Digest =
|
||||
## Calculate SHA1 from input string
|
||||
computeInternal(src)
|
||||
@@ -209,7 +209,10 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
|
||||
pickBest(callOp)
|
||||
|
||||
if overloadsState == csEmpty and result.state == csEmpty:
|
||||
localError(n.info, errUndeclaredIdentifier, considerQuotedIdent(f).s)
|
||||
if nfDotField in n.flags and nfExplicitCall notin n.flags:
|
||||
localError(n.info, errUndeclaredField, considerQuotedIdent(f).s)
|
||||
else:
|
||||
localError(n.info, errUndeclaredRoutine, considerQuotedIdent(f).s)
|
||||
return
|
||||
elif result.state != csMatch:
|
||||
if nfExprCall in n.flags:
|
||||
|
||||
@@ -179,7 +179,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
|
||||
else:
|
||||
result = makeRangeF(a, abs(getFloat(a.n.sons[1])),
|
||||
abs(getFloat(a.n.sons[0])))
|
||||
of mAbsI, mAbsI64:
|
||||
of mAbsI:
|
||||
let a = n.sons[1].typ
|
||||
if isIntRange(a):
|
||||
if a.n[0].intVal <= 0:
|
||||
@@ -200,13 +200,13 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
|
||||
if isIntRange(a) and isIntLit(b):
|
||||
result = makeRange(a, pickMinInt(n.sons[1]) |-| pickMinInt(n.sons[2]),
|
||||
pickMaxInt(n.sons[1]) |-| pickMaxInt(n.sons[2]))
|
||||
of mAddI, mAddI64, mAddU:
|
||||
of mAddI, mAddU:
|
||||
commutativeOp(`|+|`)
|
||||
of mMulI, mMulI64, mMulU:
|
||||
of mMulI, mMulU:
|
||||
commutativeOp(`|*|`)
|
||||
of mSubI, mSubI64, mSubU:
|
||||
of mSubI, mSubU:
|
||||
binaryOp(`|-|`)
|
||||
of mBitandI, mBitandI64:
|
||||
of mBitandI:
|
||||
# since uint64 is still not even valid for 'range' (since it's no ordinal
|
||||
# yet), we exclude it from the list (see bug #1638) for now:
|
||||
var a = n.sons[1]
|
||||
@@ -225,7 +225,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
|
||||
result = makeRange(a.typ, 0, b.intVal-1)
|
||||
else:
|
||||
result = makeRange(a.typ, b.intVal+1, 0)
|
||||
of mModI, mModI64:
|
||||
of mModI:
|
||||
# so ... if you ever wondered about modulo's signedness; this defines it:
|
||||
let a = n.sons[1]
|
||||
let b = n.sons[2]
|
||||
@@ -234,7 +234,7 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
|
||||
result = makeRange(a.typ, -(b.intVal-1), b.intVal-1)
|
||||
else:
|
||||
result = makeRange(a.typ, b.intVal+1, -(b.intVal+1))
|
||||
of mDivI, mDivI64, mDivU:
|
||||
of mDivI, mDivU:
|
||||
binaryOp(`|div|`)
|
||||
of mMinI:
|
||||
commutativeOp(min)
|
||||
@@ -243,8 +243,8 @@ proc getIntervalType*(m: TMagic, n: PNode): PType =
|
||||
else: discard
|
||||
|
||||
discard """
|
||||
mShlI, mShlI64,
|
||||
mShrI, mShrI64, mAddF64, mSubF64, mMulF64, mDivF64, mMaxF64, mMinF64
|
||||
mShlI,
|
||||
mShrI, mAddF64, mSubF64, mMulF64, mDivF64, mMaxF64, mMinF64
|
||||
"""
|
||||
|
||||
proc evalIs(n, a: PNode): PNode =
|
||||
@@ -285,7 +285,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
|
||||
of mUnaryMinusF64: result = newFloatNodeT(- getFloat(a), n)
|
||||
of mNot: result = newIntNodeT(1 - getInt(a), n)
|
||||
of mCard: result = newIntNodeT(nimsets.cardSet(a), n)
|
||||
of mBitnotI, mBitnotI64: result = newIntNodeT(not getInt(a), n)
|
||||
of mBitnotI: result = newIntNodeT(not getInt(a), n)
|
||||
of mLengthStr, mXLenStr:
|
||||
if a.kind == nkNilLit: result = newIntNodeT(0, n)
|
||||
else: result = newIntNodeT(len(getStr(a)), n)
|
||||
@@ -298,7 +298,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
|
||||
result = newFloatNodeT(toFloat(int(getInt(a))), n)
|
||||
of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n)
|
||||
of mAbsF64: result = newFloatNodeT(abs(getFloat(a)), n)
|
||||
of mAbsI, mAbsI64:
|
||||
of mAbsI:
|
||||
if getInt(a) >= 0: result = a
|
||||
else: result = newIntNodeT(- getInt(a), n)
|
||||
of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64:
|
||||
@@ -310,16 +310,16 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
|
||||
of mUnaryLt: result = newIntNodeT(getOrdValue(a) - 1, n)
|
||||
of mSucc: result = newIntNodeT(getOrdValue(a) + getInt(b), n)
|
||||
of mPred: result = newIntNodeT(getOrdValue(a) - getInt(b), n)
|
||||
of mAddI, mAddI64: result = newIntNodeT(getInt(a) + getInt(b), n)
|
||||
of mSubI, mSubI64: result = newIntNodeT(getInt(a) - getInt(b), n)
|
||||
of mMulI, mMulI64: result = newIntNodeT(getInt(a) * getInt(b), n)
|
||||
of mAddI: result = newIntNodeT(getInt(a) + getInt(b), n)
|
||||
of mSubI: result = newIntNodeT(getInt(a) - getInt(b), n)
|
||||
of mMulI: result = newIntNodeT(getInt(a) * getInt(b), n)
|
||||
of mMinI:
|
||||
if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n)
|
||||
else: result = newIntNodeT(getInt(a), n)
|
||||
of mMaxI:
|
||||
if getInt(a) > getInt(b): result = newIntNodeT(getInt(a), n)
|
||||
else: result = newIntNodeT(getInt(b), n)
|
||||
of mShlI, mShlI64:
|
||||
of mShlI:
|
||||
case skipTypes(n.typ, abstractRange).kind
|
||||
of tyInt8: result = newIntNodeT(int8(getInt(a)) shl int8(getInt(b)), n)
|
||||
of tyInt16: result = newIntNodeT(int16(getInt(a)) shl int16(getInt(b)), n)
|
||||
@@ -327,7 +327,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
|
||||
of tyInt64, tyInt, tyUInt..tyUInt64:
|
||||
result = newIntNodeT(`shl`(getInt(a), getInt(b)), n)
|
||||
else: internalError(n.info, "constant folding for shl")
|
||||
of mShrI, mShrI64:
|
||||
of mShrI:
|
||||
case skipTypes(n.typ, abstractRange).kind
|
||||
of tyInt8: result = newIntNodeT(int8(getInt(a)) shr int8(getInt(b)), n)
|
||||
of tyInt16: result = newIntNodeT(int16(getInt(a)) shr int16(getInt(b)), n)
|
||||
@@ -335,11 +335,11 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
|
||||
of tyInt64, tyInt, tyUInt..tyUInt64:
|
||||
result = newIntNodeT(`shr`(getInt(a), getInt(b)), n)
|
||||
else: internalError(n.info, "constant folding for shr")
|
||||
of mDivI, mDivI64:
|
||||
of mDivI:
|
||||
let y = getInt(b)
|
||||
if y != 0:
|
||||
result = newIntNodeT(getInt(a) div y, n)
|
||||
of mModI, mModI64:
|
||||
of mModI:
|
||||
let y = getInt(b)
|
||||
if y != 0:
|
||||
result = newIntNodeT(getInt(a) mod y, n)
|
||||
@@ -359,11 +359,11 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
|
||||
if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(b), n)
|
||||
else: result = newFloatNodeT(getFloat(a), n)
|
||||
of mIsNil: result = newIntNodeT(ord(a.kind == nkNilLit), n)
|
||||
of mLtI, mLtI64, mLtB, mLtEnum, mLtCh:
|
||||
of mLtI, mLtB, mLtEnum, mLtCh:
|
||||
result = newIntNodeT(ord(getOrdValue(a) < getOrdValue(b)), n)
|
||||
of mLeI, mLeI64, mLeB, mLeEnum, mLeCh:
|
||||
of mLeI, mLeB, mLeEnum, mLeCh:
|
||||
result = newIntNodeT(ord(getOrdValue(a) <= getOrdValue(b)), n)
|
||||
of mEqI, mEqI64, mEqB, mEqEnum, mEqCh:
|
||||
of mEqI, mEqB, mEqEnum, mEqCh:
|
||||
result = newIntNodeT(ord(getOrdValue(a) == getOrdValue(b)), n)
|
||||
of mLtF64: result = newIntNodeT(ord(getFloat(a) < getFloat(b)), n)
|
||||
of mLeF64: result = newIntNodeT(ord(getFloat(a) <= getFloat(b)), n)
|
||||
@@ -375,9 +375,9 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
|
||||
result = newIntNodeT(ord(`<%`(getOrdValue(a), getOrdValue(b))), n)
|
||||
of mLeU, mLeU64:
|
||||
result = newIntNodeT(ord(`<=%`(getOrdValue(a), getOrdValue(b))), n)
|
||||
of mBitandI, mBitandI64, mAnd: result = newIntNodeT(a.getInt and b.getInt, n)
|
||||
of mBitorI, mBitorI64, mOr: result = newIntNodeT(getInt(a) or getInt(b), n)
|
||||
of mBitxorI, mBitxorI64, mXor: result = newIntNodeT(a.getInt xor b.getInt, n)
|
||||
of mBitandI, mAnd: result = newIntNodeT(a.getInt and b.getInt, n)
|
||||
of mBitorI, mOr: result = newIntNodeT(getInt(a) or getInt(b), n)
|
||||
of mBitxorI, mXor: result = newIntNodeT(a.getInt xor b.getInt, n)
|
||||
of mAddU: result = newIntNodeT(`+%`(getInt(a), getInt(b)), n)
|
||||
of mSubU: result = newIntNodeT(`-%`(getInt(a), getInt(b)), n)
|
||||
of mMulU: result = newIntNodeT(`*%`(getInt(a), getInt(b)), n)
|
||||
|
||||
@@ -207,9 +207,9 @@ proc markGcUnsafe(a: PEffects; reason: PNode) =
|
||||
a.owner.gcUnsafetyReason = newSym(skUnknown, getIdent("<unknown>"),
|
||||
a.owner, reason.info)
|
||||
|
||||
proc listGcUnsafety(s: PSym; onlyWarning: bool) =
|
||||
proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet) =
|
||||
let u = s.gcUnsafetyReason
|
||||
if u != nil:
|
||||
if u != nil and not cycleCheck.containsOrIncl(u.id):
|
||||
let msgKind = if onlyWarning: warnGcUnsafe2 else: errGenerated
|
||||
if u.kind in {skLet, skVar}:
|
||||
message(s.info, msgKind,
|
||||
@@ -218,7 +218,7 @@ proc listGcUnsafety(s: PSym; onlyWarning: bool) =
|
||||
elif u.kind in routineKinds:
|
||||
# recursive call *always* produces only a warning so the full error
|
||||
# message is printed:
|
||||
listGcUnsafety(u, true)
|
||||
listGcUnsafety(u, true, cycleCheck)
|
||||
message(s.info, msgKind,
|
||||
"'$#' is not GC-safe as it calls '$#'" %
|
||||
[s.name.s, u.name.s])
|
||||
@@ -227,6 +227,10 @@ proc listGcUnsafety(s: PSym; onlyWarning: bool) =
|
||||
message(u.info, msgKind,
|
||||
"'$#' is not GC-safe as it performs an indirect call here" % s.name.s)
|
||||
|
||||
proc listGcUnsafety(s: PSym; onlyWarning: bool) =
|
||||
var cycleCheck = initIntSet()
|
||||
listGcUnsafety(s, onlyWarning, cycleCheck)
|
||||
|
||||
proc useVar(a: PEffects, n: PNode) =
|
||||
let s = n.sym
|
||||
if isLocalVar(a, s):
|
||||
|
||||
@@ -1268,6 +1268,8 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode =
|
||||
of wLocks:
|
||||
result = n
|
||||
result.typ = n.sons[1].typ
|
||||
of wNoRewrite:
|
||||
incl(result.flags, nfNoRewrite)
|
||||
else: discard
|
||||
|
||||
proc semStaticStmt(c: PContext, n: PNode): PNode =
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
import
|
||||
hashes, ast, astalgo, types
|
||||
|
||||
proc hashTree(n: PNode): THash =
|
||||
proc hashTree(n: PNode): Hash =
|
||||
if n == nil: return
|
||||
result = ord(n.kind)
|
||||
case n.kind
|
||||
@@ -53,8 +53,8 @@ proc treesEquivalent(a, b: PNode): bool =
|
||||
result = true
|
||||
if result: result = sameTypeOrNil(a.typ, b.typ)
|
||||
|
||||
proc nodeTableRawGet(t: TNodeTable, k: THash, key: PNode): int =
|
||||
var h: THash = k and high(t.data)
|
||||
proc nodeTableRawGet(t: TNodeTable, k: Hash, key: PNode): int =
|
||||
var h: Hash = k and high(t.data)
|
||||
while t.data[h].key != nil:
|
||||
if (t.data[h].h == k) and treesEquivalent(t.data[h].key, key):
|
||||
return h
|
||||
@@ -66,9 +66,9 @@ proc nodeTableGet*(t: TNodeTable, key: PNode): int =
|
||||
if index >= 0: result = t.data[index].val
|
||||
else: result = low(int)
|
||||
|
||||
proc nodeTableRawInsert(data: var TNodePairSeq, k: THash, key: PNode,
|
||||
proc nodeTableRawInsert(data: var TNodePairSeq, k: Hash, key: PNode,
|
||||
val: int) =
|
||||
var h: THash = k and high(data)
|
||||
var h: Hash = k and high(data)
|
||||
while data[h].key != nil: h = nextTry(h, high(data))
|
||||
assert(data[h].key == nil)
|
||||
data[h].h = k
|
||||
@@ -77,7 +77,7 @@ proc nodeTableRawInsert(data: var TNodePairSeq, k: THash, key: PNode,
|
||||
|
||||
proc nodeTablePut*(t: var TNodeTable, key: PNode, val: int) =
|
||||
var n: TNodePairSeq
|
||||
var k: THash = hashTree(key)
|
||||
var k: Hash = hashTree(key)
|
||||
var index = nodeTableRawGet(t, k, key)
|
||||
if index >= 0:
|
||||
assert(t.data[index].key != nil)
|
||||
@@ -94,7 +94,7 @@ proc nodeTablePut*(t: var TNodeTable, key: PNode, val: int) =
|
||||
|
||||
proc nodeTableTestOrSet*(t: var TNodeTable, key: PNode, val: int): int =
|
||||
var n: TNodePairSeq
|
||||
var k: THash = hashTree(key)
|
||||
var k: Hash = hashTree(key)
|
||||
var index = nodeTableRawGet(t, k, key)
|
||||
if index >= 0:
|
||||
assert(t.data[index].key != nil)
|
||||
|
||||
@@ -682,11 +682,15 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
of opcLtu:
|
||||
decodeBC(rkInt)
|
||||
regs[ra].intVal = ord(regs[rb].intVal <% regs[rc].intVal)
|
||||
of opcEqRef, opcEqNimrodNode:
|
||||
of opcEqRef:
|
||||
decodeBC(rkInt)
|
||||
regs[ra].intVal = ord((regs[rb].node.kind == nkNilLit and
|
||||
regs[rc].node.kind == nkNilLit) or
|
||||
regs[rb].node == regs[rc].node)
|
||||
of opcEqNimrodNode:
|
||||
decodeBC(rkInt)
|
||||
regs[ra].intVal =
|
||||
ord(exprStructuralEquivalent(regs[rb].node, regs[rc].node))
|
||||
of opcXor:
|
||||
decodeBC(rkInt)
|
||||
regs[ra].intVal = ord(regs[rb].intVal != regs[rc].intVal)
|
||||
|
||||
@@ -710,9 +710,9 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||
if dest < 0: dest = c.getTemp(n.typ)
|
||||
c.gABI(n, opcSubImmInt, dest, tmp, 1)
|
||||
c.freeTemp(tmp)
|
||||
of mPred, mSubI, mSubI64:
|
||||
of mPred, mSubI:
|
||||
c.genAddSubInt(n, dest, opcSubInt)
|
||||
of mSucc, mAddI, mAddI64:
|
||||
of mSucc, mAddI:
|
||||
c.genAddSubInt(n, dest, opcAddInt)
|
||||
of mInc, mDec:
|
||||
unused(n, dest)
|
||||
@@ -759,28 +759,28 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||
c.freeTemp(d)
|
||||
c.freeTemp(tmp)
|
||||
of mCard: genCard(c, n, dest)
|
||||
of mMulI, mMulI64: genBinaryABCnarrow(c, n, dest, opcMulInt)
|
||||
of mDivI, mDivI64: genBinaryABCnarrow(c, n, dest, opcDivInt)
|
||||
of mModI, mModI64: genBinaryABCnarrow(c, n, dest, opcModInt)
|
||||
of mMulI: genBinaryABCnarrow(c, n, dest, opcMulInt)
|
||||
of mDivI: genBinaryABCnarrow(c, n, dest, opcDivInt)
|
||||
of mModI: genBinaryABCnarrow(c, n, dest, opcModInt)
|
||||
of mAddF64: genBinaryABC(c, n, dest, opcAddFloat)
|
||||
of mSubF64: genBinaryABC(c, n, dest, opcSubFloat)
|
||||
of mMulF64: genBinaryABC(c, n, dest, opcMulFloat)
|
||||
of mDivF64: genBinaryABC(c, n, dest, opcDivFloat)
|
||||
of mShrI, mShrI64: genBinaryABCnarrowU(c, n, dest, opcShrInt)
|
||||
of mShlI, mShlI64: genBinaryABCnarrowU(c, n, dest, opcShlInt)
|
||||
of mBitandI, mBitandI64: genBinaryABCnarrowU(c, n, dest, opcBitandInt)
|
||||
of mBitorI, mBitorI64: genBinaryABCnarrowU(c, n, dest, opcBitorInt)
|
||||
of mBitxorI, mBitxorI64: genBinaryABCnarrowU(c, n, dest, opcBitxorInt)
|
||||
of mShrI: genBinaryABCnarrowU(c, n, dest, opcShrInt)
|
||||
of mShlI: genBinaryABCnarrowU(c, n, dest, opcShlInt)
|
||||
of mBitandI: genBinaryABCnarrowU(c, n, dest, opcBitandInt)
|
||||
of mBitorI: genBinaryABCnarrowU(c, n, dest, opcBitorInt)
|
||||
of mBitxorI: genBinaryABCnarrowU(c, n, dest, opcBitxorInt)
|
||||
of mAddU: genBinaryABCnarrowU(c, n, dest, opcAddu)
|
||||
of mSubU: genBinaryABCnarrowU(c, n, dest, opcSubu)
|
||||
of mMulU: genBinaryABCnarrowU(c, n, dest, opcMulu)
|
||||
of mDivU: genBinaryABCnarrowU(c, n, dest, opcDivu)
|
||||
of mModU: genBinaryABCnarrowU(c, n, dest, opcModu)
|
||||
of mEqI, mEqI64, mEqB, mEqEnum, mEqCh:
|
||||
of mEqI, mEqB, mEqEnum, mEqCh:
|
||||
genBinaryABC(c, n, dest, opcEqInt)
|
||||
of mLeI, mLeI64, mLeEnum, mLeCh, mLeB:
|
||||
of mLeI, mLeEnum, mLeCh, mLeB:
|
||||
genBinaryABC(c, n, dest, opcLeInt)
|
||||
of mLtI, mLtI64, mLtEnum, mLtCh, mLtB:
|
||||
of mLtI, mLtEnum, mLtCh, mLtB:
|
||||
genBinaryABC(c, n, dest, opcLtInt)
|
||||
of mEqF64: genBinaryABC(c, n, dest, opcEqFloat)
|
||||
of mLeF64: genBinaryABC(c, n, dest, opcLeFloat)
|
||||
@@ -796,7 +796,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||
genNarrow(c, n, dest)
|
||||
of mUnaryMinusF64: genUnaryABC(c, n, dest, opcUnaryMinusFloat)
|
||||
of mUnaryPlusI, mUnaryPlusF64: gen(c, n.sons[1], dest)
|
||||
of mBitnotI, mBitnotI64:
|
||||
of mBitnotI:
|
||||
genUnaryABC(c, n, dest, opcBitnotInt)
|
||||
genNarrowU(c, n, dest)
|
||||
of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64,
|
||||
@@ -1013,7 +1013,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||
c.gABC(n, opcCallSite, dest)
|
||||
of mNGenSym: genBinaryABC(c, n, dest, opcGenSym)
|
||||
of mMinI, mMaxI, mAbsF64, mMinF64, mMaxF64, mAbsI,
|
||||
mAbsI64, mDotDot:
|
||||
mDotDot:
|
||||
c.genCall(n, dest)
|
||||
of mExpandToAst:
|
||||
if n.len != 2:
|
||||
|
||||
@@ -55,7 +55,7 @@ type
|
||||
wFloatchecks, wNanChecks, wInfChecks,
|
||||
wAssertions, wPatterns, wWarnings,
|
||||
wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects, wTags,
|
||||
wDeadCodeElim, wSafecode, wNoForward,
|
||||
wDeadCodeElim, wSafecode, wNoForward, wNoRewrite,
|
||||
wPragma,
|
||||
wCompileTime, wNoInit,
|
||||
wPassc, wPassl, wBorrow, wDiscardable,
|
||||
@@ -139,7 +139,7 @@ const
|
||||
|
||||
"assertions", "patterns", "warnings", "hints",
|
||||
"optimization", "raises", "writes", "reads", "size", "effects", "tags",
|
||||
"deadcodeelim", "safecode", "noforward",
|
||||
"deadcodeelim", "safecode", "noforward", "norewrite",
|
||||
"pragma",
|
||||
"compiletime", "noinit",
|
||||
"passc", "passl", "borrow", "discardable", "fieldchecks",
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
* `FloatInvalidOpError <system.html#FloatInvalidOpError>`_
|
||||
* `FloatOverflowError <system.html#FloatOverflowError>`_
|
||||
* `FloatUnderflowError <system.html#FloatUnderflowError>`_
|
||||
* `FieldError <system.html#InvalidFieldError>`_
|
||||
* `IndexError <system.html#InvalidIndexError>`_
|
||||
* `FieldError <system.html#FieldError>`_
|
||||
* `IndexError <system.html#IndexError>`_
|
||||
* `ObjectAssignmentError <system.html#ObjectAssignmentError>`_
|
||||
* `ObjectConversionError <system.html#ObjectConversionError>`_
|
||||
* `ValueError <system.html#ValueError>`_
|
||||
|
||||
@@ -164,11 +164,13 @@ Alternatively, the ``distinct`` type modifier can be applied to the type class
|
||||
to allow each param matching the type class to bind to a different type.
|
||||
|
||||
If a proc param doesn't have a type specified, Nim will use the
|
||||
``distinct auto`` type class (also known as ``any``):
|
||||
``distinct auto`` type class (also known as ``any``). Note this behavior is
|
||||
deprecated for procs; templates, however, support them:
|
||||
|
||||
.. code-block:: nim
|
||||
# allow any combination of param types
|
||||
proc concat(a, b): string = $a & $b
|
||||
proc concat(a, b): string = $a & $b # deprecated
|
||||
proc concat(a, b: any): string = $a & $b # preferred
|
||||
|
||||
Procs written with the implicitly generic style will often need to refer to the
|
||||
type parameters of the matched generic type. They can be easily accessed using
|
||||
|
||||
@@ -101,25 +101,28 @@ Two identifiers are considered equal if the following algorithm returns true:
|
||||
|
||||
.. code-block:: nim
|
||||
proc sameIdentifier(a, b: string): bool =
|
||||
a[0] == b[0] and a.replace("_", "").toLower == b.replace("_", "").toLower
|
||||
a[0] == b[0] and
|
||||
a.replace(re"_|–", "").toLower == b.replace(re"_|–", "").toLower
|
||||
|
||||
That means only the first letters are compared in a case sensitive manner. Other
|
||||
letters are compared case insensitively and underscores are ignored.
|
||||
letters are compared case insensitively and underscores and en-dash (Unicode
|
||||
point U+2013) are ignored.
|
||||
|
||||
This rather strange way to do identifier comparisons is called
|
||||
This rather unorthodox way to do identifier comparisons is called
|
||||
`partial case insensitivity`:idx: and has some advantages over the conventional
|
||||
case sensitivity:
|
||||
|
||||
It allows programmers to mostly use their own preferred
|
||||
spelling style and libraries written by different programmers cannot use
|
||||
incompatible conventions. A Nim-aware editor or IDE can show the identifiers as
|
||||
preferred. Another advantage is that it frees the programmer from remembering
|
||||
spelling style, be it humpStyle, snake_style or dash–style and libraries written
|
||||
by different programmers cannot use incompatible conventions.
|
||||
A Nim-aware editor or IDE can show the identifiers as preferred.
|
||||
Another advantage is that it frees the programmer from remembering
|
||||
the exact spelling of an identifier. The exception with respect to the first
|
||||
letter allows common code like ``var foo: Foo`` to be parsed unambiguously.
|
||||
|
||||
Historically, Nim was a `style-insensitive`:idx: language. This means that it
|
||||
was not case-sensitive and underscores were ignored and there was no distinction
|
||||
between ``foo`` and ``Foo``.
|
||||
Historically, Nim was a fully `style-insensitive`:idx: language. This meant that
|
||||
it was not case-sensitive and underscores were ignored and there was no even a
|
||||
distinction between ``foo`` and ``Foo``.
|
||||
|
||||
|
||||
String literals
|
||||
@@ -276,7 +279,7 @@ Numerical constants are of a single type and have the form::
|
||||
bindigit = '0'..'1'
|
||||
HEX_LIT = '0' ('x' | 'X' ) hexdigit ( ['_'] hexdigit )*
|
||||
DEC_LIT = digit ( ['_'] digit )*
|
||||
OCT_LIT = '0o' octdigit ( ['_'] octdigit )*
|
||||
OCT_LIT = '0' ('o' | 'c' | 'C') octdigit ( ['_'] octdigit )*
|
||||
BIN_LIT = '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )*
|
||||
|
||||
INT_LIT = HEX_LIT
|
||||
@@ -297,15 +300,17 @@ Numerical constants are of a single type and have the form::
|
||||
|
||||
exponent = ('e' | 'E' ) ['+' | '-'] digit ( ['_'] digit )*
|
||||
FLOAT_LIT = digit (['_'] digit)* (('.' (['_'] digit)* [exponent]) |exponent)
|
||||
FLOAT32_LIT = HEX_LIT '\'' ('f'|'F') '32'
|
||||
| (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] ('f'|'F') '32'
|
||||
FLOAT64_LIT = HEX_LIT '\'' ('f'|'F') '64'
|
||||
| (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] ('f'|'F') '64'
|
||||
FLOAT32_SUFFIX = ('f' | 'F') ['32']
|
||||
FLOAT32_LIT = HEX_LIT '\'' FLOAT32_SUFFIX
|
||||
| (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] FLOAT32_SUFFIX
|
||||
FLOAT64_SUFFIX = ( ('f' | 'F') '64' ) | 'd' | 'D'
|
||||
FLOAT64_LIT = HEX_LIT '\'' FLOAT64_SUFFIX
|
||||
| (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] FLOAT64_SUFFIX
|
||||
|
||||
|
||||
As can be seen in the productions, numerical constants can contain underscores
|
||||
for readability. Integer and floating point literals may be given in decimal (no
|
||||
prefix), binary (prefix ``0b``), octal (prefix ``0o``) and hexadecimal
|
||||
prefix), binary (prefix ``0b``), octal (prefix ``0o`` or ``0c``) and hexadecimal
|
||||
(prefix ``0x``) notation.
|
||||
|
||||
There exists a literal for each numerical type that is
|
||||
@@ -331,8 +336,11 @@ The type suffixes are:
|
||||
``'u16`` uint16
|
||||
``'u32`` uint32
|
||||
``'u64`` uint64
|
||||
``'f`` float32
|
||||
``'d`` float64
|
||||
``'f32`` float32
|
||||
``'f64`` float64
|
||||
``'f128`` float128
|
||||
================= =========================
|
||||
|
||||
Floating point literals may also be in binary, octal or hexadecimal
|
||||
@@ -340,12 +348,18 @@ notation:
|
||||
``0B0_10001110100_0000101001000111101011101111111011000101001101001001'f64``
|
||||
is approximately 1.72826e35 according to the IEEE floating point standard.
|
||||
|
||||
Literals are bounds checked so that they fit the datatype. Non base-10
|
||||
literals are used mainly for flags and bit pattern representations, therefore
|
||||
bounds checking is done on bit width, not value range. If the literal fits in
|
||||
the bit width of the datatype, it is accepted.
|
||||
Hence: 0b10000000'u8 == 0x80'u8 == 128, but, 0b10000000'i8 == 0x80'i8 == -1
|
||||
instead of causing an overflow error.
|
||||
|
||||
Operators
|
||||
---------
|
||||
|
||||
In Nim one can define his own operators. An operator is any
|
||||
combination of the following characters::
|
||||
Nim allows user defined operators. An operator is any combination of the
|
||||
following characters::
|
||||
|
||||
= + - * / < >
|
||||
@ $ ~ & % |
|
||||
@@ -355,7 +369,7 @@ These keywords are also operators:
|
||||
``and or not xor shl shr div mod in notin is isnot of``.
|
||||
|
||||
`=`:tok:, `:`:tok:, `::`:tok: are not available as general operators; they
|
||||
are used for other notational purposes.
|
||||
are used for other notational purposes.
|
||||
|
||||
``*:`` is as a special case the two tokens `*`:tok: and `:`:tok:
|
||||
(to support ``var v*: T``).
|
||||
|
||||
@@ -146,6 +146,14 @@ modules don't need to import a module's dependencies:
|
||||
var x: MyObject
|
||||
echo($x)
|
||||
|
||||
Note on paths
|
||||
-----------
|
||||
In module related statements, if any part of the module name /
|
||||
path begins with a number, you may have to quote it in double quotes.
|
||||
In the following example, it would be seen as a literal number '3.0' of type
|
||||
'float64' if not quoted, if uncertain - quote it:
|
||||
.. code-block:: nim
|
||||
import "gfx/3d/somemodule"
|
||||
|
||||
Scope rules
|
||||
-----------
|
||||
|
||||
@@ -404,7 +404,7 @@ dispatch.
|
||||
result.a = a
|
||||
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
|
||||
because they should use static binding, but ``eval`` is a method because it
|
||||
|
||||
14
install.txt
14
install.txt
@@ -9,10 +9,10 @@ should be in your ``$PATH`` (most likely the case). Note that some few Linux
|
||||
distributions do not ship with a GCC compiler preinstalled - then you have to
|
||||
install it.
|
||||
|
||||
Install Nim by downloading the appropriate ``.zip`` file and extracting it
|
||||
Install Nim by downloading the appropriate ``.zip`` file and extracting it
|
||||
to a directory of your choice. The Nim Compiler will stay in this
|
||||
directory (unless you copy it somewhere else). The compiler does not need
|
||||
write access to its directory, so copying the nim folder to ``/opt``
|
||||
directory (unless you copy it somewhere else). The compiler does not need
|
||||
write access to its directory, so copying the nim folder to ``/opt``
|
||||
works.
|
||||
|
||||
Then run the following command::
|
||||
@@ -26,7 +26,7 @@ manually. An alternative is to create a symbolic link in ``/usr/bin``::
|
||||
|
||||
[sudo] ln -s $your_install_dir/bin/nim /usr/bin/nim
|
||||
|
||||
There are also ``install.sh`` and ``deinstall.sh`` scripts for distributing
|
||||
There are also ``install.sh`` and ``deinstall.sh`` scripts for distributing
|
||||
the files over the UNIX hierarchy. However, updating your Nim installation
|
||||
is more cumbersome then.
|
||||
|
||||
@@ -35,8 +35,8 @@ Installation on the Macintosh
|
||||
-----------------------------
|
||||
|
||||
Only MacOS X is supported.
|
||||
Since MacOS X is UNIX based too, it works like the installation on Linux.
|
||||
However, for unknown reasons the symbolic link method does not work on MacOS X.
|
||||
Since MacOS X is UNIX based too, it works like the installation on Linux.
|
||||
However, for unknown reasons the symbolic link method does not work on MacOS X.
|
||||
You need to install Apple's developer's tools for the GNU Compiler Collection.
|
||||
|
||||
|
||||
@@ -64,5 +64,5 @@ However, most testing is done with GCC.
|
||||
Bootstrapping from Github
|
||||
-------------------------
|
||||
|
||||
Take a look at the readme file on github `here <https://github.com/Araq/Nim#readme>`_
|
||||
Take a look at the readme file on github `here <https://github.com/nim-lang/Nim#readme>`_
|
||||
for instructions.
|
||||
|
||||
3
koch.nim
3
koch.nim
@@ -333,6 +333,9 @@ proc tests(args: string) =
|
||||
# we compile the tester with taintMode:on to have a basic
|
||||
# taint mode test :-)
|
||||
exec "nim cc --taintMode:on tests/testament/tester"
|
||||
# Since tests take a long time (on my machine), and we want to defy Murhpys
|
||||
# law - lets make sure the compiler really is freshly compiled!
|
||||
exec "nim c --lib:lib -d:release --opt:speed compiler/nim.nim"
|
||||
let tester = quoteShell(getCurrentDir() / "tests/testament/tester".exe)
|
||||
let success = tryExec tester & " " & (args|"all")
|
||||
exec tester & " html"
|
||||
|
||||
@@ -12,44 +12,46 @@
|
||||
include "system/syslocks"
|
||||
|
||||
type
|
||||
TLock* = TSysLock ## Nim lock; whether this is re-entrant
|
||||
Lock* = SysLock ## Nim lock; whether this is re-entrant
|
||||
## or not is unspecified!
|
||||
TCond* = TSysCond ## Nim condition variable
|
||||
|
||||
proc initLock*(lock: var TLock) {.inline.} =
|
||||
Cond* = SysCond ## Nim condition variable
|
||||
|
||||
{.deprecated: [TLock: Lock, TCond: Cond].}
|
||||
|
||||
proc initLock*(lock: var Lock) {.inline.} =
|
||||
## Initializes the given lock.
|
||||
initSysLock(lock)
|
||||
|
||||
proc deinitLock*(lock: var TLock) {.inline.} =
|
||||
proc deinitLock*(lock: var Lock) {.inline.} =
|
||||
## Frees the resources associated with the lock.
|
||||
deinitSys(lock)
|
||||
|
||||
proc tryAcquire*(lock: var TLock): bool =
|
||||
proc tryAcquire*(lock: var Lock): bool =
|
||||
## Tries to acquire the given lock. Returns `true` on success.
|
||||
result = tryAcquireSys(lock)
|
||||
|
||||
proc acquire*(lock: var TLock) =
|
||||
proc acquire*(lock: var Lock) =
|
||||
## Acquires the given lock.
|
||||
acquireSys(lock)
|
||||
|
||||
proc release*(lock: var TLock) =
|
||||
proc release*(lock: var Lock) =
|
||||
## Releases the given lock.
|
||||
releaseSys(lock)
|
||||
|
||||
|
||||
proc initCond*(cond: var TCond) {.inline.} =
|
||||
proc initCond*(cond: var Cond) {.inline.} =
|
||||
## Initializes the given condition variable.
|
||||
initSysCond(cond)
|
||||
|
||||
proc deinitCond*(cond: var TCond) {.inline.} =
|
||||
proc deinitCond*(cond: var Cond) {.inline.} =
|
||||
## Frees the resources associated with the lock.
|
||||
deinitSysCond(cond)
|
||||
|
||||
proc wait*(cond: var TCond, lock: var TLock) {.inline.} =
|
||||
proc wait*(cond: var Cond, lock: var Lock) {.inline.} =
|
||||
## waits on the condition variable `cond`.
|
||||
waitSysCond(cond, lock)
|
||||
|
||||
proc signal*(cond: var TCond) {.inline.} =
|
||||
proc signal*(cond: var Cond) {.inline.} =
|
||||
## sends a signal to the condition variable `cond`.
|
||||
signalSysCond(cond)
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
## This module implements an interface to Nim's `runtime type information`:idx:
|
||||
## (`RTTI`:idx:).
|
||||
## Note that even though ``TAny`` and its operations hide the nasty low level
|
||||
## Note that even though ``Any`` and its operations hide the nasty low level
|
||||
## details from its clients, it remains inherently unsafe!
|
||||
##
|
||||
## See the `marshal <marshal.html>`_ module for what this module allows you
|
||||
@@ -23,7 +23,7 @@ include "system/hti.nim"
|
||||
{.pop.}
|
||||
|
||||
type
|
||||
TAnyKind* = enum ## what kind of ``any`` it is
|
||||
AnyKind* = enum ## what kind of ``any`` it is
|
||||
akNone = 0, ## invalid any
|
||||
akBool = 1, ## any represents a ``bool``
|
||||
akChar = 2, ## any represents a ``char``
|
||||
@@ -55,9 +55,9 @@ type
|
||||
akUInt32 = 43, ## any represents an unsigned int32
|
||||
akUInt64 = 44, ## any represents an unsigned int64
|
||||
|
||||
TAny* = object ## can represent any nim value; NOTE: the wrapped
|
||||
Any* = object ## can represent any nim value; NOTE: the wrapped
|
||||
## value can be modified with its wrapper! This means
|
||||
## that ``TAny`` keeps a non-traced pointer to its
|
||||
## that ``Any`` keeps a non-traced pointer to its
|
||||
## wrapped value and **must not** live longer than
|
||||
## its wrapped value.
|
||||
value: pointer
|
||||
@@ -69,6 +69,7 @@ type
|
||||
TGenericSeq {.importc.} = object
|
||||
len, space, elemSize: int
|
||||
PGenSeq = ptr TGenericSeq
|
||||
{.deprecated: [TAny: Any, TAnyKind: AnyKind].}
|
||||
|
||||
const
|
||||
GenericSeqSize = (3 * sizeof(int))
|
||||
@@ -103,58 +104,58 @@ proc selectBranch(aa: pointer, n: ptr TNimNode): ptr TNimNode =
|
||||
else:
|
||||
result = n.sons[n.len]
|
||||
|
||||
proc newAny(value: pointer, rawType: PNimType): TAny =
|
||||
proc newAny(value: pointer, rawType: PNimType): Any =
|
||||
result.value = value
|
||||
result.rawType = rawType
|
||||
|
||||
when declared(system.TVarSlot):
|
||||
proc toAny*(x: TVarSlot): TAny {.inline.} =
|
||||
## constructs a ``TAny`` object from a variable slot ``x``.
|
||||
when declared(system.VarSlot):
|
||||
proc toAny*(x: VarSlot): Any {.inline.} =
|
||||
## constructs a ``Any`` object from a variable slot ``x``.
|
||||
## This captures `x`'s address, so `x` can be modified with its
|
||||
## ``TAny`` wrapper! The client needs to ensure that the wrapper
|
||||
## ``Any`` wrapper! The client needs to ensure that the wrapper
|
||||
## **does not** live longer than `x`!
|
||||
## This is provided for easier reflection capabilities of a debugger.
|
||||
result.value = x.address
|
||||
result.rawType = x.typ
|
||||
|
||||
proc toAny*[T](x: var T): TAny {.inline.} =
|
||||
## constructs a ``TAny`` object from `x`. This captures `x`'s address, so
|
||||
## `x` can be modified with its ``TAny`` wrapper! The client needs to ensure
|
||||
proc toAny*[T](x: var T): Any {.inline.} =
|
||||
## constructs a ``Any`` object from `x`. This captures `x`'s address, so
|
||||
## `x` can be modified with its ``Any`` wrapper! The client needs to ensure
|
||||
## that the wrapper **does not** live longer than `x`!
|
||||
result.value = addr(x)
|
||||
result.rawType = cast[PNimType](getTypeInfo(x))
|
||||
|
||||
proc kind*(x: TAny): TAnyKind {.inline.} =
|
||||
proc kind*(x: Any): AnyKind {.inline.} =
|
||||
## get the type kind
|
||||
result = TAnyKind(ord(x.rawType.kind))
|
||||
result = AnyKind(ord(x.rawType.kind))
|
||||
|
||||
proc size*(x: TAny): int {.inline.} =
|
||||
proc size*(x: Any): int {.inline.} =
|
||||
## returns the size of `x`'s type.
|
||||
result = x.rawType.size
|
||||
|
||||
proc baseTypeKind*(x: TAny): TAnyKind {.inline.} =
|
||||
proc baseTypeKind*(x: Any): AnyKind {.inline.} =
|
||||
## get the base type's kind; ``akNone`` is returned if `x` has no base type.
|
||||
if x.rawType.base != nil:
|
||||
result = TAnyKind(ord(x.rawType.base.kind))
|
||||
result = AnyKind(ord(x.rawType.base.kind))
|
||||
|
||||
proc baseTypeSize*(x: TAny): int {.inline.} =
|
||||
proc baseTypeSize*(x: Any): int {.inline.} =
|
||||
## returns the size of `x`'s basetype.
|
||||
if x.rawType.base != nil:
|
||||
result = x.rawType.base.size
|
||||
|
||||
proc invokeNew*(x: TAny) =
|
||||
proc invokeNew*(x: Any) =
|
||||
## performs ``new(x)``. `x` needs to represent a ``ref``.
|
||||
assert x.rawType.kind == tyRef
|
||||
var z = newObj(x.rawType, x.rawType.base.size)
|
||||
genericAssign(x.value, addr(z), x.rawType)
|
||||
|
||||
proc invokeNewSeq*(x: TAny, len: int) =
|
||||
proc invokeNewSeq*(x: Any, len: int) =
|
||||
## performs ``newSeq(x, len)``. `x` needs to represent a ``seq``.
|
||||
assert x.rawType.kind == tySequence
|
||||
var z = newSeq(x.rawType, len)
|
||||
genericShallowAssign(x.value, addr(z), x.rawType)
|
||||
|
||||
proc extendSeq*(x: TAny) =
|
||||
proc extendSeq*(x: Any) =
|
||||
## performs ``setLen(x, x.len+1)``. `x` needs to represent a ``seq``.
|
||||
assert x.rawType.kind == tySequence
|
||||
var y = cast[ptr PGenSeq](x.value)[]
|
||||
@@ -164,7 +165,7 @@ proc extendSeq*(x: TAny) =
|
||||
cast[ppointer](x.value)[] = z
|
||||
#genericShallowAssign(x.value, addr(z), x.rawType)
|
||||
|
||||
proc setObjectRuntimeType*(x: TAny) =
|
||||
proc setObjectRuntimeType*(x: Any) =
|
||||
## this needs to be called to set `x`'s runtime object type field.
|
||||
assert x.rawType.kind == tyObject
|
||||
objectInit(x.value, x.rawType)
|
||||
@@ -173,7 +174,7 @@ proc skipRange(x: PNimType): PNimType {.inline.} =
|
||||
result = x
|
||||
if result.kind == tyRange: result = result.base
|
||||
|
||||
proc `[]`*(x: TAny, i: int): TAny =
|
||||
proc `[]`*(x: Any, i: int): Any =
|
||||
## accessor for an any `x` that represents an array or a sequence.
|
||||
case x.rawType.kind
|
||||
of tyArray:
|
||||
@@ -190,7 +191,7 @@ proc `[]`*(x: TAny, i: int): TAny =
|
||||
return newAny(s +!! (GenericSeqSize+i*bs), x.rawType.base)
|
||||
else: assert false
|
||||
|
||||
proc `[]=`*(x: TAny, i: int, y: TAny) =
|
||||
proc `[]=`*(x: Any, i: int, y: Any) =
|
||||
## accessor for an any `x` that represents an array or a sequence.
|
||||
case x.rawType.kind
|
||||
of tyArray:
|
||||
@@ -209,7 +210,7 @@ proc `[]=`*(x: TAny, i: int, y: TAny) =
|
||||
genericAssign(s +!! (GenericSeqSize+i*bs), y.value, y.rawType)
|
||||
else: assert false
|
||||
|
||||
proc len*(x: TAny): int =
|
||||
proc len*(x: Any): int =
|
||||
## len for an any `x` that represents an array or a sequence.
|
||||
case x.rawType.kind
|
||||
of tyArray: result = x.rawType.size div x.rawType.base.size
|
||||
@@ -217,20 +218,20 @@ proc len*(x: TAny): int =
|
||||
else: assert false
|
||||
|
||||
|
||||
proc base*(x: TAny): TAny =
|
||||
## returns base TAny (useful for inherited object types).
|
||||
proc base*(x: Any): Any =
|
||||
## returns base Any (useful for inherited object types).
|
||||
result.rawType = x.rawType.base
|
||||
result.value = x.value
|
||||
|
||||
|
||||
proc isNil*(x: TAny): bool =
|
||||
proc isNil*(x: Any): bool =
|
||||
## `isNil` for an any `x` that represents a sequence, string, cstring,
|
||||
## proc or some pointer type.
|
||||
assert x.rawType.kind in {tyString, tyCString, tyRef, tyPtr, tyPointer,
|
||||
tySequence, tyProc}
|
||||
result = isNil(cast[ppointer](x.value)[])
|
||||
|
||||
proc getPointer*(x: TAny): pointer =
|
||||
proc getPointer*(x: Any): pointer =
|
||||
## retrieve the pointer value out of `x`. ``x`` needs to be of kind
|
||||
## ``akString``, ``akCString``, ``akProc``, ``akRef``, ``akPtr``,
|
||||
## ``akPointer``, ``akSequence``.
|
||||
@@ -238,7 +239,7 @@ proc getPointer*(x: TAny): pointer =
|
||||
tySequence, tyProc}
|
||||
result = cast[ppointer](x.value)[]
|
||||
|
||||
proc setPointer*(x: TAny, y: pointer) =
|
||||
proc setPointer*(x: Any, y: pointer) =
|
||||
## sets the pointer value of `x`. ``x`` needs to be of kind
|
||||
## ``akString``, ``akCString``, ``akProc``, ``akRef``, ``akPtr``,
|
||||
## ``akPointer``, ``akSequence``.
|
||||
@@ -247,7 +248,7 @@ proc setPointer*(x: TAny, y: pointer) =
|
||||
cast[ppointer](x.value)[] = y
|
||||
|
||||
proc fieldsAux(p: pointer, n: ptr TNimNode,
|
||||
ret: var seq[tuple[name: cstring, any: TAny]]) =
|
||||
ret: var seq[tuple[name: cstring, any: Any]]) =
|
||||
case n.kind
|
||||
of nkNone: assert(false)
|
||||
of nkSlot:
|
||||
@@ -260,7 +261,7 @@ proc fieldsAux(p: pointer, n: ptr TNimNode,
|
||||
ret.add((n.name, newAny(p +!! n.offset, n.typ)))
|
||||
if m != nil: fieldsAux(p, m, ret)
|
||||
|
||||
iterator fields*(x: TAny): tuple[name: string, any: TAny] =
|
||||
iterator fields*(x: Any): tuple[name: string, any: Any] =
|
||||
## iterates over every active field of the any `x` that represents an object
|
||||
## or a tuple.
|
||||
assert x.rawType.kind in {tyTuple, tyObject}
|
||||
@@ -269,7 +270,7 @@ iterator fields*(x: TAny): tuple[name: string, any: TAny] =
|
||||
# XXX BUG: does not work yet, however is questionable anyway
|
||||
when false:
|
||||
if x.rawType.kind == tyObject: t = cast[ptr PNimType](x.value)[]
|
||||
var ret: seq[tuple[name: cstring, any: TAny]] = @[]
|
||||
var ret: seq[tuple[name: cstring, any: Any]] = @[]
|
||||
if t.kind == tyObject:
|
||||
while true:
|
||||
fieldsAux(p, t.node, ret)
|
||||
@@ -314,7 +315,7 @@ proc getFieldNode(p: pointer, n: ptr TNimNode,
|
||||
var m = selectBranch(p, n)
|
||||
if m != nil: result = getFieldNode(p, m, name)
|
||||
|
||||
proc `[]=`*(x: TAny, fieldName: string, value: TAny) =
|
||||
proc `[]=`*(x: Any, fieldName: string, value: Any) =
|
||||
## sets a field of `x`; `x` represents an object or a tuple.
|
||||
var t = x.rawType
|
||||
# XXX BUG: does not work yet, however is questionable anyway
|
||||
@@ -328,7 +329,7 @@ proc `[]=`*(x: TAny, fieldName: string, value: TAny) =
|
||||
else:
|
||||
raise newException(ValueError, "invalid field name: " & fieldName)
|
||||
|
||||
proc `[]`*(x: TAny, fieldName: string): TAny =
|
||||
proc `[]`*(x: Any, fieldName: string): Any =
|
||||
## gets a field of `x`; `x` represents an object or a tuple.
|
||||
var t = x.rawType
|
||||
# XXX BUG: does not work yet, however is questionable anyway
|
||||
@@ -339,47 +340,49 @@ proc `[]`*(x: TAny, fieldName: string): TAny =
|
||||
if n != nil:
|
||||
result.value = x.value +!! n.offset
|
||||
result.rawType = n.typ
|
||||
elif x.rawType.kind == tyObject and x.rawType.base != nil:
|
||||
return `[]`(TAny(value: x.value, rawType: x.rawType.base), fieldName)
|
||||
else:
|
||||
raise newException(ValueError, "invalid field name: " & fieldName)
|
||||
|
||||
proc `[]`*(x: TAny): TAny =
|
||||
proc `[]`*(x: Any): Any =
|
||||
## dereference operation for the any `x` that represents a ptr or a ref.
|
||||
assert x.rawType.kind in {tyRef, tyPtr}
|
||||
result.value = cast[ppointer](x.value)[]
|
||||
result.rawType = x.rawType.base
|
||||
|
||||
proc `[]=`*(x, y: TAny) =
|
||||
proc `[]=`*(x, y: Any) =
|
||||
## dereference operation for the any `x` that represents a ptr or a ref.
|
||||
assert x.rawType.kind in {tyRef, tyPtr}
|
||||
assert y.rawType == x.rawType.base
|
||||
genericAssign(cast[ppointer](x.value)[], y.value, y.rawType)
|
||||
|
||||
proc getInt*(x: TAny): int =
|
||||
proc getInt*(x: Any): int =
|
||||
## retrieve the int value out of `x`. `x` needs to represent an int.
|
||||
assert skipRange(x.rawType).kind == tyInt
|
||||
result = cast[ptr int](x.value)[]
|
||||
|
||||
proc getInt8*(x: TAny): int8 =
|
||||
proc getInt8*(x: Any): int8 =
|
||||
## retrieve the int8 value out of `x`. `x` needs to represent an int8.
|
||||
assert skipRange(x.rawType).kind == tyInt8
|
||||
result = cast[ptr int8](x.value)[]
|
||||
|
||||
proc getInt16*(x: TAny): int16 =
|
||||
proc getInt16*(x: Any): int16 =
|
||||
## retrieve the int16 value out of `x`. `x` needs to represent an int16.
|
||||
assert skipRange(x.rawType).kind == tyInt16
|
||||
result = cast[ptr int16](x.value)[]
|
||||
|
||||
proc getInt32*(x: TAny): int32 =
|
||||
proc getInt32*(x: Any): int32 =
|
||||
## retrieve the int32 value out of `x`. `x` needs to represent an int32.
|
||||
assert skipRange(x.rawType).kind == tyInt32
|
||||
result = cast[ptr int32](x.value)[]
|
||||
|
||||
proc getInt64*(x: TAny): int64 =
|
||||
proc getInt64*(x: Any): int64 =
|
||||
## retrieve the int64 value out of `x`. `x` needs to represent an int64.
|
||||
assert skipRange(x.rawType).kind == tyInt64
|
||||
result = cast[ptr int64](x.value)[]
|
||||
|
||||
proc getBiggestInt*(x: TAny): BiggestInt =
|
||||
proc getBiggestInt*(x: Any): BiggestInt =
|
||||
## retrieve the integer value out of `x`. `x` needs to represent
|
||||
## some integer, a bool, a char, an enum or a small enough bit set.
|
||||
## The value might be sign-extended to ``BiggestInt``.
|
||||
@@ -405,7 +408,7 @@ proc getBiggestInt*(x: TAny): BiggestInt =
|
||||
of tyUInt32: result = BiggestInt(cast[ptr uint32](x.value)[])
|
||||
else: assert false
|
||||
|
||||
proc setBiggestInt*(x: TAny, y: BiggestInt) =
|
||||
proc setBiggestInt*(x: Any, y: BiggestInt) =
|
||||
## sets the integer value of `x`. `x` needs to represent
|
||||
## some integer, a bool, a char, an enum or a small enough bit set.
|
||||
var t = skipRange(x.rawType)
|
||||
@@ -430,36 +433,36 @@ proc setBiggestInt*(x: TAny, y: BiggestInt) =
|
||||
of tyUInt32: cast[ptr uint32](x.value)[] = uint32(y)
|
||||
else: assert false
|
||||
|
||||
proc getUInt*(x: TAny): uint =
|
||||
proc getUInt*(x: Any): uint =
|
||||
## retrieve the uint value out of `x`, `x` needs to represent an uint.
|
||||
assert skipRange(x.rawType).kind == tyUInt
|
||||
result = cast[ptr uint](x.value)[]
|
||||
|
||||
proc getUInt8*(x: TAny): uint8 =
|
||||
proc getUInt8*(x: Any): uint8 =
|
||||
## retrieve the uint8 value out of `x`, `x` needs to represent an
|
||||
## uint8.
|
||||
assert skipRange(x.rawType).kind == tyUInt8
|
||||
result = cast[ptr uint8](x.value)[]
|
||||
|
||||
proc getUInt16*(x: TAny): uint16 =
|
||||
proc getUInt16*(x: Any): uint16 =
|
||||
## retrieve the uint16 value out of `x`, `x` needs to represent an
|
||||
## uint16.
|
||||
assert skipRange(x.rawType).kind == tyUInt16
|
||||
result = cast[ptr uint16](x.value)[]
|
||||
|
||||
proc getUInt32*(x: TAny): uint32 =
|
||||
proc getUInt32*(x: Any): uint32 =
|
||||
## retrieve the uint32 value out of `x`, `x` needs to represent an
|
||||
## uint32.
|
||||
assert skipRange(x.rawType).kind == tyUInt32
|
||||
result = cast[ptr uint32](x.value)[]
|
||||
|
||||
proc getUInt64*(x: TAny): uint64 =
|
||||
proc getUInt64*(x: Any): uint64 =
|
||||
## retrieve the uint64 value out of `x`, `x` needs to represent an
|
||||
## uint64.
|
||||
assert skipRange(x.rawType).kind == tyUInt64
|
||||
result = cast[ptr uint64](x.value)[]
|
||||
|
||||
proc getBiggestUint*(x: TAny): uint64 =
|
||||
proc getBiggestUint*(x: Any): uint64 =
|
||||
## retrieve the unsigned integer value out of `x`. `x` needs to
|
||||
## represent an unsigned integer.
|
||||
var t = skipRange(x.rawType)
|
||||
@@ -471,7 +474,7 @@ proc getBiggestUint*(x: TAny): uint64 =
|
||||
of tyUInt64: result = uint64(cast[ptr uint64](x.value)[])
|
||||
else: assert false
|
||||
|
||||
proc setBiggestUint*(x: TAny; y: uint64) =
|
||||
proc setBiggestUint*(x: Any; y: uint64) =
|
||||
## sets the unsigned integer value of `c`. `c` needs to represent an
|
||||
## unsigned integer.
|
||||
var t = skipRange(x.rawType)
|
||||
@@ -483,25 +486,25 @@ proc setBiggestUint*(x: TAny; y: uint64) =
|
||||
of tyUInt64: cast[ptr uint64](x.value)[] = uint64(y)
|
||||
else: assert false
|
||||
|
||||
proc getChar*(x: TAny): char =
|
||||
proc getChar*(x: Any): char =
|
||||
## retrieve the char value out of `x`. `x` needs to represent a char.
|
||||
var t = skipRange(x.rawType)
|
||||
assert t.kind == tyChar
|
||||
result = cast[ptr char](x.value)[]
|
||||
|
||||
proc getBool*(x: TAny): bool =
|
||||
proc getBool*(x: Any): bool =
|
||||
## retrieve the bool value out of `x`. `x` needs to represent a bool.
|
||||
var t = skipRange(x.rawType)
|
||||
assert t.kind == tyBool
|
||||
result = cast[ptr bool](x.value)[]
|
||||
|
||||
proc skipRange*(x: TAny): TAny =
|
||||
proc skipRange*(x: Any): Any =
|
||||
## skips the range information of `x`.
|
||||
assert x.rawType.kind == tyRange
|
||||
result.rawType = x.rawType.base
|
||||
result.value = x.value
|
||||
|
||||
proc getEnumOrdinal*(x: TAny, name: string): int =
|
||||
proc getEnumOrdinal*(x: Any, name: string): int =
|
||||
## gets the enum field ordinal from `name`. `x` needs to represent an enum
|
||||
## but is only used to access the type information. In case of an error
|
||||
## ``low(int)`` is returned.
|
||||
@@ -517,7 +520,7 @@ proc getEnumOrdinal*(x: TAny, name: string): int =
|
||||
return s[i].offset
|
||||
result = low(int)
|
||||
|
||||
proc getEnumField*(x: TAny, ordinalValue: int): string =
|
||||
proc getEnumField*(x: Any, ordinalValue: int): string =
|
||||
## gets the enum field name as a string. `x` needs to represent an enum
|
||||
## but is only used to access the type information. The field name of
|
||||
## `ordinalValue` is returned.
|
||||
@@ -535,26 +538,26 @@ proc getEnumField*(x: TAny, ordinalValue: int): string =
|
||||
if s[i].offset == e: return $s[i].name
|
||||
result = $e
|
||||
|
||||
proc getEnumField*(x: TAny): string =
|
||||
proc getEnumField*(x: Any): string =
|
||||
## gets the enum field name as a string. `x` needs to represent an enum.
|
||||
result = getEnumField(x, getBiggestInt(x).int)
|
||||
|
||||
proc getFloat*(x: TAny): float =
|
||||
proc getFloat*(x: Any): float =
|
||||
## retrieve the float value out of `x`. `x` needs to represent an float.
|
||||
assert skipRange(x.rawType).kind == tyFloat
|
||||
result = cast[ptr float](x.value)[]
|
||||
|
||||
proc getFloat32*(x: TAny): float32 =
|
||||
proc getFloat32*(x: Any): float32 =
|
||||
## retrieve the float32 value out of `x`. `x` needs to represent an float32.
|
||||
assert skipRange(x.rawType).kind == tyFloat32
|
||||
result = cast[ptr float32](x.value)[]
|
||||
|
||||
proc getFloat64*(x: TAny): float64 =
|
||||
proc getFloat64*(x: Any): float64 =
|
||||
## retrieve the float64 value out of `x`. `x` needs to represent an float64.
|
||||
assert skipRange(x.rawType).kind == tyFloat64
|
||||
result = cast[ptr float64](x.value)[]
|
||||
|
||||
proc getBiggestFloat*(x: TAny): BiggestFloat =
|
||||
proc getBiggestFloat*(x: Any): BiggestFloat =
|
||||
## retrieve the float value out of `x`. `x` needs to represent
|
||||
## some float. The value is extended to ``BiggestFloat``.
|
||||
case skipRange(x.rawType).kind
|
||||
@@ -563,7 +566,7 @@ proc getBiggestFloat*(x: TAny): BiggestFloat =
|
||||
of tyFloat64: result = BiggestFloat(cast[ptr float64](x.value)[])
|
||||
else: assert false
|
||||
|
||||
proc setBiggestFloat*(x: TAny, y: BiggestFloat) =
|
||||
proc setBiggestFloat*(x: Any, y: BiggestFloat) =
|
||||
## sets the float value of `x`. `x` needs to represent
|
||||
## some float.
|
||||
case skipRange(x.rawType).kind
|
||||
@@ -572,29 +575,29 @@ proc setBiggestFloat*(x: TAny, y: BiggestFloat) =
|
||||
of tyFloat64: cast[ptr float64](x.value)[] = y
|
||||
else: assert false
|
||||
|
||||
proc getString*(x: TAny): string =
|
||||
proc getString*(x: Any): string =
|
||||
## retrieve the string value out of `x`. `x` needs to represent a string.
|
||||
assert x.rawType.kind == tyString
|
||||
if not isNil(cast[ptr pointer](x.value)[]):
|
||||
result = cast[ptr string](x.value)[]
|
||||
|
||||
proc setString*(x: TAny, y: string) =
|
||||
proc setString*(x: Any, y: string) =
|
||||
## sets the string value of `x`. `x` needs to represent a string.
|
||||
assert x.rawType.kind == tyString
|
||||
cast[ptr string](x.value)[] = y
|
||||
|
||||
proc getCString*(x: TAny): cstring =
|
||||
proc getCString*(x: Any): cstring =
|
||||
## retrieve the cstring value out of `x`. `x` needs to represent a cstring.
|
||||
assert x.rawType.kind == tyCString
|
||||
result = cast[ptr cstring](x.value)[]
|
||||
|
||||
proc assign*(x, y: TAny) =
|
||||
## copies the value of `y` to `x`. The assignment operator for ``TAny``
|
||||
proc assign*(x, y: Any) =
|
||||
## copies the value of `y` to `x`. The assignment operator for ``Any``
|
||||
## does NOT do this; it performs a shallow copy instead!
|
||||
assert y.rawType == x.rawType
|
||||
genericAssign(x.value, y.value, y.rawType)
|
||||
|
||||
iterator elements*(x: TAny): int =
|
||||
iterator elements*(x: Any): int =
|
||||
## iterates over every element of `x` that represents a Nim bitset.
|
||||
assert x.rawType.kind == tySet
|
||||
var typ = x.rawType
|
||||
@@ -616,7 +619,7 @@ iterator elements*(x: TAny): int =
|
||||
if (u and (1'i64 shl int64(i))) != 0'i64:
|
||||
yield i+typ.node.len
|
||||
|
||||
proc inclSetElement*(x: TAny, elem: int) =
|
||||
proc inclSetElement*(x: Any, elem: int) =
|
||||
## includes an element `elem` in `x`. `x` needs to represent a Nim bitset.
|
||||
assert x.rawType.kind == tySet
|
||||
var typ = x.rawType
|
||||
|
||||
@@ -13,27 +13,31 @@
|
||||
import strutils, mysql
|
||||
|
||||
type
|
||||
TDbConn* = PMySQL ## encapsulates a database connection
|
||||
TRow* = seq[string] ## a row of a dataset. NULL database values will be
|
||||
DbConn* = PMySQL ## encapsulates a database connection
|
||||
Row* = seq[string] ## a row of a dataset. NULL database values will be
|
||||
## transformed always to the empty string.
|
||||
InstantRow* = tuple[row: cstringArray, len: int] ## a handle that can be
|
||||
## used to get a row's
|
||||
## column text on demand
|
||||
EDb* = object of IOError ## exception that is raised if a database error occurs
|
||||
|
||||
TSqlQuery* = distinct string ## an SQL query string
|
||||
SqlQuery* = distinct string ## an SQL query string
|
||||
|
||||
FDb* = object of IOEffect ## effect that denotes a database operation
|
||||
FReadDb* = object of FDb ## effect that denotes a read operation
|
||||
FWriteDb* = object of FDb ## effect that denotes a write operation
|
||||
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
|
||||
|
||||
proc sql*(query: string): TSqlQuery {.noSideEffect, inline.} =
|
||||
## constructs a TSqlQuery from the string `query`. This is supposed to be
|
||||
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
|
||||
## constructs a SqlQuery from the string `query`. This is supposed to be
|
||||
## used as a raw-string-literal modifier:
|
||||
## ``sql"update user set counter = counter + 1"``
|
||||
##
|
||||
## If assertions are turned off, it does nothing. If assertions are turned
|
||||
## on, later versions will check the string for valid syntax.
|
||||
result = TSqlQuery(query)
|
||||
result = SqlQuery(query)
|
||||
|
||||
proc dbError(db: TDbConn) {.noreturn.} =
|
||||
proc dbError(db: DbConn) {.noreturn.} =
|
||||
## raises an EDb exception.
|
||||
var e: ref EDb
|
||||
new(e)
|
||||
@@ -48,7 +52,7 @@ proc dbError*(msg: string) {.noreturn.} =
|
||||
raise e
|
||||
|
||||
when false:
|
||||
proc dbQueryOpt*(db: TDbConn, query: string, args: varargs[string, `$`]) =
|
||||
proc dbQueryOpt*(db: DbConn, query: string, args: varargs[string, `$`]) =
|
||||
var stmt = mysql_stmt_init(db)
|
||||
if stmt == nil: dbError(db)
|
||||
if mysql_stmt_prepare(stmt, query, len(query)) != 0:
|
||||
@@ -65,7 +69,7 @@ proc dbQuote*(s: string): string =
|
||||
else: add(result, c)
|
||||
add(result, '\'')
|
||||
|
||||
proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
|
||||
proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
|
||||
result = ""
|
||||
var a = 0
|
||||
for c in items(string(formatstr)):
|
||||
@@ -78,23 +82,23 @@ proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
|
||||
else:
|
||||
add(result, c)
|
||||
|
||||
proc tryExec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]): bool {.
|
||||
proc tryExec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {.
|
||||
tags: [FReadDB, FWriteDb].} =
|
||||
## tries to execute the query and returns true if successful, false otherwise.
|
||||
var q = dbFormat(query, args)
|
||||
return mysql.realQuery(db, q, q.len) == 0'i32
|
||||
|
||||
proc rawExec(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) =
|
||||
proc rawExec(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) =
|
||||
var q = dbFormat(query, args)
|
||||
if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db)
|
||||
|
||||
proc exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) {.
|
||||
proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
|
||||
tags: [FReadDB, FWriteDb].} =
|
||||
## executes the query and raises EDB if not successful.
|
||||
var q = dbFormat(query, args)
|
||||
if mysql.realQuery(db, q, q.len) != 0'i32: dbError(db)
|
||||
|
||||
proc newRow(L: int): TRow =
|
||||
proc newRow(L: int): Row =
|
||||
newSeq(result, L)
|
||||
for i in 0..L-1: result[i] = ""
|
||||
|
||||
@@ -103,8 +107,8 @@ proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) =
|
||||
while mysql.fetchRow(sqlres) != nil: discard
|
||||
mysql.freeResult(sqlres)
|
||||
|
||||
iterator fastRows*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
|
||||
iterator fastRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
||||
## executes the query and iterates over the result dataset. This is very
|
||||
## fast, but potenially dangerous: If the for-loop-body executes another
|
||||
## query, the results can be undefined. For MySQL this is the case!.
|
||||
@@ -126,10 +130,34 @@ iterator fastRows*(db: TDbConn, query: TSqlQuery,
|
||||
yield result
|
||||
properFreeResult(sqlres, row)
|
||||
|
||||
proc getRow*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
|
||||
iterator instantRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): InstantRow
|
||||
{.tags: [FReadDb].} =
|
||||
## same as fastRows but returns a handle that can be used to get column text
|
||||
## on demand using []. Returned handle is valid only within interator body.
|
||||
rawExec(db, query, args)
|
||||
var sqlres = mysql.useResult(db)
|
||||
if sqlres != nil:
|
||||
let L = int(mysql.numFields(sqlres))
|
||||
var row: cstringArray
|
||||
while true:
|
||||
row = mysql.fetchRow(sqlres)
|
||||
if row == nil: break
|
||||
yield (row: row, len: L)
|
||||
properFreeResult(sqlres, row)
|
||||
|
||||
proc `[]`*(row: InstantRow, col: int): string {.inline.} =
|
||||
## returns text for given column of the row
|
||||
$row.row[col]
|
||||
|
||||
proc len*(row: InstantRow): int {.inline.} =
|
||||
## returns number of columns in the row
|
||||
row.len
|
||||
|
||||
proc getRow*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
||||
## retrieves a single row. If the query doesn't return any rows, this proc
|
||||
## will return a TRow with empty strings for each column.
|
||||
## will return a Row with empty strings for each column.
|
||||
rawExec(db, query, args)
|
||||
var sqlres = mysql.useResult(db)
|
||||
if sqlres != nil:
|
||||
@@ -145,8 +173,8 @@ proc getRow*(db: TDbConn, query: TSqlQuery,
|
||||
add(result[i], row[i])
|
||||
properFreeResult(sqlres, row)
|
||||
|
||||
proc getAllRows*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): seq[TRow] {.tags: [FReadDB].} =
|
||||
proc getAllRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): seq[Row] {.tags: [FReadDB].} =
|
||||
## executes the query and returns the whole result dataset.
|
||||
result = @[]
|
||||
rawExec(db, query, args)
|
||||
@@ -168,12 +196,12 @@ proc getAllRows*(db: TDbConn, query: TSqlQuery,
|
||||
inc(j)
|
||||
mysql.freeResult(sqlres)
|
||||
|
||||
iterator rows*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
|
||||
iterator rows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
||||
## same as `fastRows`, but slower and safe.
|
||||
for r in items(getAllRows(db, query, args)): yield r
|
||||
|
||||
proc getValue*(db: TDbConn, query: TSqlQuery,
|
||||
proc getValue*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): string {.tags: [FReadDB].} =
|
||||
## executes the query and returns the first column of the first row of the
|
||||
## result dataset. Returns "" if the dataset contains no rows or the database
|
||||
@@ -183,7 +211,7 @@ proc getValue*(db: TDbConn, query: TSqlQuery,
|
||||
result = row[0]
|
||||
break
|
||||
|
||||
proc tryInsertId*(db: TDbConn, query: TSqlQuery,
|
||||
proc tryInsertId*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
## generated ID for the row or -1 in case of an error.
|
||||
@@ -193,14 +221,14 @@ proc tryInsertId*(db: TDbConn, query: TSqlQuery,
|
||||
else:
|
||||
result = mysql.insertId(db)
|
||||
|
||||
proc insertId*(db: TDbConn, query: TSqlQuery,
|
||||
proc insertId*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
## generated ID for the row.
|
||||
result = tryInsertID(db, query, args)
|
||||
if result < 0: dbError(db)
|
||||
|
||||
proc execAffectedRows*(db: TDbConn, query: TSqlQuery,
|
||||
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.
|
||||
tags: [FReadDB, FWriteDb].} =
|
||||
## runs the query (typically "UPDATE") and returns the
|
||||
@@ -208,11 +236,11 @@ proc execAffectedRows*(db: TDbConn, query: TSqlQuery,
|
||||
rawExec(db, query, args)
|
||||
result = mysql.affectedRows(db)
|
||||
|
||||
proc close*(db: TDbConn) {.tags: [FDb].} =
|
||||
proc close*(db: DbConn) {.tags: [FDb].} =
|
||||
## closes the database connection.
|
||||
if db != nil: mysql.close(db)
|
||||
|
||||
proc open*(connection, user, password, database: string): TDbConn {.
|
||||
proc open*(connection, user, password, database: string): DbConn {.
|
||||
tags: [FDb].} =
|
||||
## opens a database connection. Raises `EDb` if the connection could not
|
||||
## be established.
|
||||
@@ -230,8 +258,8 @@ proc open*(connection, user, password, database: string): TDbConn {.
|
||||
db_mysql.close(result)
|
||||
dbError(errmsg)
|
||||
|
||||
proc setEncoding*(connection: TDbConn, encoding: string): bool {.
|
||||
proc setEncoding*(connection: DbConn, encoding: string): bool {.
|
||||
tags: [FDb].} =
|
||||
## sets the encoding of a database connection, returns true for
|
||||
## success, false for failure.
|
||||
result = mysql.set_character_set(connection, encoding) == 0
|
||||
result = mysql.set_character_set(connection, encoding) == 0
|
||||
|
||||
@@ -13,28 +13,33 @@
|
||||
import strutils, postgres
|
||||
|
||||
type
|
||||
TDbConn* = PPGconn ## encapsulates a database connection
|
||||
TRow* = seq[string] ## a row of a dataset. NULL database values will be
|
||||
DbConn* = PPGconn ## encapsulates a database connection
|
||||
Row* = seq[string] ## a row of a dataset. NULL database values will be
|
||||
## transformed always to the empty string.
|
||||
InstantRow* = tuple[res: PPGresult, line: int32] ## a handle that can be
|
||||
## used to get a row's
|
||||
## column text on demand
|
||||
EDb* = object of IOError ## exception that is raised if a database error occurs
|
||||
|
||||
TSqlQuery* = distinct string ## an SQL query string
|
||||
TSqlPrepared* = distinct string ## a identifier for the prepared queries
|
||||
SqlQuery* = distinct string ## an SQL query string
|
||||
SqlPrepared* = distinct string ## a identifier for the prepared queries
|
||||
|
||||
FDb* = object of IOEffect ## effect that denotes a database operation
|
||||
FReadDb* = object of FDb ## effect that denotes a read operation
|
||||
FWriteDb* = object of FDb ## effect that denotes a write operation
|
||||
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn,
|
||||
TSqlPrepared: SqlPrepared].}
|
||||
|
||||
proc sql*(query: string): TSqlQuery {.noSideEffect, inline.} =
|
||||
## constructs a TSqlQuery from the string `query`. This is supposed to be
|
||||
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
|
||||
## constructs a SqlQuery from the string `query`. This is supposed to be
|
||||
## used as a raw-string-literal modifier:
|
||||
## ``sql"update user set counter = counter + 1"``
|
||||
##
|
||||
## If assertions are turned off, it does nothing. If assertions are turned
|
||||
## on, later versions will check the string for valid syntax.
|
||||
result = TSqlQuery(query)
|
||||
result = SqlQuery(query)
|
||||
|
||||
proc dbError*(db: TDbConn) {.noreturn.} =
|
||||
proc dbError*(db: DbConn) {.noreturn.} =
|
||||
## raises an EDb exception.
|
||||
var e: ref EDb
|
||||
new(e)
|
||||
@@ -56,7 +61,7 @@ proc dbQuote*(s: string): string =
|
||||
else: add(result, c)
|
||||
add(result, '\'')
|
||||
|
||||
proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
|
||||
proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
|
||||
result = ""
|
||||
var a = 0
|
||||
for c in items(string(formatstr)):
|
||||
@@ -69,7 +74,7 @@ proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
|
||||
else:
|
||||
add(result, c)
|
||||
|
||||
proc tryExec*(db: TDbConn, query: TSqlQuery,
|
||||
proc tryExec*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): bool {.tags: [FReadDB, FWriteDb].} =
|
||||
## tries to execute the query and returns true if successful, false otherwise.
|
||||
var arr = allocCStringArray(args)
|
||||
@@ -79,7 +84,7 @@ proc tryExec*(db: TDbConn, query: TSqlQuery,
|
||||
result = pqresultStatus(res) == PGRES_COMMAND_OK
|
||||
pqclear(res)
|
||||
|
||||
proc exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) {.
|
||||
proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
|
||||
tags: [FReadDB, FWriteDb].} =
|
||||
## executes the query and raises EDB if not successful.
|
||||
var arr = allocCStringArray(args)
|
||||
@@ -89,7 +94,7 @@ proc exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) {.
|
||||
if pqresultStatus(res) != PGRES_COMMAND_OK: dbError(db)
|
||||
pqclear(res)
|
||||
|
||||
proc exec*(db: TDbConn, stmtName: TSqlPrepared,
|
||||
proc exec*(db: DbConn, stmtName: SqlPrepared,
|
||||
args: varargs[string]) {.tags: [FReadDB, FWriteDb].} =
|
||||
var arr = allocCStringArray(args)
|
||||
var res = pqexecPrepared(db, stmtName.string, int32(args.len), arr,
|
||||
@@ -98,11 +103,11 @@ proc exec*(db: TDbConn, stmtName: TSqlPrepared,
|
||||
if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db)
|
||||
pqclear(res)
|
||||
|
||||
proc newRow(L: int): TRow =
|
||||
proc newRow(L: int): Row =
|
||||
newSeq(result, L)
|
||||
for i in 0..L-1: result[i] = ""
|
||||
|
||||
proc setupQuery(db: TDbConn, query: TSqlQuery,
|
||||
proc setupQuery(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string]): PPGresult =
|
||||
var arr = allocCStringArray(args)
|
||||
result = pqexecParams(db, query.string, int32(args.len), nil, arr,
|
||||
@@ -110,7 +115,7 @@ proc setupQuery(db: TDbConn, query: TSqlQuery,
|
||||
deallocCStringArray(arr)
|
||||
if pqResultStatus(result) != PGRES_TUPLES_OK: dbError(db)
|
||||
|
||||
proc setupQuery(db: TDbConn, stmtName: TSqlPrepared,
|
||||
proc setupQuery(db: DbConn, stmtName: SqlPrepared,
|
||||
args: varargs[string]): PPGresult =
|
||||
var arr = allocCStringArray(args)
|
||||
result = pqexecPrepared(db, stmtName.string, int32(args.len), arr,
|
||||
@@ -118,13 +123,13 @@ proc setupQuery(db: TDbConn, stmtName: TSqlPrepared,
|
||||
deallocCStringArray(arr)
|
||||
if pqResultStatus(result) != PGRES_TUPLES_OK: dbError(db)
|
||||
|
||||
proc prepare*(db: TDbConn; stmtName: string, query: TSqlQuery;
|
||||
nParams: int): TSqlPrepared =
|
||||
proc prepare*(db: DbConn; stmtName: string, query: SqlQuery;
|
||||
nParams: int): SqlPrepared =
|
||||
var res = pqprepare(db, stmtName, query.string, int32(nParams), nil)
|
||||
if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db)
|
||||
return TSqlPrepared(stmtName)
|
||||
return SqlPrepared(stmtName)
|
||||
|
||||
proc setRow(res: PPGresult, r: var TRow, line, cols: int32) =
|
||||
proc setRow(res: PPGresult, r: var Row, line, cols: int32) =
|
||||
for col in 0..cols-1:
|
||||
setLen(r[col], 0)
|
||||
let x = pqgetvalue(res, line, col)
|
||||
@@ -133,8 +138,8 @@ proc setRow(res: PPGresult, r: var TRow, line, cols: int32) =
|
||||
else:
|
||||
add(r[col], x)
|
||||
|
||||
iterator fastRows*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
|
||||
iterator fastRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
||||
## executes the query and iterates over the result dataset. This is very
|
||||
## fast, but potenially dangerous: If the for-loop-body executes another
|
||||
## query, the results can be undefined. For Postgres it is safe though.
|
||||
@@ -146,8 +151,8 @@ iterator fastRows*(db: TDbConn, query: TSqlQuery,
|
||||
yield result
|
||||
pqclear(res)
|
||||
|
||||
iterator fastRows*(db: TDbConn, stmtName: TSqlPrepared,
|
||||
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
|
||||
iterator fastRows*(db: DbConn, stmtName: SqlPrepared,
|
||||
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
||||
## executes the prepared query and iterates over the result dataset.
|
||||
var res = setupQuery(db, stmtName, args)
|
||||
var L = pqNfields(res)
|
||||
@@ -157,44 +162,62 @@ iterator fastRows*(db: TDbConn, stmtName: TSqlPrepared,
|
||||
yield result
|
||||
pqClear(res)
|
||||
|
||||
proc getRow*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
|
||||
iterator instantRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): InstantRow
|
||||
{.tags: [FReadDb].} =
|
||||
## same as fastRows but returns a handle that can be used to get column text
|
||||
## on demand using []. Returned handle is valid only within interator body.
|
||||
var res = setupQuery(db, query, args)
|
||||
for i in 0..pqNtuples(res)-1:
|
||||
yield (res: res, line: i)
|
||||
pqClear(res)
|
||||
|
||||
proc `[]`*(row: InstantRow, col: int32): string {.inline.} =
|
||||
## returns text for given column of the row
|
||||
$pqgetvalue(row.res, row.line, col)
|
||||
|
||||
proc len*(row: InstantRow): int32 {.inline.} =
|
||||
## returns number of columns in the row
|
||||
pqNfields(row.res)
|
||||
|
||||
proc getRow*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
||||
## retrieves a single row. If the query doesn't return any rows, this proc
|
||||
## will return a TRow with empty strings for each column.
|
||||
## will return a Row with empty strings for each column.
|
||||
var res = setupQuery(db, query, args)
|
||||
var L = pqnfields(res)
|
||||
result = newRow(L)
|
||||
setRow(res, result, 0, L)
|
||||
pqclear(res)
|
||||
|
||||
proc getRow*(db: TDbConn, stmtName: TSqlPrepared,
|
||||
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
|
||||
proc getRow*(db: DbConn, stmtName: SqlPrepared,
|
||||
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
||||
var res = setupQuery(db, stmtName, args)
|
||||
var L = pqNfields(res)
|
||||
result = newRow(L)
|
||||
setRow(res, result, 0, L)
|
||||
pqClear(res)
|
||||
|
||||
proc getAllRows*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): seq[TRow] {.tags: [FReadDB].} =
|
||||
proc getAllRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): seq[Row] {.tags: [FReadDB].} =
|
||||
## executes the query and returns the whole result dataset.
|
||||
result = @[]
|
||||
for r in fastRows(db, query, args):
|
||||
result.add(r)
|
||||
|
||||
proc getAllRows*(db: TDbConn, stmtName: TSqlPrepared,
|
||||
args: varargs[string, `$`]): seq[TRow] {.tags: [FReadDB].} =
|
||||
proc getAllRows*(db: DbConn, stmtName: SqlPrepared,
|
||||
args: varargs[string, `$`]): seq[Row] {.tags: [FReadDB].} =
|
||||
## executes the prepared query and returns the whole result dataset.
|
||||
result = @[]
|
||||
for r in fastRows(db, stmtName, args):
|
||||
result.add(r)
|
||||
|
||||
iterator rows*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
|
||||
iterator rows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): Row {.tags: [FReadDB].} =
|
||||
## same as `fastRows`, but slower and safe.
|
||||
for r in items(getAllRows(db, query, args)): yield r
|
||||
|
||||
proc getValue*(db: TDbConn, query: TSqlQuery,
|
||||
proc getValue*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): string {.tags: [FReadDB].} =
|
||||
## executes the query and returns the first column of the first row of the
|
||||
## result dataset. Returns "" if the dataset contains no rows or the database
|
||||
@@ -202,20 +225,20 @@ proc getValue*(db: TDbConn, query: TSqlQuery,
|
||||
var x = pqgetvalue(setupQuery(db, query, args), 0, 0)
|
||||
result = if isNil(x): "" else: $x
|
||||
|
||||
proc tryInsertID*(db: TDbConn, query: TSqlQuery,
|
||||
proc tryInsertID*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].}=
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
## generated ID for the row or -1 in case of an error. For Postgre this adds
|
||||
## ``RETURNING id`` to the query, so it only works if your primary key is
|
||||
## named ``id``.
|
||||
var x = pqgetvalue(setupQuery(db, TSqlQuery(string(query) & " RETURNING id"),
|
||||
var x = pqgetvalue(setupQuery(db, SqlQuery(string(query) & " RETURNING id"),
|
||||
args), 0, 0)
|
||||
if not isNil(x):
|
||||
result = parseBiggestInt($x)
|
||||
else:
|
||||
result = -1
|
||||
|
||||
proc insertID*(db: TDbConn, query: TSqlQuery,
|
||||
proc insertID*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
## generated ID for the row. For Postgre this adds
|
||||
@@ -224,7 +247,7 @@ proc insertID*(db: TDbConn, query: TSqlQuery,
|
||||
result = tryInsertID(db, query, args)
|
||||
if result < 0: dbError(db)
|
||||
|
||||
proc execAffectedRows*(db: TDbConn, query: TSqlQuery,
|
||||
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.tags: [
|
||||
FReadDB, FWriteDb].} =
|
||||
## executes the query (typically "UPDATE") and returns the
|
||||
@@ -235,11 +258,11 @@ proc execAffectedRows*(db: TDbConn, query: TSqlQuery,
|
||||
result = parseBiggestInt($pqcmdTuples(res))
|
||||
pqclear(res)
|
||||
|
||||
proc close*(db: TDbConn) {.tags: [FDb].} =
|
||||
proc close*(db: DbConn) {.tags: [FDb].} =
|
||||
## closes the database connection.
|
||||
if db != nil: pqfinish(db)
|
||||
|
||||
proc open*(connection, user, password, database: string): TDbConn {.
|
||||
proc open*(connection, user, password, database: string): DbConn {.
|
||||
tags: [FDb].} =
|
||||
## opens a database connection. Raises `EDb` if the connection could not
|
||||
## be established.
|
||||
@@ -261,8 +284,8 @@ proc open*(connection, user, password, database: string): TDbConn {.
|
||||
result = pqsetdbLogin(nil, nil, nil, nil, database, user, password)
|
||||
if pqStatus(result) != CONNECTION_OK: dbError(result) # result = nil
|
||||
|
||||
proc setEncoding*(connection: TDbConn, encoding: string): bool {.
|
||||
proc setEncoding*(connection: DbConn, encoding: string): bool {.
|
||||
tags: [FDb].} =
|
||||
## sets the encoding of a database connection, returns true for
|
||||
## success, false for failure.
|
||||
return pqsetClientEncoding(connection, encoding) == 0
|
||||
return pqsetClientEncoding(connection, encoding) == 0
|
||||
|
||||
@@ -13,27 +13,30 @@
|
||||
import strutils, sqlite3
|
||||
|
||||
type
|
||||
TDbConn* = PSqlite3 ## encapsulates a database connection
|
||||
TRow* = seq[string] ## a row of a dataset. NULL database values will be
|
||||
DbConn* = PSqlite3 ## encapsulates a database connection
|
||||
Row* = seq[string] ## a row of a dataset. NULL database values will be
|
||||
## transformed always to the empty string.
|
||||
InstantRow* = Pstmt ## a handle that can be used to get a row's column
|
||||
## text on demand
|
||||
EDb* = object of IOError ## exception that is raised if a database error occurs
|
||||
|
||||
TSqlQuery* = distinct string ## an SQL query string
|
||||
SqlQuery* = distinct string ## an SQL query string
|
||||
|
||||
FDb* = object of IOEffect ## effect that denotes a database operation
|
||||
FReadDb* = object of FDb ## effect that denotes a read operation
|
||||
FWriteDb* = object of FDb ## effect that denotes a write operation
|
||||
{.deprecated: [TRow: Row, TSqlQuery: SqlQuery, TDbConn: DbConn].}
|
||||
|
||||
proc sql*(query: string): TSqlQuery {.noSideEffect, inline.} =
|
||||
## constructs a TSqlQuery from the string `query`. This is supposed to be
|
||||
proc sql*(query: string): SqlQuery {.noSideEffect, inline.} =
|
||||
## constructs a SqlQuery from the string `query`. This is supposed to be
|
||||
## used as a raw-string-literal modifier:
|
||||
## ``sql"update user set counter = counter + 1"``
|
||||
##
|
||||
## If assertions are turned off, it does nothing. If assertions are turned
|
||||
## on, later versions will check the string for valid syntax.
|
||||
result = TSqlQuery(query)
|
||||
result = SqlQuery(query)
|
||||
|
||||
proc dbError(db: TDbConn) {.noreturn.} =
|
||||
proc dbError(db: DbConn) {.noreturn.} =
|
||||
## raises an EDb exception.
|
||||
var e: ref EDb
|
||||
new(e)
|
||||
@@ -55,7 +58,7 @@ proc dbQuote(s: string): string =
|
||||
else: add(result, c)
|
||||
add(result, '\'')
|
||||
|
||||
proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
|
||||
proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
|
||||
result = ""
|
||||
var a = 0
|
||||
for c in items(string(formatstr)):
|
||||
@@ -65,7 +68,7 @@ proc dbFormat(formatstr: TSqlQuery, args: varargs[string]): string =
|
||||
else:
|
||||
add(result, c)
|
||||
|
||||
proc tryExec*(db: TDbConn, query: TSqlQuery,
|
||||
proc tryExec*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): bool {.tags: [FReadDb, FWriteDb].} =
|
||||
## tries to execute the query and returns true if successful, false otherwise.
|
||||
var q = dbFormat(query, args)
|
||||
@@ -74,29 +77,29 @@ proc tryExec*(db: TDbConn, query: TSqlQuery,
|
||||
if step(stmt) == SQLITE_DONE:
|
||||
result = finalize(stmt) == SQLITE_OK
|
||||
|
||||
proc exec*(db: TDbConn, query: TSqlQuery, args: varargs[string, `$`]) {.
|
||||
proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
|
||||
tags: [FReadDb, FWriteDb].} =
|
||||
## executes the query and raises EDB if not successful.
|
||||
if not tryExec(db, query, args): dbError(db)
|
||||
|
||||
proc newRow(L: int): TRow =
|
||||
proc newRow(L: int): Row =
|
||||
newSeq(result, L)
|
||||
for i in 0..L-1: result[i] = ""
|
||||
|
||||
proc setupQuery(db: TDbConn, query: TSqlQuery,
|
||||
proc setupQuery(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string]): Pstmt =
|
||||
var q = dbFormat(query, args)
|
||||
if prepare_v2(db, q, q.len.cint, result, nil) != SQLITE_OK: dbError(db)
|
||||
|
||||
proc setRow(stmt: Pstmt, r: var TRow, cols: cint) =
|
||||
proc setRow(stmt: Pstmt, r: var Row, cols: cint) =
|
||||
for col in 0..cols-1:
|
||||
setLen(r[col], column_bytes(stmt, col)) # set capacity
|
||||
setLen(r[col], 0)
|
||||
let x = column_text(stmt, col)
|
||||
if not isNil(x): add(r[col], x)
|
||||
|
||||
iterator fastRows*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): TRow {.tags: [FReadDb].} =
|
||||
iterator fastRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
|
||||
## executes the query and iterates over the result dataset. This is very
|
||||
## fast, but potenially dangerous: If the for-loop-body executes another
|
||||
## query, the results can be undefined. For Sqlite it is safe though.
|
||||
@@ -108,10 +111,28 @@ iterator fastRows*(db: TDbConn, query: TSqlQuery,
|
||||
yield result
|
||||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||
|
||||
proc getRow*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): TRow {.tags: [FReadDb].} =
|
||||
iterator instantRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): InstantRow
|
||||
{.tags: [FReadDb].} =
|
||||
## same as fastRows but returns a handle that can be used to get column text
|
||||
## on demand using []. Returned handle is valid only within interator body.
|
||||
var stmt = setupQuery(db, query, args)
|
||||
while step(stmt) == SQLITE_ROW:
|
||||
yield stmt
|
||||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||
|
||||
proc `[]`*(row: InstantRow, col: int32): string {.inline.} =
|
||||
## returns text for given column of the row
|
||||
$column_text(row, col)
|
||||
|
||||
proc len*(row: InstantRow): int32 {.inline.} =
|
||||
## returns number of columns in the row
|
||||
column_count(row)
|
||||
|
||||
proc getRow*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
|
||||
## retrieves a single row. If the query doesn't return any rows, this proc
|
||||
## will return a TRow with empty strings for each column.
|
||||
## will return a Row with empty strings for each column.
|
||||
var stmt = setupQuery(db, query, args)
|
||||
var L = (column_count(stmt))
|
||||
result = newRow(L)
|
||||
@@ -119,19 +140,19 @@ proc getRow*(db: TDbConn, query: TSqlQuery,
|
||||
setRow(stmt, result, L)
|
||||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||
|
||||
proc getAllRows*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): seq[TRow] {.tags: [FReadDb].} =
|
||||
proc getAllRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): seq[Row] {.tags: [FReadDb].} =
|
||||
## executes the query and returns the whole result dataset.
|
||||
result = @[]
|
||||
for r in fastRows(db, query, args):
|
||||
result.add(r)
|
||||
|
||||
iterator rows*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): TRow {.tags: [FReadDb].} =
|
||||
iterator rows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): Row {.tags: [FReadDb].} =
|
||||
## same as `FastRows`, but slower and safe.
|
||||
for r in fastRows(db, query, args): yield r
|
||||
|
||||
proc getValue*(db: TDbConn, query: TSqlQuery,
|
||||
proc getValue*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): string {.tags: [FReadDb].} =
|
||||
## executes the query and returns the first column of the first row of the
|
||||
## result dataset. Returns "" if the dataset contains no rows or the database
|
||||
@@ -148,7 +169,7 @@ proc getValue*(db: TDbConn, query: TSqlQuery,
|
||||
result = ""
|
||||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||
|
||||
proc tryInsertID*(db: TDbConn, query: TSqlQuery,
|
||||
proc tryInsertID*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64
|
||||
{.tags: [FWriteDb], raises: [].} =
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
@@ -162,7 +183,7 @@ proc tryInsertID*(db: TDbConn, query: TSqlQuery,
|
||||
if finalize(stmt) != SQLITE_OK:
|
||||
result = -1
|
||||
|
||||
proc insertID*(db: TDbConn, query: TSqlQuery,
|
||||
proc insertID*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
## generated ID for the row. For Postgre this adds
|
||||
@@ -171,7 +192,7 @@ proc insertID*(db: TDbConn, query: TSqlQuery,
|
||||
result = tryInsertID(db, query, args)
|
||||
if result < 0: dbError(db)
|
||||
|
||||
proc execAffectedRows*(db: TDbConn, query: TSqlQuery,
|
||||
proc execAffectedRows*(db: DbConn, query: SqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.
|
||||
tags: [FReadDb, FWriteDb].} =
|
||||
## executes the query (typically "UPDATE") and returns the
|
||||
@@ -179,21 +200,21 @@ proc execAffectedRows*(db: TDbConn, query: TSqlQuery,
|
||||
exec(db, query, args)
|
||||
result = changes(db)
|
||||
|
||||
proc close*(db: TDbConn) {.tags: [FDb].} =
|
||||
proc close*(db: DbConn) {.tags: [FDb].} =
|
||||
## closes the database connection.
|
||||
if sqlite3.close(db) != SQLITE_OK: dbError(db)
|
||||
|
||||
proc open*(connection, user, password, database: string): TDbConn {.
|
||||
proc open*(connection, user, password, database: string): DbConn {.
|
||||
tags: [FDb].} =
|
||||
## opens a database connection. Raises `EDb` if the connection could not
|
||||
## be established. Only the ``connection`` parameter is used for ``sqlite``.
|
||||
var db: TDbConn
|
||||
var db: DbConn
|
||||
if sqlite3.open(connection, db) == SQLITE_OK:
|
||||
result = db
|
||||
else:
|
||||
dbError(db)
|
||||
|
||||
proc setEncoding*(connection: TDbConn, encoding: string): bool {.
|
||||
proc setEncoding*(connection: DbConn, encoding: string): bool {.
|
||||
tags: [FDb].} =
|
||||
## sets the encoding of a database connection, returns true for
|
||||
## success, false for failure.
|
||||
@@ -215,5 +236,7 @@ when not defined(testing) and isMainModule:
|
||||
#db.query("insert into tbl1 values('goodbye', 20)")
|
||||
for r in db.rows(sql"select * from tbl1", []):
|
||||
echo(r[0], r[1])
|
||||
|
||||
for r in db.instantRows(sql"select * from tbl1", []):
|
||||
echo(r[0], r[1])
|
||||
|
||||
db_sqlite.close(db)
|
||||
|
||||
@@ -17,23 +17,24 @@ from sdl import PSurface # Bug
|
||||
from sdl_ttf import openFont, closeFont
|
||||
|
||||
type
|
||||
TRect* = tuple[x, y, width, height: int]
|
||||
TPoint* = tuple[x, y: int]
|
||||
Rect* = tuple[x, y, width, height: int]
|
||||
Point* = tuple[x, y: int]
|
||||
|
||||
PSurface* = ref TSurface ## a surface to draw onto
|
||||
TSurface* {.pure, final.} = object
|
||||
PSurface* = ref Surface ## a surface to draw onto
|
||||
Surface* {.pure, final.} = object
|
||||
w*, h*: Natural
|
||||
s*: sdl.PSurface
|
||||
|
||||
EGraphics* = object of IOError
|
||||
|
||||
TFont {.pure, final.} = object
|
||||
Font {.pure, final.} = object
|
||||
f: sdl_ttf.PFont
|
||||
color: sdl.TColor
|
||||
PFont* = ref TFont ## represents a font
|
||||
color: sdl.Color
|
||||
PFont* = ref Font ## represents a font
|
||||
{.deprecated: [TSurface: Surface, TFont: Font, TRect: Rect, TPoint: Point].}
|
||||
|
||||
proc toSdlColor*(c: Color): sdl.TColor =
|
||||
## Convert colors.TColor to sdl.TColor
|
||||
proc toSdlColor*(c: Color): sdl.Color =
|
||||
## Convert colors.Color to sdl.Color
|
||||
var x = c.extractRGB
|
||||
result.r = x.r and 0xff
|
||||
result.g = x.g and 0xff
|
||||
@@ -45,8 +46,8 @@ proc createSdlColor*(sur: PSurface, c: Color, alpha: int = 0): int32 =
|
||||
return sdl.mapRGBA(sur.s.format, x.r and 0xff, x.g and 0xff,
|
||||
x.b and 0xff, alpha and 0xff)
|
||||
|
||||
proc toSdlRect*(r: TRect): sdl.TRect =
|
||||
## Convert ``graphics.TRect`` to ``sdl.TRect``.
|
||||
proc toSdlRect*(r: Rect): sdl.Rect =
|
||||
## Convert ``graphics.Rect`` to ``sdl.Rect``.
|
||||
result.x = int16(r.x)
|
||||
result.y = int16(r.y)
|
||||
result.w = uint16(r.width)
|
||||
@@ -103,8 +104,9 @@ proc writeToBMP*(sur: PSurface, filename: string) =
|
||||
raise newException(IOError, "cannot write: " & filename)
|
||||
|
||||
type
|
||||
TPixels = array[0..1000_000-1, int32]
|
||||
PPixels = ptr TPixels
|
||||
Pixels = array[0..1000_000-1, int32]
|
||||
PPixels = ptr Pixels
|
||||
{.deprecated: [TPixels: Pixels].}
|
||||
|
||||
template setPix(video, pitch, x, y, col: expr): stmt =
|
||||
video[y * pitch + x] = int32(col)
|
||||
@@ -128,7 +130,7 @@ proc setPixel(sur: PSurface, x, y: Natural, col: colors.Color) {.inline.} =
|
||||
#pixs[y * (sur.s.pitch div colSize) + x] = int(col)
|
||||
setPix(pixs, sur.s.pitch.int div ColSize, x, y, col)
|
||||
|
||||
proc `[]`*(sur: PSurface, p: TPoint): Color =
|
||||
proc `[]`*(sur: PSurface, p: Point): Color =
|
||||
## get pixel at position `p`. No range checking is done!
|
||||
result = getPixel(sur, p.x, p.y)
|
||||
|
||||
@@ -136,7 +138,7 @@ proc `[]`*(sur: PSurface, x, y: int): Color =
|
||||
## get pixel at position ``(x, y)``. No range checking is done!
|
||||
result = getPixel(sur, x, y)
|
||||
|
||||
proc `[]=`*(sur: PSurface, p: TPoint, col: Color) =
|
||||
proc `[]=`*(sur: PSurface, p: Point, col: Color) =
|
||||
## set the pixel at position `p`. No range checking is done!
|
||||
setPixel(sur, p.x, p.y, col)
|
||||
|
||||
@@ -144,10 +146,10 @@ proc `[]=`*(sur: PSurface, x, y: int, col: Color) =
|
||||
## set the pixel at position ``(x, y)``. No range checking is done!
|
||||
setPixel(sur, x, y, col)
|
||||
|
||||
proc blit*(destSurf: PSurface, destRect: TRect, srcSurf: PSurface,
|
||||
srcRect: TRect) =
|
||||
proc blit*(destSurf: PSurface, destRect: Rect, srcSurf: PSurface,
|
||||
srcRect: Rect) =
|
||||
## Copies ``srcSurf`` into ``destSurf``
|
||||
var destTRect, srcTRect: sdl.TRect
|
||||
var destTRect, srcTRect: sdl.Rect
|
||||
|
||||
destTRect.x = int16(destRect.x)
|
||||
destTRect.y = int16(destRect.y)
|
||||
@@ -168,7 +170,7 @@ proc textBounds*(text: string, font = defaultFont): tuple[width, height: int] =
|
||||
result.width = int(w)
|
||||
result.height = int(h)
|
||||
|
||||
proc drawText*(sur: PSurface, p: TPoint, text: string, font = defaultFont) =
|
||||
proc drawText*(sur: PSurface, p: Point, text: string, font = defaultFont) =
|
||||
## Draws text with a transparent background, at location ``p`` with the given
|
||||
## font.
|
||||
var textSur: PSurface # This surface will have the text drawn on it
|
||||
@@ -179,7 +181,7 @@ proc drawText*(sur: PSurface, p: TPoint, text: string, font = defaultFont) =
|
||||
# Merge the text surface with sur
|
||||
sur.blit((p.x, p.y, sur.w, sur.h), textSur, (0, 0, sur.w, sur.h))
|
||||
|
||||
proc drawText*(sur: PSurface, p: TPoint, text: string,
|
||||
proc drawText*(sur: PSurface, p: Point, text: string,
|
||||
bg: Color, font = defaultFont) =
|
||||
## Draws text, at location ``p`` with font ``font``. ``bg``
|
||||
## is the background color.
|
||||
@@ -189,7 +191,7 @@ proc drawText*(sur: PSurface, p: TPoint, text: string,
|
||||
# Merge the text surface with sur
|
||||
sur.blit((p.x, p.y, sur.w, sur.h), textSur, (0, 0, sur.w, sur.h))
|
||||
|
||||
proc drawCircle*(sur: PSurface, p: TPoint, 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
|
||||
## onto the surface `sur`.
|
||||
var video = cast[PPixels](sur.s.pixels)
|
||||
@@ -229,7 +231,7 @@ proc `>-<`(val: int, s: PSurface): int {.inline.} =
|
||||
proc `>|<`(val: int, s: PSurface): int {.inline.} =
|
||||
return if val < 0: 0 elif val >= s.h: s.h-1 else: val
|
||||
|
||||
proc drawLine*(sur: PSurface, p1, p2: TPoint, color: Color) =
|
||||
proc drawLine*(sur: PSurface, p1, p2: Point, color: Color) =
|
||||
## draws a line between the two points `p1` and `p2` with the given color
|
||||
## onto the surface `sur`.
|
||||
var stepx, stepy: int = 0
|
||||
@@ -291,7 +293,7 @@ proc drawVerLine*(sur: PSurface, x, y, h: Natural, color: Color) =
|
||||
for i in 0 .. min(sur.s.h-y, h)-1:
|
||||
setPix(video, pitch, x, y + i, color)
|
||||
|
||||
proc fillCircle*(s: PSurface, p: TPoint, r: Natural, color: Color) =
|
||||
proc fillCircle*(s: PSurface, p: Point, r: Natural, color: Color) =
|
||||
## draws a circle with center `p` and radius `r` with the given color
|
||||
## onto the surface `sur` and fills it.
|
||||
var a = 1 - r
|
||||
@@ -319,7 +321,7 @@ proc fillCircle*(s: PSurface, p: TPoint, r: Natural, color: Color) =
|
||||
drawVerLine(s, x - py - 1, y - px, px, color)
|
||||
px = px + 1
|
||||
|
||||
proc drawRect*(sur: PSurface, r: TRect, color: Color) =
|
||||
proc drawRect*(sur: PSurface, r: Rect, color: Color) =
|
||||
## draws a rectangle.
|
||||
var video = cast[PPixels](sur.s.pixels)
|
||||
var pitch = sur.s.pitch.int div ColSize
|
||||
@@ -337,7 +339,7 @@ proc drawRect*(sur: PSurface, r: TRect, color: Color) =
|
||||
setPix(video, pitch, r.x, r.y + i, color)
|
||||
setPix(video, pitch, r.x + minW - 1, r.y + i, color) # Draw right side
|
||||
|
||||
proc fillRect*(sur: PSurface, r: TRect, col: Color) =
|
||||
proc fillRect*(sur: PSurface, r: Rect, col: Color) =
|
||||
## Fills a rectangle using sdl's ``FillRect`` function.
|
||||
var rect = toSdlRect(r)
|
||||
if sdl.fillRect(sur.s, addr(rect), sur.createSdlColor(col)) == -1:
|
||||
@@ -424,7 +426,7 @@ template cround(x: expr): expr = ipart(x + 0.5)
|
||||
template fpart(x: expr): expr = x - ipart(x)
|
||||
template rfpart(x: expr): expr = 1.0 - fpart(x)
|
||||
|
||||
proc drawLineAA*(sur: PSurface, p1, p2: TPoint, color: Color) =
|
||||
proc drawLineAA*(sur: PSurface, p1, p2: Point, color: Color) =
|
||||
## Draws a anti-aliased line from ``p1`` to ``p2``, using Xiaolin Wu's
|
||||
## line algorithm
|
||||
var (x1, x2, y1, y2) = (p1.x.toFloat(), p2.x.toFloat(),
|
||||
@@ -490,9 +492,9 @@ proc fillSurface*(sur: PSurface, color: Color) =
|
||||
template withEvents*(surf: PSurface, event: expr, actions: stmt): stmt {.
|
||||
immediate.} =
|
||||
## Simple template which creates an event loop. ``Event`` is the name of the
|
||||
## variable containing the TEvent object.
|
||||
## variable containing the Event object.
|
||||
while true:
|
||||
var event: sdl.TEvent
|
||||
var event: sdl.Event
|
||||
if sdl.waitEvent(addr(event)) == 1:
|
||||
actions
|
||||
|
||||
|
||||
682
lib/impure/nre.nim
Normal file
682
lib/impure/nre.nim
Normal file
@@ -0,0 +1,682 @@
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2015 Nim Contributers
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
|
||||
from pcre import nil
|
||||
import nre.private.util
|
||||
import tables
|
||||
import unsigned
|
||||
from strutils import toLower, `%`
|
||||
from math import ceil
|
||||
import options
|
||||
from unicode import runeLenAt
|
||||
|
||||
|
||||
## What is NRE?
|
||||
## ============
|
||||
##
|
||||
## A regular expression library for Nim using PCRE to do the hard work.
|
||||
##
|
||||
## Licencing
|
||||
## ---------
|
||||
##
|
||||
## PCRE has some additional terms that you must comply with if you use this module.::
|
||||
##
|
||||
## > Copyright (c) 1997-2001 University of Cambridge
|
||||
## >
|
||||
## > Permission is granted to anyone to use this software for any purpose on any
|
||||
## > computer system, and to redistribute it freely, subject to the following
|
||||
## > restrictions:
|
||||
## >
|
||||
## > 1. This software is distributed in the hope that it will be useful,
|
||||
## > but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## > MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
## >
|
||||
## > 2. The origin of this software must not be misrepresented, either by
|
||||
## > explicit claim or by omission. In practice, this means that if you use
|
||||
## > PCRE in software that you distribute to others, commercially or
|
||||
## > otherwise, you must put a sentence like this
|
||||
## >
|
||||
## > Regular expression support is provided by the PCRE library package,
|
||||
## > which is open source software, written by Philip Hazel, and copyright
|
||||
## > by the University of Cambridge, England.
|
||||
## >
|
||||
## > somewhere reasonably visible in your documentation and in any relevant
|
||||
## > files or online help data or similar. A reference to the ftp site for
|
||||
## > the source, that is, to
|
||||
## >
|
||||
## > ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/
|
||||
## >
|
||||
## > should also be given in the documentation. However, this condition is not
|
||||
## > intended to apply to whole chains of software. If package A includes PCRE,
|
||||
## > it must acknowledge it, but if package B is software that includes package
|
||||
## > A, the condition is not imposed on package B (unless it uses PCRE
|
||||
## > independently).
|
||||
## >
|
||||
## > 3. Altered versions must be plainly marked as such, and must not be
|
||||
## > misrepresented as being the original software.
|
||||
## >
|
||||
## > 4. If PCRE is embedded in any software that is released under the GNU
|
||||
## > General Purpose Licence (GPL), or Lesser General Purpose Licence (LGPL),
|
||||
## > then the terms of that licence shall supersede any condition above with
|
||||
## > which it is incompatible.
|
||||
|
||||
|
||||
# Type definitions {{{
|
||||
type
|
||||
Regex* = ref object
|
||||
## Represents the pattern that things are matched against, constructed with
|
||||
## ``re(string)``. Examples: ``re"foo"``, ``re(r"(*ANYCRLF)(?x)foo #
|
||||
## comment".``
|
||||
##
|
||||
## ``pattern: string``
|
||||
## the string that was used to create the pattern.
|
||||
##
|
||||
## ``captureCount: int``
|
||||
## the number of captures that the pattern has.
|
||||
##
|
||||
## ``captureNameId: Table[string, int]``
|
||||
## a table from the capture names to their numeric id.
|
||||
##
|
||||
##
|
||||
## Options
|
||||
## .......
|
||||
##
|
||||
## The following options may appear anywhere in the pattern, and they affect
|
||||
## the rest of it.
|
||||
##
|
||||
## - ``(?i)`` - case insensitive
|
||||
## - ``(?m)`` - multi-line: ``^`` and ``$`` match the beginning and end of
|
||||
## lines, not of the subject string
|
||||
## - ``(?s)`` - ``.`` also matches newline (*dotall*)
|
||||
## - ``(?U)`` - expressions are not greedy by default. ``?`` can be added
|
||||
## to a qualifier to make it greedy
|
||||
## - ``(?x)`` - whitespace and comments (``#``) are ignored (*extended*)
|
||||
## - ``(?X)`` - character escapes without special meaning (``\w`` vs.
|
||||
## ``\a``) are errors (*extra*)
|
||||
##
|
||||
## One or a combination of these options may appear only at the beginning
|
||||
## of the pattern:
|
||||
##
|
||||
## - ``(*UTF8)`` - treat both the pattern and subject as UTF-8
|
||||
## - ``(*UCP)`` - Unicode character properties; ``\w`` matches ``я``
|
||||
## - ``(*U)`` - a combination of the two options above
|
||||
## - ``(*FIRSTLINE*)`` - fails if there is not a match on the first line
|
||||
## - ``(*NO_AUTO_CAPTURE)`` - turn off auto-capture for groups;
|
||||
## ``(?<name>...)`` can be used to capture
|
||||
## - ``(*CR)`` - newlines are separated by ``\r``
|
||||
## - ``(*LF)`` - newlines are separated by ``\n`` (UNIX default)
|
||||
## - ``(*CRLF)`` - newlines are separated by ``\r\n`` (Windows default)
|
||||
## - ``(*ANYCRLF)`` - newlines are separated by any of the above
|
||||
## - ``(*ANY)`` - newlines are separated by any of the above and Unicode
|
||||
## newlines:
|
||||
##
|
||||
## single characters VT (vertical tab, U+000B), FF (form feed, U+000C),
|
||||
## NEL (next line, U+0085), LS (line separator, U+2028), and PS
|
||||
## (paragraph separator, U+2029). For the 8-bit library, the last two
|
||||
## are recognized only in UTF-8 mode.
|
||||
## — man pcre
|
||||
##
|
||||
## - ``(*JAVASCRIPT_COMPAT)`` - JavaScript compatibility
|
||||
## - ``(*NO_STUDY)`` - turn off studying; study is enabled by default
|
||||
##
|
||||
## For more details on the leading option groups, see the `Option
|
||||
## Setting <http://man7.org/linux/man-pages/man3/pcresyntax.3.html#OPTION_SETTING>`__
|
||||
## and the `Newline
|
||||
## Convention <http://man7.org/linux/man-pages/man3/pcresyntax.3.html#NEWLINE_CONVENTION>`__
|
||||
## sections of the `PCRE syntax
|
||||
## manual <http://man7.org/linux/man-pages/man3/pcresyntax.3.html>`__.
|
||||
pattern*: string ## not nil
|
||||
pcreObj: ptr pcre.Pcre ## not nil
|
||||
pcreExtra: ptr pcre.ExtraData ## nil
|
||||
|
||||
captureNameToId: Table[string, int]
|
||||
|
||||
RegexMatch* = object
|
||||
## Usually seen as Option[RegexMatch], it represents the result of an
|
||||
## execution. On failure, it is none, on success, it is some.
|
||||
##
|
||||
## ``pattern: Regex``
|
||||
## the pattern that is being matched
|
||||
##
|
||||
## ``str: string``
|
||||
## the string that was matched against
|
||||
##
|
||||
## ``captures[]: string``
|
||||
## the string value of whatever was captured at that id. If the value
|
||||
## is invalid, then behavior is undefined. If the id is ``-1``, then
|
||||
## the whole match is returned. If the given capture was not matched,
|
||||
## ``nil`` is returned.
|
||||
##
|
||||
## - ``"abc".match(re"(\w)").captures[0] == "a"``
|
||||
## - ``"abc".match(re"(?<letter>\w)").captures["letter"] == "a"``
|
||||
## - ``"abc".match(re"(\w)\w").captures[-1] == "ab"``
|
||||
##
|
||||
## ``captureBounds[]: Option[Slice[int]]``
|
||||
## gets the bounds of the given capture according to the same rules as
|
||||
## the above. If the capture is not filled, then ``None`` is returned.
|
||||
## The bounds are both inclusive.
|
||||
##
|
||||
## - ``"abc".match(re"(\w)").captureBounds[0] == 0 .. 0``
|
||||
## - ``"abc".match(re"").captureBounds[-1] == 0 .. -1``
|
||||
## - ``"abc".match(re"abc").captureBounds[-1] == 0 .. 2``
|
||||
##
|
||||
## ``match: string``
|
||||
## the full text of the match.
|
||||
##
|
||||
## ``matchBounds: Slice[int]``
|
||||
## the bounds of the match, as in ``captureBounds[]``
|
||||
##
|
||||
## ``(captureBounds|captures).toTable``
|
||||
## returns a table with each named capture as a key.
|
||||
##
|
||||
## ``(captureBounds|captures).toSeq``
|
||||
## returns all the captures by their number.
|
||||
##
|
||||
## ``$: string``
|
||||
## same as ``match``
|
||||
pattern*: Regex ## The regex doing the matching.
|
||||
## Not nil.
|
||||
str*: string ## The string that was matched against.
|
||||
## Not nil.
|
||||
pcreMatchBounds: seq[Slice[cint]] ## First item is the bounds of the match
|
||||
## Other items are the captures
|
||||
## `a` is inclusive start, `b` is exclusive end
|
||||
|
||||
Captures* = distinct RegexMatch
|
||||
CaptureBounds* = distinct RegexMatch
|
||||
|
||||
RegexError* = ref object of Exception
|
||||
|
||||
RegexInternalError* = ref object of RegexError
|
||||
## Internal error in the module, this probably means that there is a bug
|
||||
|
||||
InvalidUnicodeError* = ref object of RegexError
|
||||
## Thrown when matching fails due to invalid unicode in strings
|
||||
pos*: int ## the location of the invalid unicode in bytes
|
||||
|
||||
SyntaxError* = ref object of RegexError
|
||||
## Thrown when there is a syntax error in the
|
||||
## regular expression string passed in
|
||||
pos*: int ## the location of the syntax error in bytes
|
||||
pattern*: string ## the pattern that caused the problem
|
||||
|
||||
StudyError* = ref object of RegexError
|
||||
## Thrown when studying the regular expression failes
|
||||
## for whatever reason. The message contains the error
|
||||
## code.
|
||||
# }}}
|
||||
|
||||
proc getinfo[T](pattern: Regex, opt: cint): T =
|
||||
let retcode = pcre.fullinfo(pattern.pcreObj, pattern.pcreExtra, opt, addr result)
|
||||
|
||||
if retcode < 0:
|
||||
# XXX Error message that doesn't expose implementation details
|
||||
raise newException(FieldError, "Invalid getinfo for $1, errno $2" % [$opt, $retcode])
|
||||
|
||||
# Regex accessors {{{
|
||||
proc captureCount*(pattern: Regex): int =
|
||||
return getinfo[cint](pattern, pcre.INFO_CAPTURECOUNT)
|
||||
|
||||
proc captureNameId*(pattern: Regex): Table[string, int] =
|
||||
return pattern.captureNameToId
|
||||
|
||||
proc matchesCrLf(pattern: Regex): bool =
|
||||
let flags = uint32(getinfo[culong](pattern, pcre.INFO_OPTIONS))
|
||||
let newlineFlags = flags and (pcre.NEWLINE_CRLF or
|
||||
pcre.NEWLINE_ANY or
|
||||
pcre.NEWLINE_ANYCRLF)
|
||||
if newLineFlags > 0u32:
|
||||
return true
|
||||
|
||||
# get flags from build config
|
||||
var confFlags: cint
|
||||
if pcre.config(pcre.CONFIG_NEWLINE, addr confFlags) != 0:
|
||||
assert(false, "CONFIG_NEWLINE apparently got screwed up")
|
||||
|
||||
case confFlags
|
||||
of 13: return false
|
||||
of 10: return false
|
||||
of (13 shl 8) or 10: return true
|
||||
of -2: return true
|
||||
of -1: return true
|
||||
else: return false
|
||||
# }}}
|
||||
|
||||
# Capture accessors {{{
|
||||
proc captureBounds*(pattern: RegexMatch): CaptureBounds = return CaptureBounds(pattern)
|
||||
|
||||
proc captures*(pattern: RegexMatch): Captures = return Captures(pattern)
|
||||
|
||||
proc `[]`*(pattern: CaptureBounds, i: int): Option[Slice[int]] =
|
||||
let pattern = RegexMatch(pattern)
|
||||
if pattern.pcreMatchBounds[i + 1].a != -1:
|
||||
let bounds = pattern.pcreMatchBounds[i + 1]
|
||||
return some(int(bounds.a) .. int(bounds.b-1))
|
||||
else:
|
||||
return none(Slice[int])
|
||||
|
||||
proc `[]`*(pattern: Captures, i: int): string =
|
||||
let pattern = RegexMatch(pattern)
|
||||
let bounds = pattern.captureBounds[i]
|
||||
|
||||
if bounds.isSome:
|
||||
let bounds = bounds.get
|
||||
return pattern.str.substr(bounds.a, bounds.b)
|
||||
else:
|
||||
return nil
|
||||
|
||||
proc match*(pattern: RegexMatch): string =
|
||||
return pattern.captures[-1]
|
||||
|
||||
proc matchBounds*(pattern: RegexMatch): Slice[int] =
|
||||
return pattern.captureBounds[-1].get
|
||||
|
||||
proc `[]`*(pattern: CaptureBounds, name: string): Option[Slice[int]] =
|
||||
let pattern = RegexMatch(pattern)
|
||||
return pattern.captureBounds[pattern.pattern.captureNameToId.fget(name)]
|
||||
|
||||
proc `[]`*(pattern: Captures, name: string): string =
|
||||
let pattern = RegexMatch(pattern)
|
||||
return pattern.captures[pattern.pattern.captureNameToId.fget(name)]
|
||||
|
||||
template toTableImpl(cond: bool): stmt {.immediate, dirty.} =
|
||||
for key in RegexMatch(pattern).pattern.captureNameId.keys:
|
||||
let nextVal = pattern[key]
|
||||
if cond:
|
||||
result[key] = default
|
||||
else:
|
||||
result[key] = nextVal
|
||||
|
||||
proc toTable*(pattern: Captures, default: string = nil): Table[string, string] =
|
||||
result = initTable[string, string]()
|
||||
toTableImpl(nextVal == nil)
|
||||
|
||||
proc toTable*(pattern: CaptureBounds, default = none(Slice[int])):
|
||||
Table[string, Option[Slice[int]]] =
|
||||
result = initTable[string, Option[Slice[int]]]()
|
||||
toTableImpl(nextVal.isNone)
|
||||
|
||||
template itemsImpl(cond: bool): stmt {.immediate, dirty.} =
|
||||
for i in 0 .. <RegexMatch(pattern).pattern.captureCount:
|
||||
let nextVal = pattern[i]
|
||||
# done in this roundabout way to avoid multiple yields (potential code
|
||||
# bloat)
|
||||
let nextYieldVal = if cond: default else: nextVal
|
||||
yield nextYieldVal
|
||||
|
||||
|
||||
iterator items*(pattern: CaptureBounds, default = none(Slice[int])): Option[Slice[int]] =
|
||||
itemsImpl(nextVal.isNone)
|
||||
|
||||
iterator items*(pattern: Captures, default: string = nil): string =
|
||||
itemsImpl(nextVal == nil)
|
||||
|
||||
proc toSeq*(pattern: CaptureBounds, default = none(Slice[int])): seq[Option[Slice[int]]] =
|
||||
accumulateResult(pattern.items(default))
|
||||
|
||||
proc toSeq*(pattern: Captures, default: string = nil): seq[string] =
|
||||
accumulateResult(pattern.items(default))
|
||||
|
||||
proc `$`*(pattern: RegexMatch): string =
|
||||
return pattern.captures[-1]
|
||||
|
||||
proc `==`*(a, b: Regex): bool =
|
||||
if not a.isNil and not b.isNil:
|
||||
return a.pattern == b.pattern and
|
||||
a.pcreObj == b.pcreObj and
|
||||
a.pcreExtra == b.pcreExtra
|
||||
else:
|
||||
return system.`==`(a, b)
|
||||
|
||||
proc `==`*(a, b: RegexMatch): bool =
|
||||
return a.pattern == b.pattern and
|
||||
a.str == b.str
|
||||
# }}}
|
||||
|
||||
# Creation & Destruction {{{
|
||||
# PCRE Options {{{
|
||||
const PcreOptions = {
|
||||
"NEVER_UTF": pcre.NEVER_UTF,
|
||||
"ANCHORED": pcre.ANCHORED,
|
||||
"DOLLAR_ENDONLY": pcre.DOLLAR_ENDONLY,
|
||||
"FIRSTLINE": pcre.FIRSTLINE,
|
||||
"NO_AUTO_CAPTURE": pcre.NO_AUTO_CAPTURE,
|
||||
"JAVASCRIPT_COMPAT": pcre.JAVASCRIPT_COMPAT,
|
||||
"U": pcre.UTF8 or pcre.UCP
|
||||
}.toTable
|
||||
|
||||
# Options that are supported inside regular expressions themselves
|
||||
const SkipOptions = [
|
||||
"LIMIT_MATCH=", "LIMIT_RECURSION=", "NO_AUTO_POSSESS", "NO_START_OPT",
|
||||
"UTF8", "UTF16", "UTF32", "UTF", "UCP",
|
||||
"CR", "LF", "CRLF", "ANYCRLF", "ANY", "BSR_ANYCRLF", "BSR_UNICODE"
|
||||
]
|
||||
|
||||
proc extractOptions(pattern: string): tuple[pattern: string, flags: int, study: bool] =
|
||||
result = ("", 0, true)
|
||||
|
||||
var optionStart = 0
|
||||
var equals = false
|
||||
for i, c in pattern:
|
||||
if optionStart == i:
|
||||
if c != '(':
|
||||
break
|
||||
optionStart = i
|
||||
|
||||
elif optionStart == i-1:
|
||||
if c != '*':
|
||||
break
|
||||
|
||||
elif c == ')':
|
||||
let name = pattern[optionStart+2 .. i-1]
|
||||
if equals or name in SkipOptions:
|
||||
result.pattern.add pattern[optionStart .. i]
|
||||
elif PcreOptions.hasKey name:
|
||||
result.flags = result.flags or PcreOptions[name]
|
||||
elif name == "NO_STUDY":
|
||||
result.study = false
|
||||
else:
|
||||
break
|
||||
optionStart = i+1
|
||||
equals = false
|
||||
|
||||
elif not equals:
|
||||
if c == '=':
|
||||
equals = true
|
||||
if pattern[optionStart+2 .. i] notin SkipOptions:
|
||||
break
|
||||
elif c notin {'A'..'Z', '0'..'9', '_'}:
|
||||
break
|
||||
|
||||
result.pattern.add pattern[optionStart .. pattern.high]
|
||||
|
||||
# }}}
|
||||
|
||||
type UncheckedArray {.unchecked.}[T] = array[0 .. 0, T]
|
||||
|
||||
proc destroyRegex(pattern: Regex) =
|
||||
pcre.free_substring(cast[cstring](pattern.pcreObj))
|
||||
pattern.pcreObj = nil
|
||||
if pattern.pcreExtra != nil:
|
||||
pcre.free_study(pattern.pcreExtra)
|
||||
|
||||
proc getNameToNumberTable(pattern: Regex): Table[string, int] =
|
||||
let entryCount = getinfo[cint](pattern, pcre.INFO_NAMECOUNT)
|
||||
let entrySize = getinfo[cint](pattern, pcre.INFO_NAMEENTRYSIZE)
|
||||
let table = cast[ptr UncheckedArray[uint8]](
|
||||
getinfo[int](pattern, pcre.INFO_NAMETABLE))
|
||||
|
||||
result = initTable[string, int]()
|
||||
|
||||
for i in 0 .. <entryCount:
|
||||
let pos = i * entrySize
|
||||
let num = (int(table[pos]) shl 8) or int(table[pos + 1]) - 1
|
||||
var name = ""
|
||||
|
||||
var idx = 2
|
||||
while table[pos + idx] != 0:
|
||||
name.add(char(table[pos + idx]))
|
||||
idx += 1
|
||||
|
||||
result[name] = num
|
||||
|
||||
proc initRegex(pattern: string, flags: int, study = true): Regex =
|
||||
new(result, destroyRegex)
|
||||
result.pattern = pattern
|
||||
|
||||
var errorMsg: cstring
|
||||
var errOffset: cint
|
||||
|
||||
result.pcreObj = pcre.compile(cstring(pattern),
|
||||
# better hope int is at least 4 bytes..
|
||||
cint(flags), addr errorMsg,
|
||||
addr errOffset, nil)
|
||||
if result.pcreObj == nil:
|
||||
# failed to compile
|
||||
raise SyntaxError(msg: $errorMsg, pos: errOffset, pattern: pattern)
|
||||
|
||||
if study:
|
||||
# XXX investigate JIT
|
||||
result.pcreExtra = pcre.study(result.pcreObj, 0x0, addr errorMsg)
|
||||
if errorMsg != nil:
|
||||
raise StudyError(msg: $errorMsg)
|
||||
|
||||
result.captureNameToId = result.getNameToNumberTable()
|
||||
|
||||
proc re*(pattern: string): Regex =
|
||||
let (pattern, flags, study) = extractOptions(pattern)
|
||||
initRegex(pattern, flags, study)
|
||||
# }}}
|
||||
|
||||
# Operations {{{
|
||||
proc matchImpl(str: string, pattern: Regex, start, endpos: int, flags: int): Option[RegexMatch] =
|
||||
var myResult = RegexMatch(pattern : pattern, str : str)
|
||||
# See PCRE man pages.
|
||||
# 2x capture count to make room for start-end pairs
|
||||
# 1x capture count as slack space for PCRE
|
||||
let vecsize = (pattern.captureCount() + 1) * 3
|
||||
# div 2 because each element is 2 cints long
|
||||
myResult.pcreMatchBounds = newSeq[Slice[cint]](ceil(vecsize / 2).int)
|
||||
myResult.pcreMatchBounds.setLen(vecsize div 3)
|
||||
|
||||
let strlen = if endpos == int.high: str.len else: endpos+1
|
||||
doAssert(strlen <= str.len) # don't want buffer overflows
|
||||
|
||||
let execRet = pcre.exec(pattern.pcreObj,
|
||||
pattern.pcreExtra,
|
||||
cstring(str),
|
||||
cint(strlen),
|
||||
cint(start),
|
||||
cint(flags),
|
||||
cast[ptr cint](addr myResult.pcreMatchBounds[0]),
|
||||
cint(vecsize))
|
||||
if execRet >= 0:
|
||||
return some(myResult)
|
||||
|
||||
case execRet:
|
||||
of pcre.ERROR_NOMATCH:
|
||||
return none(RegexMatch)
|
||||
of pcre.ERROR_NULL:
|
||||
raise newException(AccessViolationError, "Expected non-null parameters")
|
||||
of pcre.ERROR_BADOPTION:
|
||||
raise RegexInternalError(msg : "Unknown pattern flag. Either a bug or " &
|
||||
"outdated PCRE.")
|
||||
of pcre.ERROR_BADUTF8, pcre.ERROR_SHORTUTF8, pcre.ERROR_BADUTF8_OFFSET:
|
||||
raise InvalidUnicodeError(msg : "Invalid unicode byte sequence",
|
||||
pos : myResult.pcreMatchBounds[0].a)
|
||||
else:
|
||||
raise RegexInternalError(msg : "Unknown internal error: " & $execRet)
|
||||
|
||||
proc match*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[RegexMatch] =
|
||||
## Like ```find(...)`` <#proc-find>`__, but anchored to the start of the
|
||||
## string. This means that ``"foo".match(re"f") == true``, but
|
||||
## ``"foo".match(re"o") == false``.
|
||||
return str.matchImpl(pattern, start, endpos, pcre.ANCHORED)
|
||||
|
||||
iterator findIter*(str: string, pattern: Regex, start = 0, endpos = int.high): RegexMatch =
|
||||
## Works the same as ```find(...)`` <#proc-find>`__, but finds every
|
||||
## non-overlapping match. ``"2222".find(re"22")`` is ``"22", "22"``, not
|
||||
## ``"22", "22", "22"``.
|
||||
##
|
||||
## Arguments are the same as ```find(...)`` <#proc-find>`__
|
||||
##
|
||||
## Variants:
|
||||
##
|
||||
## - ``proc findAll(...)`` returns a ``seq[string]``
|
||||
# see pcredemo for explaination
|
||||
let matchesCrLf = pattern.matchesCrLf()
|
||||
let unicode = uint32(getinfo[culong](pattern, pcre.INFO_OPTIONS) and
|
||||
pcre.UTF8) > 0u32
|
||||
let strlen = if endpos == int.high: str.len else: endpos+1
|
||||
|
||||
var offset = start
|
||||
var match: Option[RegexMatch]
|
||||
while true:
|
||||
var flags = 0
|
||||
|
||||
if match.isSome and
|
||||
match.get.matchBounds.a > match.get.matchBounds.b:
|
||||
# 0-len match
|
||||
flags = pcre.NOTEMPTY_ATSTART
|
||||
|
||||
match = str.matchImpl(pattern, offset, endpos, flags)
|
||||
|
||||
if match.isNone:
|
||||
# either the end of the input or the string
|
||||
# cannot be split here
|
||||
if offset >= strlen:
|
||||
break
|
||||
|
||||
if matchesCrLf and offset < (str.len - 1) and
|
||||
str[offset] == '\r' and str[offset + 1] == '\L':
|
||||
# if PCRE treats CrLf as newline, skip both at the same time
|
||||
offset += 2
|
||||
elif unicode:
|
||||
# XXX what about invalid unicode?
|
||||
offset += str.runeLenAt(offset)
|
||||
assert(offset <= strlen)
|
||||
else:
|
||||
offset += 1
|
||||
else:
|
||||
offset = match.get.matchBounds.b + 1
|
||||
|
||||
yield match.get
|
||||
|
||||
|
||||
proc find*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[RegexMatch] =
|
||||
## Finds the given pattern in the string between the end and start
|
||||
## positions.
|
||||
##
|
||||
## ``start``
|
||||
## The start point at which to start matching. ``|abc`` is ``0``;
|
||||
## ``a|bc`` is ``1``
|
||||
##
|
||||
## ``endpos``
|
||||
## The maximum index for a match; ``int.high`` means the end of the
|
||||
## string, otherwise it’s an inclusive upper bound.
|
||||
return str.matchImpl(pattern, start, endpos, 0)
|
||||
|
||||
proc findAll*(str: string, pattern: Regex, start = 0, endpos = int.high): seq[string] =
|
||||
result = @[]
|
||||
for match in str.findIter(pattern, start, endpos):
|
||||
result.add(match.match)
|
||||
|
||||
proc split*(str: string, pattern: Regex, maxSplit = -1, start = 0): seq[string] =
|
||||
## Splits the string with the given regex. This works according to the
|
||||
## rules that Perl and Javascript use:
|
||||
##
|
||||
## - If the match is zero-width, then the string is still split:
|
||||
## ``"123".split(r"") == @["1", "2", "3"]``.
|
||||
##
|
||||
## - If the pattern has a capture in it, it is added after the string
|
||||
## split: ``"12".split(re"(\d)") == @["", "1", "", "2", ""]``.
|
||||
##
|
||||
## - If ``maxsplit != -1``, then the string will only be split
|
||||
## ``maxsplit - 1`` times. This means that there will be ``maxsplit``
|
||||
## strings in the output seq.
|
||||
## ``"1.2.3".split(re"\.", maxsplit = 2) == @["1", "2.3"]``
|
||||
##
|
||||
## ``start`` behaves the same as in ```find(...)`` <#proc-find>`__.
|
||||
result = @[]
|
||||
var lastIdx = start
|
||||
var splits = 0
|
||||
var bounds = 0 .. 0
|
||||
|
||||
for match in str.findIter(pattern, start = start):
|
||||
# bounds are inclusive:
|
||||
#
|
||||
# 0123456
|
||||
# ^^^
|
||||
# (1, 3)
|
||||
bounds = match.matchBounds
|
||||
|
||||
# "12".split("") would be @["", "1", "2"], but
|
||||
# if we skip an empty first match, it's the correct
|
||||
# @["1", "2"]
|
||||
if bounds.a <= bounds.b or bounds.a > start:
|
||||
result.add(str.substr(lastIdx, bounds.a - 1))
|
||||
splits += 1
|
||||
|
||||
lastIdx = bounds.b + 1
|
||||
|
||||
for cap in match.captures:
|
||||
# if there are captures, include them in the result
|
||||
result.add(cap)
|
||||
|
||||
if splits == maxSplit - 1:
|
||||
break
|
||||
|
||||
# "12".split("\b") would be @["1", "2", ""], but
|
||||
# if we skip an empty last match, it's the correct
|
||||
# @["1", "2"]
|
||||
if bounds.a <= bounds.b or bounds.b < str.high:
|
||||
# last match: Each match takes the previous substring,
|
||||
# but "1 2".split(/ /) needs to return @["1", "2"].
|
||||
# This handles "2"
|
||||
result.add(str.substr(bounds.b + 1, str.high))
|
||||
|
||||
template replaceImpl(str: string, pattern: Regex,
|
||||
replacement: expr): stmt {.immediate, dirty.} =
|
||||
# XXX seems very similar to split, maybe I can reduce code duplication
|
||||
# somehow?
|
||||
result = ""
|
||||
var lastIdx = 0
|
||||
for match {.inject.} in str.findIter(pattern):
|
||||
let bounds = match.matchBounds
|
||||
result.add(str.substr(lastIdx, bounds.a - 1))
|
||||
let nextVal = replacement
|
||||
assert(nextVal != nil)
|
||||
result.add(nextVal)
|
||||
|
||||
lastIdx = bounds.b + 1
|
||||
|
||||
result.add(str.substr(lastIdx, str.len - 1))
|
||||
return result
|
||||
|
||||
proc replace*(str: string, pattern: Regex,
|
||||
subproc: proc (match: RegexMatch): string): string =
|
||||
## Replaces each match of Regex in the string with ``sub``, which should
|
||||
## never be or return ``nil``.
|
||||
##
|
||||
## If ``sub`` is a ``proc (RegexMatch): string``, then it is executed with
|
||||
## each match and the return value is the replacement value.
|
||||
##
|
||||
## If ``sub`` is a ``proc (string): string``, then it is executed with the
|
||||
## full text of the match and and the return value is the replacement
|
||||
## value.
|
||||
##
|
||||
## If ``sub`` is a string, the syntax is as follows:
|
||||
##
|
||||
## - ``$$`` - literal ``$``
|
||||
## - ``$123`` - capture number ``123``
|
||||
## - ``$foo`` - named capture ``foo``
|
||||
## - ``${foo}`` - same as above
|
||||
## - ``$1$#`` - first and second captures
|
||||
## - ``$#`` - first capture
|
||||
## - ``$0`` - full match
|
||||
##
|
||||
## If a given capture is missing, a ``ValueError`` exception is thrown.
|
||||
replaceImpl(str, pattern, subproc(match))
|
||||
|
||||
proc replace*(str: string, pattern: Regex,
|
||||
subproc: proc (match: string): string): string =
|
||||
replaceImpl(str, pattern, subproc(match.match))
|
||||
|
||||
proc replace*(str: string, pattern: Regex, sub: string): string =
|
||||
# - 1 because the string numbers are 0-indexed
|
||||
replaceImpl(str, pattern,
|
||||
formatStr(sub, match.captures[name], match.captures[id - 1]))
|
||||
|
||||
# }}}
|
||||
|
||||
let SpecialCharMatcher = re"([\\+*?[^\]$(){}=!<>|:-])"
|
||||
proc escapeRe*(str: string): string =
|
||||
## Escapes the string so it doesn’t match any special characters.
|
||||
## Incompatible with the Extra flag (``X``).
|
||||
str.replace(SpecialCharMatcher, "\\$1")
|
||||
9
lib/impure/nre/.gitignore
vendored
Normal file
9
lib/impure/nre/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
# all executables
|
||||
*
|
||||
!*/
|
||||
!*.*
|
||||
*.exe
|
||||
|
||||
# Wildcard patterns.
|
||||
*.swp
|
||||
nimcache
|
||||
63
lib/impure/nre/private/util.nim
Normal file
63
lib/impure/nre/private/util.nim
Normal file
@@ -0,0 +1,63 @@
|
||||
## INTERNAL FILE FOR USE ONLY BY nre.nim.
|
||||
import tables
|
||||
|
||||
proc fget*[K, V](self: Table[K, V], key: K): V =
|
||||
if self.hasKey(key):
|
||||
return self[key]
|
||||
else:
|
||||
raise newException(KeyError, "Key does not exist in table: " & $key)
|
||||
|
||||
const Ident = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'}
|
||||
const StartIdent = Ident - {'0'..'9'}
|
||||
|
||||
proc checkNil(arg: string): string =
|
||||
if arg == nil:
|
||||
raise newException(ValueError, "Cannot use nil capture")
|
||||
else:
|
||||
return arg
|
||||
|
||||
template formatStr*(howExpr, namegetter, idgetter: expr): expr =
|
||||
let how = howExpr
|
||||
var val = newStringOfCap(how.len)
|
||||
var i = 0
|
||||
var lastNum = 1
|
||||
|
||||
while i < how.len:
|
||||
if how[i] != '$':
|
||||
val.add(how[i])
|
||||
i += 1
|
||||
else:
|
||||
if how[i + 1] == '$':
|
||||
val.add('$')
|
||||
i += 2
|
||||
elif how[i + 1] == '#':
|
||||
var id {.inject.} = lastNum
|
||||
val.add(checkNil(idgetter))
|
||||
lastNum += 1
|
||||
i += 2
|
||||
elif how[i + 1] in {'0'..'9'}:
|
||||
i += 1
|
||||
var id {.inject.} = 0
|
||||
while i < how.len and how[i] in {'0'..'9'}:
|
||||
id += (id * 10) + (ord(how[i]) - ord('0'))
|
||||
i += 1
|
||||
val.add(checkNil(idgetter))
|
||||
lastNum = id + 1
|
||||
elif how[i + 1] in StartIdent:
|
||||
i += 1
|
||||
var name {.inject.} = ""
|
||||
while i < how.len and how[i] in Ident:
|
||||
name.add(how[i])
|
||||
i += 1
|
||||
val.add(checkNil(namegetter))
|
||||
elif how[i + 1] == '{':
|
||||
i += 2
|
||||
var name {.inject.} = ""
|
||||
while i < how.len and how[i] != '}':
|
||||
name.add(how[i])
|
||||
i += 1
|
||||
i += 1
|
||||
val.add(checkNil(namegetter))
|
||||
else:
|
||||
raise newException(Exception, "Syntax error in format string at " & $i)
|
||||
val
|
||||
@@ -135,7 +135,7 @@ else:
|
||||
var cur, old: Termios
|
||||
discard fd.tcgetattr(cur.addr)
|
||||
old = cur
|
||||
cur.c_lflag = cur.c_lflag and not Tcflag(ECHO)
|
||||
cur.c_lflag = cur.c_lflag and not Cflag(ECHO)
|
||||
discard fd.tcsetattr(TCSADRAIN, cur.addr)
|
||||
stdout.write prompt
|
||||
result = stdin.readLine(password)
|
||||
|
||||
@@ -7,11 +7,8 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Regular expression support for Nim. Consider using the pegs module instead.
|
||||
##
|
||||
## There is an alternative regular expressions library with a more unified API:
|
||||
## `nre <https://github.com/flaviut/nre>`_. It may be added to the standard
|
||||
## library in the future, instead of `re`.
|
||||
## Regular expression support for Nim. Deprecated. Consider using the ``nre``
|
||||
## or ``pegs`` modules instead.
|
||||
##
|
||||
## **Note:** The 're' proc defaults to the **extended regular expression
|
||||
## syntax** which lets you use whitespace freely to make your regexes readable.
|
||||
@@ -49,7 +46,7 @@ type
|
||||
h: ptr Pcre
|
||||
e: ptr ExtraData
|
||||
|
||||
Regex* = ref RegexDesc ## a compiled regular expression
|
||||
Regex* {.deprecated.} = ref RegexDesc ## a compiled regular expression
|
||||
|
||||
RegexError* = object of ValueError
|
||||
## is raised if the pattern is no valid regular expression.
|
||||
@@ -79,7 +76,7 @@ proc finalizeRegEx(x: Regex) =
|
||||
if not isNil(x.e):
|
||||
pcre.free_substring(cast[cstring](x.e))
|
||||
|
||||
proc re*(s: string, flags = {reExtended, reStudy}): Regex =
|
||||
proc re*(s: string, flags = {reExtended, reStudy}): Regex {.deprecated.} =
|
||||
## Constructor of regular expressions. Note that Nim's
|
||||
## extended raw string literals support this syntax ``re"[abc]"`` as
|
||||
## a short form for ``re(r"[abc]")``.
|
||||
|
||||
@@ -15,11 +15,12 @@
|
||||
import openssl, strutils, os
|
||||
|
||||
type
|
||||
TSecureSocket* = object
|
||||
SecureSocket* = object
|
||||
ssl: SslPtr
|
||||
bio: BIO
|
||||
{.deprecated: [TSecureSocket: SecureSocket].}
|
||||
|
||||
proc connect*(sock: var TSecureSocket, address: string,
|
||||
proc connect*(sock: var SecureSocket, address: string,
|
||||
port: int): int =
|
||||
## Connects to the specified `address` on the specified `port`.
|
||||
## Returns the result of the certificate validation.
|
||||
@@ -52,7 +53,7 @@ proc connect*(sock: var TSecureSocket, address: string,
|
||||
|
||||
result = SSL_get_verify_result(sock.ssl)
|
||||
|
||||
proc recvLine*(sock: TSecureSocket, line: var TaintedString): bool =
|
||||
proc recvLine*(sock: SecureSocket, line: var TaintedString): bool =
|
||||
## Acts in a similar fashion to the `recvLine` in the sockets module.
|
||||
## Returns false when no data is available to be read.
|
||||
## `Line` must be initialized and not nil!
|
||||
@@ -71,19 +72,19 @@ proc recvLine*(sock: TSecureSocket, line: var TaintedString): bool =
|
||||
add(line.string, c)
|
||||
|
||||
|
||||
proc send*(sock: TSecureSocket, data: string) =
|
||||
proc send*(sock: SecureSocket, data: string) =
|
||||
## Writes `data` to the socket.
|
||||
if BIO_write(sock.bio, data, data.len.cint) <= 0:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc close*(sock: TSecureSocket) =
|
||||
proc close*(sock: SecureSocket) =
|
||||
## Closes the socket
|
||||
if BIO_free(sock.bio) <= 0:
|
||||
ERR_print_errors_fp(stderr)
|
||||
raiseOSError(osLastError())
|
||||
|
||||
when not defined(testing) and isMainModule:
|
||||
var s: TSecureSocket
|
||||
var s: SecureSocket
|
||||
echo connect(s, "smtp.gmail.com", 465)
|
||||
|
||||
#var buffer: array[0..255, char]
|
||||
|
||||
@@ -13,18 +13,18 @@ import
|
||||
streams, libzip, times, os, strutils
|
||||
|
||||
type
|
||||
TZipArchive* = object of RootObj ## represents a zip archive
|
||||
ZipArchive* = object of RootObj ## represents a zip archive
|
||||
mode: FileMode
|
||||
w: PZip
|
||||
{.deprecated: [TZipArchive: ZipArchive].}
|
||||
|
||||
|
||||
proc zipError(z: var TZipArchive) =
|
||||
proc zipError(z: var ZipArchive) =
|
||||
var e: ref IOError
|
||||
new(e)
|
||||
e.msg = $zip_strerror(z.w)
|
||||
raise e
|
||||
|
||||
proc open*(z: var TZipArchive, filename: string, mode: FileMode = fmRead): bool =
|
||||
proc open*(z: var ZipArchive, filename: string, mode: FileMode = fmRead): bool =
|
||||
## Opens a zip file for reading, writing or appending. All file modes are
|
||||
## supported. Returns true iff successful, false otherwise.
|
||||
var err, flags: int32
|
||||
@@ -38,11 +38,11 @@ proc open*(z: var TZipArchive, filename: string, mode: FileMode = fmRead): bool
|
||||
z.mode = mode
|
||||
result = z.w != nil
|
||||
|
||||
proc close*(z: var TZipArchive) =
|
||||
proc close*(z: var ZipArchive) =
|
||||
## Closes a zip file.
|
||||
zip_close(z.w)
|
||||
|
||||
proc createDir*(z: var TZipArchive, dir: string) =
|
||||
proc createDir*(z: var ZipArchive, dir: string) =
|
||||
## Creates a directory within the `z` archive. This does not fail if the
|
||||
## directory already exists. Note that for adding a file like
|
||||
## ``"path1/path2/filename"`` it is not necessary
|
||||
@@ -52,7 +52,7 @@ proc createDir*(z: var TZipArchive, dir: string) =
|
||||
discard zip_add_dir(z.w, dir)
|
||||
zip_error_clear(z.w)
|
||||
|
||||
proc addFile*(z: var TZipArchive, dest, src: string) =
|
||||
proc addFile*(z: var ZipArchive, dest, src: string) =
|
||||
## Adds the file `src` to the archive `z` with the name `dest`. `dest`
|
||||
## may contain a path that will be created.
|
||||
assert(z.mode != fmRead)
|
||||
@@ -67,13 +67,13 @@ proc addFile*(z: var TZipArchive, dest, src: string) =
|
||||
zip_source_free(zipsrc)
|
||||
zipError(z)
|
||||
|
||||
proc addFile*(z: var TZipArchive, file: string) =
|
||||
proc addFile*(z: var ZipArchive, file: string) =
|
||||
## A shortcut for ``addFile(z, file, file)``, i.e. the name of the source is
|
||||
## the name of the destination.
|
||||
addFile(z, file, file)
|
||||
|
||||
proc mySourceCallback(state, data: pointer, len: int,
|
||||
cmd: TZipSourceCmd): int {.cdecl.} =
|
||||
cmd: ZipSourceCmd): int {.cdecl.} =
|
||||
var src = cast[Stream](state)
|
||||
case cmd
|
||||
of ZIP_SOURCE_OPEN:
|
||||
@@ -86,7 +86,7 @@ proc mySourceCallback(state, data: pointer, len: int,
|
||||
zip_stat_init(stat)
|
||||
stat.size = high(int32)-1 # we don't know the size
|
||||
stat.mtime = getTime()
|
||||
result = sizeof(TZipStat)
|
||||
result = sizeof(ZipStat)
|
||||
of ZIP_SOURCE_ERROR:
|
||||
var err = cast[ptr array[0..1, cint]](data)
|
||||
err[0] = ZIP_ER_INTERNAL
|
||||
@@ -95,7 +95,7 @@ proc mySourceCallback(state, data: pointer, len: int,
|
||||
of constZIP_SOURCE_FREE: GC_unref(src)
|
||||
else: assert(false)
|
||||
|
||||
proc addFile*(z: var TZipArchive, dest: string, src: Stream) =
|
||||
proc addFile*(z: var ZipArchive, dest: string, src: Stream) =
|
||||
## Adds a file named with `dest` to the archive `z`. `dest`
|
||||
## may contain a path. The file's content is read from the `src` stream.
|
||||
assert(z.mode != fmRead)
|
||||
@@ -134,7 +134,7 @@ proc newZipFileStream(f: PZipFile): PZipFileStream =
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
proc getStream*(z: var TZipArchive, filename: string): PZipFileStream =
|
||||
proc getStream*(z: var ZipArchive, filename: string): PZipFileStream =
|
||||
## returns a stream that can be used to read the file named `filename`
|
||||
## from the archive `z`. Returns nil in case of an error.
|
||||
## The returned stream does not support the `setPosition`, `getPosition`,
|
||||
@@ -142,7 +142,7 @@ proc getStream*(z: var TZipArchive, filename: string): PZipFileStream =
|
||||
var x = zip_fopen(z.w, filename, 0'i32)
|
||||
if x != nil: result = newZipFileStream(x)
|
||||
|
||||
iterator walkFiles*(z: var TZipArchive): string =
|
||||
iterator walkFiles*(z: var ZipArchive): string =
|
||||
## walks over all files in the archive `z` and returns the filename
|
||||
## (including the path).
|
||||
var i = 0'i32
|
||||
@@ -152,7 +152,7 @@ iterator walkFiles*(z: var TZipArchive): string =
|
||||
inc(i)
|
||||
|
||||
|
||||
proc extractFile*(z: var TZipArchive, srcFile: string, dest: Stream) =
|
||||
proc extractFile*(z: var ZipArchive, srcFile: string, dest: Stream) =
|
||||
## extracts a file from the zip archive `z` to the destination stream.
|
||||
var strm = getStream(z, srcFile)
|
||||
while true:
|
||||
@@ -162,13 +162,13 @@ proc extractFile*(z: var TZipArchive, srcFile: string, dest: Stream) =
|
||||
dest.flush()
|
||||
strm.close()
|
||||
|
||||
proc extractFile*(z: var TZipArchive, srcFile: string, dest: string) =
|
||||
proc extractFile*(z: var ZipArchive, srcFile: string, dest: string) =
|
||||
## extracts a file from the zip archive `z` to the destination filename.
|
||||
var file = newFileStream(dest, fmWrite)
|
||||
extractFile(z, srcFile, file)
|
||||
file.close()
|
||||
|
||||
proc extractAll*(z: var TZipArchive, dest: string) =
|
||||
proc extractAll*(z: var ZipArchive, dest: string) =
|
||||
## extracts all files from archive `z` to the destination directory.
|
||||
for file in walkFiles(z):
|
||||
if file.endsWith("/"):
|
||||
@@ -177,7 +177,7 @@ proc extractAll*(z: var TZipArchive, dest: string) =
|
||||
extractFile(z, file, dest / file)
|
||||
|
||||
when not defined(testing) and isMainModule:
|
||||
var zip: TZipArchive
|
||||
var zip: ZipArchive
|
||||
if not zip.open("nim-0.11.0.zip"):
|
||||
raise newException(IOError, "opening zip failed")
|
||||
zip.extractAll("test")
|
||||
|
||||
@@ -15,7 +15,7 @@ import
|
||||
strutils
|
||||
|
||||
type
|
||||
TTokenClass* = enum
|
||||
TokenClass* = enum
|
||||
gtEof, gtNone, gtWhitespace, gtDecNumber, gtBinNumber, gtHexNumber,
|
||||
gtOctNumber, gtFloatNumber, gtIdentifier, gtKeyword, gtStringLit,
|
||||
gtLongStringLit, gtCharLit, gtEscapeSequence, # escape sequence like \xff
|
||||
@@ -23,20 +23,22 @@ type
|
||||
gtTagStart, gtTagEnd, gtKey, gtValue, gtRawData, gtAssembler,
|
||||
gtPreprocessor, gtDirective, gtCommand, gtRule, gtHyperlink, gtLabel,
|
||||
gtReference, gtOther
|
||||
TGeneralTokenizer* = object of RootObj
|
||||
kind*: TTokenClass
|
||||
GeneralTokenizer* = object of RootObj
|
||||
kind*: TokenClass
|
||||
start*, length*: int
|
||||
buf: cstring
|
||||
pos: int
|
||||
state: TTokenClass
|
||||
state: TokenClass
|
||||
|
||||
TSourceLanguage* = enum
|
||||
SourceLanguage* = enum
|
||||
langNone, langNim, langNimrod, langCpp, langCsharp, langC, langJava
|
||||
{.deprecated: [TSourceLanguage: SourceLanguage, TTokenClass: TokenClass,
|
||||
TGeneralTokenizer: GeneralTokenizer].}
|
||||
|
||||
const
|
||||
sourceLanguageToStr*: array[TSourceLanguage, string] = ["none",
|
||||
sourceLanguageToStr*: array[SourceLanguage, string] = ["none",
|
||||
"Nim", "Nimrod", "C++", "C#", "C", "Java"]
|
||||
tokenClassToStr*: array[TTokenClass, string] = ["Eof", "None", "Whitespace",
|
||||
tokenClassToStr*: array[TokenClass, string] = ["Eof", "None", "Whitespace",
|
||||
"DecNumber", "BinNumber", "HexNumber", "OctNumber", "FloatNumber",
|
||||
"Identifier", "Keyword", "StringLit", "LongStringLit", "CharLit",
|
||||
"EscapeSequence", "Operator", "Punctuation", "Comment", "LongComment",
|
||||
@@ -58,29 +60,29 @@ const
|
||||
"template", "try", "tuple", "type", "using", "var", "when", "while", "with",
|
||||
"without", "xor", "yield"]
|
||||
|
||||
proc getSourceLanguage*(name: string): TSourceLanguage =
|
||||
for i in countup(succ(low(TSourceLanguage)), high(TSourceLanguage)):
|
||||
proc getSourceLanguage*(name: string): SourceLanguage =
|
||||
for i in countup(succ(low(SourceLanguage)), high(SourceLanguage)):
|
||||
if cmpIgnoreStyle(name, sourceLanguageToStr[i]) == 0:
|
||||
return i
|
||||
result = langNone
|
||||
|
||||
proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: cstring) =
|
||||
proc initGeneralTokenizer*(g: var GeneralTokenizer, buf: cstring) =
|
||||
g.buf = buf
|
||||
g.kind = low(TTokenClass)
|
||||
g.kind = low(TokenClass)
|
||||
g.start = 0
|
||||
g.length = 0
|
||||
g.state = low(TTokenClass)
|
||||
g.state = low(TokenClass)
|
||||
var pos = 0 # skip initial whitespace:
|
||||
while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos)
|
||||
g.pos = pos
|
||||
|
||||
proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: string) =
|
||||
proc initGeneralTokenizer*(g: var GeneralTokenizer, buf: string) =
|
||||
initGeneralTokenizer(g, cstring(buf))
|
||||
|
||||
proc deinitGeneralTokenizer*(g: var TGeneralTokenizer) =
|
||||
proc deinitGeneralTokenizer*(g: var GeneralTokenizer) =
|
||||
discard
|
||||
|
||||
proc nimGetKeyword(id: string): TTokenClass =
|
||||
proc nimGetKeyword(id: string): TokenClass =
|
||||
for k in nimKeywords:
|
||||
if cmpIgnoreStyle(id, k) == 0: return gtKeyword
|
||||
result = gtIdentifier
|
||||
@@ -92,7 +94,7 @@ proc nimGetKeyword(id: string): TTokenClass =
|
||||
else:
|
||||
result = gtIdentifier
|
||||
|
||||
proc nimNumberPostfix(g: var TGeneralTokenizer, position: int): int =
|
||||
proc nimNumberPostfix(g: var GeneralTokenizer, position: int): int =
|
||||
var pos = position
|
||||
if g.buf[pos] == '\'':
|
||||
inc(pos)
|
||||
@@ -110,7 +112,7 @@ proc nimNumberPostfix(g: var TGeneralTokenizer, position: int): int =
|
||||
discard
|
||||
result = pos
|
||||
|
||||
proc nimNumber(g: var TGeneralTokenizer, position: int): int =
|
||||
proc nimNumber(g: var GeneralTokenizer, position: int): int =
|
||||
const decChars = {'0'..'9', '_'}
|
||||
var pos = position
|
||||
g.kind = gtDecNumber
|
||||
@@ -130,7 +132,7 @@ const
|
||||
OpChars = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', '.',
|
||||
'|', '=', '%', '&', '$', '@', '~', ':', '\x80'..'\xFF'}
|
||||
|
||||
proc nimNextToken(g: var TGeneralTokenizer) =
|
||||
proc nimNextToken(g: var GeneralTokenizer) =
|
||||
const
|
||||
hexChars = {'0'..'9', 'A'..'F', 'a'..'f', '_'}
|
||||
octChars = {'0'..'7', '_'}
|
||||
@@ -278,7 +280,7 @@ proc nimNextToken(g: var TGeneralTokenizer) =
|
||||
assert false, "nimNextToken: produced an empty token"
|
||||
g.pos = pos
|
||||
|
||||
proc generalNumber(g: var TGeneralTokenizer, position: int): int =
|
||||
proc generalNumber(g: var GeneralTokenizer, position: int): int =
|
||||
const decChars = {'0'..'9'}
|
||||
var pos = position
|
||||
g.kind = gtDecNumber
|
||||
@@ -294,7 +296,7 @@ proc generalNumber(g: var TGeneralTokenizer, position: int): int =
|
||||
while g.buf[pos] in decChars: inc(pos)
|
||||
result = pos
|
||||
|
||||
proc generalStrLit(g: var TGeneralTokenizer, position: int): int =
|
||||
proc generalStrLit(g: var GeneralTokenizer, position: int): int =
|
||||
const
|
||||
decChars = {'0'..'9'}
|
||||
hexChars = {'0'..'9', 'A'..'F', 'a'..'f'}
|
||||
@@ -355,12 +357,13 @@ proc isKeywordIgnoreCase(x: openArray[string], y: string): int =
|
||||
result = - 1
|
||||
|
||||
type
|
||||
TTokenizerFlag = enum
|
||||
TokenizerFlag = enum
|
||||
hasPreprocessor, hasNestedComments
|
||||
TTokenizerFlags = set[TTokenizerFlag]
|
||||
TokenizerFlags = set[TokenizerFlag]
|
||||
{.deprecated: [TTokenizerFlag: TokenizerFlag, TTokenizerFlags: TokenizerFlags].}
|
||||
|
||||
proc clikeNextToken(g: var TGeneralTokenizer, keywords: openArray[string],
|
||||
flags: TTokenizerFlags) =
|
||||
proc clikeNextToken(g: var GeneralTokenizer, keywords: openArray[string],
|
||||
flags: TokenizerFlags) =
|
||||
const
|
||||
hexChars = {'0'..'9', 'A'..'F', 'a'..'f'}
|
||||
octChars = {'0'..'7'}
|
||||
@@ -493,7 +496,7 @@ proc clikeNextToken(g: var TGeneralTokenizer, keywords: openArray[string],
|
||||
assert false, "clikeNextToken: produced an empty token"
|
||||
g.pos = pos
|
||||
|
||||
proc cNextToken(g: var TGeneralTokenizer) =
|
||||
proc cNextToken(g: var GeneralTokenizer) =
|
||||
const
|
||||
keywords: array[0..36, string] = ["_Bool", "_Complex", "_Imaginary", "auto",
|
||||
"break", "case", "char", "const", "continue", "default", "do", "double",
|
||||
@@ -503,7 +506,7 @@ proc cNextToken(g: var TGeneralTokenizer) =
|
||||
"volatile", "while"]
|
||||
clikeNextToken(g, keywords, {hasPreprocessor})
|
||||
|
||||
proc cppNextToken(g: var TGeneralTokenizer) =
|
||||
proc cppNextToken(g: var GeneralTokenizer) =
|
||||
const
|
||||
keywords: array[0..47, string] = ["asm", "auto", "break", "case", "catch",
|
||||
"char", "class", "const", "continue", "default", "delete", "do", "double",
|
||||
@@ -514,7 +517,7 @@ proc cppNextToken(g: var TGeneralTokenizer) =
|
||||
"union", "unsigned", "virtual", "void", "volatile", "while"]
|
||||
clikeNextToken(g, keywords, {hasPreprocessor})
|
||||
|
||||
proc csharpNextToken(g: var TGeneralTokenizer) =
|
||||
proc csharpNextToken(g: var GeneralTokenizer) =
|
||||
const
|
||||
keywords: array[0..76, string] = ["abstract", "as", "base", "bool", "break",
|
||||
"byte", "case", "catch", "char", "checked", "class", "const", "continue",
|
||||
@@ -529,7 +532,7 @@ proc csharpNextToken(g: var TGeneralTokenizer) =
|
||||
"virtual", "void", "volatile", "while"]
|
||||
clikeNextToken(g, keywords, {hasPreprocessor})
|
||||
|
||||
proc javaNextToken(g: var TGeneralTokenizer) =
|
||||
proc javaNextToken(g: var GeneralTokenizer) =
|
||||
const
|
||||
keywords: array[0..52, string] = ["abstract", "assert", "boolean", "break",
|
||||
"byte", "case", "catch", "char", "class", "const", "continue", "default",
|
||||
@@ -541,7 +544,7 @@ proc javaNextToken(g: var TGeneralTokenizer) =
|
||||
"try", "void", "volatile", "while"]
|
||||
clikeNextToken(g, keywords, {})
|
||||
|
||||
proc getNextToken*(g: var TGeneralTokenizer, lang: TSourceLanguage) =
|
||||
proc getNextToken*(g: var GeneralTokenizer, lang: SourceLanguage) =
|
||||
case lang
|
||||
of langNone: assert false
|
||||
of langNim, langNimrod: nimNextToken(g)
|
||||
|
||||
@@ -15,7 +15,7 @@ import
|
||||
os, strutils, rstast
|
||||
|
||||
type
|
||||
TRstParseOption* = enum ## options for the RST parser
|
||||
RstParseOption* = enum ## options for the RST parser
|
||||
roSkipPounds, ## skip ``#`` at line beginning (documentation
|
||||
## embedded in Nim comments)
|
||||
roSupportSmilies, ## make the RST parser support smilies like ``:)``
|
||||
@@ -23,14 +23,14 @@ type
|
||||
## it for sandboxing)
|
||||
roSupportMarkdown ## support additional features of markdown
|
||||
|
||||
TRstParseOptions* = set[TRstParseOption]
|
||||
RstParseOptions* = set[RstParseOption]
|
||||
|
||||
TMsgClass* = enum
|
||||
MsgClass* = enum
|
||||
mcHint = "Hint",
|
||||
mcWarning = "Warning",
|
||||
mcError = "Error"
|
||||
|
||||
TMsgKind* = enum ## the possible messages
|
||||
MsgKind* = enum ## the possible messages
|
||||
meCannotOpenFile,
|
||||
meExpected,
|
||||
meGridTableNotImplemented,
|
||||
@@ -42,12 +42,14 @@ type
|
||||
mwUnsupportedLanguage,
|
||||
mwUnsupportedField
|
||||
|
||||
TMsgHandler* = proc (filename: string, line, col: int, msgKind: TMsgKind,
|
||||
MsgHandler* = proc (filename: string, line, col: int, msgKind: MsgKind,
|
||||
arg: string) {.nimcall.} ## what to do in case of an error
|
||||
TFindFileHandler* = proc (filename: string): string {.nimcall.}
|
||||
FindFileHandler* = proc (filename: string): string {.nimcall.}
|
||||
{.deprecated: [TRstParseOptions: RstParseOptions, TRstParseOption: RstParseOption,
|
||||
TMsgKind: MsgKind].}
|
||||
|
||||
const
|
||||
messages: array [TMsgKind, string] = [
|
||||
messages: array [MsgKind, string] = [
|
||||
meCannotOpenFile: "cannot open '$1'",
|
||||
meExpected: "'$1' expected",
|
||||
meGridTableNotImplemented: "grid table is not implemented",
|
||||
@@ -111,23 +113,24 @@ const
|
||||
}
|
||||
|
||||
type
|
||||
TTokType = enum
|
||||
TokType = enum
|
||||
tkEof, tkIndent, tkWhite, tkWord, tkAdornment, tkPunct, tkOther
|
||||
TToken = object # a RST token
|
||||
kind*: TTokType # the type of the token
|
||||
Token = object # a RST token
|
||||
kind*: TokType # the type of the token
|
||||
ival*: int # the indentation or parsed integer value
|
||||
symbol*: string # the parsed symbol as string
|
||||
line*, col*: int # line and column of the token
|
||||
|
||||
TTokenSeq = seq[TToken]
|
||||
TLexer = object of RootObj
|
||||
TokenSeq = seq[Token]
|
||||
Lexer = object of RootObj
|
||||
buf*: cstring
|
||||
bufpos*: int
|
||||
line*, col*, baseIndent*: int
|
||||
skipPounds*: bool
|
||||
{.deprecated: [TTokType: TokType, TToken: Token, TTokenSeq: TokenSeq,
|
||||
TLexer: Lexer].}
|
||||
|
||||
|
||||
proc getThing(L: var TLexer, tok: var TToken, s: set[char]) =
|
||||
proc getThing(L: var Lexer, tok: var Token, s: set[char]) =
|
||||
tok.kind = tkWord
|
||||
tok.line = L.line
|
||||
tok.col = L.col
|
||||
@@ -139,7 +142,7 @@ proc getThing(L: var TLexer, tok: var TToken, s: set[char]) =
|
||||
inc(L.col, pos - L.bufpos)
|
||||
L.bufpos = pos
|
||||
|
||||
proc getAdornment(L: var TLexer, tok: var TToken) =
|
||||
proc getAdornment(L: var Lexer, tok: var Token) =
|
||||
tok.kind = tkAdornment
|
||||
tok.line = L.line
|
||||
tok.col = L.col
|
||||
@@ -152,7 +155,7 @@ proc getAdornment(L: var TLexer, tok: var TToken) =
|
||||
inc(L.col, pos - L.bufpos)
|
||||
L.bufpos = pos
|
||||
|
||||
proc getIndentAux(L: var TLexer, start: int): int =
|
||||
proc getIndentAux(L: var Lexer, start: int): int =
|
||||
var pos = start
|
||||
var buf = L.buf
|
||||
# skip the newline (but include it in the token!)
|
||||
@@ -181,7 +184,7 @@ proc getIndentAux(L: var TLexer, start: int): int =
|
||||
result = getIndentAux(L, pos)
|
||||
L.bufpos = pos # no need to set back buf
|
||||
|
||||
proc getIndent(L: var TLexer, tok: var TToken) =
|
||||
proc getIndent(L: var Lexer, tok: var Token) =
|
||||
tok.col = 0
|
||||
tok.kind = tkIndent # skip the newline (but include it in the token!)
|
||||
tok.ival = getIndentAux(L, L.bufpos)
|
||||
@@ -191,7 +194,7 @@ proc getIndent(L: var TLexer, tok: var TToken) =
|
||||
tok.ival = max(tok.ival - L.baseIndent, 0)
|
||||
tok.symbol = "\n" & spaces(tok.ival)
|
||||
|
||||
proc rawGetTok(L: var TLexer, tok: var TToken) =
|
||||
proc rawGetTok(L: var Lexer, tok: var Token) =
|
||||
tok.symbol = ""
|
||||
tok.ival = 0
|
||||
var c = L.buf[L.bufpos]
|
||||
@@ -222,8 +225,8 @@ proc rawGetTok(L: var TLexer, tok: var TToken) =
|
||||
inc(L.col)
|
||||
tok.col = max(tok.col - L.baseIndent, 0)
|
||||
|
||||
proc getTokens(buffer: string, skipPounds: bool, tokens: var TTokenSeq): int =
|
||||
var L: TLexer
|
||||
proc getTokens(buffer: string, skipPounds: bool, tokens: var TokenSeq): int =
|
||||
var L: Lexer
|
||||
var length = len(tokens)
|
||||
L.buf = cstring(buffer)
|
||||
L.line = 0 # skip UTF-8 BOM
|
||||
@@ -253,31 +256,31 @@ proc getTokens(buffer: string, skipPounds: bool, tokens: var TTokenSeq): int =
|
||||
tokens[0].kind = tkIndent
|
||||
|
||||
type
|
||||
TLevelMap = array[char, int]
|
||||
TSubstitution = object
|
||||
LevelMap = array[char, int]
|
||||
Substitution = object
|
||||
key*: string
|
||||
value*: PRstNode
|
||||
|
||||
TSharedState = object
|
||||
options: TRstParseOptions # parsing options
|
||||
SharedState = object
|
||||
options: RstParseOptions # parsing options
|
||||
uLevel, oLevel: int # counters for the section levels
|
||||
subs: seq[TSubstitution] # substitutions
|
||||
refs: seq[TSubstitution] # references
|
||||
underlineToLevel: TLevelMap # Saves for each possible title adornment
|
||||
subs: seq[Substitution] # substitutions
|
||||
refs: seq[Substitution] # references
|
||||
underlineToLevel: LevelMap # Saves for each possible title adornment
|
||||
# character its level in the
|
||||
# current document.
|
||||
# This is for single underline adornments.
|
||||
overlineToLevel: TLevelMap # Saves for each possible title adornment
|
||||
overlineToLevel: LevelMap # Saves for each possible title adornment
|
||||
# character its level in the current
|
||||
# document.
|
||||
# This is for over-underline adornments.
|
||||
msgHandler: TMsgHandler # How to handle errors.
|
||||
findFile: TFindFileHandler # How to find files.
|
||||
msgHandler: MsgHandler # How to handle errors.
|
||||
findFile: FindFileHandler # How to find files.
|
||||
|
||||
PSharedState = ref TSharedState
|
||||
TRstParser = object of RootObj
|
||||
PSharedState = ref SharedState
|
||||
RstParser = object of RootObj
|
||||
idx*: int
|
||||
tok*: TTokenSeq
|
||||
tok*: TokenSeq
|
||||
s*: PSharedState
|
||||
indentStack*: seq[int]
|
||||
filename*: string
|
||||
@@ -285,8 +288,12 @@ type
|
||||
hasToc*: bool
|
||||
|
||||
EParseError* = object of ValueError
|
||||
{.deprecated: [TLevelMap: LevelMap, TSubstitution: Substitution,
|
||||
TSharedState: SharedState, TRstParser: RstParser,
|
||||
TMsgHandler: MsgHandler, TFindFileHandler: FindFileHandler,
|
||||
TMsgClass: MsgClass].}
|
||||
|
||||
proc whichMsgClass*(k: TMsgKind): TMsgClass =
|
||||
proc whichMsgClass*(k: MsgKind): MsgClass =
|
||||
## returns which message class `k` belongs to.
|
||||
case ($k)[1]
|
||||
of 'e', 'E': result = mcError
|
||||
@@ -294,7 +301,7 @@ proc whichMsgClass*(k: TMsgKind): TMsgClass =
|
||||
of 'h', 'H': result = mcHint
|
||||
else: assert false, "msgkind does not fit naming scheme"
|
||||
|
||||
proc defaultMsgHandler*(filename: string, line, col: int, msgkind: TMsgKind,
|
||||
proc defaultMsgHandler*(filename: string, line, col: int, msgkind: MsgKind,
|
||||
arg: string) {.procvar.} =
|
||||
let mc = msgkind.whichMsgClass
|
||||
let a = messages[msgkind] % arg
|
||||
@@ -306,9 +313,9 @@ proc defaultFindFile*(filename: string): string {.procvar.} =
|
||||
if existsFile(filename): result = filename
|
||||
else: result = ""
|
||||
|
||||
proc newSharedState(options: TRstParseOptions,
|
||||
findFile: TFindFileHandler,
|
||||
msgHandler: TMsgHandler): PSharedState =
|
||||
proc newSharedState(options: RstParseOptions,
|
||||
findFile: FindFileHandler,
|
||||
msgHandler: MsgHandler): PSharedState =
|
||||
new(result)
|
||||
result.subs = @[]
|
||||
result.refs = @[]
|
||||
@@ -316,34 +323,34 @@ proc newSharedState(options: TRstParseOptions,
|
||||
result.msgHandler = if not isNil(msgHandler): msgHandler else: defaultMsgHandler
|
||||
result.findFile = if not isNil(findFile): findFile else: defaultFindFile
|
||||
|
||||
proc rstMessage(p: TRstParser, msgKind: TMsgKind, arg: string) =
|
||||
proc rstMessage(p: RstParser, msgKind: MsgKind, arg: string) =
|
||||
p.s.msgHandler(p.filename, p.line + p.tok[p.idx].line,
|
||||
p.col + p.tok[p.idx].col, msgKind, arg)
|
||||
|
||||
proc rstMessage(p: TRstParser, msgKind: TMsgKind, arg: string, line, col: int) =
|
||||
proc rstMessage(p: RstParser, msgKind: MsgKind, arg: string, line, col: int) =
|
||||
p.s.msgHandler(p.filename, p.line + line,
|
||||
p.col + col, msgKind, arg)
|
||||
|
||||
proc rstMessage(p: TRstParser, msgKind: TMsgKind) =
|
||||
proc rstMessage(p: RstParser, msgKind: MsgKind) =
|
||||
p.s.msgHandler(p.filename, p.line + p.tok[p.idx].line,
|
||||
p.col + p.tok[p.idx].col, msgKind,
|
||||
p.tok[p.idx].symbol)
|
||||
|
||||
when false:
|
||||
proc corrupt(p: TRstParser) =
|
||||
proc corrupt(p: RstParser) =
|
||||
assert p.indentStack[0] == 0
|
||||
for i in 1 .. high(p.indentStack): assert p.indentStack[i] < 1_000
|
||||
|
||||
proc currInd(p: TRstParser): int =
|
||||
proc currInd(p: RstParser): int =
|
||||
result = p.indentStack[high(p.indentStack)]
|
||||
|
||||
proc pushInd(p: var TRstParser, ind: int) =
|
||||
proc pushInd(p: var RstParser, ind: int) =
|
||||
add(p.indentStack, ind)
|
||||
|
||||
proc popInd(p: var TRstParser) =
|
||||
proc popInd(p: var RstParser) =
|
||||
if len(p.indentStack) > 1: setLen(p.indentStack, len(p.indentStack) - 1)
|
||||
|
||||
proc initParser(p: var TRstParser, sharedState: PSharedState) =
|
||||
proc initParser(p: var RstParser, sharedState: PSharedState) =
|
||||
p.indentStack = @[0]
|
||||
p.tok = @[]
|
||||
p.idx = 0
|
||||
@@ -393,7 +400,7 @@ proc rstnodeToRefname(n: PRstNode): string =
|
||||
var b = false
|
||||
rstnodeToRefnameAux(n, result, b)
|
||||
|
||||
proc findSub(p: var TRstParser, n: PRstNode): int =
|
||||
proc findSub(p: var RstParser, n: PRstNode): int =
|
||||
var key = addNodes(n)
|
||||
# the spec says: if no exact match, try one without case distinction:
|
||||
for i in countup(0, high(p.s.subs)):
|
||||
@@ -404,7 +411,7 @@ proc findSub(p: var TRstParser, n: PRstNode): int =
|
||||
return i
|
||||
result = -1
|
||||
|
||||
proc setSub(p: var TRstParser, key: string, value: PRstNode) =
|
||||
proc setSub(p: var RstParser, key: string, value: PRstNode) =
|
||||
var length = len(p.s.subs)
|
||||
for i in countup(0, length - 1):
|
||||
if key == p.s.subs[i].key:
|
||||
@@ -414,7 +421,7 @@ proc setSub(p: var TRstParser, key: string, value: PRstNode) =
|
||||
p.s.subs[length].key = key
|
||||
p.s.subs[length].value = value
|
||||
|
||||
proc setRef(p: var TRstParser, key: string, value: PRstNode) =
|
||||
proc setRef(p: var RstParser, key: string, value: PRstNode) =
|
||||
var length = len(p.s.refs)
|
||||
for i in countup(0, length - 1):
|
||||
if key == p.s.refs[i].key:
|
||||
@@ -427,15 +434,15 @@ proc setRef(p: var TRstParser, key: string, value: PRstNode) =
|
||||
p.s.refs[length].key = key
|
||||
p.s.refs[length].value = value
|
||||
|
||||
proc findRef(p: var TRstParser, key: string): PRstNode =
|
||||
proc findRef(p: var RstParser, key: string): PRstNode =
|
||||
for i in countup(0, high(p.s.refs)):
|
||||
if key == p.s.refs[i].key:
|
||||
return p.s.refs[i].value
|
||||
|
||||
proc newLeaf(p: var TRstParser): PRstNode =
|
||||
proc newLeaf(p: var RstParser): PRstNode =
|
||||
result = newRstNode(rnLeaf, p.tok[p.idx].symbol)
|
||||
|
||||
proc getReferenceName(p: var TRstParser, endStr: string): PRstNode =
|
||||
proc getReferenceName(p: var RstParser, endStr: string): PRstNode =
|
||||
var res = newRstNode(rnInner)
|
||||
while true:
|
||||
case p.tok[p.idx].kind
|
||||
@@ -453,17 +460,17 @@ proc getReferenceName(p: var TRstParser, endStr: string): PRstNode =
|
||||
inc(p.idx)
|
||||
result = res
|
||||
|
||||
proc untilEol(p: var TRstParser): PRstNode =
|
||||
proc untilEol(p: var RstParser): PRstNode =
|
||||
result = newRstNode(rnInner)
|
||||
while not (p.tok[p.idx].kind in {tkIndent, tkEof}):
|
||||
add(result, newLeaf(p))
|
||||
inc(p.idx)
|
||||
|
||||
proc expect(p: var TRstParser, tok: string) =
|
||||
proc expect(p: var RstParser, tok: string) =
|
||||
if p.tok[p.idx].symbol == tok: inc(p.idx)
|
||||
else: rstMessage(p, meExpected, tok)
|
||||
|
||||
proc isInlineMarkupEnd(p: TRstParser, markup: string): bool =
|
||||
proc isInlineMarkupEnd(p: RstParser, markup: string): bool =
|
||||
result = p.tok[p.idx].symbol == markup
|
||||
if not result:
|
||||
return # Rule 3:
|
||||
@@ -480,7 +487,7 @@ proc isInlineMarkupEnd(p: TRstParser, markup: string): bool =
|
||||
if (markup != "``") and (p.tok[p.idx - 1].symbol == "\\"):
|
||||
result = false
|
||||
|
||||
proc isInlineMarkupStart(p: TRstParser, markup: string): bool =
|
||||
proc isInlineMarkupStart(p: RstParser, markup: string): bool =
|
||||
var d: char
|
||||
result = p.tok[p.idx].symbol == markup
|
||||
if not result:
|
||||
@@ -507,7 +514,7 @@ proc isInlineMarkupStart(p: TRstParser, markup: string): bool =
|
||||
else: d = '\0'
|
||||
if d != '\0': result = p.tok[p.idx + 1].symbol[0] != d
|
||||
|
||||
proc match(p: TRstParser, start: int, expr: string): bool =
|
||||
proc match(p: RstParser, start: int, expr: string): bool =
|
||||
# regular expressions are:
|
||||
# special char exact match
|
||||
# 'w' tkWord
|
||||
@@ -562,7 +569,7 @@ proc fixupEmbeddedRef(n, a, b: PRstNode) =
|
||||
for i in countup(0, sep - incr): add(a, n.sons[i])
|
||||
for i in countup(sep + 1, len(n) - 2): add(b, n.sons[i])
|
||||
|
||||
proc parsePostfix(p: var TRstParser, n: PRstNode): PRstNode =
|
||||
proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode =
|
||||
result = n
|
||||
if isInlineMarkupEnd(p, "_") or isInlineMarkupEnd(p, "__"):
|
||||
inc(p.idx)
|
||||
@@ -606,7 +613,7 @@ proc parsePostfix(p: var TRstParser, n: PRstNode): PRstNode =
|
||||
add(result, newRstNode(rnLeaf, p.tok[p.idx + 1].symbol))
|
||||
inc(p.idx, 3)
|
||||
|
||||
proc matchVerbatim(p: TRstParser, start: int, expr: string): int =
|
||||
proc matchVerbatim(p: RstParser, start: int, expr: string): int =
|
||||
result = start
|
||||
var j = 0
|
||||
while j < expr.len and result < p.tok.len and
|
||||
@@ -615,7 +622,7 @@ proc matchVerbatim(p: TRstParser, start: int, expr: string): int =
|
||||
inc result
|
||||
if j < expr.len: result = 0
|
||||
|
||||
proc parseSmiley(p: var TRstParser): PRstNode =
|
||||
proc parseSmiley(p: var RstParser): PRstNode =
|
||||
if p.tok[p.idx].symbol[0] notin SmileyStartChars: return
|
||||
for key, val in items(Smilies):
|
||||
let m = matchVerbatim(p, p.idx, key)
|
||||
@@ -631,12 +638,12 @@ when false:
|
||||
'$', '(', ')', '~', '_', '?', '+', '-', '=', '\\', '.', '&',
|
||||
'\128'..'\255'}
|
||||
|
||||
proc isUrl(p: TRstParser, i: int): bool =
|
||||
proc isUrl(p: RstParser, i: int): bool =
|
||||
result = (p.tok[i+1].symbol == ":") and (p.tok[i+2].symbol == "//") and
|
||||
(p.tok[i+3].kind == tkWord) and
|
||||
(p.tok[i].symbol in ["http", "https", "ftp", "telnet", "file"])
|
||||
|
||||
proc parseUrl(p: var TRstParser, father: PRstNode) =
|
||||
proc parseUrl(p: var RstParser, father: PRstNode) =
|
||||
#if p.tok[p.idx].symbol[strStart] == '<':
|
||||
if isUrl(p, p.idx):
|
||||
var n = newRstNode(rnStandaloneHyperlink)
|
||||
@@ -656,7 +663,7 @@ proc parseUrl(p: var TRstParser, father: PRstNode) =
|
||||
if p.tok[p.idx].symbol == "_": n = parsePostfix(p, n)
|
||||
add(father, n)
|
||||
|
||||
proc parseBackslash(p: var TRstParser, father: PRstNode) =
|
||||
proc parseBackslash(p: var RstParser, father: PRstNode) =
|
||||
assert(p.tok[p.idx].kind == tkPunct)
|
||||
if p.tok[p.idx].symbol == "\\\\":
|
||||
add(father, newRstNode(rnLeaf, "\\"))
|
||||
@@ -671,7 +678,7 @@ proc parseBackslash(p: var TRstParser, father: PRstNode) =
|
||||
inc(p.idx)
|
||||
|
||||
when false:
|
||||
proc parseAdhoc(p: var TRstParser, father: PRstNode, verbatim: bool) =
|
||||
proc parseAdhoc(p: var RstParser, father: PRstNode, verbatim: bool) =
|
||||
if not verbatim and isURL(p, p.idx):
|
||||
var n = newRstNode(rnStandaloneHyperlink)
|
||||
while true:
|
||||
@@ -694,7 +701,7 @@ when false:
|
||||
if p.tok[p.idx].symbol == "_": n = parsePostfix(p, n)
|
||||
add(father, n)
|
||||
|
||||
proc parseUntil(p: var TRstParser, father: PRstNode, postfix: string,
|
||||
proc parseUntil(p: var RstParser, father: PRstNode, postfix: string,
|
||||
interpretBackslash: bool) =
|
||||
let
|
||||
line = p.tok[p.idx].line
|
||||
@@ -725,7 +732,7 @@ proc parseUntil(p: var TRstParser, father: PRstNode, postfix: string,
|
||||
inc(p.idx)
|
||||
else: rstMessage(p, meExpected, postfix, line, col)
|
||||
|
||||
proc parseMarkdownCodeblock(p: var TRstParser): PRstNode =
|
||||
proc parseMarkdownCodeblock(p: var RstParser): PRstNode =
|
||||
var args = newRstNode(rnDirArg)
|
||||
if p.tok[p.idx].kind == tkWord:
|
||||
add(args, newLeaf(p))
|
||||
@@ -755,7 +762,7 @@ proc parseMarkdownCodeblock(p: var TRstParser): PRstNode =
|
||||
add(result, nil)
|
||||
add(result, lb)
|
||||
|
||||
proc parseInline(p: var TRstParser, father: PRstNode) =
|
||||
proc parseInline(p: var RstParser, father: PRstNode) =
|
||||
case p.tok[p.idx].kind
|
||||
of tkPunct:
|
||||
if isInlineMarkupStart(p, "***"):
|
||||
@@ -810,7 +817,7 @@ proc parseInline(p: var TRstParser, father: PRstNode) =
|
||||
inc(p.idx)
|
||||
else: discard
|
||||
|
||||
proc getDirective(p: var TRstParser): string =
|
||||
proc getDirective(p: var RstParser): string =
|
||||
if p.tok[p.idx].kind == tkWhite and p.tok[p.idx+1].kind == tkWord:
|
||||
var j = p.idx
|
||||
inc(p.idx)
|
||||
@@ -830,7 +837,7 @@ proc getDirective(p: var TRstParser): string =
|
||||
else:
|
||||
result = ""
|
||||
|
||||
proc parseComment(p: var TRstParser): PRstNode =
|
||||
proc parseComment(p: var RstParser): PRstNode =
|
||||
case p.tok[p.idx].kind
|
||||
of tkIndent, tkEof:
|
||||
if p.tok[p.idx].kind != tkEof and p.tok[p.idx + 1].kind == tkIndent:
|
||||
@@ -851,34 +858,35 @@ proc parseComment(p: var TRstParser): PRstNode =
|
||||
result = nil
|
||||
|
||||
type
|
||||
TDirKind = enum # must be ordered alphabetically!
|
||||
DirKind = enum # must be ordered alphabetically!
|
||||
dkNone, dkAuthor, dkAuthors, dkCode, dkCodeBlock, dkContainer, dkContents,
|
||||
dkFigure, dkImage, dkInclude, dkIndex, dkRaw, dkTitle
|
||||
{.deprecated: [TDirKind: DirKind].}
|
||||
|
||||
const
|
||||
DirIds: array[0..12, string] = ["", "author", "authors", "code",
|
||||
"code-block", "container", "contents", "figure", "image", "include",
|
||||
"index", "raw", "title"]
|
||||
|
||||
proc getDirKind(s: string): TDirKind =
|
||||
proc getDirKind(s: string): DirKind =
|
||||
let i = find(DirIds, s)
|
||||
if i >= 0: result = TDirKind(i)
|
||||
if i >= 0: result = DirKind(i)
|
||||
else: result = dkNone
|
||||
|
||||
proc parseLine(p: var TRstParser, father: PRstNode) =
|
||||
proc parseLine(p: var RstParser, father: PRstNode) =
|
||||
while true:
|
||||
case p.tok[p.idx].kind
|
||||
of tkWhite, tkWord, tkOther, tkPunct: parseInline(p, father)
|
||||
else: break
|
||||
|
||||
proc parseUntilNewline(p: var TRstParser, father: PRstNode) =
|
||||
proc parseUntilNewline(p: var RstParser, father: PRstNode) =
|
||||
while true:
|
||||
case p.tok[p.idx].kind
|
||||
of tkWhite, tkWord, tkAdornment, tkOther, tkPunct: parseInline(p, father)
|
||||
of tkEof, tkIndent: break
|
||||
|
||||
proc parseSection(p: var TRstParser, result: PRstNode) {.gcsafe.}
|
||||
proc parseField(p: var TRstParser): PRstNode =
|
||||
proc parseSection(p: var RstParser, result: PRstNode) {.gcsafe.}
|
||||
proc parseField(p: var RstParser): PRstNode =
|
||||
## Returns a parsed rnField node.
|
||||
##
|
||||
## rnField nodes have two children nodes, a rnFieldName and a rnFieldBody.
|
||||
@@ -897,7 +905,7 @@ proc parseField(p: var TRstParser): PRstNode =
|
||||
add(result, fieldname)
|
||||
add(result, fieldbody)
|
||||
|
||||
proc parseFields(p: var TRstParser): PRstNode =
|
||||
proc parseFields(p: var RstParser): PRstNode =
|
||||
## Parses fields for a section or directive block.
|
||||
##
|
||||
## This proc may return nil if the parsing doesn't find anything of value,
|
||||
@@ -947,8 +955,8 @@ proc getArgument(n: PRstNode): string =
|
||||
if n.sons[0] == nil: result = ""
|
||||
else: result = addNodes(n.sons[0])
|
||||
|
||||
proc parseDotDot(p: var TRstParser): PRstNode {.gcsafe.}
|
||||
proc parseLiteralBlock(p: var TRstParser): PRstNode =
|
||||
proc parseDotDot(p: var RstParser): PRstNode {.gcsafe.}
|
||||
proc parseLiteralBlock(p: var RstParser): PRstNode =
|
||||
result = newRstNode(rnLiteralBlock)
|
||||
var n = newRstNode(rnLeaf, "")
|
||||
if p.tok[p.idx].kind == tkIndent:
|
||||
@@ -974,13 +982,13 @@ proc parseLiteralBlock(p: var TRstParser): PRstNode =
|
||||
inc(p.idx)
|
||||
add(result, n)
|
||||
|
||||
proc getLevel(map: var TLevelMap, lvl: var int, c: char): int =
|
||||
proc getLevel(map: var LevelMap, lvl: var int, c: char): int =
|
||||
if map[c] == 0:
|
||||
inc(lvl)
|
||||
map[c] = lvl
|
||||
result = map[c]
|
||||
|
||||
proc tokenAfterNewline(p: TRstParser): int =
|
||||
proc tokenAfterNewline(p: RstParser): int =
|
||||
result = p.idx
|
||||
while true:
|
||||
case p.tok[result].kind
|
||||
@@ -991,28 +999,28 @@ proc tokenAfterNewline(p: TRstParser): int =
|
||||
break
|
||||
else: inc(result)
|
||||
|
||||
proc isLineBlock(p: TRstParser): bool =
|
||||
proc isLineBlock(p: RstParser): bool =
|
||||
var j = tokenAfterNewline(p)
|
||||
result = (p.tok[p.idx].col == p.tok[j].col) and (p.tok[j].symbol == "|") or
|
||||
(p.tok[j].col > p.tok[p.idx].col)
|
||||
|
||||
proc predNL(p: TRstParser): bool =
|
||||
proc predNL(p: RstParser): bool =
|
||||
result = true
|
||||
if p.idx > 0:
|
||||
result = p.tok[p.idx-1].kind == tkIndent and
|
||||
p.tok[p.idx-1].ival == currInd(p)
|
||||
|
||||
proc isDefList(p: TRstParser): bool =
|
||||
proc isDefList(p: RstParser): bool =
|
||||
var j = tokenAfterNewline(p)
|
||||
result = (p.tok[p.idx].col < p.tok[j].col) and
|
||||
(p.tok[j].kind in {tkWord, tkOther, tkPunct}) and
|
||||
(p.tok[j - 2].symbol != "::")
|
||||
|
||||
proc isOptionList(p: TRstParser): bool =
|
||||
proc isOptionList(p: RstParser): bool =
|
||||
result = match(p, p.idx, "-w") or match(p, p.idx, "--w") or
|
||||
match(p, p.idx, "/w") or match(p, p.idx, "//w")
|
||||
|
||||
proc whichSection(p: TRstParser): TRstNodeKind =
|
||||
proc whichSection(p: RstParser): RstNodeKind =
|
||||
case p.tok[p.idx].kind
|
||||
of tkAdornment:
|
||||
if match(p, p.idx + 1, "ii"): result = rnTransition
|
||||
@@ -1053,7 +1061,7 @@ proc whichSection(p: TRstParser): TRstNodeKind =
|
||||
else: result = rnParagraph
|
||||
else: result = rnLeaf
|
||||
|
||||
proc parseLineBlock(p: var TRstParser): PRstNode =
|
||||
proc parseLineBlock(p: var RstParser): PRstNode =
|
||||
result = nil
|
||||
if p.tok[p.idx + 1].kind == tkWhite:
|
||||
var col = p.tok[p.idx].col
|
||||
@@ -1072,7 +1080,7 @@ proc parseLineBlock(p: var TRstParser): PRstNode =
|
||||
break
|
||||
popInd(p)
|
||||
|
||||
proc parseParagraph(p: var TRstParser, result: PRstNode) =
|
||||
proc parseParagraph(p: var RstParser, result: PRstNode) =
|
||||
while true:
|
||||
case p.tok[p.idx].kind
|
||||
of tkIndent:
|
||||
@@ -1103,7 +1111,7 @@ proc parseParagraph(p: var TRstParser, result: PRstNode) =
|
||||
parseInline(p, result)
|
||||
else: break
|
||||
|
||||
proc parseHeadline(p: var TRstParser): PRstNode =
|
||||
proc parseHeadline(p: var RstParser): PRstNode =
|
||||
result = newRstNode(rnHeadline)
|
||||
parseUntilNewline(p, result)
|
||||
assert(p.tok[p.idx].kind == tkIndent)
|
||||
@@ -1113,12 +1121,13 @@ proc parseHeadline(p: var TRstParser): PRstNode =
|
||||
result.level = getLevel(p.s.underlineToLevel, p.s.uLevel, c)
|
||||
|
||||
type
|
||||
TIntSeq = seq[int]
|
||||
IntSeq = seq[int]
|
||||
{.deprecated: [TIntSeq: IntSeq].}
|
||||
|
||||
proc tokEnd(p: TRstParser): int =
|
||||
proc tokEnd(p: RstParser): int =
|
||||
result = p.tok[p.idx].col + len(p.tok[p.idx].symbol) - 1
|
||||
|
||||
proc getColumns(p: var TRstParser, cols: var TIntSeq) =
|
||||
proc getColumns(p: var RstParser, cols: var IntSeq) =
|
||||
var L = 0
|
||||
while true:
|
||||
inc(L)
|
||||
@@ -1133,15 +1142,15 @@ proc getColumns(p: var TRstParser, cols: var TIntSeq) =
|
||||
# last column has no limit:
|
||||
cols[L - 1] = 32000
|
||||
|
||||
proc parseDoc(p: var TRstParser): PRstNode {.gcsafe.}
|
||||
proc parseDoc(p: var RstParser): PRstNode {.gcsafe.}
|
||||
|
||||
proc parseSimpleTable(p: var TRstParser): PRstNode =
|
||||
proc parseSimpleTable(p: var RstParser): PRstNode =
|
||||
var
|
||||
cols: TIntSeq
|
||||
cols: IntSeq
|
||||
row: seq[string]
|
||||
i, last, line: int
|
||||
c: char
|
||||
q: TRstParser
|
||||
q: RstParser
|
||||
a, b: PRstNode
|
||||
result = newRstNode(rnTable)
|
||||
cols = @[]
|
||||
@@ -1188,13 +1197,13 @@ proc parseSimpleTable(p: var TRstParser): PRstNode =
|
||||
add(a, b)
|
||||
add(result, a)
|
||||
|
||||
proc parseTransition(p: var TRstParser): PRstNode =
|
||||
proc parseTransition(p: var RstParser): PRstNode =
|
||||
result = newRstNode(rnTransition)
|
||||
inc(p.idx)
|
||||
if p.tok[p.idx].kind == tkIndent: inc(p.idx)
|
||||
if p.tok[p.idx].kind == tkIndent: inc(p.idx)
|
||||
|
||||
proc parseOverline(p: var TRstParser): PRstNode =
|
||||
proc parseOverline(p: var RstParser): PRstNode =
|
||||
var c = p.tok[p.idx].symbol[0]
|
||||
inc(p.idx, 2)
|
||||
result = newRstNode(rnOverline)
|
||||
@@ -1213,7 +1222,7 @@ proc parseOverline(p: var TRstParser): PRstNode =
|
||||
inc(p.idx) # XXX: check?
|
||||
if p.tok[p.idx].kind == tkIndent: inc(p.idx)
|
||||
|
||||
proc parseBulletList(p: var TRstParser): PRstNode =
|
||||
proc parseBulletList(p: var RstParser): PRstNode =
|
||||
result = nil
|
||||
if p.tok[p.idx + 1].kind == tkWhite:
|
||||
var bullet = p.tok[p.idx].symbol
|
||||
@@ -1233,7 +1242,7 @@ proc parseBulletList(p: var TRstParser): PRstNode =
|
||||
break
|
||||
popInd(p)
|
||||
|
||||
proc parseOptionList(p: var TRstParser): PRstNode =
|
||||
proc parseOptionList(p: var RstParser): PRstNode =
|
||||
result = newRstNode(rnOptionList)
|
||||
while true:
|
||||
if isOptionList(p):
|
||||
@@ -1262,7 +1271,7 @@ proc parseOptionList(p: var TRstParser): PRstNode =
|
||||
else:
|
||||
break
|
||||
|
||||
proc parseDefinitionList(p: var TRstParser): PRstNode =
|
||||
proc parseDefinitionList(p: var RstParser): PRstNode =
|
||||
result = nil
|
||||
var j = tokenAfterNewline(p) - 1
|
||||
if (j >= 1) and (p.tok[j].kind == tkIndent) and
|
||||
@@ -1298,7 +1307,7 @@ proc parseDefinitionList(p: var TRstParser): PRstNode =
|
||||
break
|
||||
if len(result) == 0: result = nil
|
||||
|
||||
proc parseEnumList(p: var TRstParser): PRstNode =
|
||||
proc parseEnumList(p: var RstParser): PRstNode =
|
||||
const
|
||||
wildcards: array[0..2, string] = ["(e) ", "e) ", "e. "]
|
||||
wildpos: array[0..2, int] = [1, 0, 0]
|
||||
@@ -1328,11 +1337,11 @@ proc parseEnumList(p: var TRstParser): PRstNode =
|
||||
dec(p.idx, wildpos[w] + 3)
|
||||
result = nil
|
||||
|
||||
proc sonKind(father: PRstNode, i: int): TRstNodeKind =
|
||||
proc sonKind(father: PRstNode, i: int): RstNodeKind =
|
||||
result = rnLeaf
|
||||
if i < len(father): result = father.sons[i].kind
|
||||
|
||||
proc parseSection(p: var TRstParser, result: PRstNode) =
|
||||
proc parseSection(p: var RstParser, result: PRstNode) =
|
||||
while true:
|
||||
var leave = false
|
||||
assert(p.idx >= 0)
|
||||
@@ -1380,16 +1389,16 @@ proc parseSection(p: var TRstParser, result: PRstNode) =
|
||||
if sonKind(result, 0) == rnParagraph and sonKind(result, 1) != rnParagraph:
|
||||
result.sons[0].kind = rnInner
|
||||
|
||||
proc parseSectionWrapper(p: var TRstParser): PRstNode =
|
||||
proc parseSectionWrapper(p: var RstParser): PRstNode =
|
||||
result = newRstNode(rnInner)
|
||||
parseSection(p, result)
|
||||
while (result.kind == rnInner) and (len(result) == 1):
|
||||
result = result.sons[0]
|
||||
|
||||
proc `$`(t: TToken): string =
|
||||
proc `$`(t: Token): string =
|
||||
result = $t.kind & ' ' & (if isNil(t.symbol): "NIL" else: t.symbol)
|
||||
|
||||
proc parseDoc(p: var TRstParser): PRstNode =
|
||||
proc parseDoc(p: var RstParser): PRstNode =
|
||||
result = parseSectionWrapper(p)
|
||||
if p.tok[p.idx].kind != tkEof:
|
||||
when false:
|
||||
@@ -1403,12 +1412,14 @@ proc parseDoc(p: var TRstParser): PRstNode =
|
||||
rstMessage(p, meGeneralParseError)
|
||||
|
||||
type
|
||||
TDirFlag = enum
|
||||
DirFlag = enum
|
||||
hasArg, hasOptions, argIsFile, argIsWord
|
||||
TDirFlags = set[TDirFlag]
|
||||
TSectionParser = proc (p: var TRstParser): PRstNode {.nimcall.}
|
||||
DirFlags = set[DirFlag]
|
||||
SectionParser = proc (p: var RstParser): PRstNode {.nimcall.}
|
||||
{.deprecated: [TDirFlag: DirFlag, TDirFlags: DirFlags,
|
||||
TSectionParser: SectionParser].}
|
||||
|
||||
proc parseDirective(p: var TRstParser, flags: TDirFlags): PRstNode =
|
||||
proc parseDirective(p: var RstParser, flags: DirFlags): PRstNode =
|
||||
## Parses arguments and options for a directive block.
|
||||
##
|
||||
## A directive block will always have three sons: the arguments for the
|
||||
@@ -1446,11 +1457,11 @@ proc parseDirective(p: var TRstParser, flags: TDirFlags): PRstNode =
|
||||
options = parseFields(p)
|
||||
add(result, options)
|
||||
|
||||
proc indFollows(p: TRstParser): bool =
|
||||
proc indFollows(p: RstParser): bool =
|
||||
result = p.tok[p.idx].kind == tkIndent and p.tok[p.idx].ival > currInd(p)
|
||||
|
||||
proc parseDirective(p: var TRstParser, flags: TDirFlags,
|
||||
contentParser: TSectionParser): PRstNode =
|
||||
proc parseDirective(p: var RstParser, flags: DirFlags,
|
||||
contentParser: SectionParser): PRstNode =
|
||||
## Returns a generic rnDirective tree.
|
||||
##
|
||||
## The children are rnDirArg, rnFieldList and rnLineBlock. Any might be nil.
|
||||
@@ -1463,13 +1474,13 @@ proc parseDirective(p: var TRstParser, flags: TDirFlags,
|
||||
else:
|
||||
add(result, nil)
|
||||
|
||||
proc parseDirBody(p: var TRstParser, contentParser: TSectionParser): PRstNode =
|
||||
proc parseDirBody(p: var RstParser, contentParser: SectionParser): PRstNode =
|
||||
if indFollows(p):
|
||||
pushInd(p, p.tok[p.idx].ival)
|
||||
result = contentParser(p)
|
||||
popInd(p)
|
||||
|
||||
proc dirInclude(p: var TRstParser): PRstNode =
|
||||
proc dirInclude(p: var RstParser): PRstNode =
|
||||
#
|
||||
#The following options are recognized:
|
||||
#
|
||||
@@ -1498,7 +1509,7 @@ proc dirInclude(p: var TRstParser): PRstNode =
|
||||
result = newRstNode(rnLiteralBlock)
|
||||
add(result, newRstNode(rnLeaf, readFile(path)))
|
||||
else:
|
||||
var q: TRstParser
|
||||
var q: RstParser
|
||||
initParser(q, p.s)
|
||||
q.filename = filename
|
||||
q.col += getTokens(readFile(path), false, q.tok)
|
||||
@@ -1507,7 +1518,7 @@ proc dirInclude(p: var TRstParser): PRstNode =
|
||||
# InternalError("Too many binary zeros in include file")
|
||||
result = parseDoc(q)
|
||||
|
||||
proc dirCodeBlock(p: var TRstParser, nimrodExtension = false): PRstNode =
|
||||
proc dirCodeBlock(p: var RstParser, nimrodExtension = false): PRstNode =
|
||||
## Parses a code block.
|
||||
##
|
||||
## Code blocks are rnDirective trees with a `kind` of rnCodeBlock. See the
|
||||
@@ -1548,35 +1559,35 @@ proc dirCodeBlock(p: var TRstParser, nimrodExtension = false): PRstNode =
|
||||
|
||||
result.kind = rnCodeBlock
|
||||
|
||||
proc dirContainer(p: var TRstParser): PRstNode =
|
||||
proc dirContainer(p: var RstParser): PRstNode =
|
||||
result = parseDirective(p, {hasArg}, parseSectionWrapper)
|
||||
assert(result.kind == rnDirective)
|
||||
assert(len(result) == 3)
|
||||
result.kind = rnContainer
|
||||
|
||||
proc dirImage(p: var TRstParser): PRstNode =
|
||||
proc dirImage(p: var RstParser): PRstNode =
|
||||
result = parseDirective(p, {hasOptions, hasArg, argIsFile}, nil)
|
||||
result.kind = rnImage
|
||||
|
||||
proc dirFigure(p: var TRstParser): PRstNode =
|
||||
proc dirFigure(p: var RstParser): PRstNode =
|
||||
result = parseDirective(p, {hasOptions, hasArg, argIsFile},
|
||||
parseSectionWrapper)
|
||||
result.kind = rnFigure
|
||||
|
||||
proc dirTitle(p: var TRstParser): PRstNode =
|
||||
proc dirTitle(p: var RstParser): PRstNode =
|
||||
result = parseDirective(p, {hasArg}, nil)
|
||||
result.kind = rnTitle
|
||||
|
||||
proc dirContents(p: var TRstParser): PRstNode =
|
||||
proc dirContents(p: var RstParser): PRstNode =
|
||||
result = parseDirective(p, {hasArg}, nil)
|
||||
result.kind = rnContents
|
||||
|
||||
proc dirIndex(p: var TRstParser): PRstNode =
|
||||
proc dirIndex(p: var RstParser): PRstNode =
|
||||
result = parseDirective(p, {}, parseSectionWrapper)
|
||||
result.kind = rnIndex
|
||||
|
||||
proc dirRawAux(p: var TRstParser, result: var PRstNode, kind: TRstNodeKind,
|
||||
contentParser: TSectionParser) =
|
||||
proc dirRawAux(p: var RstParser, result: var PRstNode, kind: RstNodeKind,
|
||||
contentParser: SectionParser) =
|
||||
var filename = getFieldValue(result, "file")
|
||||
if filename.len > 0:
|
||||
var path = p.s.findFile(filename)
|
||||
@@ -1590,7 +1601,7 @@ proc dirRawAux(p: var TRstParser, result: var PRstNode, kind: TRstNodeKind,
|
||||
result.kind = kind
|
||||
add(result, parseDirBody(p, contentParser))
|
||||
|
||||
proc dirRaw(p: var TRstParser): PRstNode =
|
||||
proc dirRaw(p: var RstParser): PRstNode =
|
||||
#
|
||||
#The following options are recognized:
|
||||
#
|
||||
@@ -1610,7 +1621,7 @@ proc dirRaw(p: var TRstParser): PRstNode =
|
||||
else:
|
||||
dirRawAux(p, result, rnRaw, parseSectionWrapper)
|
||||
|
||||
proc parseDotDot(p: var TRstParser): PRstNode =
|
||||
proc parseDotDot(p: var RstParser): PRstNode =
|
||||
result = nil
|
||||
var col = p.tok[p.idx].col
|
||||
inc(p.idx)
|
||||
@@ -1667,7 +1678,7 @@ proc parseDotDot(p: var TRstParser): PRstNode =
|
||||
else:
|
||||
result = parseComment(p)
|
||||
|
||||
proc resolveSubs(p: var TRstParser, n: PRstNode): PRstNode =
|
||||
proc resolveSubs(p: var RstParser, n: PRstNode): PRstNode =
|
||||
result = n
|
||||
if n == nil: return
|
||||
case n.kind
|
||||
@@ -1696,10 +1707,10 @@ proc resolveSubs(p: var TRstParser, n: PRstNode): PRstNode =
|
||||
|
||||
proc rstParse*(text, filename: string,
|
||||
line, column: int, hasToc: var bool,
|
||||
options: TRstParseOptions,
|
||||
findFile: TFindFileHandler = nil,
|
||||
msgHandler: TMsgHandler = nil): PRstNode =
|
||||
var p: TRstParser
|
||||
options: RstParseOptions,
|
||||
findFile: FindFileHandler = nil,
|
||||
msgHandler: MsgHandler = nil): PRstNode =
|
||||
var p: RstParser
|
||||
initParser(p, newSharedState(options, findFile, msgHandler))
|
||||
p.filename = filename
|
||||
p.line = line
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
import strutils, json
|
||||
|
||||
type
|
||||
TRstNodeKind* = enum ## the possible node kinds of an PRstNode
|
||||
RstNodeKind* = enum ## the possible node kinds of an PRstNode
|
||||
rnInner, # an inner node or a root
|
||||
rnHeadline, # a headline
|
||||
rnOverline, # an over- and underlined headline
|
||||
@@ -62,24 +62,26 @@ type
|
||||
# leaf val
|
||||
|
||||
|
||||
PRstNode* = ref TRstNode ## an RST node
|
||||
TRstNodeSeq* = seq[PRstNode]
|
||||
TRstNode* {.acyclic, final.} = object ## an RST node's description
|
||||
kind*: TRstNodeKind ## the node's kind
|
||||
PRstNode* = ref RstNode ## an RST node
|
||||
RstNodeSeq* = seq[PRstNode]
|
||||
RstNode* {.acyclic, final.} = object ## an RST node's description
|
||||
kind*: RstNodeKind ## the node's kind
|
||||
text*: string ## valid for leafs in the AST; and the title of
|
||||
## the document or the section
|
||||
level*: int ## valid for some node kinds
|
||||
sons*: TRstNodeSeq ## the node's sons
|
||||
sons*: RstNodeSeq ## the node's sons
|
||||
{.deprecated: [TRstNodeKind: RstNodeKind, TRstNodeSeq: RstNodeSeq,
|
||||
TRstNode: RstNode].}
|
||||
|
||||
proc len*(n: PRstNode): int =
|
||||
result = len(n.sons)
|
||||
|
||||
proc newRstNode*(kind: TRstNodeKind): PRstNode =
|
||||
proc newRstNode*(kind: RstNodeKind): PRstNode =
|
||||
new(result)
|
||||
result.sons = @[]
|
||||
result.kind = kind
|
||||
|
||||
proc newRstNode*(kind: TRstNodeKind, s: string): PRstNode =
|
||||
proc newRstNode*(kind: RstNodeKind, s: string): PRstNode =
|
||||
result = newRstNode(kind)
|
||||
result.text = s
|
||||
|
||||
@@ -94,18 +96,19 @@ proc addIfNotNil*(father, son: PRstNode) =
|
||||
|
||||
|
||||
type
|
||||
TRenderContext {.pure.} = object
|
||||
RenderContext {.pure.} = object
|
||||
indent: int
|
||||
verbatim: int
|
||||
{.deprecated: [TRenderContext: RenderContext].}
|
||||
|
||||
proc renderRstToRst(d: var TRenderContext, n: PRstNode,
|
||||
proc renderRstToRst(d: var RenderContext, n: PRstNode,
|
||||
result: var string) {.gcsafe.}
|
||||
|
||||
proc renderRstSons(d: var TRenderContext, n: PRstNode, result: var string) =
|
||||
proc renderRstSons(d: var RenderContext, n: PRstNode, result: var string) =
|
||||
for i in countup(0, len(n) - 1):
|
||||
renderRstToRst(d, n.sons[i], result)
|
||||
|
||||
proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) =
|
||||
proc renderRstToRst(d: var RenderContext, n: PRstNode, result: var string) =
|
||||
# this is needed for the index generation; it may also be useful for
|
||||
# debugging, but most code is already debugged...
|
||||
const
|
||||
@@ -284,7 +287,7 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) =
|
||||
|
||||
proc renderRstToRst*(n: PRstNode, result: var string) =
|
||||
## renders `n` into its string representation and appends to `result`.
|
||||
var d: TRenderContext
|
||||
var d: RenderContext
|
||||
renderRstToRst(d, n, result)
|
||||
|
||||
proc renderRstToJsonNode(node: PRstNode): JsonNode =
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
## document you provide yourself, so it won't contain the usual ``<header>`` or
|
||||
## ``<body>`` parts.
|
||||
##
|
||||
## You can also create a ``TRstGenerator`` structure and populate it with the
|
||||
## You can also create a ``RstGenerator`` structure and populate it with the
|
||||
## other lower level methods to finally build complete documents. This requires
|
||||
## many options and tweaking, but you are not limited to snippets and can
|
||||
## generate `LaTeX documents <https://en.wikipedia.org/wiki/LaTeX>`_ too.
|
||||
@@ -31,29 +31,29 @@ const
|
||||
IndexExt* = ".idx"
|
||||
|
||||
type
|
||||
TOutputTarget* = enum ## which document type to generate
|
||||
OutputTarget* = enum ## which document type to generate
|
||||
outHtml, # output is HTML
|
||||
outLatex # output is Latex
|
||||
|
||||
TTocEntry = object
|
||||
TocEntry = object
|
||||
n*: PRstNode
|
||||
refname*, header*: string
|
||||
|
||||
TMetaEnum* = enum
|
||||
MetaEnum* = enum
|
||||
metaNone, metaTitle, metaSubtitle, metaAuthor, metaVersion
|
||||
|
||||
TRstGenerator* = object of RootObj
|
||||
target*: TOutputTarget
|
||||
RstGenerator* = object of RootObj
|
||||
target*: OutputTarget
|
||||
config*: StringTableRef
|
||||
splitAfter*: int # split too long entries in the TOC
|
||||
tocPart*: seq[TTocEntry]
|
||||
tocPart*: seq[TocEntry]
|
||||
hasToc*: bool
|
||||
theIndex: string # Contents of the index file to be dumped at the end.
|
||||
options*: TRstParseOptions
|
||||
findFile*: TFindFileHandler
|
||||
msgHandler*: TMsgHandler
|
||||
options*: RstParseOptions
|
||||
findFile*: FindFileHandler
|
||||
msgHandler*: MsgHandler
|
||||
filename*: string
|
||||
meta*: array[TMetaEnum, string]
|
||||
meta*: array[MetaEnum, string]
|
||||
currentSection: string ## \
|
||||
## Stores the empty string or the last headline/overline found in the rst
|
||||
## document, so it can be used as a prettier name for term index generation.
|
||||
@@ -61,14 +61,15 @@ type
|
||||
## Keeps count of same text index terms to generate different identifiers
|
||||
## for hyperlinks. See renderIndexTerm proc for details.
|
||||
|
||||
PDoc = var TRstGenerator ## Alias to type less.
|
||||
PDoc = var RstGenerator ## Alias to type less.
|
||||
|
||||
CodeBlockParams = object ## Stores code block params.
|
||||
numberLines: bool ## True if the renderer has to show line numbers.
|
||||
startLine: int ## The starting line of the code block, by default 1.
|
||||
langStr: string ## Input string used to specify the language.
|
||||
lang: TSourceLanguage ## Type of highlighting, by default none.
|
||||
|
||||
lang: SourceLanguage ## Type of highlighting, by default none.
|
||||
{.deprecated: [TRstGenerator: RstGenerator, TTocEntry: TocEntry,
|
||||
TOutputTarget: OutputTarget, TMetaEnum: MetaEnum].}
|
||||
|
||||
proc init(p: var CodeBlockParams) =
|
||||
## Default initialisation of CodeBlockParams to sane values.
|
||||
@@ -76,14 +77,14 @@ proc init(p: var CodeBlockParams) =
|
||||
p.lang = langNone
|
||||
p.langStr = ""
|
||||
|
||||
proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget,
|
||||
proc initRstGenerator*(g: var RstGenerator, target: OutputTarget,
|
||||
config: StringTableRef, filename: string,
|
||||
options: TRstParseOptions,
|
||||
findFile: TFindFileHandler=nil,
|
||||
msgHandler: TMsgHandler=nil) =
|
||||
## Initializes a ``TRstGenerator``.
|
||||
options: RstParseOptions,
|
||||
findFile: FindFileHandler=nil,
|
||||
msgHandler: MsgHandler=nil) =
|
||||
## Initializes a ``RstGenerator``.
|
||||
##
|
||||
## You need to call this before using a ``TRstGenerator`` with any other
|
||||
## You need to call this before using a ``RstGenerator`` with any other
|
||||
## procs in this module. Pass a non ``nil`` ``StringTableRef`` value as
|
||||
## `config` with parameters used by the HTML output generator. If you don't
|
||||
## know what to use, pass the results of the `defaultConfig()
|
||||
@@ -96,7 +97,7 @@ proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget,
|
||||
## filename``. This default title can be overriden by the embedded rst, but
|
||||
## it helps to prettify the generated index if no title is found.
|
||||
##
|
||||
## The ``TRstParseOptions``, ``TFindFileHandler`` and ``TMsgHandler`` types
|
||||
## The ``RstParseOptions``, ``FindFileHandler`` and ``MsgHandler`` types
|
||||
## are defined in the the `packages/docutils/rst module <rst.html>`_.
|
||||
## ``options`` selects the behaviour of the rst parser.
|
||||
##
|
||||
@@ -120,7 +121,7 @@ proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget,
|
||||
##
|
||||
## import packages/docutils/rstgen
|
||||
##
|
||||
## var gen: TRstGenerator
|
||||
## var gen: RstGenerator
|
||||
## gen.initRstGenerator(outHtml, defaultConfig(), "filename", {})
|
||||
g.config = config
|
||||
g.target = target
|
||||
@@ -141,7 +142,7 @@ proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget,
|
||||
if s != "": g.splitAfter = parseInt(s)
|
||||
for i in low(g.meta)..high(g.meta): g.meta[i] = ""
|
||||
|
||||
proc writeIndexFile*(g: var TRstGenerator, outfile: string) =
|
||||
proc writeIndexFile*(g: var RstGenerator, outfile: string) =
|
||||
## Writes the current index buffer to the specified output file.
|
||||
##
|
||||
## You previously need to add entries to the index with the `setIndexTerm()
|
||||
@@ -183,7 +184,7 @@ proc addTexChar(dest: var string, c: char) =
|
||||
|
||||
var splitter*: string = "<wbr />"
|
||||
|
||||
proc escChar*(target: TOutputTarget, dest: var string, c: char) {.inline.} =
|
||||
proc escChar*(target: OutputTarget, dest: var string, c: char) {.inline.} =
|
||||
case target
|
||||
of outHtml: addXmlChar(dest, c)
|
||||
of outLatex: addTexChar(dest, c)
|
||||
@@ -200,7 +201,7 @@ proc nextSplitPoint*(s: string, start: int): int =
|
||||
inc(result)
|
||||
dec(result) # last valid index
|
||||
|
||||
proc esc*(target: TOutputTarget, s: string, splitAfter = -1): string =
|
||||
proc esc*(target: OutputTarget, s: string, splitAfter = -1): string =
|
||||
result = ""
|
||||
if splitAfter >= 0:
|
||||
var partLen = 0
|
||||
@@ -217,16 +218,16 @@ proc esc*(target: TOutputTarget, s: string, splitAfter = -1): string =
|
||||
for i in countup(0, len(s) - 1): escChar(target, result, s[i])
|
||||
|
||||
|
||||
proc disp(target: TOutputTarget, xml, tex: string): string =
|
||||
proc disp(target: OutputTarget, xml, tex: string): string =
|
||||
if target != outLatex: result = xml
|
||||
else: result = tex
|
||||
|
||||
proc dispF(target: TOutputTarget, xml, tex: string,
|
||||
proc dispF(target: OutputTarget, xml, tex: string,
|
||||
args: varargs[string]): string =
|
||||
if target != outLatex: result = xml % args
|
||||
else: result = tex % args
|
||||
|
||||
proc dispA(target: TOutputTarget, dest: var string,
|
||||
proc dispA(target: OutputTarget, dest: var string,
|
||||
xml, tex: string, args: varargs[string]) =
|
||||
if target != outLatex: addf(dest, xml, args)
|
||||
else: addf(dest, tex, args)
|
||||
@@ -234,10 +235,10 @@ proc dispA(target: TOutputTarget, dest: var string,
|
||||
proc `or`(x, y: string): string {.inline.} =
|
||||
result = if x.isNil: y else: x
|
||||
|
||||
proc renderRstToOut*(d: var TRstGenerator, n: PRstNode, result: var string)
|
||||
proc renderRstToOut*(d: var RstGenerator, n: PRstNode, result: var string)
|
||||
## Writes into ``result`` the rst ast ``n`` using the ``d`` configuration.
|
||||
##
|
||||
## Before using this proc you need to initialise a ``TRstGenerator`` with
|
||||
## Before using this proc you need to initialise a ``RstGenerator`` with
|
||||
## ``initRstGenerator`` and parse a rst file with ``rstParse`` from the
|
||||
## `packages/docutils/rst module <rst.html>`_. Example:
|
||||
##
|
||||
@@ -277,7 +278,7 @@ proc unquoteIndexColumn(text: string): string =
|
||||
## Returns the unquoted version generated by ``quoteIndexColumn``.
|
||||
result = text.replace("\\t", "\t").replace("\\n", "\n").replace("\\\\", "\\")
|
||||
|
||||
proc setIndexTerm*(d: var TRstGenerator, id, term: string,
|
||||
proc setIndexTerm*(d: var RstGenerator, id, term: string,
|
||||
linkTitle, linkDesc = "") =
|
||||
## Adds a `term` to the index using the specified hyperlink identifier.
|
||||
##
|
||||
@@ -351,30 +352,30 @@ proc renderIndexTerm*(d: PDoc, n: PRstNode, result: var string) =
|
||||
[id, term])
|
||||
|
||||
type
|
||||
TIndexEntry = object
|
||||
IndexEntry = object
|
||||
keyword: string
|
||||
link: string
|
||||
linkTitle: string ## If not nil, contains a prettier text for the href
|
||||
linkDesc: string ## If not nil, the title attribute of the final href
|
||||
|
||||
TIndexedDocs = Table[TIndexEntry, seq[TIndexEntry]] ## \
|
||||
IndexedDocs = Table[IndexEntry, seq[IndexEntry]] ## \
|
||||
## Contains the index sequences for doc types.
|
||||
##
|
||||
## The key is a *fake* TIndexEntry which will contain the title of the
|
||||
## The key is a *fake* IndexEntry which will contain the title of the
|
||||
## document in the `keyword` field and `link` will contain the html
|
||||
## filename for the document. `linkTitle` and `linkDesc` will be nil.
|
||||
##
|
||||
## The value indexed by this TIndexEntry is a sequence with the real index
|
||||
## The value indexed by this IndexEntry is a sequence with the real index
|
||||
## entries found in the ``.idx`` file.
|
||||
{.deprecated: [TIndexEntry: IndexEntry, TIndexedDocs: IndexedDocs].}
|
||||
|
||||
|
||||
proc cmp(a, b: TIndexEntry): int =
|
||||
## Sorts two ``TIndexEntry`` first by `keyword` field, then by `link`.
|
||||
proc cmp(a, b: IndexEntry): int =
|
||||
## Sorts two ``IndexEntry`` first by `keyword` field, then by `link`.
|
||||
result = cmpIgnoreStyle(a.keyword, b.keyword)
|
||||
if result == 0:
|
||||
result = cmpIgnoreStyle(a.link, b.link)
|
||||
|
||||
proc hash(x: TIndexEntry): THash =
|
||||
proc hash(x: IndexEntry): Hash =
|
||||
## Returns the hash for the combined fields of the type.
|
||||
##
|
||||
## The hash is computed as the chained hash of the individual string hashes.
|
||||
@@ -385,7 +386,7 @@ proc hash(x: TIndexEntry): THash =
|
||||
result = result !& (x.linkDesc or "").hash
|
||||
result = !$result
|
||||
|
||||
proc `<-`(a: var TIndexEntry, b: TIndexEntry) =
|
||||
proc `<-`(a: var IndexEntry, b: IndexEntry) =
|
||||
shallowCopy a.keyword, b.keyword
|
||||
shallowCopy a.link, b.link
|
||||
if b.linkTitle.isNil: a.linkTitle = nil
|
||||
@@ -393,7 +394,7 @@ proc `<-`(a: var TIndexEntry, b: TIndexEntry) =
|
||||
if b.linkDesc.isNil: a.linkDesc = nil
|
||||
else: shallowCopy a.linkDesc, b.linkDesc
|
||||
|
||||
proc sortIndex(a: var openArray[TIndexEntry]) =
|
||||
proc sortIndex(a: var openArray[IndexEntry]) =
|
||||
# we use shellsort here; fast and simple
|
||||
let n = len(a)
|
||||
var h = 1
|
||||
@@ -403,7 +404,7 @@ proc sortIndex(a: var openArray[TIndexEntry]) =
|
||||
while true:
|
||||
h = h div 3
|
||||
for i in countup(h, n - 1):
|
||||
var v: TIndexEntry
|
||||
var v: IndexEntry
|
||||
v <- a[i]
|
||||
var j = i
|
||||
while cmp(a[j-h], v) >= 0:
|
||||
@@ -413,7 +414,7 @@ proc sortIndex(a: var openArray[TIndexEntry]) =
|
||||
a[j] <- v
|
||||
if h == 1: break
|
||||
|
||||
proc generateSymbolIndex(symbols: seq[TIndexEntry]): string =
|
||||
proc generateSymbolIndex(symbols: seq[IndexEntry]): string =
|
||||
result = ""
|
||||
var i = 0
|
||||
while i < symbols.len:
|
||||
@@ -466,7 +467,7 @@ proc indentToLevel(level: var int, newLevel: int): string =
|
||||
result = repeat("</ul>", level - newLevel)
|
||||
level = newLevel
|
||||
|
||||
proc generateDocumentationTOC(entries: seq[TIndexEntry]): string =
|
||||
proc generateDocumentationTOC(entries: seq[IndexEntry]): string =
|
||||
## Returns the sequence of index entries in an HTML hierarchical list.
|
||||
result = ""
|
||||
# Build a list of levels and extracted titles to make processing easier.
|
||||
@@ -507,12 +508,12 @@ proc generateDocumentationTOC(entries: seq[TIndexEntry]): string =
|
||||
assert(not titleRef.isNil,
|
||||
"Can't use this proc on an API index, docs always have a title entry")
|
||||
|
||||
proc generateDocumentationIndex(docs: TIndexedDocs): string =
|
||||
proc generateDocumentationIndex(docs: IndexedDocs): string =
|
||||
## Returns all the documentation TOCs in an HTML hierarchical list.
|
||||
result = ""
|
||||
|
||||
# Sort the titles to generate their toc in alphabetical order.
|
||||
var titles = toSeq(keys[TIndexEntry, seq[TIndexEntry]](docs))
|
||||
var titles = toSeq(keys[IndexEntry, seq[IndexEntry]](docs))
|
||||
sort(titles, cmp)
|
||||
|
||||
for title in titles:
|
||||
@@ -520,12 +521,12 @@ proc generateDocumentationIndex(docs: TIndexedDocs): string =
|
||||
result.add("<ul><li><a href=\"" &
|
||||
title.link & "\">" & title.keyword & "</a>\n" & tocList & "</ul>\n")
|
||||
|
||||
proc generateDocumentationJumps(docs: TIndexedDocs): string =
|
||||
proc generateDocumentationJumps(docs: IndexedDocs): string =
|
||||
## Returns a plain list of hyperlinks to documentation TOCs in HTML.
|
||||
result = "Documents: "
|
||||
|
||||
# Sort the titles to generate their toc in alphabetical order.
|
||||
var titles = toSeq(keys[TIndexEntry, seq[TIndexEntry]](docs))
|
||||
var titles = toSeq(keys[IndexEntry, seq[IndexEntry]](docs))
|
||||
sort(titles, cmp)
|
||||
|
||||
var chunks: seq[string] = @[]
|
||||
@@ -545,14 +546,14 @@ proc generateModuleJumps(modules: seq[string]): string =
|
||||
result.add(chunks.join(", ") & ".<br>")
|
||||
|
||||
proc readIndexDir(dir: string):
|
||||
tuple[modules: seq[string], symbols: seq[TIndexEntry], docs: TIndexedDocs] =
|
||||
## Walks `dir` reading ``.idx`` files converting them in TIndexEntry items.
|
||||
tuple[modules: seq[string], symbols: seq[IndexEntry], docs: IndexedDocs] =
|
||||
## Walks `dir` reading ``.idx`` files converting them in IndexEntry items.
|
||||
##
|
||||
## Returns the list of found module names, the list of free symbol entries
|
||||
## and the different documentation indexes. The list of modules is sorted.
|
||||
## See the documentation of ``mergeIndexes`` for details.
|
||||
result.modules = @[]
|
||||
result.docs = initTable[TIndexEntry, seq[TIndexEntry]](32)
|
||||
result.docs = initTable[IndexEntry, seq[IndexEntry]](32)
|
||||
newSeq(result.symbols, 15_000)
|
||||
setLen(result.symbols, 0)
|
||||
var L = 0
|
||||
@@ -560,8 +561,8 @@ proc readIndexDir(dir: string):
|
||||
for kind, path in walkDir(dir):
|
||||
if kind == pcFile and path.endsWith(IndexExt):
|
||||
var
|
||||
fileEntries: seq[TIndexEntry]
|
||||
title: TIndexEntry
|
||||
fileEntries: seq[IndexEntry]
|
||||
title: IndexEntry
|
||||
F = 0
|
||||
newSeq(fileEntries, 500)
|
||||
setLen(fileEntries, 0)
|
||||
@@ -662,7 +663,7 @@ proc mergeIndexes*(dir: string): string =
|
||||
proc stripTOCHTML(s: string): string =
|
||||
## Ugly quick hack to remove HTML tags from TOC titles.
|
||||
##
|
||||
## A TTocEntry.header field already contains rendered HTML tags. Instead of
|
||||
## A TocEntry.header field already contains rendered HTML tags. Instead of
|
||||
## implementing a proper version of renderRstToOut() which recursively
|
||||
## renders an rst tree to plain text, we simply remove text found between
|
||||
## angled brackets. Given the limited possibilities of rst inside TOC titles
|
||||
@@ -728,12 +729,12 @@ proc renderOverline(d: PDoc, n: PRstNode, result: var string) =
|
||||
rstnodeToRefname(n), tmp, $chr(n.level - 1 + ord('A'))])
|
||||
|
||||
|
||||
proc renderTocEntry(d: PDoc, e: TTocEntry, result: var string) =
|
||||
proc renderTocEntry(d: PDoc, e: TocEntry, result: var string) =
|
||||
dispA(d.target, result,
|
||||
"<li><a class=\"reference\" id=\"$1_toc\" href=\"#$1\">$2</a></li>\n",
|
||||
"\\item\\label{$1_toc} $2\\ref{$1}\n", [e.refname, e.header])
|
||||
|
||||
proc renderTocEntries*(d: var TRstGenerator, j: var int, lvl: int,
|
||||
proc renderTocEntries*(d: var RstGenerator, j: var int, lvl: int,
|
||||
result: var string) =
|
||||
var tmp = ""
|
||||
while j <= high(d.tocPart):
|
||||
@@ -878,7 +879,7 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) =
|
||||
d.msgHandler(d.filename, 1, 0, mwUnsupportedLanguage, params.langStr)
|
||||
for letter in m.text: escChar(d.target, result, letter)
|
||||
else:
|
||||
var g: TGeneralTokenizer
|
||||
var g: GeneralTokenizer
|
||||
initGeneralTokenizer(g, m.text)
|
||||
while true:
|
||||
getNextToken(g, params.lang)
|
||||
@@ -1214,7 +1215,7 @@ $content
|
||||
|
||||
# ---------- forum ---------------------------------------------------------
|
||||
|
||||
proc rstToHtml*(s: string, options: TRstParseOptions,
|
||||
proc rstToHtml*(s: string, options: RstParseOptions,
|
||||
config: StringTableRef): string =
|
||||
## Converts an input rst string into embeddable HTML.
|
||||
##
|
||||
@@ -1233,7 +1234,7 @@ proc rstToHtml*(s: string, options: TRstParseOptions,
|
||||
## # --> <em>Hello</em> <strong>world</strong>!
|
||||
##
|
||||
## If you need to allow the rst ``include`` directive or tweak the generated
|
||||
## output you have to create your own ``TRstGenerator`` with
|
||||
## output you have to create your own ``RstGenerator`` with
|
||||
## ``initRstGenerator`` and related procs.
|
||||
|
||||
proc myFindFile(filename: string): string =
|
||||
@@ -1241,7 +1242,7 @@ proc rstToHtml*(s: string, options: TRstParseOptions,
|
||||
result = ""
|
||||
|
||||
const filen = "input"
|
||||
var d: TRstGenerator
|
||||
var d: RstGenerator
|
||||
initRstGenerator(d, outHtml, config, filen, options, myFindFile,
|
||||
rst.defaultMsgHandler)
|
||||
var dummyHasToc = false
|
||||
|
||||
@@ -12,14 +12,15 @@
|
||||
# Get the platform-dependent flags.
|
||||
# Structure describing an inotify event.
|
||||
type
|
||||
Tinotify_event*{.pure, final, importc: "struct inotify_event",
|
||||
InotifyEvent*{.pure, final, importc: "struct inotify_event",
|
||||
header: "<sys/inotify.h>".} = object
|
||||
wd*{.importc: "wd".}: cint # Watch descriptor.
|
||||
mask*{.importc: "mask".}: uint32 # Watch mask.
|
||||
cookie*{.importc: "cookie".}: uint32 # Cookie to synchronize two events.
|
||||
len*{.importc: "len".}: uint32 # Length (including NULs) of name.
|
||||
name*{.importc: "name".}: char # Name.
|
||||
|
||||
{.deprecated: [Tinotify_event: InotifyEvent].}
|
||||
|
||||
# Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH.
|
||||
const
|
||||
IN_ACCESS* = 0x00000001 # File was accessed.
|
||||
@@ -69,4 +70,4 @@ proc inotify_add_watch*(fd: cint; name: cstring; mask: uint32): cint{.
|
||||
cdecl, importc: "inotify_add_watch", header: "<sys/inotify.h>".}
|
||||
# Remove the watch specified by WD from the inotify instance FD.
|
||||
proc inotify_rm_watch*(fd: cint; wd: cint): cint{.cdecl,
|
||||
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.}
|
||||
proc clone*(fn: pointer; child_stack: pointer; flags: cint;
|
||||
arg: pointer; ptid: ptr TPid; tls: pointer;
|
||||
ctid: ptr TPid): cint {.importc, header: "<sched.h>".}
|
||||
arg: pointer; ptid: ptr Pid; tls: pointer;
|
||||
ctid: ptr Pid): cint {.importc, header: "<sched.h>".}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,17 +12,18 @@ import posix
|
||||
|
||||
type
|
||||
Speed* = cuint
|
||||
Tcflag* = cuint
|
||||
Cflag* = cuint
|
||||
{.deprecated: [Tcflag: Cflag].}
|
||||
|
||||
const
|
||||
NCCS* = 32
|
||||
|
||||
type
|
||||
Termios* {.importc: "struct termios", header: "<termios.h>".} = object
|
||||
c_iflag*: Tcflag # input mode flags
|
||||
c_oflag*: Tcflag # output mode flags
|
||||
c_cflag*: Tcflag # control mode flags
|
||||
c_lflag*: Tcflag # local mode flags
|
||||
c_iflag*: Cflag # input mode flags
|
||||
c_oflag*: Cflag # output mode flags
|
||||
c_cflag*: Cflag # control mode flags
|
||||
c_lflag*: Cflag # local mode flags
|
||||
c_line*: cuchar # line discipline
|
||||
c_cc*: array[NCCS, cuchar] # control characters
|
||||
|
||||
@@ -258,4 +259,4 @@ proc tcFlow*(fd: cint; action: cint): cint {.importc: "tcflow",
|
||||
header: "<termios.h>".}
|
||||
# Get process group ID for session leader for controlling terminal FD.
|
||||
|
||||
proc tcGetSid*(fd: cint): TPid {.importc: "tcgetsid", header: "<termios.h>".}
|
||||
proc tcGetSid*(fd: cint): Pid {.importc: "tcgetsid", header: "<termios.h>".}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## var
|
||||
## a: TActorPool[int, void]
|
||||
## a: ActorPool[int, void]
|
||||
## createActorPool(a)
|
||||
## for i in 0 .. < 300:
|
||||
## a.spawn(i, proc (x: int) {.thread.} = echo x)
|
||||
@@ -30,75 +30,76 @@
|
||||
from os import sleep
|
||||
|
||||
type
|
||||
TTask*[TIn, TOut] = object{.pure, final.} ## a task
|
||||
when TOut isnot void:
|
||||
receiver*: ptr TChannel[TOut] ## the receiver channel of the response
|
||||
action*: proc (x: TIn): TOut {.thread.} ## action to execute;
|
||||
Task*[In, Out] = object{.pure, final.} ## a task
|
||||
when Out isnot void:
|
||||
receiver*: ptr Channel[Out] ## the receiver channel of the response
|
||||
action*: proc (x: In): Out {.thread.} ## action to execute;
|
||||
## sometimes useful
|
||||
shutDown*: bool ## set to tell an actor to shut-down
|
||||
data*: TIn ## the data to process
|
||||
data*: In ## the data to process
|
||||
|
||||
TActor[TIn, TOut] = object{.pure, final.}
|
||||
i: TChannel[TTask[TIn, TOut]]
|
||||
t: TThread[ptr TActor[TIn, TOut]]
|
||||
Actor[In, Out] = object{.pure, final.}
|
||||
i: Channel[Task[In, Out]]
|
||||
t: TThread[ptr Actor[In, Out]]
|
||||
|
||||
PActor*[TIn, TOut] = ptr TActor[TIn, TOut] ## an actor
|
||||
|
||||
proc spawn*[TIn, TOut](action: proc(
|
||||
self: PActor[TIn, TOut]){.thread.}): PActor[TIn, TOut] =
|
||||
PActor*[In, Out] = ptr Actor[In, Out] ## an actor
|
||||
{.deprecated: [TTask: Task, TActor: Actor].}
|
||||
|
||||
proc spawn*[In, Out](action: proc(
|
||||
self: PActor[In, Out]){.thread.}): PActor[In, Out] =
|
||||
## creates an actor; that is a thread with an inbox. The caller MUST call
|
||||
## ``join`` because that also frees the actor's associated resources.
|
||||
result = cast[PActor[TIn, TOut]](allocShared0(sizeof(result[])))
|
||||
result = cast[PActor[In, Out]](allocShared0(sizeof(result[])))
|
||||
open(result.i)
|
||||
createThread(result.t, action, result)
|
||||
|
||||
proc inbox*[TIn, TOut](self: PActor[TIn, TOut]): ptr TChannel[TIn] =
|
||||
proc inbox*[In, Out](self: PActor[In, Out]): ptr Channel[In] =
|
||||
## gets a pointer to the associated inbox of the actor `self`.
|
||||
result = addr(self.i)
|
||||
|
||||
proc running*[TIn, TOut](a: PActor[TIn, TOut]): bool =
|
||||
proc running*[In, Out](a: PActor[In, Out]): bool =
|
||||
## returns true if the actor `a` is running.
|
||||
result = running(a.t)
|
||||
|
||||
proc ready*[TIn, TOut](a: PActor[TIn, TOut]): bool =
|
||||
proc ready*[In, Out](a: PActor[In, Out]): bool =
|
||||
## returns true if the actor `a` is ready to process new messages.
|
||||
result = ready(a.i)
|
||||
|
||||
proc join*[TIn, TOut](a: PActor[TIn, TOut]) =
|
||||
proc join*[In, Out](a: PActor[In, Out]) =
|
||||
## joins an actor.
|
||||
joinThread(a.t)
|
||||
close(a.i)
|
||||
deallocShared(a)
|
||||
|
||||
proc recv*[TIn, TOut](a: PActor[TIn, TOut]): TTask[TIn, TOut] =
|
||||
proc recv*[In, Out](a: PActor[In, Out]): Task[In, Out] =
|
||||
## receives a task from `a`'s inbox.
|
||||
result = recv(a.i)
|
||||
|
||||
proc send*[TIn, TOut, X, Y](receiver: PActor[TIn, TOut], msg: TIn,
|
||||
proc send*[In, Out, X, Y](receiver: PActor[In, Out], msg: In,
|
||||
sender: PActor[X, Y]) =
|
||||
## sends a message to `a`'s inbox.
|
||||
var t: TTask[TIn, TOut]
|
||||
var t: Task[In, Out]
|
||||
t.receiver = addr(sender.i)
|
||||
shallowCopy(t.data, msg)
|
||||
send(receiver.i, t)
|
||||
|
||||
proc send*[TIn, TOut](receiver: PActor[TIn, TOut], msg: TIn,
|
||||
sender: ptr TChannel[TOut] = nil) =
|
||||
proc send*[In, Out](receiver: PActor[In, Out], msg: In,
|
||||
sender: ptr Channel[Out] = nil) =
|
||||
## sends a message to `receiver`'s inbox.
|
||||
var t: TTask[TIn, TOut]
|
||||
var t: Task[In, Out]
|
||||
t.receiver = sender
|
||||
shallowCopy(t.data, msg)
|
||||
send(receiver.i, t)
|
||||
|
||||
proc sendShutdown*[TIn, TOut](receiver: PActor[TIn, TOut]) =
|
||||
proc sendShutdown*[In, Out](receiver: PActor[In, Out]) =
|
||||
## send a shutdown message to `receiver`.
|
||||
var t: TTask[TIn, TOut]
|
||||
var t: Task[In, Out]
|
||||
t.shutdown = true
|
||||
send(receiver.i, t)
|
||||
|
||||
proc reply*[TIn, TOut](t: TTask[TIn, TOut], m: TOut) =
|
||||
proc reply*[In, Out](t: Task[In, Out], m: Out) =
|
||||
## sends a message to io's output message box.
|
||||
when TOut is void:
|
||||
when Out is void:
|
||||
{.error: "you cannot reply to a void outbox".}
|
||||
assert t.receiver != nil
|
||||
send(t.receiver[], m)
|
||||
@@ -107,34 +108,35 @@ proc reply*[TIn, TOut](t: TTask[TIn, TOut], m: TOut) =
|
||||
# ----------------- actor pools ----------------------------------------------
|
||||
|
||||
type
|
||||
TActorPool*[TIn, TOut] = object{.pure, final.} ## an actor pool
|
||||
actors: seq[PActor[TIn, TOut]]
|
||||
when TOut isnot void:
|
||||
outputs: TChannel[TOut]
|
||||
ActorPool*[In, Out] = object{.pure, final.} ## an actor pool
|
||||
actors: seq[PActor[In, Out]]
|
||||
when Out isnot void:
|
||||
outputs: Channel[Out]
|
||||
{.deprecated: [TActorPool: ActorPool].}
|
||||
|
||||
proc `^`*[T](f: ptr TChannel[T]): T =
|
||||
proc `^`*[T](f: ptr Channel[T]): T =
|
||||
## alias for 'recv'.
|
||||
result = recv(f[])
|
||||
|
||||
proc poolWorker[TIn, TOut](self: PActor[TIn, TOut]) {.thread.} =
|
||||
proc poolWorker[In, Out](self: PActor[In, Out]) {.thread.} =
|
||||
while true:
|
||||
var m = self.recv
|
||||
if m.shutDown: break
|
||||
when TOut is void:
|
||||
when Out is void:
|
||||
m.action(m.data)
|
||||
else:
|
||||
send(m.receiver[], m.action(m.data))
|
||||
#self.reply()
|
||||
|
||||
proc createActorPool*[TIn, TOut](a: var TActorPool[TIn, TOut], poolSize = 4) =
|
||||
proc createActorPool*[In, Out](a: var ActorPool[In, Out], poolSize = 4) =
|
||||
## creates an actor pool.
|
||||
newSeq(a.actors, poolSize)
|
||||
when TOut isnot void:
|
||||
when Out isnot void:
|
||||
open(a.outputs)
|
||||
for i in 0 .. < a.actors.len:
|
||||
a.actors[i] = spawn(poolWorker[TIn, TOut])
|
||||
a.actors[i] = spawn(poolWorker[In, Out])
|
||||
|
||||
proc sync*[TIn, TOut](a: var TActorPool[TIn, TOut], 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
|
||||
## 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
|
||||
@@ -157,18 +159,18 @@ proc sync*[TIn, TOut](a: var TActorPool[TIn, TOut], polling=50) =
|
||||
if allReadyCount > 1: break
|
||||
sleep(polling)
|
||||
|
||||
proc terminate*[TIn, TOut](a: var TActorPool[TIn, TOut]) =
|
||||
proc terminate*[In, Out](a: var ActorPool[In, Out]) =
|
||||
## terminates each actor in the actor pool `a` and frees the
|
||||
## resources attached to `a`.
|
||||
var t: TTask[TIn, TOut]
|
||||
var t: Task[In, Out]
|
||||
t.shutdown = true
|
||||
for i in 0.. <a.actors.len: send(a.actors[i].i, t)
|
||||
for i in 0.. <a.actors.len: join(a.actors[i])
|
||||
when TOut isnot void:
|
||||
when Out isnot void:
|
||||
close(a.outputs)
|
||||
a.actors = nil
|
||||
|
||||
proc join*[TIn, TOut](a: var TActorPool[TIn, TOut]) =
|
||||
proc join*[In, Out](a: var ActorPool[In, Out]) =
|
||||
## short-cut for `sync` and then `terminate`.
|
||||
sync(a)
|
||||
terminate(a)
|
||||
@@ -202,28 +204,28 @@ template schedule =
|
||||
else:
|
||||
raise newException(DeadThreadError, "cannot send message; thread died")
|
||||
|
||||
proc spawn*[TIn, TOut](p: var TActorPool[TIn, TOut], input: TIn,
|
||||
action: proc (input: TIn): TOut {.thread.}
|
||||
): ptr TChannel[TOut] =
|
||||
proc spawn*[In, Out](p: var ActorPool[In, Out], input: In,
|
||||
action: proc (input: In): Out {.thread.}
|
||||
): ptr Channel[Out] =
|
||||
## uses the actor pool to run ``action(input)`` concurrently.
|
||||
## `spawn` is guaranteed to not block.
|
||||
var t: TTask[TIn, TOut]
|
||||
var t: Task[In, Out]
|
||||
setupTask()
|
||||
result = addr(p.outputs)
|
||||
t.receiver = result
|
||||
schedule()
|
||||
|
||||
proc spawn*[TIn](p: var TActorPool[TIn, void], input: TIn,
|
||||
action: proc (input: TIn) {.thread.}) =
|
||||
proc spawn*[In](p: var ActorPool[In, void], input: In,
|
||||
action: proc (input: In) {.thread.}) =
|
||||
## uses the actor pool to run ``action(input)`` concurrently.
|
||||
## `spawn` is guaranteed to not block.
|
||||
var t: TTask[TIn, void]
|
||||
var t: Task[In, void]
|
||||
setupTask()
|
||||
schedule()
|
||||
|
||||
when not defined(testing) and isMainModule:
|
||||
var
|
||||
a: TActorPool[int, void]
|
||||
a: ActorPool[int, void]
|
||||
createActorPool(a)
|
||||
for i in 0 .. < 300:
|
||||
a.spawn(i, proc (x: int) {.thread.} = echo x)
|
||||
|
||||
@@ -99,16 +99,13 @@ proc lowerBound*[T](a: openArray[T], key: T, cmp: proc(x,y: T): int {.closure.})
|
||||
## arr.insert(4, arr.lowerBound(4))
|
||||
## `after running the above arr is `[1,2,3,4,5,6,7,8,9]`
|
||||
result = a.low
|
||||
var pos = result
|
||||
var count, step: int
|
||||
count = a.high - a.low + 1
|
||||
var count = a.high - a.low + 1
|
||||
var step, pos: int
|
||||
while count != 0:
|
||||
pos = result
|
||||
step = count div 2
|
||||
pos += step
|
||||
pos = result + step
|
||||
if cmp(a[pos], key) < 0:
|
||||
pos.inc
|
||||
result = pos
|
||||
result = pos + 1
|
||||
count -= step + 1
|
||||
else:
|
||||
count = step
|
||||
@@ -331,3 +328,16 @@ proc prevPermutation*[T](x: var openarray[T]): bool {.discardable.} =
|
||||
swap x[i-1], x[j]
|
||||
|
||||
result = true
|
||||
|
||||
when isMainModule:
|
||||
# Tests for lowerBound
|
||||
var arr = @[1,2,3,5,6,7,8,9]
|
||||
assert arr.lowerBound(0) == 0
|
||||
assert arr.lowerBound(4) == 3
|
||||
assert arr.lowerBound(5) == 3
|
||||
assert arr.lowerBound(10) == 8
|
||||
arr = @[1,5,10]
|
||||
assert arr.lowerBound(4) == 1
|
||||
assert arr.lowerBound(5) == 1
|
||||
assert arr.lowerBound(6) == 2
|
||||
|
||||
|
||||
@@ -323,32 +323,34 @@ proc processTimers(p: PDispatcherBase) =
|
||||
when defined(windows) or defined(nimdoc):
|
||||
import winlean, sets, hashes
|
||||
type
|
||||
TCompletionKey = Dword
|
||||
CompletionKey = Dword
|
||||
|
||||
TCompletionData* = object
|
||||
fd*: TAsyncFD # TODO: Rename this.
|
||||
cb*: proc (fd: TAsyncFD, bytesTransferred: Dword,
|
||||
CompletionData* = object
|
||||
fd*: AsyncFD # TODO: Rename this.
|
||||
cb*: proc (fd: AsyncFD, bytesTransferred: Dword,
|
||||
errcode: OSErrorCode) {.closure,gcsafe.}
|
||||
|
||||
PDispatcher* = ref object of PDispatcherBase
|
||||
ioPort: THandle
|
||||
handles: HashSet[TAsyncFD]
|
||||
ioPort: Handle
|
||||
handles: HashSet[AsyncFD]
|
||||
|
||||
TCustomOverlapped = object of TOVERLAPPED
|
||||
data*: TCompletionData
|
||||
CustomOverlapped = object of TOVERLAPPED
|
||||
data*: CompletionData
|
||||
|
||||
PCustomOverlapped* = ref TCustomOverlapped
|
||||
PCustomOverlapped* = ref CustomOverlapped
|
||||
|
||||
TAsyncFD* = distinct int
|
||||
AsyncFD* = distinct int
|
||||
{.deprecated: [TCompletionKey: CompletionKey, TAsyncFD: AsyncFD,
|
||||
TCustomOverlapped: CustomOverlapped, TCompletionData: CompletionData].}
|
||||
|
||||
proc hash(x: TAsyncFD): THash {.borrow.}
|
||||
proc `==`*(x: TAsyncFD, y: TAsyncFD): bool {.borrow.}
|
||||
proc hash(x: AsyncFD): Hash {.borrow.}
|
||||
proc `==`*(x: AsyncFD, y: AsyncFD): bool {.borrow.}
|
||||
|
||||
proc newDispatcher*(): PDispatcher =
|
||||
## Creates a new Dispatcher instance.
|
||||
new result
|
||||
result.ioPort = createIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1)
|
||||
result.handles = initSet[TAsyncFD]()
|
||||
result.handles = initSet[AsyncFD]()
|
||||
result.timers = @[]
|
||||
|
||||
var gDisp{.threadvar.}: PDispatcher ## Global dispatcher
|
||||
@@ -357,15 +359,15 @@ when defined(windows) or defined(nimdoc):
|
||||
if gDisp.isNil: gDisp = newDispatcher()
|
||||
result = gDisp
|
||||
|
||||
proc register*(fd: TAsyncFD) =
|
||||
proc register*(fd: AsyncFD) =
|
||||
## Registers ``fd`` with the dispatcher.
|
||||
let p = getGlobalDispatcher()
|
||||
if createIoCompletionPort(fd.THandle, p.ioPort,
|
||||
cast[TCompletionKey](fd), 1) == 0:
|
||||
if createIoCompletionPort(fd.Handle, p.ioPort,
|
||||
cast[CompletionKey](fd), 1) == 0:
|
||||
raiseOSError(osLastError())
|
||||
p.handles.incl(fd)
|
||||
|
||||
proc verifyPresence(fd: TAsyncFD) =
|
||||
proc verifyPresence(fd: AsyncFD) =
|
||||
## Ensures that file descriptor has been registered with the dispatcher.
|
||||
let p = getGlobalDispatcher()
|
||||
if fd notin p.handles:
|
||||
@@ -394,7 +396,7 @@ when defined(windows) or defined(nimdoc):
|
||||
# TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html
|
||||
if res:
|
||||
# This is useful for ensuring the reliability of the overlapped struct.
|
||||
assert customOverlapped.data.fd == lpCompletionKey.TAsyncFD
|
||||
assert customOverlapped.data.fd == lpCompletionKey.AsyncFD
|
||||
|
||||
customOverlapped.data.cb(customOverlapped.data.fd,
|
||||
lpNumberOfBytesTransferred, OSErrorCode(-1))
|
||||
@@ -402,7 +404,7 @@ when defined(windows) or defined(nimdoc):
|
||||
else:
|
||||
let errCode = osLastError()
|
||||
if customOverlapped != nil:
|
||||
assert customOverlapped.data.fd == lpCompletionKey.TAsyncFD
|
||||
assert customOverlapped.data.fd == lpCompletionKey.AsyncFD
|
||||
customOverlapped.data.cb(customOverlapped.data.fd,
|
||||
lpNumberOfBytesTransferred, errCode)
|
||||
GC_unref(customOverlapped)
|
||||
@@ -480,7 +482,7 @@ when defined(windows) or defined(nimdoc):
|
||||
dwRemoteAddressLength, LocalSockaddr, LocalSockaddrLength,
|
||||
RemoteSockaddr, RemoteSockaddrLength)
|
||||
|
||||
proc connect*(socket: TAsyncFD, address: string, port: Port,
|
||||
proc connect*(socket: AsyncFD, address: string, port: Port,
|
||||
af = AF_INET): Future[void] =
|
||||
## Connects ``socket`` to server at ``address:port``.
|
||||
##
|
||||
@@ -506,8 +508,8 @@ when defined(windows) or defined(nimdoc):
|
||||
# http://blogs.msdn.com/b/oldnewthing/archive/2011/02/02/10123392.aspx
|
||||
var ol = PCustomOverlapped()
|
||||
GC_ref(ol)
|
||||
ol.data = TCompletionData(fd: socket, cb:
|
||||
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
ol.data = CompletionData(fd: socket, cb:
|
||||
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
if not retFuture.finished:
|
||||
if errcode == OSErrorCode(-1):
|
||||
retFuture.complete()
|
||||
@@ -542,7 +544,7 @@ when defined(windows) or defined(nimdoc):
|
||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||
return retFuture
|
||||
|
||||
proc recv*(socket: TAsyncFD, size: int,
|
||||
proc recv*(socket: AsyncFD, size: int,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[string] =
|
||||
## Reads **up to** ``size`` bytes from ``socket``. Returned future will
|
||||
## complete once all the data requested is read, a part of the data has been
|
||||
@@ -570,8 +572,8 @@ when defined(windows) or defined(nimdoc):
|
||||
var flagsio = flags.toOSFlags().Dword
|
||||
var ol = PCustomOverlapped()
|
||||
GC_ref(ol)
|
||||
ol.data = TCompletionData(fd: socket, cb:
|
||||
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
ol.data = CompletionData(fd: socket, cb:
|
||||
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
if not retFuture.finished:
|
||||
if errcode == OSErrorCode(-1):
|
||||
if bytesCount == 0 and dataBuf.buf[0] == '\0':
|
||||
@@ -634,7 +636,7 @@ when defined(windows) or defined(nimdoc):
|
||||
# free ``ol``.
|
||||
return retFuture
|
||||
|
||||
proc recvInto*(socket: TAsyncFD, buf: cstring, size: int,
|
||||
proc recvInto*(socket: AsyncFD, buf: cstring, size: int,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[int] =
|
||||
## Reads **up to** ``size`` bytes from ``socket`` into ``buf``, which must
|
||||
## at least be of that size. Returned future will complete once all the
|
||||
@@ -665,8 +667,8 @@ when defined(windows) or defined(nimdoc):
|
||||
var flagsio = flags.toOSFlags().Dword
|
||||
var ol = PCustomOverlapped()
|
||||
GC_ref(ol)
|
||||
ol.data = TCompletionData(fd: socket, cb:
|
||||
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
ol.data = CompletionData(fd: socket, cb:
|
||||
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
if not retFuture.finished:
|
||||
if errcode == OSErrorCode(-1):
|
||||
if bytesCount == 0 and dataBuf.buf[0] == '\0':
|
||||
@@ -721,7 +723,7 @@ when defined(windows) or defined(nimdoc):
|
||||
# free ``ol``.
|
||||
return retFuture
|
||||
|
||||
proc send*(socket: TAsyncFD, data: string,
|
||||
proc send*(socket: AsyncFD, data: string,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[void] =
|
||||
## Sends ``data`` to ``socket``. The returned future will complete once all
|
||||
## data has been sent.
|
||||
@@ -735,8 +737,8 @@ when defined(windows) or defined(nimdoc):
|
||||
var bytesReceived, lowFlags: Dword
|
||||
var ol = PCustomOverlapped()
|
||||
GC_ref(ol)
|
||||
ol.data = TCompletionData(fd: socket, cb:
|
||||
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
ol.data = CompletionData(fd: socket, cb:
|
||||
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
if not retFuture.finished:
|
||||
if errcode == OSErrorCode(-1):
|
||||
retFuture.complete()
|
||||
@@ -764,8 +766,8 @@ when defined(windows) or defined(nimdoc):
|
||||
# free ``ol``.
|
||||
return retFuture
|
||||
|
||||
proc acceptAddr*(socket: TAsyncFD, flags = {SocketFlag.SafeDisconn}):
|
||||
Future[tuple[address: string, client: TAsyncFD]] =
|
||||
proc acceptAddr*(socket: AsyncFD, flags = {SocketFlag.SafeDisconn}):
|
||||
Future[tuple[address: string, client: AsyncFD]] =
|
||||
## Accepts a new connection. Returns a future containing the client socket
|
||||
## corresponding to that connection and the remote address of the client.
|
||||
## The future will complete when the connection is successfully accepted.
|
||||
@@ -778,7 +780,7 @@ when defined(windows) or defined(nimdoc):
|
||||
## flag is specified then this error will not be raised and instead
|
||||
## accept will be called again.
|
||||
verifyPresence(socket)
|
||||
var retFuture = newFuture[tuple[address: string, client: TAsyncFD]]("acceptAddr")
|
||||
var retFuture = newFuture[tuple[address: string, client: AsyncFD]]("acceptAddr")
|
||||
|
||||
var clientSock = newRawSocket()
|
||||
if clientSock == osInvalidSocket: raiseOSError(osLastError())
|
||||
@@ -803,11 +805,11 @@ when defined(windows) or defined(nimdoc):
|
||||
dwLocalAddressLength, dwRemoteAddressLength,
|
||||
addr localSockaddr, addr localLen,
|
||||
addr remoteSockaddr, addr remoteLen)
|
||||
register(clientSock.TAsyncFD)
|
||||
register(clientSock.AsyncFD)
|
||||
# TODO: IPv6. Check ``sa_family``. http://stackoverflow.com/a/9212542/492186
|
||||
retFuture.complete(
|
||||
(address: $inet_ntoa(cast[ptr Sockaddr_in](remoteSockAddr).sin_addr),
|
||||
client: clientSock.TAsyncFD)
|
||||
client: clientSock.AsyncFD)
|
||||
)
|
||||
|
||||
template failAccept(errcode): stmt =
|
||||
@@ -824,8 +826,8 @@ when defined(windows) or defined(nimdoc):
|
||||
|
||||
var ol = PCustomOverlapped()
|
||||
GC_ref(ol)
|
||||
ol.data = TCompletionData(fd: socket, cb:
|
||||
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
ol.data = CompletionData(fd: socket, cb:
|
||||
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
if not retFuture.finished:
|
||||
if errcode == OSErrorCode(-1):
|
||||
completeAccept()
|
||||
@@ -853,26 +855,26 @@ when defined(windows) or defined(nimdoc):
|
||||
|
||||
return retFuture
|
||||
|
||||
proc newAsyncRawSocket*(domain, typ, protocol: cint): TAsyncFD =
|
||||
proc newAsyncRawSocket*(domain, typ, protocol: cint): AsyncFD =
|
||||
## Creates a new socket and registers it with the dispatcher implicitly.
|
||||
result = newRawSocket(domain, typ, protocol).TAsyncFD
|
||||
result = newRawSocket(domain, typ, protocol).AsyncFD
|
||||
result.SocketHandle.setBlocking(false)
|
||||
register(result)
|
||||
|
||||
proc newAsyncRawSocket*(domain: Domain = AF_INET,
|
||||
typ: SockType = SOCK_STREAM,
|
||||
protocol: Protocol = IPPROTO_TCP): TAsyncFD =
|
||||
protocol: Protocol = IPPROTO_TCP): AsyncFD =
|
||||
## Creates a new socket and registers it with the dispatcher implicitly.
|
||||
result = newRawSocket(domain, typ, protocol).TAsyncFD
|
||||
result = newRawSocket(domain, typ, protocol).AsyncFD
|
||||
result.SocketHandle.setBlocking(false)
|
||||
register(result)
|
||||
|
||||
proc closeSocket*(socket: TAsyncFD) =
|
||||
proc closeSocket*(socket: AsyncFD) =
|
||||
## Closes a socket and ensures that it is unregistered.
|
||||
socket.SocketHandle.close()
|
||||
getGlobalDispatcher().handles.excl(socket)
|
||||
|
||||
proc unregister*(fd: TAsyncFD) =
|
||||
proc unregister*(fd: AsyncFD) =
|
||||
## Unregisters ``fd``.
|
||||
getGlobalDispatcher().handles.excl(fd)
|
||||
|
||||
@@ -892,18 +894,19 @@ else:
|
||||
MSG_NOSIGNAL
|
||||
|
||||
type
|
||||
TAsyncFD* = distinct cint
|
||||
TCallback = proc (fd: TAsyncFD): bool {.closure,gcsafe.}
|
||||
AsyncFD* = distinct cint
|
||||
Callback = proc (fd: AsyncFD): bool {.closure,gcsafe.}
|
||||
|
||||
PData* = ref object of RootRef
|
||||
fd: TAsyncFD
|
||||
readCBs: seq[TCallback]
|
||||
writeCBs: seq[TCallback]
|
||||
fd: AsyncFD
|
||||
readCBs: seq[Callback]
|
||||
writeCBs: seq[Callback]
|
||||
|
||||
PDispatcher* = ref object of PDispatcherBase
|
||||
selector: Selector
|
||||
{.deprecated: [TAsyncFD: AsyncFD, TCallback: Callback].}
|
||||
|
||||
proc `==`*(x, y: TAsyncFD): bool {.borrow.}
|
||||
proc `==`*(x, y: AsyncFD): bool {.borrow.}
|
||||
|
||||
proc newDispatcher*(): PDispatcher =
|
||||
new result
|
||||
@@ -915,18 +918,18 @@ else:
|
||||
if gDisp.isNil: gDisp = newDispatcher()
|
||||
result = gDisp
|
||||
|
||||
proc update(fd: TAsyncFD, events: set[Event]) =
|
||||
proc update(fd: AsyncFD, events: set[Event]) =
|
||||
let p = getGlobalDispatcher()
|
||||
assert fd.SocketHandle in p.selector
|
||||
discard p.selector.update(fd.SocketHandle, events)
|
||||
|
||||
proc register*(fd: TAsyncFD) =
|
||||
proc register*(fd: AsyncFD) =
|
||||
let p = getGlobalDispatcher()
|
||||
var data = PData(fd: fd, readCBs: @[], writeCBs: @[])
|
||||
p.selector.register(fd.SocketHandle, {}, data.RootRef)
|
||||
|
||||
proc newAsyncRawSocket*(domain: cint, typ: cint, protocol: cint): TAsyncFD =
|
||||
result = newRawSocket(domain, typ, protocol).TAsyncFD
|
||||
proc newAsyncRawSocket*(domain: cint, typ: cint, protocol: cint): AsyncFD =
|
||||
result = newRawSocket(domain, typ, protocol).AsyncFD
|
||||
result.SocketHandle.setBlocking(false)
|
||||
when defined(macosx):
|
||||
result.SocketHandle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1)
|
||||
@@ -934,29 +937,29 @@ else:
|
||||
|
||||
proc newAsyncRawSocket*(domain: Domain = AF_INET,
|
||||
typ: SockType = SOCK_STREAM,
|
||||
protocol: Protocol = IPPROTO_TCP): TAsyncFD =
|
||||
result = newRawSocket(domain, typ, protocol).TAsyncFD
|
||||
protocol: Protocol = IPPROTO_TCP): AsyncFD =
|
||||
result = newRawSocket(domain, typ, protocol).AsyncFD
|
||||
result.SocketHandle.setBlocking(false)
|
||||
when defined(macosx):
|
||||
result.SocketHandle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1)
|
||||
register(result)
|
||||
|
||||
proc closeSocket*(sock: TAsyncFD) =
|
||||
proc closeSocket*(sock: AsyncFD) =
|
||||
let disp = getGlobalDispatcher()
|
||||
sock.SocketHandle.close()
|
||||
disp.selector.unregister(sock.SocketHandle)
|
||||
|
||||
proc unregister*(fd: TAsyncFD) =
|
||||
proc unregister*(fd: AsyncFD) =
|
||||
getGlobalDispatcher().selector.unregister(fd.SocketHandle)
|
||||
|
||||
proc addRead*(fd: TAsyncFD, cb: TCallback) =
|
||||
proc addRead*(fd: AsyncFD, cb: Callback) =
|
||||
let p = getGlobalDispatcher()
|
||||
if fd.SocketHandle notin p.selector:
|
||||
raise newException(ValueError, "File descriptor not registered.")
|
||||
p.selector[fd.SocketHandle].data.PData.readCBs.add(cb)
|
||||
update(fd, p.selector[fd.SocketHandle].events + {EvRead})
|
||||
|
||||
proc addWrite*(fd: TAsyncFD, cb: TCallback) =
|
||||
proc addWrite*(fd: AsyncFD, cb: Callback) =
|
||||
let p = getGlobalDispatcher()
|
||||
if fd.SocketHandle notin p.selector:
|
||||
raise newException(ValueError, "File descriptor not registered.")
|
||||
@@ -967,7 +970,7 @@ else:
|
||||
let p = getGlobalDispatcher()
|
||||
for info in p.selector.select(timeout):
|
||||
let data = PData(info.key.data)
|
||||
assert data.fd == info.key.fd.TAsyncFD
|
||||
assert data.fd == info.key.fd.AsyncFD
|
||||
#echo("In poll ", data.fd.cint)
|
||||
if EvError in info.events:
|
||||
closeSocket(data.fd)
|
||||
@@ -1005,11 +1008,11 @@ else:
|
||||
|
||||
processTimers(p)
|
||||
|
||||
proc connect*(socket: TAsyncFD, address: string, port: Port,
|
||||
proc connect*(socket: AsyncFD, address: string, port: Port,
|
||||
af = AF_INET): Future[void] =
|
||||
var retFuture = newFuture[void]("connect")
|
||||
|
||||
proc cb(fd: TAsyncFD): bool =
|
||||
proc cb(fd: AsyncFD): bool =
|
||||
# We have connected.
|
||||
retFuture.complete()
|
||||
return true
|
||||
@@ -1040,13 +1043,13 @@ else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||
return retFuture
|
||||
|
||||
proc recv*(socket: TAsyncFD, size: int,
|
||||
proc recv*(socket: AsyncFD, size: int,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[string] =
|
||||
var retFuture = newFuture[string]("recv")
|
||||
|
||||
var readBuffer = newString(size)
|
||||
|
||||
proc cb(sock: TAsyncFD): bool =
|
||||
proc cb(sock: AsyncFD): bool =
|
||||
result = true
|
||||
let res = recv(sock.SocketHandle, addr readBuffer[0], size.cint,
|
||||
flags.toOSFlags())
|
||||
@@ -1070,11 +1073,11 @@ else:
|
||||
addRead(socket, cb)
|
||||
return retFuture
|
||||
|
||||
proc recvInto*(socket: TAsyncFD, buf: cstring, size: int,
|
||||
proc recvInto*(socket: AsyncFD, buf: cstring, size: int,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[int] =
|
||||
var retFuture = newFuture[int]("recvInto")
|
||||
|
||||
proc cb(sock: TAsyncFD): bool =
|
||||
proc cb(sock: AsyncFD): bool =
|
||||
result = true
|
||||
let res = recv(sock.SocketHandle, buf, size.cint,
|
||||
flags.toOSFlags())
|
||||
@@ -1094,13 +1097,13 @@ else:
|
||||
addRead(socket, cb)
|
||||
return retFuture
|
||||
|
||||
proc send*(socket: TAsyncFD, data: string,
|
||||
proc send*(socket: AsyncFD, data: string,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[void] =
|
||||
var retFuture = newFuture[void]("send")
|
||||
|
||||
var written = 0
|
||||
|
||||
proc cb(sock: TAsyncFD): bool =
|
||||
proc cb(sock: AsyncFD): bool =
|
||||
result = true
|
||||
let netSize = data.len-written
|
||||
var d = data.cstring
|
||||
@@ -1126,11 +1129,11 @@ else:
|
||||
addWrite(socket, cb)
|
||||
return retFuture
|
||||
|
||||
proc acceptAddr*(socket: TAsyncFD, flags = {SocketFlag.SafeDisconn}):
|
||||
Future[tuple[address: string, client: TAsyncFD]] =
|
||||
proc acceptAddr*(socket: AsyncFD, flags = {SocketFlag.SafeDisconn}):
|
||||
Future[tuple[address: string, client: AsyncFD]] =
|
||||
var retFuture = newFuture[tuple[address: string,
|
||||
client: TAsyncFD]]("acceptAddr")
|
||||
proc cb(sock: TAsyncFD): bool =
|
||||
client: AsyncFD]]("acceptAddr")
|
||||
proc cb(sock: AsyncFD): bool =
|
||||
result = true
|
||||
var sockAddress: SockAddr_in
|
||||
var addrLen = sizeof(sockAddress).Socklen
|
||||
@@ -1147,8 +1150,8 @@ else:
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||
else:
|
||||
register(client.TAsyncFD)
|
||||
retFuture.complete(($inet_ntoa(sockAddress.sin_addr), client.TAsyncFD))
|
||||
register(client.AsyncFD)
|
||||
retFuture.complete(($inet_ntoa(sockAddress.sin_addr), client.AsyncFD))
|
||||
addRead(socket, cb)
|
||||
return retFuture
|
||||
|
||||
@@ -1160,15 +1163,15 @@ proc sleepAsync*(ms: int): Future[void] =
|
||||
p.timers.add((epochTime() + (ms / 1000), retFuture))
|
||||
return retFuture
|
||||
|
||||
proc accept*(socket: TAsyncFD,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[TAsyncFD] =
|
||||
proc accept*(socket: AsyncFD,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[AsyncFD] =
|
||||
## Accepts a new connection. Returns a future containing the client socket
|
||||
## corresponding to that connection.
|
||||
## The future will complete when the connection is successfully accepted.
|
||||
var retFut = newFuture[TAsyncFD]("accept")
|
||||
var retFut = newFuture[AsyncFD]("accept")
|
||||
var fut = acceptAddr(socket, flags)
|
||||
fut.callback =
|
||||
proc (future: Future[tuple[address: string, client: TAsyncFD]]) =
|
||||
proc (future: Future[tuple[address: string, client: AsyncFD]]) =
|
||||
assert future.finished
|
||||
if future.failed:
|
||||
retFut.fail(future.error)
|
||||
@@ -1495,7 +1498,7 @@ macro async*(prc: stmt): stmt {.immediate.} =
|
||||
#if prc[0].getName == "test":
|
||||
# echo(toStrLit(result))
|
||||
|
||||
proc recvLine*(socket: TAsyncFD): Future[string] {.async.} =
|
||||
proc recvLine*(socket: AsyncFD): Future[string] {.async.} =
|
||||
## Reads a line of data from ``socket``. Returned future will complete once
|
||||
## a full line is read or an error occurs.
|
||||
##
|
||||
|
||||
@@ -31,7 +31,7 @@ else:
|
||||
|
||||
type
|
||||
AsyncFile* = ref object
|
||||
fd: TAsyncFd
|
||||
fd: AsyncFd
|
||||
offset: int64
|
||||
|
||||
when defined(windows) or defined(nimdoc):
|
||||
@@ -72,7 +72,7 @@ proc getFileSize(f: AsyncFile): int64 =
|
||||
## Retrieves the specified file's size.
|
||||
when defined(windows) or defined(nimdoc):
|
||||
var high: DWord
|
||||
let low = getFileSize(f.fd.THandle, addr high)
|
||||
let low = getFileSize(f.fd.Handle, addr high)
|
||||
if low == INVALID_FILE_SIZE:
|
||||
raiseOSError(osLastError())
|
||||
return (high shl 32) or low
|
||||
@@ -88,13 +88,13 @@ proc openAsync*(filename: string, mode = fmRead): AsyncFile =
|
||||
when useWinUnicode:
|
||||
result.fd = createFileW(newWideCString(filename), desiredAccess,
|
||||
FILE_SHARE_READ,
|
||||
nil, creationDisposition, flags, 0).TAsyncFd
|
||||
nil, creationDisposition, flags, 0).AsyncFd
|
||||
else:
|
||||
result.fd = createFileA(filename, desiredAccess,
|
||||
FILE_SHARE_READ,
|
||||
nil, creationDisposition, flags, 0).TAsyncFd
|
||||
nil, creationDisposition, flags, 0).AsyncFd
|
||||
|
||||
if result.fd.THandle == INVALID_HANDLE_VALUE:
|
||||
if result.fd.Handle == INVALID_HANDLE_VALUE:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
register(result.fd)
|
||||
@@ -106,7 +106,7 @@ proc openAsync*(filename: string, mode = fmRead): AsyncFile =
|
||||
let flags = getPosixFlags(mode)
|
||||
# RW (Owner), RW (Group), R (Other)
|
||||
let perm = S_IRUSR or S_IWUSR or S_IRGRP or S_IWGRP or S_IROTH
|
||||
result.fd = open(filename, flags, perm).TAsyncFD
|
||||
result.fd = open(filename, flags, perm).AsyncFD
|
||||
if result.fd.cint == -1:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
@@ -125,8 +125,8 @@ proc read*(f: AsyncFile, size: int): Future[string] =
|
||||
|
||||
var ol = PCustomOverlapped()
|
||||
GC_ref(ol)
|
||||
ol.data = TCompletionData(fd: f.fd, cb:
|
||||
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
ol.data = CompletionData(fd: f.fd, cb:
|
||||
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
if not retFuture.finished:
|
||||
if errcode == OSErrorCode(-1):
|
||||
assert bytesCount > 0
|
||||
@@ -148,7 +148,7 @@ proc read*(f: AsyncFile, size: int): Future[string] =
|
||||
ol.offsetHigh = DWord(f.offset shr 32)
|
||||
|
||||
# According to MSDN we're supposed to pass nil to lpNumberOfBytesRead.
|
||||
let ret = readFile(f.fd.THandle, buffer, size.int32, nil,
|
||||
let ret = readFile(f.fd.Handle, buffer, size.int32, nil,
|
||||
cast[POVERLAPPED](ol))
|
||||
if not ret.bool:
|
||||
let err = osLastError()
|
||||
@@ -161,7 +161,7 @@ proc read*(f: AsyncFile, size: int): Future[string] =
|
||||
else:
|
||||
# Request completed immediately.
|
||||
var bytesRead: DWord
|
||||
let overlappedRes = getOverlappedResult(f.fd.THandle,
|
||||
let overlappedRes = getOverlappedResult(f.fd.Handle,
|
||||
cast[POverlapped](ol)[], bytesRead, false.WinBool)
|
||||
if not overlappedRes.bool:
|
||||
let err = osLastError()
|
||||
@@ -179,7 +179,7 @@ proc read*(f: AsyncFile, size: int): Future[string] =
|
||||
else:
|
||||
var readBuffer = newString(size)
|
||||
|
||||
proc cb(fd: TAsyncFD): bool =
|
||||
proc cb(fd: AsyncFD): bool =
|
||||
result = true
|
||||
let res = read(fd.cint, addr readBuffer[0], size.cint)
|
||||
if res < 0:
|
||||
@@ -251,8 +251,8 @@ proc write*(f: AsyncFile, data: string): Future[void] =
|
||||
|
||||
var ol = PCustomOverlapped()
|
||||
GC_ref(ol)
|
||||
ol.data = TCompletionData(fd: f.fd, cb:
|
||||
proc (fd: TAsyncFD, bytesCount: DWord, errcode: OSErrorCode) =
|
||||
ol.data = CompletionData(fd: f.fd, cb:
|
||||
proc (fd: AsyncFD, bytesCount: DWord, errcode: OSErrorCode) =
|
||||
if not retFuture.finished:
|
||||
if errcode == OSErrorCode(-1):
|
||||
assert bytesCount == data.len.int32
|
||||
@@ -268,7 +268,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
|
||||
ol.offsetHigh = DWord(f.offset shr 32)
|
||||
|
||||
# According to MSDN we're supposed to pass nil to lpNumberOfBytesWritten.
|
||||
let ret = writeFile(f.fd.THandle, buffer, data.len.int32, nil,
|
||||
let ret = writeFile(f.fd.Handle, buffer, data.len.int32, nil,
|
||||
cast[POVERLAPPED](ol))
|
||||
if not ret.bool:
|
||||
let err = osLastError()
|
||||
@@ -281,7 +281,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
|
||||
else:
|
||||
# Request completed immediately.
|
||||
var bytesWritten: DWord
|
||||
let overlappedRes = getOverlappedResult(f.fd.THandle,
|
||||
let overlappedRes = getOverlappedResult(f.fd.Handle,
|
||||
cast[POverlapped](ol)[], bytesWritten, false.WinBool)
|
||||
if not overlappedRes.bool:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
|
||||
@@ -292,7 +292,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
|
||||
else:
|
||||
var written = 0
|
||||
|
||||
proc cb(fd: TAsyncFD): bool =
|
||||
proc cb(fd: AsyncFD): bool =
|
||||
result = true
|
||||
let remainderSize = data.len-written
|
||||
let res = write(fd.cint, addr copy[written], remainderSize.cint)
|
||||
@@ -317,7 +317,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
|
||||
proc close*(f: AsyncFile) =
|
||||
## Closes the file specified.
|
||||
when defined(windows) or defined(nimdoc):
|
||||
if not closeHandle(f.fd.THandle).bool:
|
||||
if not closeHandle(f.fd.Handle).bool:
|
||||
raiseOSError(osLastError())
|
||||
else:
|
||||
if close(f.fd.cint) == -1:
|
||||
|
||||
@@ -210,6 +210,7 @@ proc processClient(client: AsyncSocket, address: string,
|
||||
var contentLength = 0
|
||||
if parseInt(request.headers["Content-Length"], contentLength) == 0:
|
||||
await request.respond(Http400, "Bad Request. Invalid Content-Length.")
|
||||
continue
|
||||
else:
|
||||
request.body = await client.recv(contentLength)
|
||||
assert request.body.len == contentLength
|
||||
|
||||
@@ -188,8 +188,8 @@ proc asyncSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
|
||||
result.socket.setBlocking(false)
|
||||
|
||||
proc toAsyncSocket*(sock: Socket, state: SocketStatus = SockConnected): AsyncSocket =
|
||||
## Wraps an already initialized ``TSocket`` into a AsyncSocket.
|
||||
## This is useful if you want to use an already connected TSocket as an
|
||||
## Wraps an already initialized ``Socket`` into a AsyncSocket.
|
||||
## This is useful if you want to use an already connected Socket as an
|
||||
## asynchronous AsyncSocket in asyncio's event loop.
|
||||
##
|
||||
## ``state`` may be overriden, i.e. if ``sock`` is not connected it should be
|
||||
|
||||
@@ -91,13 +91,13 @@ type
|
||||
|
||||
# TODO: Save AF, domain etc info and reuse it in procs which need it like connect.
|
||||
|
||||
proc newAsyncSocket*(fd: TAsyncFD, isBuff: bool): AsyncSocket =
|
||||
proc newAsyncSocket*(fd: AsyncFD, buffered = true): AsyncSocket =
|
||||
## Creates a new ``AsyncSocket`` based on the supplied params.
|
||||
assert fd != osInvalidSocket.TAsyncFD
|
||||
assert fd != osInvalidSocket.AsyncFD
|
||||
new(result)
|
||||
result.fd = fd.SocketHandle
|
||||
result.isBuffered = isBuff
|
||||
if isBuff:
|
||||
result.isBuffered = buffered
|
||||
if buffered:
|
||||
result.currPos = 0
|
||||
|
||||
proc newAsyncSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
|
||||
@@ -142,7 +142,7 @@ when defined(ssl):
|
||||
if read < 0:
|
||||
raiseSslError()
|
||||
data.setLen(read)
|
||||
await socket.fd.TAsyncFd.send(data, flags)
|
||||
await socket.fd.AsyncFd.send(data, flags)
|
||||
|
||||
proc appeaseSsl(socket: AsyncSocket, flags: set[SocketFlag],
|
||||
sslError: cint) {.async.} =
|
||||
@@ -150,7 +150,7 @@ when defined(ssl):
|
||||
of SSL_ERROR_WANT_WRITE:
|
||||
await sendPendingSslData(socket, flags)
|
||||
of SSL_ERROR_WANT_READ:
|
||||
var data = await recv(socket.fd.TAsyncFD, BufferSize, flags)
|
||||
var data = await recv(socket.fd.AsyncFD, BufferSize, flags)
|
||||
let ret = bioWrite(socket.bioIn, addr data[0], data.len.cint)
|
||||
if ret < 0:
|
||||
raiseSSLError()
|
||||
@@ -175,7 +175,7 @@ proc connect*(socket: AsyncSocket, address: string, port: Port,
|
||||
##
|
||||
## Returns a ``Future`` which will complete when the connection succeeds
|
||||
## or an error occurs.
|
||||
await connect(socket.fd.TAsyncFD, address, port, af)
|
||||
await connect(socket.fd.AsyncFD, address, port, af)
|
||||
if socket.isSsl:
|
||||
when defined(ssl):
|
||||
let flags = {SocketFlag.SafeDisconn}
|
||||
@@ -194,7 +194,7 @@ template readInto(buf: cstring, size: int, socket: AsyncSocket,
|
||||
sslRead(socket.sslHandle, buf, size.cint))
|
||||
res = opResult
|
||||
else:
|
||||
var recvIntoFut = recvInto(socket.fd.TAsyncFD, buf, size, flags)
|
||||
var recvIntoFut = recvInto(socket.fd.AsyncFD, buf, size, flags)
|
||||
yield recvIntoFut
|
||||
# Not in SSL mode.
|
||||
res = recvIntoFut.read()
|
||||
@@ -271,7 +271,7 @@ proc send*(socket: AsyncSocket, data: string,
|
||||
sslWrite(socket.sslHandle, addr copy[0], copy.len.cint))
|
||||
await sendPendingSslData(socket, flags)
|
||||
else:
|
||||
await send(socket.fd.TAsyncFD, data, flags)
|
||||
await send(socket.fd.AsyncFD, data, flags)
|
||||
|
||||
proc acceptAddr*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn}):
|
||||
Future[tuple[address: string, client: AsyncSocket]] =
|
||||
@@ -279,9 +279,9 @@ proc acceptAddr*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn}):
|
||||
## corresponding to that connection and the remote address of the client.
|
||||
## The future will complete when the connection is successfully accepted.
|
||||
var retFuture = newFuture[tuple[address: string, client: AsyncSocket]]("asyncnet.acceptAddr")
|
||||
var fut = acceptAddr(socket.fd.TAsyncFD, flags)
|
||||
var fut = acceptAddr(socket.fd.AsyncFD, flags)
|
||||
fut.callback =
|
||||
proc (future: Future[tuple[address: string, client: TAsyncFD]]) =
|
||||
proc (future: Future[tuple[address: string, client: AsyncFD]]) =
|
||||
assert future.finished
|
||||
if future.failed:
|
||||
retFuture.fail(future.readError)
|
||||
@@ -445,7 +445,7 @@ proc bindAddr*(socket: AsyncSocket, port = Port(0), address = "") {.
|
||||
proc close*(socket: AsyncSocket) =
|
||||
## Closes the socket.
|
||||
defer:
|
||||
socket.fd.TAsyncFD.closeSocket()
|
||||
socket.fd.AsyncFD.closeSocket()
|
||||
when defined(ssl):
|
||||
if socket.isSSL:
|
||||
let res = SslShutdown(socket.sslHandle)
|
||||
|
||||
@@ -20,20 +20,20 @@ import strutils
|
||||
##
|
||||
## # Create a matrix which first rotates, then scales and at last translates
|
||||
##
|
||||
## var m:TMatrix2d=rotate(DEG90) & scale(2.0) & move(100.0,200.0)
|
||||
## var m:Matrix2d=rotate(DEG90) & scale(2.0) & move(100.0,200.0)
|
||||
##
|
||||
## # Create a 2d point at (100,0) and a vector (5,2)
|
||||
##
|
||||
## var pt:TPoint2d=point2d(100.0,0.0)
|
||||
## var pt:Point2d=point2d(100.0,0.0)
|
||||
##
|
||||
## var vec:TVector2d=vector2d(5.0,2.0)
|
||||
## var vec:Vector2d=vector2d(5.0,2.0)
|
||||
##
|
||||
##
|
||||
## pt &= m # transforms pt in place
|
||||
##
|
||||
## var pt2:TPoint2d=pt & m #concatenates pt with m and returns a new point
|
||||
## var pt2:Point2d=pt & m #concatenates pt with m and returns a new point
|
||||
##
|
||||
## var vec2:TVector2d=vec & m #concatenates vec with m and returns a new vector
|
||||
## var vec2:Vector2d=vec & m #concatenates vec with m and returns a new vector
|
||||
|
||||
|
||||
const
|
||||
@@ -57,46 +57,46 @@ const
|
||||
## used internally by DegToRad and RadToDeg
|
||||
|
||||
type
|
||||
TMatrix2d* = object
|
||||
Matrix2d* = object
|
||||
## Implements a row major 2d matrix, which means
|
||||
## transformations are applied the order they are concatenated.
|
||||
## The rightmost column of the 3x3 matrix is left out since normally
|
||||
## not used for geometric transformations in 2d.
|
||||
ax*,ay*,bx*,by*,tx*,ty*:float
|
||||
TPoint2d* = object
|
||||
## Implements a non-homegeneous 2d point stored as
|
||||
Point2d* = object
|
||||
## Implements a non-homogeneous 2d point stored as
|
||||
## an `x` coordinate and an `y` coordinate.
|
||||
x*,y*:float
|
||||
TVector2d* = object
|
||||
Vector2d* = object
|
||||
## Implements a 2d **direction vector** stored as
|
||||
## an `x` coordinate and an `y` coordinate. Direction vector means,
|
||||
## that when transforming a vector with a matrix, the translational
|
||||
## part of the matrix is ignored.
|
||||
x*,y*:float
|
||||
|
||||
{.deprecated: [TMatrix2d: Matrix2d, TPoint2d: Point2d, TVector2d: Vector2d].}
|
||||
|
||||
|
||||
# Some forward declarations...
|
||||
proc matrix2d*(ax,ay,bx,by,tx,ty:float):TMatrix2d {.noInit.}
|
||||
proc matrix2d*(ax,ay,bx,by,tx,ty:float):Matrix2d {.noInit.}
|
||||
## Creates a new matrix.
|
||||
## `ax`,`ay` is the local x axis
|
||||
## `bx`,`by` is the local y axis
|
||||
## `tx`,`ty` is the translation
|
||||
proc vector2d*(x,y:float):TVector2d {.noInit,inline.}
|
||||
proc vector2d*(x,y:float):Vector2d {.noInit,inline.}
|
||||
## Returns a new vector (`x`,`y`)
|
||||
proc point2d*(x,y:float):TPoint2d {.noInit,inline.}
|
||||
proc point2d*(x,y:float):Point2d {.noInit,inline.}
|
||||
## Returns a new point (`x`,`y`)
|
||||
|
||||
|
||||
|
||||
let
|
||||
IDMATRIX*:TMatrix2d=matrix2d(1.0,0.0,0.0,1.0,0.0,0.0)
|
||||
IDMATRIX*:Matrix2d=matrix2d(1.0,0.0,0.0,1.0,0.0,0.0)
|
||||
## Quick access to an identity matrix
|
||||
ORIGO*:TPoint2d=point2d(0.0,0.0)
|
||||
ORIGO*:Point2d=point2d(0.0,0.0)
|
||||
## Quick acces to point (0,0)
|
||||
XAXIS*:TVector2d=vector2d(1.0,0.0)
|
||||
XAXIS*:Vector2d=vector2d(1.0,0.0)
|
||||
## Quick acces to an 2d x-axis unit vector
|
||||
YAXIS*:TVector2d=vector2d(0.0,1.0)
|
||||
YAXIS*:Vector2d=vector2d(0.0,1.0)
|
||||
## Quick acces to an 2d y-axis unit vector
|
||||
|
||||
|
||||
@@ -116,21 +116,21 @@ proc safeArccos(v:float):float=
|
||||
|
||||
template makeBinOpVector(s:expr)=
|
||||
## implements binary operators + , - , * and / for vectors
|
||||
proc s*(a,b:TVector2d):TVector2d {.inline,noInit.} = vector2d(s(a.x,b.x),s(a.y,b.y))
|
||||
proc s*(a:TVector2d,b:float):TVector2d {.inline,noInit.} = vector2d(s(a.x,b),s(a.y,b))
|
||||
proc s*(a:float,b:TVector2d):TVector2d {.inline,noInit.} = vector2d(s(a,b.x),s(a,b.y))
|
||||
proc s*(a,b:Vector2d):Vector2d {.inline,noInit.} = vector2d(s(a.x,b.x),s(a.y,b.y))
|
||||
proc s*(a:Vector2d,b:float):Vector2d {.inline,noInit.} = vector2d(s(a.x,b),s(a.y,b))
|
||||
proc s*(a:float,b:Vector2d):Vector2d {.inline,noInit.} = vector2d(s(a,b.x),s(a,b.y))
|
||||
|
||||
template makeBinOpAssignVector(s:expr)=
|
||||
## implements inplace binary operators += , -= , /= and *= for vectors
|
||||
proc s*(a:var TVector2d,b:TVector2d) {.inline.} = s(a.x,b.x) ; s(a.y,b.y)
|
||||
proc s*(a:var TVector2d,b:float) {.inline.} = s(a.x,b) ; s(a.y,b)
|
||||
proc s*(a:var Vector2d,b:Vector2d) {.inline.} = s(a.x,b.x) ; s(a.y,b.y)
|
||||
proc s*(a:var Vector2d,b:float) {.inline.} = s(a.x,b) ; s(a.y,b)
|
||||
|
||||
|
||||
# ***************************************
|
||||
# TMatrix2d implementation
|
||||
# Matrix2d implementation
|
||||
# ***************************************
|
||||
|
||||
proc setElements*(t:var TMatrix2d,ax,ay,bx,by,tx,ty:float) {.inline.}=
|
||||
proc setElements*(t:var Matrix2d,ax,ay,bx,by,tx,ty:float) {.inline.}=
|
||||
## Sets arbitrary elements in an existing matrix.
|
||||
t.ax=ax
|
||||
t.ay=ay
|
||||
@@ -139,10 +139,10 @@ proc setElements*(t:var TMatrix2d,ax,ay,bx,by,tx,ty:float) {.inline.}=
|
||||
t.tx=tx
|
||||
t.ty=ty
|
||||
|
||||
proc matrix2d*(ax,ay,bx,by,tx,ty:float):TMatrix2d =
|
||||
proc matrix2d*(ax,ay,bx,by,tx,ty:float):Matrix2d =
|
||||
result.setElements(ax,ay,bx,by,tx,ty)
|
||||
|
||||
proc `&`*(a,b:TMatrix2d):TMatrix2d {.noInit.} = #concatenate matrices
|
||||
proc `&`*(a,b:Matrix2d):Matrix2d {.noInit.} = #concatenate matrices
|
||||
## Concatenates matrices returning a new matrix.
|
||||
|
||||
# | a.AX a.AY 0 | | b.AX b.AY 0 |
|
||||
@@ -157,34 +157,34 @@ proc `&`*(a,b:TMatrix2d):TMatrix2d {.noInit.} = #concatenate matrices
|
||||
a.tx * b.ay + a.ty * b.by + b.ty)
|
||||
|
||||
|
||||
proc scale*(s:float):TMatrix2d {.noInit.} =
|
||||
proc scale*(s:float):Matrix2d {.noInit.} =
|
||||
## Returns a new scale matrix.
|
||||
result.setElements(s,0,0,s,0,0)
|
||||
|
||||
proc scale*(s:float,org:TPoint2d):TMatrix2d {.noInit.} =
|
||||
proc scale*(s:float,org:Point2d):Matrix2d {.noInit.} =
|
||||
## Returns a new scale matrix using, `org` as scale origin.
|
||||
result.setElements(s,0,0,s,org.x-s*org.x,org.y-s*org.y)
|
||||
|
||||
proc stretch*(sx,sy:float):TMatrix2d {.noInit.} =
|
||||
proc stretch*(sx,sy:float):Matrix2d {.noInit.} =
|
||||
## Returns new a stretch matrix, which is a
|
||||
## scale matrix with non uniform scale in x and y.
|
||||
result.setElements(sx,0,0,sy,0,0)
|
||||
|
||||
proc stretch*(sx,sy:float,org:TPoint2d):TMatrix2d {.noInit.} =
|
||||
proc stretch*(sx,sy:float,org:Point2d):Matrix2d {.noInit.} =
|
||||
## Returns a new stretch matrix, which is a
|
||||
## scale matrix with non uniform scale in x and y.
|
||||
## `org` is used as stretch origin.
|
||||
result.setElements(sx,0,0,sy,org.x-sx*org.x,org.y-sy*org.y)
|
||||
|
||||
proc move*(dx,dy:float):TMatrix2d {.noInit.} =
|
||||
proc move*(dx,dy:float):Matrix2d {.noInit.} =
|
||||
## Returns a new translation matrix.
|
||||
result.setElements(1,0,0,1,dx,dy)
|
||||
|
||||
proc move*(v:TVector2d):TMatrix2d {.noInit.} =
|
||||
proc move*(v:Vector2d):Matrix2d {.noInit.} =
|
||||
## Returns a new translation matrix from a vector.
|
||||
result.setElements(1,0,0,1,v.x,v.y)
|
||||
|
||||
proc rotate*(rad:float):TMatrix2d {.noInit.} =
|
||||
proc rotate*(rad:float):Matrix2d {.noInit.} =
|
||||
## Returns a new rotation matrix, which
|
||||
## represents a rotation by `rad` radians
|
||||
let
|
||||
@@ -192,7 +192,7 @@ proc rotate*(rad:float):TMatrix2d {.noInit.} =
|
||||
c=cos(rad)
|
||||
result.setElements(c,s,-s,c,0,0)
|
||||
|
||||
proc rotate*(rad:float,org:TPoint2d):TMatrix2d {.noInit.} =
|
||||
proc rotate*(rad:float,org:Point2d):Matrix2d {.noInit.} =
|
||||
## Returns a new rotation matrix, which
|
||||
## represents a rotation by `rad` radians around
|
||||
## the origin `org`
|
||||
@@ -201,7 +201,7 @@ proc rotate*(rad:float,org:TPoint2d):TMatrix2d {.noInit.} =
|
||||
c=cos(rad)
|
||||
result.setElements(c,s,-s,c,org.x+s*org.y-c*org.x,org.y-c*org.y-s*org.x)
|
||||
|
||||
proc mirror*(v:TVector2d):TMatrix2d {.noInit.} =
|
||||
proc mirror*(v:Vector2d):Matrix2d {.noInit.} =
|
||||
## Returns a new mirror matrix, mirroring
|
||||
## around the line that passes through origo and
|
||||
## has the direction of `v`
|
||||
@@ -220,7 +220,7 @@ proc mirror*(v:TVector2d):TMatrix2d {.noInit.} =
|
||||
xy2,-sqd,
|
||||
0.0,0.0)
|
||||
|
||||
proc mirror*(org:TPoint2d,v:TVector2d):TMatrix2d {.noInit.} =
|
||||
proc mirror*(org:Point2d,v:Vector2d):Matrix2d {.noInit.} =
|
||||
## Returns a new mirror matrix, mirroring
|
||||
## around the line that passes through `org` and
|
||||
## has the direction of `v`
|
||||
@@ -241,20 +241,20 @@ proc mirror*(org:TPoint2d,v:TVector2d):TMatrix2d {.noInit.} =
|
||||
|
||||
|
||||
|
||||
proc skew*(xskew,yskew:float):TMatrix2d {.noInit.} =
|
||||
proc skew*(xskew,yskew:float):Matrix2d {.noInit.} =
|
||||
## Returns a new skew matrix, which has its
|
||||
## x axis rotated `xskew` radians from the local x axis, and
|
||||
## y axis rotated `yskew` radians from the local y axis
|
||||
result.setElements(cos(yskew),sin(yskew),-sin(xskew),cos(xskew),0,0)
|
||||
|
||||
|
||||
proc `$`* (t:TMatrix2d):string {.noInit.} =
|
||||
proc `$`* (t:Matrix2d):string {.noInit.} =
|
||||
## Returns a string representation of the matrix
|
||||
return rtos(t.ax) & "," & rtos(t.ay) &
|
||||
"," & rtos(t.bx) & "," & rtos(t.by) &
|
||||
"," & rtos(t.tx) & "," & rtos(t.ty)
|
||||
|
||||
proc isUniform*(t:TMatrix2d,tol=1.0e-6):bool=
|
||||
proc isUniform*(t:Matrix2d,tol=1.0e-6):bool=
|
||||
## Checks if the transform is uniform, that is
|
||||
## perpendicular axes of equal length, which means (for example)
|
||||
## it cannot transform a circle into an ellipse.
|
||||
@@ -268,18 +268,18 @@ proc isUniform*(t:TMatrix2d,tol=1.0e-6):bool=
|
||||
return true
|
||||
return false
|
||||
|
||||
proc determinant*(t:TMatrix2d):float=
|
||||
proc determinant*(t:Matrix2d):float=
|
||||
## Computes the determinant of the matrix.
|
||||
|
||||
#NOTE: equivalent with perp.dot product for two 2d vectors
|
||||
return t.ax*t.by-t.bx*t.ay
|
||||
|
||||
proc isMirroring* (m:TMatrix2d):bool=
|
||||
proc isMirroring* (m:Matrix2d):bool=
|
||||
## Checks if the `m` is a mirroring matrix,
|
||||
## which means it will reverse direction of a curve transformed with it
|
||||
return m.determinant<0.0
|
||||
|
||||
proc inverse*(m:TMatrix2d):TMatrix2d {.noInit.} =
|
||||
proc inverse*(m:Matrix2d):Matrix2d {.noInit.} =
|
||||
## Returns a new matrix, which is the inverse of the matrix
|
||||
## If the matrix is not invertible (determinant=0), an EDivByZero
|
||||
## will be raised.
|
||||
@@ -293,7 +293,7 @@ proc inverse*(m:TMatrix2d):TMatrix2d {.noInit.} =
|
||||
(m.bx*m.ty-m.by*m.tx)/d,
|
||||
(m.ay*m.tx-m.ax*m.ty)/d)
|
||||
|
||||
proc equals*(m1:TMatrix2d,m2:TMatrix2d,tol=1.0e-6):bool=
|
||||
proc equals*(m1:Matrix2d,m2:Matrix2d,tol=1.0e-6):bool=
|
||||
## Checks if all elements of `m1`and `m2` is equal within
|
||||
## a given tolerance `tol`.
|
||||
return
|
||||
@@ -304,17 +304,17 @@ proc equals*(m1:TMatrix2d,m2:TMatrix2d,tol=1.0e-6):bool=
|
||||
abs(m1.tx-m2.tx)<=tol and
|
||||
abs(m1.ty-m2.ty)<=tol
|
||||
|
||||
proc `=~`*(m1,m2:TMatrix2d):bool=
|
||||
proc `=~`*(m1,m2:Matrix2d):bool=
|
||||
## Checks if `m1`and `m2` is approximately equal, using a
|
||||
## tolerance of 1e-6.
|
||||
equals(m1,m2)
|
||||
|
||||
proc isIdentity*(m:TMatrix2d,tol=1.0e-6):bool=
|
||||
proc isIdentity*(m:Matrix2d,tol=1.0e-6):bool=
|
||||
## Checks is a matrix is approximately an identity matrix,
|
||||
## using `tol` as tolerance for each element.
|
||||
return equals(m,IDMATRIX,tol)
|
||||
|
||||
proc apply*(m:TMatrix2d,x,y:var float,translate=false)=
|
||||
proc apply*(m:Matrix2d,x,y:var float,translate=false)=
|
||||
## Applies transformation `m` onto `x`,`y`, optionally
|
||||
## using the translation part of the matrix.
|
||||
if translate: # positional style transform
|
||||
@@ -329,29 +329,29 @@ proc apply*(m:TMatrix2d,x,y:var float,translate=false)=
|
||||
|
||||
|
||||
# ***************************************
|
||||
# TVector2d implementation
|
||||
# Vector2d implementation
|
||||
# ***************************************
|
||||
proc vector2d*(x,y:float):TVector2d = #forward decl.
|
||||
proc vector2d*(x,y:float):Vector2d = #forward decl.
|
||||
result.x=x
|
||||
result.y=y
|
||||
|
||||
proc polarVector2d*(ang:float,len:float):TVector2d {.noInit.} =
|
||||
proc polarVector2d*(ang:float,len:float):Vector2d {.noInit.} =
|
||||
## Returns a new vector with angle `ang` and magnitude `len`
|
||||
result.x=cos(ang)*len
|
||||
result.y=sin(ang)*len
|
||||
|
||||
proc slopeVector2d*(slope:float,len:float):TVector2d {.noInit.} =
|
||||
proc slopeVector2d*(slope:float,len:float):Vector2d {.noInit.} =
|
||||
## Returns a new vector having slope (dy/dx) given by
|
||||
## `slope`, and a magnitude of `len`
|
||||
let ang=arctan(slope)
|
||||
result.x=cos(ang)*len
|
||||
result.y=sin(ang)*len
|
||||
|
||||
proc len*(v:TVector2d):float {.inline.}=
|
||||
proc len*(v:Vector2d):float {.inline.}=
|
||||
## Returns the length of the vector.
|
||||
sqrt(v.x*v.x+v.y*v.y)
|
||||
|
||||
proc `len=`*(v:var TVector2d,newlen:float) {.noInit.} =
|
||||
proc `len=`*(v:var Vector2d,newlen:float) {.noInit.} =
|
||||
## Sets the length of the vector, keeping its angle.
|
||||
let fac=newlen/v.len
|
||||
|
||||
@@ -369,25 +369,25 @@ proc `len=`*(v:var TVector2d,newlen:float) {.noInit.} =
|
||||
v.x*=fac
|
||||
v.y*=fac
|
||||
|
||||
proc sqrLen*(v:TVector2d):float {.inline.}=
|
||||
proc sqrLen*(v:Vector2d):float {.inline.}=
|
||||
## Computes the squared length of the vector, which is
|
||||
## faster than computing the absolute length.
|
||||
v.x*v.x+v.y*v.y
|
||||
|
||||
proc angle*(v:TVector2d):float=
|
||||
proc angle*(v:Vector2d):float=
|
||||
## Returns the angle of the vector.
|
||||
## (The counter clockwise plane angle between posetive x axis and `v`)
|
||||
result=arctan2(v.y,v.x)
|
||||
if result<0.0: result+=DEG360
|
||||
|
||||
proc `$` *(v:TVector2d):string=
|
||||
proc `$` *(v:Vector2d):string=
|
||||
## String representation of `v`
|
||||
result=rtos(v.x)
|
||||
result.add(",")
|
||||
result.add(rtos(v.y))
|
||||
|
||||
|
||||
proc `&` *(v:TVector2d,m:TMatrix2d):TVector2d {.noInit.} =
|
||||
proc `&` *(v:Vector2d,m:Matrix2d):Vector2d {.noInit.} =
|
||||
## Concatenate vector `v` with a transformation matrix.
|
||||
## Transforming a vector ignores the translational part
|
||||
## of the matrix.
|
||||
@@ -399,7 +399,7 @@ proc `&` *(v:TVector2d,m:TMatrix2d):TVector2d {.noInit.} =
|
||||
result.y=v.x*m.ay+v.y*m.by
|
||||
|
||||
|
||||
proc `&=`*(v:var TVector2d,m:TMatrix2d) {.inline.}=
|
||||
proc `&=`*(v:var Vector2d,m:Matrix2d) {.inline.}=
|
||||
## Applies transformation `m` onto `v` in place.
|
||||
## Transforming a vector ignores the translational part
|
||||
## of the matrix.
|
||||
@@ -412,7 +412,7 @@ proc `&=`*(v:var TVector2d,m:TMatrix2d) {.inline.}=
|
||||
v.x=newx
|
||||
|
||||
|
||||
proc tryNormalize*(v:var TVector2d):bool=
|
||||
proc tryNormalize*(v:var Vector2d):bool=
|
||||
## Modifies `v` to have a length of 1.0, keeping its angle.
|
||||
## If `v` has zero length (and thus no angle), it is left unmodified and
|
||||
## false is returned, otherwise true is returned.
|
||||
@@ -427,13 +427,13 @@ proc tryNormalize*(v:var TVector2d):bool=
|
||||
return true
|
||||
|
||||
|
||||
proc normalize*(v:var TVector2d) {.inline.}=
|
||||
proc normalize*(v:var Vector2d) {.inline.}=
|
||||
## Modifies `v` to have a length of 1.0, keeping its angle.
|
||||
## If `v` has zero length, an EDivByZero will be raised.
|
||||
if not tryNormalize(v):
|
||||
raise newException(DivByZeroError,"Cannot normalize zero length vector")
|
||||
|
||||
proc transformNorm*(v:var TVector2d,t:TMatrix2d)=
|
||||
proc transformNorm*(v:var Vector2d,t:Matrix2d)=
|
||||
## Applies a normal direction transformation `t` onto `v` in place.
|
||||
## The resulting vector is *not* normalized. Transforming a vector ignores the
|
||||
## translational part of the matrix. If the matrix is not invertible
|
||||
@@ -452,7 +452,7 @@ proc transformNorm*(v:var TVector2d,t:TMatrix2d)=
|
||||
v.y = (t.ax*v.y-t.bx*v.x)/d
|
||||
v.x = newx
|
||||
|
||||
proc transformInv*(v:var TVector2d,t:TMatrix2d)=
|
||||
proc transformInv*(v:var Vector2d,t:Matrix2d)=
|
||||
## Applies inverse of a transformation `t` to `v` in place.
|
||||
## This is faster than creating an inverse matrix and apply() it.
|
||||
## Transforming a vector ignores the translational part
|
||||
@@ -467,7 +467,7 @@ proc transformInv*(v:var TVector2d,t:TMatrix2d)=
|
||||
v.y = (t.ax*v.y-t.ay*v.x)/d
|
||||
v.x = newx
|
||||
|
||||
proc transformNormInv*(v:var TVector2d,t:TMatrix2d)=
|
||||
proc transformNormInv*(v:var Vector2d,t:Matrix2d)=
|
||||
## Applies an inverse normal direction transformation `t` onto `v` in place.
|
||||
## This is faster than creating an inverse
|
||||
## matrix and transformNorm(...) it. Transforming a vector ignores the
|
||||
@@ -484,25 +484,25 @@ proc transformNormInv*(v:var TVector2d,t:TMatrix2d)=
|
||||
v.y=t.by*v.y+t.bx*v.x
|
||||
v.x=newx
|
||||
|
||||
proc rotate90*(v:var TVector2d) {.inline.}=
|
||||
proc rotate90*(v:var Vector2d) {.inline.}=
|
||||
## Quickly rotates vector `v` 90 degrees counter clockwise,
|
||||
## without using any trigonometrics.
|
||||
swap(v.x,v.y)
|
||||
v.x= -v.x
|
||||
|
||||
proc rotate180*(v:var TVector2d){.inline.}=
|
||||
proc rotate180*(v:var Vector2d){.inline.}=
|
||||
## Quickly rotates vector `v` 180 degrees counter clockwise,
|
||||
## without using any trigonometrics.
|
||||
v.x= -v.x
|
||||
v.y= -v.y
|
||||
|
||||
proc rotate270*(v:var TVector2d) {.inline.}=
|
||||
proc rotate270*(v:var Vector2d) {.inline.}=
|
||||
## Quickly rotates vector `v` 270 degrees counter clockwise,
|
||||
## without using any trigonometrics.
|
||||
swap(v.x,v.y)
|
||||
v.y= -v.y
|
||||
|
||||
proc rotate*(v:var TVector2d,rad:float) =
|
||||
proc rotate*(v:var Vector2d,rad:float) =
|
||||
## Rotates vector `v` `rad` radians in place.
|
||||
let
|
||||
s=sin(rad)
|
||||
@@ -511,18 +511,18 @@ proc rotate*(v:var TVector2d,rad:float) =
|
||||
v.y=c*v.y+s*v.x
|
||||
v.x=newx
|
||||
|
||||
proc scale*(v:var TVector2d,fac:float){.inline.}=
|
||||
proc scale*(v:var Vector2d,fac:float){.inline.}=
|
||||
## Scales vector `v` `rad` radians in place.
|
||||
v.x*=fac
|
||||
v.y*=fac
|
||||
|
||||
proc stretch*(v:var TVector2d,facx,facy:float){.inline.}=
|
||||
proc stretch*(v:var Vector2d,facx,facy:float){.inline.}=
|
||||
## Stretches vector `v` `facx` times horizontally,
|
||||
## and `facy` times vertically.
|
||||
v.x*=facx
|
||||
v.y*=facy
|
||||
|
||||
proc mirror*(v:var TVector2d,mirrvec:TVector2d)=
|
||||
proc mirror*(v:var Vector2d,mirrvec:Vector2d)=
|
||||
## Mirrors vector `v` using `mirrvec` as mirror direction.
|
||||
let
|
||||
sqx=mirrvec.x*mirrvec.x
|
||||
@@ -539,7 +539,7 @@ proc mirror*(v:var TVector2d,mirrvec:TVector2d)=
|
||||
v.x=newx
|
||||
|
||||
|
||||
proc `-` *(v:TVector2d):TVector2d=
|
||||
proc `-` *(v:Vector2d):Vector2d=
|
||||
## Negates a vector
|
||||
result.x= -v.x
|
||||
result.y= -v.y
|
||||
@@ -555,27 +555,27 @@ makeBinOpAssignVector(`*=`)
|
||||
makeBinOpAssignVector(`/=`)
|
||||
|
||||
|
||||
proc dot*(v1,v2:TVector2d):float=
|
||||
proc dot*(v1,v2:Vector2d):float=
|
||||
## Computes the dot product of two vectors.
|
||||
## Returns 0.0 if the vectors are perpendicular.
|
||||
return v1.x*v2.x+v1.y*v2.y
|
||||
|
||||
proc cross*(v1,v2:TVector2d):float=
|
||||
proc cross*(v1,v2:Vector2d):float=
|
||||
## Computes the cross product of two vectors, also called
|
||||
## the 'perpendicular dot product' in 2d. Returns 0.0 if the vectors
|
||||
## are parallel.
|
||||
return v1.x*v2.y-v1.y*v2.x
|
||||
|
||||
proc equals*(v1,v2:TVector2d,tol=1.0e-6):bool=
|
||||
proc equals*(v1,v2:Vector2d,tol=1.0e-6):bool=
|
||||
## Checks if two vectors approximately equals with a tolerance.
|
||||
return abs(v2.x-v1.x)<=tol and abs(v2.y-v1.y)<=tol
|
||||
|
||||
proc `=~` *(v1,v2:TVector2d):bool=
|
||||
proc `=~` *(v1,v2:Vector2d):bool=
|
||||
## Checks if two vectors approximately equals with a
|
||||
## hardcoded tolerance 1e-6
|
||||
equals(v1,v2)
|
||||
|
||||
proc angleTo*(v1,v2:TVector2d):float=
|
||||
proc angleTo*(v1,v2:Vector2d):float=
|
||||
## Returns the smallest of the two possible angles
|
||||
## between `v1` and `v2` in radians.
|
||||
var
|
||||
@@ -585,7 +585,7 @@ proc angleTo*(v1,v2:TVector2d):float=
|
||||
return 0.0 # zero length vector has zero angle to any other vector
|
||||
return safeArccos(dot(nv1,nv2))
|
||||
|
||||
proc angleCCW*(v1,v2:TVector2d):float=
|
||||
proc angleCCW*(v1,v2:Vector2d):float=
|
||||
## Returns the counter clockwise plane angle from `v1` to `v2`,
|
||||
## in range 0 - 2*PI
|
||||
let a=v1.angleTo(v2)
|
||||
@@ -593,7 +593,7 @@ proc angleCCW*(v1,v2:TVector2d):float=
|
||||
return a
|
||||
return DEG360-a
|
||||
|
||||
proc angleCW*(v1,v2:TVector2d):float=
|
||||
proc angleCW*(v1,v2:Vector2d):float=
|
||||
## Returns the clockwise plane angle from `v1` to `v2`,
|
||||
## in range 0 - 2*PI
|
||||
let a=v1.angleTo(v2)
|
||||
@@ -601,7 +601,7 @@ proc angleCW*(v1,v2:TVector2d):float=
|
||||
return a
|
||||
return DEG360-a
|
||||
|
||||
proc turnAngle*(v1,v2:TVector2d):float=
|
||||
proc turnAngle*(v1,v2:Vector2d):float=
|
||||
## Returns the amount v1 should be rotated (in radians) to equal v2,
|
||||
## in range -PI to PI
|
||||
let a=v1.angleTo(v2)
|
||||
@@ -609,7 +609,7 @@ proc turnAngle*(v1,v2:TVector2d):float=
|
||||
return -a
|
||||
return a
|
||||
|
||||
proc bisect*(v1,v2:TVector2d):TVector2d {.noInit.}=
|
||||
proc bisect*(v1,v2:Vector2d):Vector2d {.noInit.}=
|
||||
## Computes the bisector between v1 and v2 as a normalized vector.
|
||||
## If one of the input vectors has zero length, a normalized version
|
||||
## of the other is returned. If both input vectors has zero length,
|
||||
@@ -645,24 +645,24 @@ proc bisect*(v1,v2:TVector2d):TVector2d {.noInit.}=
|
||||
|
||||
|
||||
# ***************************************
|
||||
# TPoint2d implementation
|
||||
# Point2d implementation
|
||||
# ***************************************
|
||||
|
||||
proc point2d*(x,y:float):TPoint2d =
|
||||
proc point2d*(x,y:float):Point2d =
|
||||
result.x=x
|
||||
result.y=y
|
||||
|
||||
proc sqrDist*(a,b:TPoint2d):float=
|
||||
proc sqrDist*(a,b:Point2d):float=
|
||||
## Computes the squared distance between `a` and `b`
|
||||
let dx=b.x-a.x
|
||||
let dy=b.y-a.y
|
||||
result=dx*dx+dy*dy
|
||||
|
||||
proc dist*(a,b:TPoint2d):float {.inline.}=
|
||||
proc dist*(a,b:Point2d):float {.inline.}=
|
||||
## Computes the absolute distance between `a` and `b`
|
||||
result=sqrt(sqrDist(a,b))
|
||||
|
||||
proc angle*(a,b:TPoint2d):float=
|
||||
proc angle*(a,b:Point2d):float=
|
||||
## Computes the angle of the vector `b`-`a`
|
||||
let dx=b.x-a.x
|
||||
let dy=b.y-a.y
|
||||
@@ -670,13 +670,13 @@ proc angle*(a,b:TPoint2d):float=
|
||||
if result<0:
|
||||
result += DEG360
|
||||
|
||||
proc `$` *(p:TPoint2d):string=
|
||||
proc `$` *(p:Point2d):string=
|
||||
## String representation of `p`
|
||||
result=rtos(p.x)
|
||||
result.add(",")
|
||||
result.add(rtos(p.y))
|
||||
|
||||
proc `&`*(p:TPoint2d,t:TMatrix2d):TPoint2d {.noInit,inline.} =
|
||||
proc `&`*(p:Point2d,t:Matrix2d):Point2d {.noInit,inline.} =
|
||||
## Concatenates a point `p` with a transform `t`,
|
||||
## resulting in a new, transformed point.
|
||||
|
||||
@@ -686,14 +686,14 @@ proc `&`*(p:TPoint2d,t:TMatrix2d):TPoint2d {.noInit,inline.} =
|
||||
result.x=p.x*t.ax+p.y*t.bx+t.tx
|
||||
result.y=p.x*t.ay+p.y*t.by+t.ty
|
||||
|
||||
proc `&=` *(p:var TPoint2d,t:TMatrix2d) {.inline.}=
|
||||
proc `&=` *(p:var Point2d,t:Matrix2d) {.inline.}=
|
||||
## Applies transformation `t` onto `p` in place.
|
||||
let newx=p.x*t.ax+p.y*t.bx+t.tx
|
||||
p.y=p.x*t.ay+p.y*t.by+t.ty
|
||||
p.x=newx
|
||||
|
||||
|
||||
proc transformInv*(p:var TPoint2d,t:TMatrix2d){.inline.}=
|
||||
proc transformInv*(p:var Point2d,t:Matrix2d){.inline.}=
|
||||
## Applies the inverse of transformation `t` onto `p` in place.
|
||||
## If the matrix is not invertable (determinant=0) , EDivByZero will
|
||||
## be raised.
|
||||
@@ -710,48 +710,48 @@ proc transformInv*(p:var TPoint2d,t:TMatrix2d){.inline.}=
|
||||
p.x=newx
|
||||
|
||||
|
||||
proc `+`*(p:TPoint2d,v:TVector2d):TPoint2d {.noInit,inline.} =
|
||||
proc `+`*(p:Point2d,v:Vector2d):Point2d {.noInit,inline.} =
|
||||
## Adds a vector `v` to a point `p`, resulting
|
||||
## in a new point.
|
||||
result.x=p.x+v.x
|
||||
result.y=p.y+v.y
|
||||
|
||||
proc `+=`*(p:var TPoint2d,v:TVector2d) {.noInit,inline.} =
|
||||
proc `+=`*(p:var Point2d,v:Vector2d) {.noInit,inline.} =
|
||||
## Adds a vector `v` to a point `p` in place.
|
||||
p.x+=v.x
|
||||
p.y+=v.y
|
||||
|
||||
proc `-`*(p:TPoint2d,v:TVector2d):TPoint2d {.noInit,inline.} =
|
||||
proc `-`*(p:Point2d,v:Vector2d):Point2d {.noInit,inline.} =
|
||||
## Subtracts a vector `v` from a point `p`, resulting
|
||||
## in a new point.
|
||||
result.x=p.x-v.x
|
||||
result.y=p.y-v.y
|
||||
|
||||
proc `-`*(p1,p2:TPoint2d):TVector2d {.noInit,inline.} =
|
||||
proc `-`*(p1,p2:Point2d):Vector2d {.noInit,inline.} =
|
||||
## Subtracts `p2`from `p1` resulting in a difference vector.
|
||||
result.x=p1.x-p2.x
|
||||
result.y=p1.y-p2.y
|
||||
|
||||
proc `-=`*(p:var TPoint2d,v:TVector2d) {.noInit,inline.} =
|
||||
proc `-=`*(p:var Point2d,v:Vector2d) {.noInit,inline.} =
|
||||
## Subtracts a vector `v` from a point `p` in place.
|
||||
p.x-=v.x
|
||||
p.y-=v.y
|
||||
|
||||
proc equals(p1,p2:TPoint2d,tol=1.0e-6):bool {.inline.}=
|
||||
proc equals(p1,p2:Point2d,tol=1.0e-6):bool {.inline.}=
|
||||
## Checks if two points approximately equals with a tolerance.
|
||||
return abs(p2.x-p1.x)<=tol and abs(p2.y-p1.y)<=tol
|
||||
|
||||
proc `=~`*(p1,p2:TPoint2d):bool {.inline.}=
|
||||
proc `=~`*(p1,p2:Point2d):bool {.inline.}=
|
||||
## Checks if two vectors approximately equals with a
|
||||
## hardcoded tolerance 1e-6
|
||||
equals(p1,p2)
|
||||
|
||||
proc polar*(p:TPoint2d,ang,dist:float):TPoint2d {.noInit.} =
|
||||
proc polar*(p:Point2d,ang,dist:float):Point2d {.noInit.} =
|
||||
## Returns a point with a given angle and distance away from `p`
|
||||
result.x=p.x+cos(ang)*dist
|
||||
result.y=p.y+sin(ang)*dist
|
||||
|
||||
proc rotate*(p:var TPoint2d,rad:float)=
|
||||
proc rotate*(p:var Point2d,rad:float)=
|
||||
## Rotates a point in place `rad` radians around origo.
|
||||
let
|
||||
c=cos(rad)
|
||||
@@ -760,7 +760,7 @@ proc rotate*(p:var TPoint2d,rad:float)=
|
||||
p.y=p.y*c+p.x*s
|
||||
p.x=newx
|
||||
|
||||
proc rotate*(p:var TPoint2d,rad:float,org:TPoint2d)=
|
||||
proc rotate*(p:var Point2d,rad:float,org:Point2d)=
|
||||
## Rotates a point in place `rad` radians using `org` as
|
||||
## center of rotation.
|
||||
let
|
||||
@@ -770,50 +770,50 @@ proc rotate*(p:var TPoint2d,rad:float,org:TPoint2d)=
|
||||
p.y=(p.y - org.y) * c + (p.x - org.x) * s + org.y
|
||||
p.x=newx
|
||||
|
||||
proc scale*(p:var TPoint2d,fac:float) {.inline.}=
|
||||
proc scale*(p:var Point2d,fac:float) {.inline.}=
|
||||
## Scales a point in place `fac` times with world origo as origin.
|
||||
p.x*=fac
|
||||
p.y*=fac
|
||||
|
||||
proc scale*(p:var TPoint2d,fac:float,org:TPoint2d){.inline.}=
|
||||
proc scale*(p:var Point2d,fac:float,org:Point2d){.inline.}=
|
||||
## Scales the point in place `fac` times with `org` as origin.
|
||||
p.x=(p.x - org.x) * fac + org.x
|
||||
p.y=(p.y - org.y) * fac + org.y
|
||||
|
||||
proc stretch*(p:var TPoint2d,facx,facy:float){.inline.}=
|
||||
proc stretch*(p:var Point2d,facx,facy:float){.inline.}=
|
||||
## Scales a point in place non uniformly `facx` and `facy` times with
|
||||
## world origo as origin.
|
||||
p.x*=facx
|
||||
p.y*=facy
|
||||
|
||||
proc stretch*(p:var TPoint2d,facx,facy:float,org:TPoint2d){.inline.}=
|
||||
proc stretch*(p:var Point2d,facx,facy:float,org:Point2d){.inline.}=
|
||||
## Scales the point in place non uniformly `facx` and `facy` times with
|
||||
## `org` as origin.
|
||||
p.x=(p.x - org.x) * facx + org.x
|
||||
p.y=(p.y - org.y) * facy + org.y
|
||||
|
||||
proc move*(p:var TPoint2d,dx,dy:float){.inline.}=
|
||||
proc move*(p:var Point2d,dx,dy:float){.inline.}=
|
||||
## Translates a point `dx`, `dy` in place.
|
||||
p.x+=dx
|
||||
p.y+=dy
|
||||
|
||||
proc move*(p:var TPoint2d,v:TVector2d){.inline.}=
|
||||
proc move*(p:var Point2d,v:Vector2d){.inline.}=
|
||||
## Translates a point with vector `v` in place.
|
||||
p.x+=v.x
|
||||
p.y+=v.y
|
||||
|
||||
proc sgnArea*(a,b,c:TPoint2d):float=
|
||||
proc sgnArea*(a,b,c:Point2d):float=
|
||||
## Computes the signed area of the triangle thru points `a`,`b` and `c`
|
||||
## result>0.0 for counter clockwise triangle
|
||||
## result<0.0 for clockwise triangle
|
||||
## This is commonly used to determinate side of a point with respect to a line.
|
||||
return ((b.x - c.x) * (b.y - a.y)-(b.y - c.y) * (b.x - a.x))*0.5
|
||||
|
||||
proc area*(a,b,c:TPoint2d):float=
|
||||
proc area*(a,b,c:Point2d):float=
|
||||
## Computes the area of the triangle thru points `a`,`b` and `c`
|
||||
return abs(sgnArea(a,b,c))
|
||||
|
||||
proc closestPoint*(p:TPoint2d,pts:varargs[TPoint2d]):TPoint2d=
|
||||
proc closestPoint*(p:Point2d,pts:varargs[Point2d]):Point2d=
|
||||
## Returns a point selected from `pts`, that has the closest
|
||||
## euclidean distance to `p`
|
||||
assert(pts.len>0) # must have at least one point
|
||||
|
||||
@@ -25,25 +25,25 @@ import times
|
||||
##
|
||||
## # Create a matrix which first rotates, then scales and at last translates
|
||||
##
|
||||
## var m:TMatrix3d=rotate(PI,vector3d(1,1,2.5)) & scale(2.0) & move(100.0,200.0,300.0)
|
||||
## var m:Matrix3d=rotate(PI,vector3d(1,1,2.5)) & scale(2.0) & move(100.0,200.0,300.0)
|
||||
##
|
||||
## # Create a 3d point at (100,150,200) and a vector (5,2,3)
|
||||
##
|
||||
## var pt:TPoint3d=point3d(100.0,150.0,200.0)
|
||||
## var pt:Point3d=point3d(100.0,150.0,200.0)
|
||||
##
|
||||
## var vec:TVector3d=vector3d(5.0,2.0,3.0)
|
||||
## var vec:Vector3d=vector3d(5.0,2.0,3.0)
|
||||
##
|
||||
##
|
||||
## pt &= m # transforms pt in place
|
||||
##
|
||||
## var pt2:TPoint3d=pt & m #concatenates pt with m and returns a new point
|
||||
## var pt2:Point3d=pt & m #concatenates pt with m and returns a new point
|
||||
##
|
||||
## var vec2:TVector3d=vec & m #concatenates vec with m and returns a new vector
|
||||
## var vec2:Vector3d=vec & m #concatenates vec with m and returns a new vector
|
||||
|
||||
|
||||
|
||||
type
|
||||
TMatrix3d* =object
|
||||
Matrix3d* =object
|
||||
## Implements a row major 3d matrix, which means
|
||||
## transformations are applied the order they are concatenated.
|
||||
## This matrix is stored as an 4x4 matrix:
|
||||
@@ -52,31 +52,31 @@ type
|
||||
## [ cx cy cz cw ]
|
||||
## [ tx ty tz tw ]
|
||||
ax*,ay*,az*,aw*, bx*,by*,bz*,bw*, cx*,cy*,cz*,cw*, tx*,ty*,tz*,tw*:float
|
||||
TPoint3d* = object
|
||||
## Implements a non-homegeneous 2d point stored as
|
||||
Point3d* = object
|
||||
## Implements a non-homogeneous 3d point stored as
|
||||
## an `x` , `y` and `z` coordinate.
|
||||
x*,y*,z*:float
|
||||
TVector3d* = object
|
||||
Vector3d* = object
|
||||
## Implements a 3d **direction vector** stored as
|
||||
## an `x` , `y` and `z` coordinate. Direction vector means,
|
||||
## that when transforming a vector with a matrix, the translational
|
||||
## part of the matrix is ignored.
|
||||
x*,y*,z*:float
|
||||
|
||||
{.deprecated: [TMatrix3d: Matrix3d, TPoint3d: Point3d, TVector3d: Vector3d].}
|
||||
|
||||
|
||||
# Some forward declarations
|
||||
proc matrix3d*(ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw:float):TMatrix3d {.noInit.}
|
||||
proc matrix3d*(ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw:float):Matrix3d {.noInit.}
|
||||
## Creates a new 4x4 3d transformation matrix.
|
||||
## `ax` , `ay` , `az` is the local x axis.
|
||||
## `bx` , `by` , `bz` is the local y axis.
|
||||
## `cx` , `cy` , `cz` is the local z axis.
|
||||
## `tx` , `ty` , `tz` is the translation.
|
||||
proc vector3d*(x,y,z:float):TVector3d {.noInit,inline.}
|
||||
proc vector3d*(x,y,z:float):Vector3d {.noInit,inline.}
|
||||
## Returns a new 3d vector (`x`,`y`,`z`)
|
||||
proc point3d*(x,y,z:float):TPoint3d {.noInit,inline.}
|
||||
proc point3d*(x,y,z:float):Point3d {.noInit,inline.}
|
||||
## Returns a new 4d point (`x`,`y`,`z`)
|
||||
proc tryNormalize*(v:var TVector3d):bool
|
||||
proc tryNormalize*(v:var Vector3d):bool
|
||||
## Modifies `v` to have a length of 1.0, keeping its angle.
|
||||
## If `v` has zero length (and thus no angle), it is left unmodified and false is
|
||||
## returned, otherwise true is returned.
|
||||
@@ -84,19 +84,19 @@ proc tryNormalize*(v:var TVector3d):bool
|
||||
|
||||
|
||||
let
|
||||
IDMATRIX*:TMatrix3d=matrix3d(
|
||||
IDMATRIX*:Matrix3d=matrix3d(
|
||||
1.0,0.0,0.0,0.0,
|
||||
0.0,1.0,0.0,0.0,
|
||||
0.0,0.0,1.0,0.0,
|
||||
0.0,0.0,0.0,1.0)
|
||||
## Quick access to a 3d identity matrix
|
||||
ORIGO*:TPoint3d=point3d(0.0,0.0,0.0)
|
||||
ORIGO*:Point3d=point3d(0.0,0.0,0.0)
|
||||
## Quick access to point (0,0)
|
||||
XAXIS*:TVector3d=vector3d(1.0,0.0,0.0)
|
||||
XAXIS*:Vector3d=vector3d(1.0,0.0,0.0)
|
||||
## Quick access to an 3d x-axis unit vector
|
||||
YAXIS*:TVector3d=vector3d(0.0,1.0,0.0)
|
||||
YAXIS*:Vector3d=vector3d(0.0,1.0,0.0)
|
||||
## Quick access to an 3d y-axis unit vector
|
||||
ZAXIS*:TVector3d=vector3d(0.0,0.0,1.0)
|
||||
ZAXIS*:Vector3d=vector3d(0.0,0.0,1.0)
|
||||
## Quick access to an 3d z-axis unit vector
|
||||
|
||||
|
||||
@@ -116,27 +116,27 @@ proc safeArccos(v:float):float=
|
||||
|
||||
template makeBinOpVector(s:expr)=
|
||||
## implements binary operators + , - , * and / for vectors
|
||||
proc s*(a,b:TVector3d):TVector3d {.inline,noInit.} =
|
||||
proc s*(a,b:Vector3d):Vector3d {.inline,noInit.} =
|
||||
vector3d(s(a.x,b.x),s(a.y,b.y),s(a.z,b.z))
|
||||
proc s*(a:TVector3d,b:float):TVector3d {.inline,noInit.} =
|
||||
proc s*(a:Vector3d,b:float):Vector3d {.inline,noInit.} =
|
||||
vector3d(s(a.x,b),s(a.y,b),s(a.z,b))
|
||||
proc s*(a:float,b:TVector3d):TVector3d {.inline,noInit.} =
|
||||
proc s*(a:float,b:Vector3d):Vector3d {.inline,noInit.} =
|
||||
vector3d(s(a,b.x),s(a,b.y),s(a,b.z))
|
||||
|
||||
template makeBinOpAssignVector(s:expr)=
|
||||
## implements inplace binary operators += , -= , /= and *= for vectors
|
||||
proc s*(a:var TVector3d,b:TVector3d) {.inline.} =
|
||||
proc s*(a:var Vector3d,b:Vector3d) {.inline.} =
|
||||
s(a.x,b.x) ; s(a.y,b.y) ; s(a.z,b.z)
|
||||
proc s*(a:var TVector3d,b:float) {.inline.} =
|
||||
proc s*(a:var Vector3d,b:float) {.inline.} =
|
||||
s(a.x,b) ; s(a.y,b) ; s(a.z,b)
|
||||
|
||||
|
||||
|
||||
# ***************************************
|
||||
# TMatrix3d implementation
|
||||
# Matrix3d implementation
|
||||
# ***************************************
|
||||
|
||||
proc setElements*(t:var TMatrix3d,ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw:float) {.inline.}=
|
||||
proc setElements*(t:var Matrix3d,ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw:float) {.inline.}=
|
||||
## Sets arbitrary elements in an exisitng matrix.
|
||||
t.ax=ax
|
||||
t.ay=ay
|
||||
@@ -155,10 +155,10 @@ proc setElements*(t:var TMatrix3d,ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,t
|
||||
t.tz=tz
|
||||
t.tw=tw
|
||||
|
||||
proc matrix3d*(ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw:float):TMatrix3d =
|
||||
proc matrix3d*(ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw:float):Matrix3d =
|
||||
result.setElements(ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw)
|
||||
|
||||
proc `&`*(a,b:TMatrix3d):TMatrix3d {.noinit.} =
|
||||
proc `&`*(a,b:Matrix3d):Matrix3d {.noinit.} =
|
||||
## Concatenates matrices returning a new matrix.
|
||||
result.setElements(
|
||||
a.aw*b.tx+a.az*b.cx+a.ay*b.bx+a.ax*b.ax,
|
||||
@@ -182,36 +182,36 @@ proc `&`*(a,b:TMatrix3d):TMatrix3d {.noinit.} =
|
||||
a.tw*b.tw+a.tz*b.cw+a.ty*b.bw+a.tx*b.aw)
|
||||
|
||||
|
||||
proc scale*(s:float):TMatrix3d {.noInit.} =
|
||||
proc scale*(s:float):Matrix3d {.noInit.} =
|
||||
## Returns a new scaling matrix.
|
||||
result.setElements(s,0,0,0, 0,s,0,0, 0,0,s,0, 0,0,0,1)
|
||||
|
||||
proc scale*(s:float,org:TPoint3d):TMatrix3d {.noInit.} =
|
||||
proc scale*(s:float,org:Point3d):Matrix3d {.noInit.} =
|
||||
## Returns a new scaling matrix using, `org` as scale origin.
|
||||
result.setElements(s,0,0,0, 0,s,0,0, 0,0,s,0,
|
||||
org.x-s*org.x,org.y-s*org.y,org.z-s*org.z,1.0)
|
||||
|
||||
proc stretch*(sx,sy,sz:float):TMatrix3d {.noInit.} =
|
||||
proc stretch*(sx,sy,sz:float):Matrix3d {.noInit.} =
|
||||
## Returns new a stretch matrix, which is a
|
||||
## scale matrix with non uniform scale in x,y and z.
|
||||
result.setElements(sx,0,0,0, 0,sy,0,0, 0,0,sz,0, 0,0,0,1)
|
||||
|
||||
proc stretch*(sx,sy,sz:float,org:TPoint3d):TMatrix3d {.noInit.} =
|
||||
proc stretch*(sx,sy,sz:float,org:Point3d):Matrix3d {.noInit.} =
|
||||
## Returns a new stretch matrix, which is a
|
||||
## scale matrix with non uniform scale in x,y and z.
|
||||
## `org` is used as stretch origin.
|
||||
result.setElements(sx,0,0,0, 0,sy,0,0, 0,0,sz,0, org.x-sx*org.x,org.y-sy*org.y,org.z-sz*org.z,1)
|
||||
|
||||
proc move*(dx,dy,dz:float):TMatrix3d {.noInit.} =
|
||||
proc move*(dx,dy,dz:float):Matrix3d {.noInit.} =
|
||||
## Returns a new translation matrix.
|
||||
result.setElements(1,0,0,0, 0,1,0,0, 0,0,1,0, dx,dy,dz,1)
|
||||
|
||||
proc move*(v:TVector3d):TMatrix3d {.noInit.} =
|
||||
proc move*(v:Vector3d):Matrix3d {.noInit.} =
|
||||
## Returns a new translation matrix from a vector.
|
||||
result.setElements(1,0,0,0, 0,1,0,0, 0,0,1,0, v.x,v.y,v.z,1)
|
||||
|
||||
|
||||
proc rotate*(angle:float,axis:TVector3d):TMatrix3d {.noInit.}=
|
||||
proc rotate*(angle:float,axis:Vector3d):Matrix3d {.noInit.}=
|
||||
## Creates a rotation matrix that rotates `angle` radians over
|
||||
## `axis`, which passes through origo.
|
||||
|
||||
@@ -242,7 +242,7 @@ proc rotate*(angle:float,axis:TVector3d):TMatrix3d {.noInit.}=
|
||||
uwomc+vsi, vwomc-usi, w2+(1.0-w2)*cs, 0.0,
|
||||
0.0,0.0,0.0,1.0)
|
||||
|
||||
proc rotate*(angle:float,org:TPoint3d,axis:TVector3d):TMatrix3d {.noInit.}=
|
||||
proc rotate*(angle:float,org:Point3d,axis:Vector3d):Matrix3d {.noInit.}=
|
||||
## Creates a rotation matrix that rotates `angle` radians over
|
||||
## `axis`, which passes through `org`.
|
||||
|
||||
@@ -282,7 +282,7 @@ proc rotate*(angle:float,org:TPoint3d,axis:TVector3d):TMatrix3d {.noInit.}=
|
||||
(c*(u2+v2)-w*(a*u+b*v))*omc+(a*v-b*u)*si,1.0)
|
||||
|
||||
|
||||
proc rotateX*(angle:float):TMatrix3d {.noInit.}=
|
||||
proc rotateX*(angle:float):Matrix3d {.noInit.}=
|
||||
## Creates a matrix that rotates around the x-axis with `angle` radians,
|
||||
## which is also called a 'roll' matrix.
|
||||
let
|
||||
@@ -294,7 +294,7 @@ proc rotateX*(angle:float):TMatrix3d {.noInit.}=
|
||||
0,-s,c,0,
|
||||
0,0,0,1)
|
||||
|
||||
proc rotateY*(angle:float):TMatrix3d {.noInit.}=
|
||||
proc rotateY*(angle:float):Matrix3d {.noInit.}=
|
||||
## Creates a matrix that rotates around the y-axis with `angle` radians,
|
||||
## which is also called a 'pitch' matrix.
|
||||
let
|
||||
@@ -306,7 +306,7 @@ proc rotateY*(angle:float):TMatrix3d {.noInit.}=
|
||||
s,0,c,0,
|
||||
0,0,0,1)
|
||||
|
||||
proc rotateZ*(angle:float):TMatrix3d {.noInit.}=
|
||||
proc rotateZ*(angle:float):Matrix3d {.noInit.}=
|
||||
## Creates a matrix that rotates around the z-axis with `angle` radians,
|
||||
## which is also called a 'yaw' matrix.
|
||||
let
|
||||
@@ -318,7 +318,7 @@ proc rotateZ*(angle:float):TMatrix3d {.noInit.}=
|
||||
0,0,1,0,
|
||||
0,0,0,1)
|
||||
|
||||
proc isUniform*(m:TMatrix3d,tol=1.0e-6):bool=
|
||||
proc isUniform*(m:Matrix3d,tol=1.0e-6):bool=
|
||||
## Checks if the transform is uniform, that is
|
||||
## perpendicular axes of equal length, which means (for example)
|
||||
## it cannot transform a sphere into an ellipsoid.
|
||||
@@ -341,7 +341,7 @@ proc isUniform*(m:TMatrix3d,tol=1.0e-6):bool=
|
||||
|
||||
|
||||
|
||||
proc mirror*(planeperp:TVector3d):TMatrix3d {.noInit.}=
|
||||
proc mirror*(planeperp:Vector3d):Matrix3d {.noInit.}=
|
||||
## Creates a matrix that mirrors over the plane that has `planeperp` as normal,
|
||||
## and passes through origo. `planeperp` does not need to be normalized.
|
||||
|
||||
@@ -365,7 +365,7 @@ proc mirror*(planeperp:TVector3d):TMatrix3d {.noInit.}=
|
||||
0,0,0,1)
|
||||
|
||||
|
||||
proc mirror*(org:TPoint3d,planeperp:TVector3d):TMatrix3d {.noInit.}=
|
||||
proc mirror*(org:Point3d,planeperp:Vector3d):Matrix3d {.noInit.}=
|
||||
## Creates a matrix that mirrors over the plane that has `planeperp` as normal,
|
||||
## and passes through `org`. `planeperp` does not need to be normalized.
|
||||
|
||||
@@ -400,7 +400,7 @@ proc mirror*(org:TPoint3d,planeperp:TVector3d):TMatrix3d {.noInit.}=
|
||||
2*(cc*tz+bc*ty+ac*tx) ,1)
|
||||
|
||||
|
||||
proc determinant*(m:TMatrix3d):float=
|
||||
proc determinant*(m:Matrix3d):float=
|
||||
## Computes the determinant of matrix `m`.
|
||||
|
||||
# This computation is gotten from ratsimp(optimize(determinant(m)))
|
||||
@@ -419,7 +419,7 @@ proc determinant*(m:TMatrix3d):float=
|
||||
(O3*m.az-O5*m.ay+O6*m.ax)*m.bw
|
||||
|
||||
|
||||
proc inverse*(m:TMatrix3d):TMatrix3d {.noInit.}=
|
||||
proc inverse*(m:Matrix3d):Matrix3d {.noInit.}=
|
||||
## Computes the inverse of matrix `m`. If the matrix
|
||||
## determinant is zero, thus not invertible, a EDivByZero
|
||||
## will be raised.
|
||||
@@ -461,7 +461,7 @@ proc inverse*(m:TMatrix3d):TMatrix3d {.noInit.}=
|
||||
(-m.ax*O7+m.ay*O14-m.az*O18)/det , (m.ax*O10-m.ay*O16+m.az*O19)/det)
|
||||
|
||||
|
||||
proc equals*(m1:TMatrix3d,m2:TMatrix3d,tol=1.0e-6):bool=
|
||||
proc equals*(m1:Matrix3d,m2:Matrix3d,tol=1.0e-6):bool=
|
||||
## Checks if all elements of `m1`and `m2` is equal within
|
||||
## a given tolerance `tol`.
|
||||
return
|
||||
@@ -482,42 +482,42 @@ proc equals*(m1:TMatrix3d,m2:TMatrix3d,tol=1.0e-6):bool=
|
||||
abs(m1.tz-m2.tz)<=tol and
|
||||
abs(m1.tw-m2.tw)<=tol
|
||||
|
||||
proc `=~`*(m1,m2:TMatrix3d):bool=
|
||||
proc `=~`*(m1,m2:Matrix3d):bool=
|
||||
## Checks if `m1` and `m2` is approximately equal, using a
|
||||
## tolerance of 1e-6.
|
||||
equals(m1,m2)
|
||||
|
||||
proc transpose*(m:TMatrix3d):TMatrix3d {.noInit.}=
|
||||
proc transpose*(m:Matrix3d):Matrix3d {.noInit.}=
|
||||
## Returns the transpose of `m`
|
||||
result.setElements(m.ax,m.bx,m.cx,m.tx,m.ay,m.by,m.cy,m.ty,m.az,m.bz,m.cz,m.tz,m.aw,m.bw,m.cw,m.tw)
|
||||
|
||||
proc getXAxis*(m:TMatrix3d):TVector3d {.noInit.}=
|
||||
proc getXAxis*(m:Matrix3d):Vector3d {.noInit.}=
|
||||
## Gets the local x axis of `m`
|
||||
result.x=m.ax
|
||||
result.y=m.ay
|
||||
result.z=m.az
|
||||
|
||||
proc getYAxis*(m:TMatrix3d):TVector3d {.noInit.}=
|
||||
proc getYAxis*(m:Matrix3d):Vector3d {.noInit.}=
|
||||
## Gets the local y axis of `m`
|
||||
result.x=m.bx
|
||||
result.y=m.by
|
||||
result.z=m.bz
|
||||
|
||||
proc getZAxis*(m:TMatrix3d):TVector3d {.noInit.}=
|
||||
proc getZAxis*(m:Matrix3d):Vector3d {.noInit.}=
|
||||
## Gets the local y axis of `m`
|
||||
result.x=m.cx
|
||||
result.y=m.cy
|
||||
result.z=m.cz
|
||||
|
||||
|
||||
proc `$`*(m:TMatrix3d):string=
|
||||
proc `$`*(m:Matrix3d):string=
|
||||
## String representation of `m`
|
||||
return rtos(m.ax) & "," & rtos(m.ay) & "," & rtos(m.az) & "," & rtos(m.aw) &
|
||||
"\n" & rtos(m.bx) & "," & rtos(m.by) & "," & rtos(m.bz) & "," & rtos(m.bw) &
|
||||
"\n" & rtos(m.cx) & "," & rtos(m.cy) & "," & rtos(m.cz) & "," & rtos(m.cw) &
|
||||
"\n" & rtos(m.tx) & "," & rtos(m.ty) & "," & rtos(m.tz) & "," & rtos(m.tw)
|
||||
|
||||
proc apply*(m:TMatrix3d, x,y,z:var float, translate=false)=
|
||||
proc apply*(m:Matrix3d, x,y,z:var float, translate=false)=
|
||||
## Applies transformation `m` onto `x` , `y` , `z` , optionally
|
||||
## using the translation part of the matrix.
|
||||
let
|
||||
@@ -535,18 +535,18 @@ proc apply*(m:TMatrix3d, x,y,z:var float, translate=false)=
|
||||
z+=m.tz
|
||||
|
||||
# ***************************************
|
||||
# TVector3d implementation
|
||||
# Vector3d implementation
|
||||
# ***************************************
|
||||
proc vector3d*(x,y,z:float):TVector3d=
|
||||
proc vector3d*(x,y,z:float):Vector3d=
|
||||
result.x=x
|
||||
result.y=y
|
||||
result.z=z
|
||||
|
||||
proc len*(v:TVector3d):float=
|
||||
proc len*(v:Vector3d):float=
|
||||
## Returns the length of the vector `v`.
|
||||
sqrt(v.x*v.x+v.y*v.y+v.z*v.z)
|
||||
|
||||
proc `len=`*(v:var TVector3d,newlen:float) {.noInit.} =
|
||||
proc `len=`*(v:var Vector3d,newlen:float) {.noInit.} =
|
||||
## Sets the length of the vector, keeping its direction.
|
||||
## If the vector has zero length before changing it's length,
|
||||
## an arbitrary vector of the requested length is returned.
|
||||
@@ -571,12 +571,12 @@ proc `len=`*(v:var TVector3d,newlen:float) {.noInit.} =
|
||||
v.z*=fac
|
||||
|
||||
|
||||
proc sqrLen*(v:TVector3d):float {.inline.}=
|
||||
proc sqrLen*(v:Vector3d):float {.inline.}=
|
||||
## Computes the squared length of the vector, which is
|
||||
## faster than computing the absolute length.
|
||||
return v.x*v.x+v.y*v.y+v.z*v.z
|
||||
|
||||
proc `$` *(v:TVector3d):string=
|
||||
proc `$` *(v:Vector3d):string=
|
||||
## String representation of `v`
|
||||
result=rtos(v.x)
|
||||
result.add(",")
|
||||
@@ -584,7 +584,7 @@ proc `$` *(v:TVector3d):string=
|
||||
result.add(",")
|
||||
result.add(rtos(v.z))
|
||||
|
||||
proc `&` *(v:TVector3d,m:TMatrix3d):TVector3d {.noInit.} =
|
||||
proc `&` *(v:Vector3d,m:Matrix3d):Vector3d {.noInit.} =
|
||||
## Concatenate vector `v` with a transformation matrix.
|
||||
## Transforming a vector ignores the translational part
|
||||
## of the matrix.
|
||||
@@ -601,7 +601,7 @@ proc `&` *(v:TVector3d,m:TMatrix3d):TVector3d {.noInit.} =
|
||||
result.x=newx
|
||||
|
||||
|
||||
proc `&=` *(v:var TVector3d,m:TMatrix3d) {.noInit.} =
|
||||
proc `&=` *(v:var Vector3d,m:Matrix3d) {.noInit.} =
|
||||
## Applies transformation `m` onto `v` in place.
|
||||
## Transforming a vector ignores the translational part
|
||||
## of the matrix.
|
||||
@@ -618,7 +618,7 @@ proc `&=` *(v:var TVector3d,m:TMatrix3d) {.noInit.} =
|
||||
v.y=newy
|
||||
v.x=newx
|
||||
|
||||
proc transformNorm*(v:var TVector3d,m:TMatrix3d)=
|
||||
proc transformNorm*(v:var Vector3d,m:Matrix3d)=
|
||||
## Applies a normal direction transformation `m` onto `v` in place.
|
||||
## The resulting vector is *not* normalized. Transforming a vector ignores the
|
||||
## translational part of the matrix. If the matrix is not invertible
|
||||
@@ -631,7 +631,7 @@ proc transformNorm*(v:var TVector3d,m:TMatrix3d)=
|
||||
# (possibly by hardware) as well as having a consistent API with the 2d version.
|
||||
v&=transpose(inverse(m))
|
||||
|
||||
proc transformInv*(v:var TVector3d,m:TMatrix3d)=
|
||||
proc transformInv*(v:var Vector3d,m:Matrix3d)=
|
||||
## Applies the inverse of `m` on vector `v`. Transforming a vector ignores
|
||||
## the translational part of the matrix. Transforming a vector ignores the
|
||||
## translational part of the matrix.
|
||||
@@ -642,7 +642,7 @@ proc transformInv*(v:var TVector3d,m:TMatrix3d)=
|
||||
# (possibly by hardware) as well as having a consistent API with the 2d version.
|
||||
v&=m.inverse
|
||||
|
||||
proc transformNormInv*(vec:var TVector3d,m:TMatrix3d)=
|
||||
proc transformNormInv*(vec:var Vector3d,m:Matrix3d)=
|
||||
## Applies an inverse normal direction transformation `m` onto `v` in place.
|
||||
## This is faster than creating an inverse
|
||||
## matrix and transformNorm(...) it. Transforming a vector ignores the
|
||||
@@ -651,7 +651,7 @@ proc transformNormInv*(vec:var TVector3d,m:TMatrix3d)=
|
||||
# see vector2d:s equivalent for a deeper look how/why this works
|
||||
vec&=m.transpose
|
||||
|
||||
proc tryNormalize*(v:var TVector3d):bool=
|
||||
proc tryNormalize*(v:var Vector3d):bool=
|
||||
## Modifies `v` to have a length of 1.0, keeping its angle.
|
||||
## If `v` has zero length (and thus no angle), it is left unmodified and false is
|
||||
## returned, otherwise true is returned.
|
||||
@@ -666,13 +666,13 @@ proc tryNormalize*(v:var TVector3d):bool=
|
||||
|
||||
return true
|
||||
|
||||
proc normalize*(v:var TVector3d) {.inline.}=
|
||||
proc normalize*(v:var Vector3d) {.inline.}=
|
||||
## Modifies `v` to have a length of 1.0, keeping its angle.
|
||||
## If `v` has zero length, an EDivByZero will be raised.
|
||||
if not tryNormalize(v):
|
||||
raise newException(DivByZeroError,"Cannot normalize zero length vector")
|
||||
|
||||
proc rotate*(vec:var TVector3d,angle:float,axis:TVector3d)=
|
||||
proc rotate*(vec:var Vector3d,angle:float,axis:Vector3d)=
|
||||
## Rotates `vec` in place, with `angle` radians over `axis`, which passes
|
||||
## through origo.
|
||||
|
||||
@@ -699,19 +699,19 @@ proc rotate*(vec:var TVector3d,angle:float,axis:TVector3d)=
|
||||
vec.y=v*uxyzomc+y*cs+(w*x-u*z)*si
|
||||
vec.z=w*uxyzomc+z*cs+(u*y-v*x)*si
|
||||
|
||||
proc scale*(v:var TVector3d,s:float)=
|
||||
proc scale*(v:var Vector3d,s:float)=
|
||||
## Scales the vector in place with factor `s`
|
||||
v.x*=s
|
||||
v.y*=s
|
||||
v.z*=s
|
||||
|
||||
proc stretch*(v:var TVector3d,sx,sy,sz:float)=
|
||||
proc stretch*(v:var Vector3d,sx,sy,sz:float)=
|
||||
## Scales the vector non uniformly with factors `sx` , `sy` , `sz`
|
||||
v.x*=sx
|
||||
v.y*=sy
|
||||
v.z*=sz
|
||||
|
||||
proc mirror*(v:var TVector3d,planeperp:TVector3d)=
|
||||
proc mirror*(v:var Vector3d,planeperp:Vector3d)=
|
||||
## Computes the mirrored vector of `v` over the plane
|
||||
## that has `planeperp` as normal direction.
|
||||
## `planeperp` does not need to be normalized.
|
||||
@@ -735,7 +735,7 @@ proc mirror*(v:var TVector3d,planeperp:TVector3d)=
|
||||
v.z= -2*(c*c*z+bc*y+ac*x)+z
|
||||
|
||||
|
||||
proc `-` *(v:TVector3d):TVector3d=
|
||||
proc `-` *(v:Vector3d):Vector3d=
|
||||
## Negates a vector
|
||||
result.x= -v.x
|
||||
result.y= -v.y
|
||||
@@ -751,12 +751,12 @@ makeBinOpAssignVector(`-=`)
|
||||
makeBinOpAssignVector(`*=`)
|
||||
makeBinOpAssignVector(`/=`)
|
||||
|
||||
proc dot*(v1,v2:TVector3d):float {.inline.}=
|
||||
proc dot*(v1,v2:Vector3d):float {.inline.}=
|
||||
## Computes the dot product of two vectors.
|
||||
## Returns 0.0 if the vectors are perpendicular.
|
||||
return v1.x*v2.x+v1.y*v2.y+v1.z*v2.z
|
||||
|
||||
proc cross*(v1,v2:TVector3d):TVector3d {.inline.}=
|
||||
proc cross*(v1,v2:Vector3d):Vector3d {.inline.}=
|
||||
## Computes the cross product of two vectors.
|
||||
## The result is a vector which is perpendicular
|
||||
## to the plane of `v1` and `v2`, which means
|
||||
@@ -766,16 +766,16 @@ proc cross*(v1,v2:TVector3d):TVector3d {.inline.}=
|
||||
result.y = (v1.z * v2.x) - (v2.z * v1.x)
|
||||
result.z = (v1.x * v2.y) - (v2.x * v1.y)
|
||||
|
||||
proc equals*(v1,v2:TVector3d,tol=1.0e-6):bool=
|
||||
proc equals*(v1,v2:Vector3d,tol=1.0e-6):bool=
|
||||
## Checks if two vectors approximately equals with a tolerance.
|
||||
return abs(v2.x-v1.x)<=tol and abs(v2.y-v1.y)<=tol and abs(v2.z-v1.z)<=tol
|
||||
|
||||
proc `=~` *(v1,v2:TVector3d):bool=
|
||||
proc `=~` *(v1,v2:Vector3d):bool=
|
||||
## Checks if two vectors approximately equals with a
|
||||
## hardcoded tolerance 1e-6
|
||||
equals(v1,v2)
|
||||
|
||||
proc angleTo*(v1,v2:TVector3d):float=
|
||||
proc angleTo*(v1,v2:Vector3d):float=
|
||||
## Returns the smallest angle between v1 and v2,
|
||||
## which is in range 0-PI
|
||||
var
|
||||
@@ -785,13 +785,13 @@ proc angleTo*(v1,v2:TVector3d):float=
|
||||
return 0.0 # zero length vector has zero angle to any other vector
|
||||
return safeArccos(dot(nv1,nv2))
|
||||
|
||||
proc arbitraryAxis*(norm:TVector3d):TMatrix3d {.noInit.}=
|
||||
proc arbitraryAxis*(norm:Vector3d):Matrix3d {.noInit.}=
|
||||
## Computes the rotation matrix that would transform
|
||||
## world z vector into `norm`. The inverse of this matrix
|
||||
## is useful to transform a planar 3d object to 2d space.
|
||||
## This is the same algorithm used to interpret DXF and DWG files.
|
||||
const lim=1.0/64.0
|
||||
var ax,ay,az:TVector3d
|
||||
var ax,ay,az:Vector3d
|
||||
if abs(norm.x)<lim and abs(norm.y)<lim:
|
||||
ax=cross(YAXIS,norm)
|
||||
else:
|
||||
@@ -808,7 +808,7 @@ proc arbitraryAxis*(norm:TVector3d):TMatrix3d {.noInit.}=
|
||||
az.x,az.y,az.z,0.0,
|
||||
0.0,0.0,0.0,1.0)
|
||||
|
||||
proc bisect*(v1,v2:TVector3d):TVector3d {.noInit.}=
|
||||
proc bisect*(v1,v2:Vector3d):Vector3d {.noInit.}=
|
||||
## Computes the bisector between v1 and v2 as a normalized vector.
|
||||
## If one of the input vectors has zero length, a normalized version
|
||||
## of the other is returned. If both input vectors has zero length,
|
||||
@@ -851,25 +851,25 @@ proc bisect*(v1,v2:TVector3d):TVector3d {.noInit.}=
|
||||
|
||||
|
||||
# ***************************************
|
||||
# TPoint3d implementation
|
||||
# Point3d implementation
|
||||
# ***************************************
|
||||
proc point3d*(x,y,z:float):TPoint3d=
|
||||
proc point3d*(x,y,z:float):Point3d=
|
||||
result.x=x
|
||||
result.y=y
|
||||
result.z=z
|
||||
|
||||
proc sqrDist*(a,b:TPoint3d):float=
|
||||
proc sqrDist*(a,b:Point3d):float=
|
||||
## Computes the squared distance between `a`and `b`
|
||||
let dx=b.x-a.x
|
||||
let dy=b.y-a.y
|
||||
let dz=b.z-a.z
|
||||
result=dx*dx+dy*dy+dz*dz
|
||||
|
||||
proc dist*(a,b:TPoint3d):float {.inline.}=
|
||||
proc dist*(a,b:Point3d):float {.inline.}=
|
||||
## Computes the absolute distance between `a`and `b`
|
||||
result=sqrt(sqrDist(a,b))
|
||||
|
||||
proc `$` *(p:TPoint3d):string=
|
||||
proc `$` *(p:Point3d):string=
|
||||
## String representation of `p`
|
||||
result=rtos(p.x)
|
||||
result.add(",")
|
||||
@@ -877,14 +877,14 @@ proc `$` *(p:TPoint3d):string=
|
||||
result.add(",")
|
||||
result.add(rtos(p.z))
|
||||
|
||||
proc `&`*(p:TPoint3d,m:TMatrix3d):TPoint3d=
|
||||
proc `&`*(p:Point3d,m:Matrix3d):Point3d=
|
||||
## Concatenates a point `p` with a transform `m`,
|
||||
## resulting in a new, transformed point.
|
||||
result.z=m.cz*p.z+m.bz*p.y+m.az*p.x+m.tz
|
||||
result.y=m.cy*p.z+m.by*p.y+m.ay*p.x+m.ty
|
||||
result.x=m.cx*p.z+m.bx*p.y+m.ax*p.x+m.tx
|
||||
|
||||
proc `&=` *(p:var TPoint3d,m:TMatrix3d)=
|
||||
proc `&=` *(p:var Point3d,m:Matrix3d)=
|
||||
## Applies transformation `m` onto `p` in place.
|
||||
let
|
||||
x=p.x
|
||||
@@ -894,7 +894,7 @@ proc `&=` *(p:var TPoint3d,m:TMatrix3d)=
|
||||
p.y=m.cy*z+m.by*y+m.ay*x+m.ty
|
||||
p.z=m.cz*z+m.bz*y+m.az*x+m.tz
|
||||
|
||||
proc transformInv*(p:var TPoint3d,m:TMatrix3d)=
|
||||
proc transformInv*(p:var Point3d,m:Matrix3d)=
|
||||
## Applies the inverse of transformation `m` onto `p` in place.
|
||||
## If the matrix is not invertable (determinant=0) , EDivByZero will
|
||||
## be raised.
|
||||
@@ -903,48 +903,48 @@ proc transformInv*(p:var TPoint3d,m:TMatrix3d)=
|
||||
p&=inverse(m)
|
||||
|
||||
|
||||
proc `+`*(p:TPoint3d,v:TVector3d):TPoint3d {.noInit,inline.} =
|
||||
proc `+`*(p:Point3d,v:Vector3d):Point3d {.noInit,inline.} =
|
||||
## Adds a vector `v` to a point `p`, resulting
|
||||
## in a new point.
|
||||
result.x=p.x+v.x
|
||||
result.y=p.y+v.y
|
||||
result.z=p.z+v.z
|
||||
|
||||
proc `+=`*(p:var TPoint3d,v:TVector3d) {.noInit,inline.} =
|
||||
proc `+=`*(p:var Point3d,v:Vector3d) {.noInit,inline.} =
|
||||
## Adds a vector `v` to a point `p` in place.
|
||||
p.x+=v.x
|
||||
p.y+=v.y
|
||||
p.z+=v.z
|
||||
|
||||
proc `-`*(p:TPoint3d,v:TVector3d):TPoint3d {.noInit,inline.} =
|
||||
proc `-`*(p:Point3d,v:Vector3d):Point3d {.noInit,inline.} =
|
||||
## Subtracts a vector `v` from a point `p`, resulting
|
||||
## in a new point.
|
||||
result.x=p.x-v.x
|
||||
result.y=p.y-v.y
|
||||
result.z=p.z-v.z
|
||||
|
||||
proc `-`*(p1,p2:TPoint3d):TVector3d {.noInit,inline.} =
|
||||
proc `-`*(p1,p2:Point3d):Vector3d {.noInit,inline.} =
|
||||
## Subtracts `p2`from `p1` resulting in a difference vector.
|
||||
result.x=p1.x-p2.x
|
||||
result.y=p1.y-p2.y
|
||||
result.z=p1.z-p2.z
|
||||
|
||||
proc `-=`*(p:var TPoint3d,v:TVector3d) {.noInit,inline.} =
|
||||
proc `-=`*(p:var Point3d,v:Vector3d) {.noInit,inline.} =
|
||||
## Subtracts a vector `v` from a point `p` in place.
|
||||
p.x-=v.x
|
||||
p.y-=v.y
|
||||
p.z-=v.z
|
||||
|
||||
proc equals(p1,p2:TPoint3d,tol=1.0e-6):bool {.inline.}=
|
||||
proc equals(p1,p2:Point3d,tol=1.0e-6):bool {.inline.}=
|
||||
## Checks if two points approximately equals with a tolerance.
|
||||
return abs(p2.x-p1.x)<=tol and abs(p2.y-p1.y)<=tol and abs(p2.z-p1.z)<=tol
|
||||
|
||||
proc `=~`*(p1,p2:TPoint3d):bool {.inline.}=
|
||||
proc `=~`*(p1,p2:Point3d):bool {.inline.}=
|
||||
## Checks if two vectors approximately equals with a
|
||||
## hardcoded tolerance 1e-6
|
||||
equals(p1,p2)
|
||||
|
||||
proc rotate*(p:var TPoint3d,rad:float,axis:TVector3d)=
|
||||
proc rotate*(p:var Point3d,rad:float,axis:Vector3d)=
|
||||
## Rotates point `p` in place `rad` radians about an axis
|
||||
## passing through origo.
|
||||
|
||||
@@ -954,7 +954,7 @@ proc rotate*(p:var TPoint3d,rad:float,axis:TVector3d)=
|
||||
p.y=v.y
|
||||
p.z=v.z
|
||||
|
||||
proc rotate*(p:var TPoint3d,angle:float,org:TPoint3d,axis:TVector3d)=
|
||||
proc rotate*(p:var Point3d,angle:float,org:Point3d,axis:Vector3d)=
|
||||
## Rotates point `p` in place `rad` radians about an axis
|
||||
## passing through `org`
|
||||
|
||||
@@ -992,26 +992,26 @@ proc rotate*(p:var TPoint3d,angle:float,org:TPoint3d,axis:TVector3d)=
|
||||
p.y=(b*(uu+ww)-v*(au+cw-uxmvymwz))*omc + y*cs + (c*u-a*w+w*x-u*z)*si
|
||||
p.z=(c*(uu+vv)-w*(au+bv-uxmvymwz))*omc + z*cs + (a*v+u*y-b*u-v*x)*si
|
||||
|
||||
proc scale*(p:var TPoint3d,fac:float) {.inline.}=
|
||||
proc scale*(p:var Point3d,fac:float) {.inline.}=
|
||||
## Scales a point in place `fac` times with world origo as origin.
|
||||
p.x*=fac
|
||||
p.y*=fac
|
||||
p.z*=fac
|
||||
|
||||
proc scale*(p:var TPoint3d,fac:float,org:TPoint3d){.inline.}=
|
||||
proc scale*(p:var Point3d,fac:float,org:Point3d){.inline.}=
|
||||
## Scales the point in place `fac` times with `org` as origin.
|
||||
p.x=(p.x - org.x) * fac + org.x
|
||||
p.y=(p.y - org.y) * fac + org.y
|
||||
p.z=(p.z - org.z) * fac + org.z
|
||||
|
||||
proc stretch*(p:var TPoint3d,facx,facy,facz:float){.inline.}=
|
||||
proc stretch*(p:var Point3d,facx,facy,facz:float){.inline.}=
|
||||
## Scales a point in place non uniformly `facx` , `facy` , `facz` times
|
||||
## with world origo as origin.
|
||||
p.x*=facx
|
||||
p.y*=facy
|
||||
p.z*=facz
|
||||
|
||||
proc stretch*(p:var TPoint3d,facx,facy,facz:float,org:TPoint3d){.inline.}=
|
||||
proc stretch*(p:var Point3d,facx,facy,facz:float,org:Point3d){.inline.}=
|
||||
## Scales the point in place non uniformly `facx` , `facy` , `facz` times
|
||||
## with `org` as origin.
|
||||
p.x=(p.x - org.x) * facx + org.x
|
||||
@@ -1019,19 +1019,19 @@ proc stretch*(p:var TPoint3d,facx,facy,facz:float,org:TPoint3d){.inline.}=
|
||||
p.z=(p.z - org.z) * facz + org.z
|
||||
|
||||
|
||||
proc move*(p:var TPoint3d,dx,dy,dz:float){.inline.}=
|
||||
proc move*(p:var Point3d,dx,dy,dz:float){.inline.}=
|
||||
## Translates a point `dx` , `dy` , `dz` in place.
|
||||
p.x+=dx
|
||||
p.y+=dy
|
||||
p.z+=dz
|
||||
|
||||
proc move*(p:var TPoint3d,v:TVector3d){.inline.}=
|
||||
proc move*(p:var Point3d,v:Vector3d){.inline.}=
|
||||
## Translates a point with vector `v` in place.
|
||||
p.x+=v.x
|
||||
p.y+=v.y
|
||||
p.z+=v.z
|
||||
|
||||
proc area*(a,b,c:TPoint3d):float {.inline.}=
|
||||
proc area*(a,b,c:Point3d):float {.inline.}=
|
||||
## Computes the area of the triangle thru points `a` , `b` and `c`
|
||||
|
||||
# The area of a planar 3d quadliteral is the magnitude of the cross
|
||||
|
||||
@@ -46,24 +46,25 @@ const
|
||||
|
||||
when sizeof(int) == 4: # 32bit
|
||||
type
|
||||
TRaw = range[0..1073741823]
|
||||
Raw = range[0..1073741823]
|
||||
## The range of uint values that can be stored directly in a value slot
|
||||
## when on a 32 bit platform
|
||||
|
||||
{.deprecated: [TRaw: Raw].}
|
||||
elif sizeof(int) == 8: # 64bit
|
||||
type
|
||||
TRaw = range[0..4611686018427387903]
|
||||
Raw = range[0..4611686018427387903]
|
||||
## The range of uint values that can be stored directly in a value slot
|
||||
## when on a 64 bit platform
|
||||
{.deprecated: [TRaw: Raw].}
|
||||
else:
|
||||
{.error: "unsupported platform".}
|
||||
|
||||
type
|
||||
TEntry = tuple
|
||||
Entry = tuple
|
||||
key: int
|
||||
value: int
|
||||
|
||||
TEntryArr = ptr array[0..10_000_000, TEntry]
|
||||
EntryArr = ptr array[0..10_000_000, Entry]
|
||||
|
||||
PConcTable[K,V] = ptr object {.pure.}
|
||||
len: int
|
||||
@@ -72,8 +73,8 @@ type
|
||||
copyIdx: int
|
||||
copyDone: int
|
||||
next: PConcTable[K,V]
|
||||
data: TEntryArr
|
||||
|
||||
data: EntryArr
|
||||
{.deprecated: [TEntry: Entry, TEntryArr: EntryArr.}
|
||||
|
||||
proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
|
||||
expVal: int, match: bool): int
|
||||
@@ -84,7 +85,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
|
||||
proc newLFTable*[K,V](size: int = minTableSize): PConcTable[K,V] =
|
||||
let
|
||||
dataLen = max(nextPowerOfTwo(size), minTableSize)
|
||||
dataSize = dataLen*sizeof(TEntry)
|
||||
dataSize = dataLen*sizeof(Entry)
|
||||
dataMem = allocShared0(dataSize)
|
||||
tableSize = 7 * intSize
|
||||
tableMem = allocShared0(tableSize)
|
||||
@@ -95,7 +96,7 @@ proc newLFTable*[K,V](size: int = minTableSize): PConcTable[K,V] =
|
||||
table.copyIdx = 0
|
||||
table.copyDone = 0
|
||||
table.next = nil
|
||||
table.data = cast[TEntryArr](dataMem)
|
||||
table.data = cast[EntryArr](dataMem)
|
||||
result = table
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
@@ -107,7 +108,7 @@ proc deleteConcTable[K,V](tbl: PConcTable[K,V]) =
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
proc `[]`[K,V](table: var PConcTable[K,V], i: int): var TEntry {.inline.} =
|
||||
proc `[]`[K,V](table: var PConcTable[K,V], i: int): var Entry {.inline.} =
|
||||
table.data[i]
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
@@ -191,7 +192,7 @@ proc resize[K,V](self: PConcTable[K,V]): PConcTable[K,V] =
|
||||
#proc keyEQ[K](key1: ptr K, key2: ptr K): bool {.inline.} =
|
||||
proc keyEQ[K](key1: int, key2: int): bool {.inline.} =
|
||||
result = false
|
||||
when K is TRaw:
|
||||
when K is Raw:
|
||||
if key1 == key2:
|
||||
result = true
|
||||
else:
|
||||
@@ -236,7 +237,7 @@ proc copySlot[K,V](idx: int, oldTbl: var PConcTable[K,V], newTbl: var PConcTable
|
||||
break
|
||||
#echo("oldVal was = ", oldVal, " set it to prime ", box)
|
||||
if isPrime(oldVal) and isTomb(oldVal):
|
||||
#when not (K is TRaw):
|
||||
#when not (K is Raw):
|
||||
# deallocShared(popPtr[K](oldKey))
|
||||
return false
|
||||
if isTomb(oldVal):
|
||||
@@ -343,7 +344,7 @@ proc helpCopy[K,V](table: var PConcTable[K,V]): PConcTable[K,V] =
|
||||
proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
|
||||
expVal: int, match: bool): int =
|
||||
#echo("-try set- in table ", " key = ", (popPtr[K](key)[]), " val = ", val)
|
||||
when K is TRaw:
|
||||
when K is Raw:
|
||||
var idx = hashInt(key)
|
||||
else:
|
||||
var idx = popPtr[K](key)[].hash
|
||||
@@ -428,7 +429,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
|
||||
|
||||
proc getVal[K,V](table: var PConcTable[K,V], key: int): int =
|
||||
#echo("-try get- key = " & $key)
|
||||
when K is TRaw:
|
||||
when K is Raw:
|
||||
var idx = hashInt(key)
|
||||
else:
|
||||
var idx = popPtr[K](key)[].hash
|
||||
@@ -468,37 +469,37 @@ proc getVal[K,V](table: var PConcTable[K,V], key: int): int =
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#proc set*(table: var PConcTable[TRaw,TRaw], key: TRaw, val: TRaw) =
|
||||
#proc set*(table: var PConcTable[Raw,Raw], key: Raw, val: Raw) =
|
||||
# discard setVal(table, pack(key), pack(key), 0, false)
|
||||
|
||||
#proc set*[V](table: var PConcTable[TRaw,V], key: TRaw, val: ptr V) =
|
||||
#proc set*[V](table: var PConcTable[Raw,V], key: Raw, val: ptr V) =
|
||||
# discard setVal(table, pack(key), cast[int](val), 0, false)
|
||||
|
||||
proc set*[K,V](table: var PConcTable[K,V], key: var K, val: var V) =
|
||||
when not (K is TRaw):
|
||||
when not (K is Raw):
|
||||
var newKey = cast[int](copyShared(key))
|
||||
else:
|
||||
var newKey = pack(key)
|
||||
when not (V is TRaw):
|
||||
when not (V is Raw):
|
||||
var newVal = cast[int](copyShared(val))
|
||||
else:
|
||||
var newVal = pack(val)
|
||||
var oldPtr = pop(setVal(table, newKey, newVal, 0, false))
|
||||
#echo("oldPtr = ", cast[int](oldPtr), " newPtr = ", cast[int](newPtr))
|
||||
when not (V is TRaw):
|
||||
when not (V is Raw):
|
||||
if newVal != oldPtr and oldPtr != 0:
|
||||
deallocShared(cast[ptr V](oldPtr))
|
||||
|
||||
|
||||
|
||||
proc get*[K,V](table: var PConcTable[K,V], key: var K): V =
|
||||
when not (V is TRaw):
|
||||
when not (K is TRaw):
|
||||
when not (V is Raw):
|
||||
when not (K is Raw):
|
||||
return popPtr[V](getVal(table, cast[int](key.addr)))[]
|
||||
else:
|
||||
return popPtr[V](getVal(table, pack(key)))[]
|
||||
else:
|
||||
when not (K is TRaw):
|
||||
when not (K is Raw):
|
||||
return popRaw(getVal(table, cast[int](key.addr)))
|
||||
else:
|
||||
return popRaw(getVal(table, pack(key)))
|
||||
@@ -535,23 +536,24 @@ when not defined(testing) and isMainModule:
|
||||
|
||||
|
||||
type
|
||||
TTestObj = tuple
|
||||
TestObj = tuple
|
||||
thr: int
|
||||
f0: int
|
||||
f1: int
|
||||
|
||||
TData = tuple[k: string,v: TTestObj]
|
||||
PDataArr = array[0..numTests-1, TData]
|
||||
Dict = PConcTable[string,TTestObj]
|
||||
Data = tuple[k: string,v: TestObj]
|
||||
PDataArr = array[0..numTests-1, Data]
|
||||
Dict = PConcTable[string,TestObj]
|
||||
{.deprecated: [TTestObj: TestObj, TData: Data].}
|
||||
|
||||
var
|
||||
thr: array[0..numThreads-1, TThread[Dict]]
|
||||
thr: array[0..numThreads-1, Thread[Dict]]
|
||||
|
||||
table = newLFTable[string,TTestObj](8)
|
||||
table = newLFTable[string,TestObj](8)
|
||||
rand = newMersenneTwister(2525)
|
||||
|
||||
proc createSampleData(len: int): PDataArr =
|
||||
#result = cast[PDataArr](allocShared0(sizeof(TData)*numTests))
|
||||
#result = cast[PDataArr](allocShared0(sizeof(Data)*numTests))
|
||||
for i in 0..len-1:
|
||||
result[i].k = "mark" & $(i+1)
|
||||
#echo("mark" & $(i+1), " ", hash("mark" & $(i+1)))
|
||||
|
||||
@@ -30,25 +30,25 @@ const
|
||||
IntMask = 1 shl IntShift - 1
|
||||
|
||||
type
|
||||
PTrunk = ref TTrunk
|
||||
TTrunk {.final.} = object
|
||||
PTrunk = ref Trunk
|
||||
Trunk {.final.} = object
|
||||
next: PTrunk # all nodes are connected with this pointer
|
||||
key: int # start address at bit 0
|
||||
bits: array[0..IntsPerTrunk - 1, BitScalar] # a bit vector
|
||||
|
||||
TTrunkSeq = seq[PTrunk]
|
||||
TrunkSeq = seq[PTrunk]
|
||||
IntSet* = object ## an efficient set of 'int' implemented as a sparse bit set
|
||||
counter, max: int
|
||||
head: PTrunk
|
||||
data: TTrunkSeq
|
||||
data: TrunkSeq
|
||||
|
||||
{.deprecated: [TIntSet: IntSet].}
|
||||
{.deprecated: [TIntSet: IntSet, TTrunk: Trunk, TTrunkSeq: TrunkSeq].}
|
||||
|
||||
proc mustRehash(length, counter: int): bool {.inline.} =
|
||||
assert(length > counter)
|
||||
result = (length * 2 < counter * 3) or (length - counter < 4)
|
||||
|
||||
proc nextTry(h, maxHash: THash): THash {.inline.} =
|
||||
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
|
||||
result = ((5 * h) + 1) and maxHash
|
||||
|
||||
proc intSetGet(t: IntSet, key: int): PTrunk =
|
||||
@@ -59,7 +59,7 @@ proc intSetGet(t: IntSet, key: int): PTrunk =
|
||||
h = nextTry(h, t.max)
|
||||
result = nil
|
||||
|
||||
proc intSetRawInsert(t: IntSet, data: var TTrunkSeq, desc: PTrunk) =
|
||||
proc intSetRawInsert(t: IntSet, data: var TrunkSeq, desc: PTrunk) =
|
||||
var h = desc.key and t.max
|
||||
while data[h] != nil:
|
||||
assert(data[h] != desc)
|
||||
@@ -68,7 +68,7 @@ proc intSetRawInsert(t: IntSet, data: var TTrunkSeq, desc: PTrunk) =
|
||||
data[h] = desc
|
||||
|
||||
proc intSetEnlarge(t: var IntSet) =
|
||||
var n: TTrunkSeq
|
||||
var n: TrunkSeq
|
||||
var oldMax = t.max
|
||||
t.max = ((t.max + 1) * 2) - 1
|
||||
newSeq(n, t.max + 1)
|
||||
|
||||
@@ -29,7 +29,7 @@ when not defined(nimhygiene):
|
||||
# codes should never be needed, and this can pack more entries per cache-line.
|
||||
# Losing hcode entirely is also possible - if some element value is forbidden.
|
||||
type
|
||||
KeyValuePair[A] = tuple[hcode: THash, key: A]
|
||||
KeyValuePair[A] = tuple[hcode: Hash, key: A]
|
||||
KeyValuePairSeq[A] = seq[KeyValuePair[A]]
|
||||
HashSet* {.myShallow.}[A] = object ## \
|
||||
## A generic hash set.
|
||||
@@ -43,10 +43,10 @@ type
|
||||
|
||||
# hcode for real keys cannot be zero. hcode==0 signifies an empty slot. These
|
||||
# two procs retain clarity of that encoding without the space cost of an enum.
|
||||
proc isEmpty(hcode: THash): bool {.inline.} =
|
||||
proc isEmpty(hcode: Hash): bool {.inline.} =
|
||||
result = hcode == 0
|
||||
|
||||
proc isFilled(hcode: THash): bool {.inline.} =
|
||||
proc isFilled(hcode: Hash): bool {.inline.} =
|
||||
result = hcode != 0
|
||||
|
||||
proc isValid*[A](s: HashSet[A]): bool =
|
||||
@@ -58,7 +58,7 @@ proc isValid*[A](s: HashSet[A]): bool =
|
||||
## initialized. Example:
|
||||
##
|
||||
## .. code-block ::
|
||||
## proc savePreferences(options: TSet[string]) =
|
||||
## proc savePreferences(options: Set[string]) =
|
||||
## assert options.isValid, "Pass an initialized set!"
|
||||
## # Do stuff here, may crash in release builds!
|
||||
result = not s.data.isNil
|
||||
@@ -72,7 +72,7 @@ proc len*[A](s: HashSet[A]): int =
|
||||
##
|
||||
## .. code-block::
|
||||
##
|
||||
## var values: TSet[int]
|
||||
## var values: Set[int]
|
||||
## assert(not values.isValid)
|
||||
## assert values.len == 0
|
||||
result = s.counter
|
||||
@@ -123,15 +123,15 @@ proc rightSize*(count: Natural): int {.inline.} =
|
||||
## Internally, we want mustRehash(rightSize(x), x) == false.
|
||||
result = nextPowerOfTwo(count * 3 div 2 + 4)
|
||||
|
||||
proc nextTry(h, maxHash: THash): THash {.inline.} =
|
||||
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
|
||||
result = (h + 1) and maxHash
|
||||
|
||||
template rawGetKnownHCImpl() {.dirty.} =
|
||||
var h: THash = hc and high(s.data) # start with real hash value
|
||||
var h: Hash = hc and high(s.data) # start with real hash value
|
||||
while isFilled(s.data[h].hcode):
|
||||
# Compare hc THEN key with boolean short circuit. This makes the common case
|
||||
# zero ==key's for missing (e.g.inserts) and exactly one ==key for present.
|
||||
# It does slow down succeeding lookups by one extra THash cmp&and..usually
|
||||
# It does slow down succeeding lookups by one extra Hash cmp&and..usually
|
||||
# just a few clock cycles, generally worth it for any non-integer-like A.
|
||||
if s.data[h].hcode == hc and s.data[h].key == key: # compare hc THEN key
|
||||
return h
|
||||
@@ -148,10 +148,10 @@ template rawInsertImpl() {.dirty.} =
|
||||
data[h].key = key
|
||||
data[h].hcode = hc
|
||||
|
||||
proc rawGetKnownHC[A](s: HashSet[A], key: A, hc: THash): int {.inline.} =
|
||||
proc rawGetKnownHC[A](s: HashSet[A], key: A, hc: Hash): int {.inline.} =
|
||||
rawGetKnownHCImpl()
|
||||
|
||||
proc rawGet[A](s: HashSet[A], key: A, hc: var THash): int {.inline.} =
|
||||
proc rawGet[A](s: HashSet[A], key: A, hc: var Hash): int {.inline.} =
|
||||
rawGetImpl()
|
||||
|
||||
proc mget*[A](s: var HashSet[A], key: A): var A =
|
||||
@@ -160,7 +160,7 @@ proc mget*[A](s: var HashSet[A], key: A): var A =
|
||||
## when one overloaded 'hash' and '==' but still needs reference semantics
|
||||
## for sharing.
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var index = rawGet(s, key, hc)
|
||||
if index >= 0: result = s.data[index].key
|
||||
else: raise newException(KeyError, "key not found: " & $key)
|
||||
@@ -178,12 +178,12 @@ proc contains*[A](s: HashSet[A], key: A): bool =
|
||||
## values.excl(2)
|
||||
## assert(not values.contains(2))
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var index = rawGet(s, key, hc)
|
||||
result = index >= 0
|
||||
|
||||
proc rawInsert[A](s: var HashSet[A], data: var KeyValuePairSeq[A], key: A,
|
||||
hc: THash, h: THash) =
|
||||
hc: Hash, h: Hash) =
|
||||
rawInsertImpl()
|
||||
|
||||
proc enlarge[A](s: var HashSet[A]) =
|
||||
@@ -196,7 +196,7 @@ proc enlarge[A](s: var HashSet[A]) =
|
||||
rawInsert(s, s.data, n[i].key, n[i].hcode, j)
|
||||
|
||||
template inclImpl() {.dirty.} =
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var index = rawGet(s, key, hc)
|
||||
if index < 0:
|
||||
if mustRehash(len(s.data), s.counter):
|
||||
@@ -206,7 +206,7 @@ template inclImpl() {.dirty.} =
|
||||
inc(s.counter)
|
||||
|
||||
template containsOrInclImpl() {.dirty.} =
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var index = rawGet(s, key, hc)
|
||||
if index >= 0:
|
||||
result = true
|
||||
@@ -261,7 +261,7 @@ proc excl*[A](s: var HashSet[A], key: A) =
|
||||
## s.excl(2)
|
||||
## assert s.len == 3
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var i = rawGet(s, key, hc)
|
||||
var msk = high(s.data)
|
||||
if i >= 0:
|
||||
@@ -323,7 +323,7 @@ proc init*[A](s: var HashSet[A], initialSize=64) =
|
||||
## existing values and calling `excl() <#excl,TSet[A],A>`_ on them. Example:
|
||||
##
|
||||
## .. code-block ::
|
||||
## var a: TSet[int]
|
||||
## var a: Set[int]
|
||||
## a.init(4)
|
||||
## a.incl(2)
|
||||
## a.init
|
||||
@@ -552,7 +552,7 @@ proc map*[A, B](data: HashSet[A], op: proc (x: A): B {.closure.}): HashSet[B] =
|
||||
|
||||
type
|
||||
OrderedKeyValuePair[A] = tuple[
|
||||
hcode: THash, next: int, key: A]
|
||||
hcode: Hash, next: int, key: A]
|
||||
OrderedKeyValuePairSeq[A] = seq[OrderedKeyValuePair[A]]
|
||||
OrderedSet* {.myShallow.}[A] = object ## \
|
||||
## A generic hash set that remembers insertion order.
|
||||
@@ -574,7 +574,7 @@ proc isValid*[A](s: OrderedSet[A]): bool =
|
||||
## correctly initialized. Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## proc saveTarotCards(cards: TOrderedSet[int]) =
|
||||
## proc saveTarotCards(cards: OrderedSet[int]) =
|
||||
## assert cards.isValid, "Pass an initialized set!"
|
||||
## # Do stuff here, may crash in release builds!
|
||||
result = not s.data.isNil
|
||||
@@ -588,7 +588,7 @@ proc len*[A](s: OrderedSet[A]): int {.inline.} =
|
||||
##
|
||||
## .. code-block::
|
||||
##
|
||||
## var values: TOrderedSet[int]
|
||||
## var values: OrderedSet[int]
|
||||
## assert(not values.isValid)
|
||||
## assert values.len == 0
|
||||
result = s.counter
|
||||
@@ -629,10 +629,10 @@ iterator items*[A](s: OrderedSet[A]): A =
|
||||
forAllOrderedPairs:
|
||||
yield s.data[h].key
|
||||
|
||||
proc rawGetKnownHC[A](s: OrderedSet[A], key: A, hc: THash): int {.inline.} =
|
||||
proc rawGetKnownHC[A](s: OrderedSet[A], key: A, hc: Hash): int {.inline.} =
|
||||
rawGetKnownHCImpl()
|
||||
|
||||
proc rawGet[A](s: OrderedSet[A], key: A, hc: var THash): int {.inline.} =
|
||||
proc rawGet[A](s: OrderedSet[A], key: A, hc: var Hash): int {.inline.} =
|
||||
rawGetImpl()
|
||||
|
||||
proc contains*[A](s: OrderedSet[A], key: A): bool =
|
||||
@@ -646,12 +646,12 @@ proc contains*[A](s: OrderedSet[A], key: A): bool =
|
||||
## values.incl(2)
|
||||
## assert values.contains(2)
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var index = rawGet(s, key, hc)
|
||||
result = index >= 0
|
||||
|
||||
proc rawInsert[A](s: var OrderedSet[A], data: var OrderedKeyValuePairSeq[A],
|
||||
key: A, hc: THash, h: THash) =
|
||||
key: A, hc: Hash, h: Hash) =
|
||||
rawInsertImpl()
|
||||
data[h].next = -1
|
||||
if s.first < 0: s.first = h
|
||||
@@ -729,7 +729,7 @@ proc init*[A](s: var OrderedSet[A], initialSize=64) =
|
||||
## from an ordered hash set. Example:
|
||||
##
|
||||
## .. code-block ::
|
||||
## var a: TOrderedSet[int]
|
||||
## var a: OrderedSet[int]
|
||||
## a.init(4)
|
||||
## a.incl(2)
|
||||
## a.init
|
||||
|
||||
@@ -24,13 +24,13 @@
|
||||
##
|
||||
## Error: type mismatch: got (Person)
|
||||
## but expected one of:
|
||||
## hashes.hash(x: openarray[A]): THash
|
||||
## hashes.hash(x: int): THash
|
||||
## hashes.hash(x: float): THash
|
||||
## hashes.hash(x: openarray[A]): Hash
|
||||
## hashes.hash(x: int): Hash
|
||||
## hashes.hash(x: float): Hash
|
||||
## …
|
||||
##
|
||||
## What is happening here is that the types used for table keys require to have
|
||||
## a ``hash()`` proc which will convert them to a `THash <hashes.html#THash>`_
|
||||
## a ``hash()`` proc which will convert them to a `Hash <hashes.html#Hash>`_
|
||||
## value, and the compiler is listing all the hash functions it knows.
|
||||
## Additionally there has to be a ``==`` operator that provides the same
|
||||
## semantics as its corresponding ``hash`` proc.
|
||||
@@ -46,7 +46,7 @@
|
||||
## Person = object
|
||||
## firstName, lastName: string
|
||||
##
|
||||
## proc hash(x: Person): THash =
|
||||
## proc hash(x: Person): Hash =
|
||||
## ## Piggyback on the already available string hash proc.
|
||||
## ##
|
||||
## ## Without this proc nothing works!
|
||||
@@ -71,7 +71,7 @@ import
|
||||
{.pragma: myShallow.}
|
||||
|
||||
type
|
||||
KeyValuePair[A, B] = tuple[hcode: THash, key: A, val: B]
|
||||
KeyValuePair[A, B] = tuple[hcode: Hash, key: A, val: B]
|
||||
KeyValuePairSeq[A, B] = seq[KeyValuePair[A, B]]
|
||||
Table* {.myShallow.}[A, B] = object ## generic hash table
|
||||
data: KeyValuePairSeq[A, B]
|
||||
@@ -85,10 +85,10 @@ when not defined(nimhygiene):
|
||||
|
||||
# hcode for real keys cannot be zero. hcode==0 signifies an empty slot. These
|
||||
# two procs retain clarity of that encoding without the space cost of an enum.
|
||||
proc isEmpty(hcode: THash): bool {.inline.} =
|
||||
proc isEmpty(hcode: Hash): bool {.inline.} =
|
||||
result = hcode == 0
|
||||
|
||||
proc isFilled(hcode: THash): bool {.inline.} =
|
||||
proc isFilled(hcode: Hash): bool {.inline.} =
|
||||
result = hcode != 0
|
||||
|
||||
proc len*[A, B](t: Table[A, B]): int =
|
||||
@@ -137,15 +137,15 @@ proc rightSize*(count: Natural): int {.inline.} =
|
||||
## Internally, we want mustRehash(rightSize(x), x) == false.
|
||||
result = nextPowerOfTwo(count * 3 div 2 + 4)
|
||||
|
||||
proc nextTry(h, maxHash: THash): THash {.inline.} =
|
||||
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
|
||||
result = (h + 1) and maxHash
|
||||
|
||||
template rawGetKnownHCImpl() {.dirty.} =
|
||||
var h: THash = hc and high(t.data) # start with real hash value
|
||||
var h: Hash = hc and high(t.data) # start with real hash value
|
||||
while isFilled(t.data[h].hcode):
|
||||
# Compare hc THEN key with boolean short circuit. This makes the common case
|
||||
# zero ==key's for missing (e.g.inserts) and exactly one ==key for present.
|
||||
# It does slow down succeeding lookups by one extra THash cmp&and..usually
|
||||
# It does slow down succeeding lookups by one extra Hash cmp&and..usually
|
||||
# just a few clock cycles, generally worth it for any non-integer-like A.
|
||||
if t.data[h].hcode == hc and t.data[h].key == key:
|
||||
return h
|
||||
@@ -162,7 +162,7 @@ template rawGetDeepImpl() {.dirty.} = # Search algo for unconditional add
|
||||
hc = hash(key)
|
||||
if hc == 0:
|
||||
hc = 314159265
|
||||
var h: THash = hc and high(t.data)
|
||||
var h: Hash = hc and high(t.data)
|
||||
while isFilled(t.data[h].hcode):
|
||||
h = nextTry(h, high(t.data))
|
||||
result = h
|
||||
@@ -172,13 +172,13 @@ template rawInsertImpl() {.dirty.} =
|
||||
data[h].val = val
|
||||
data[h].hcode = hc
|
||||
|
||||
proc rawGetKnownHC[A, B](t: Table[A, B], key: A, hc: THash): int {.inline.} =
|
||||
proc rawGetKnownHC[A, B](t: Table[A, B], key: A, hc: Hash): int {.inline.} =
|
||||
rawGetKnownHCImpl()
|
||||
|
||||
proc rawGetDeep[A, B](t: Table[A, B], key: A, hc: var THash): int {.inline.} =
|
||||
proc rawGetDeep[A, B](t: Table[A, B], key: A, hc: var Hash): int {.inline.} =
|
||||
rawGetDeepImpl()
|
||||
|
||||
proc rawGet[A, B](t: Table[A, B], key: A, hc: var THash): int {.inline.} =
|
||||
proc rawGet[A, B](t: Table[A, B], key: A, hc: var Hash): int {.inline.} =
|
||||
rawGetImpl()
|
||||
|
||||
proc `[]`*[A, B](t: Table[A, B], key: A): B =
|
||||
@@ -186,14 +186,14 @@ proc `[]`*[A, B](t: Table[A, B], key: A): B =
|
||||
## default empty value for the type `B` is returned
|
||||
## and no exception is raised. One can check with ``hasKey`` whether the key
|
||||
## exists.
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var index = rawGet(t, key, hc)
|
||||
if index >= 0: result = t.data[index].val
|
||||
|
||||
proc mget*[A, B](t: var Table[A, B], key: A): var B =
|
||||
## retrieves the value at ``t[key]``. The value can be modified.
|
||||
## If `key` is not in `t`, the ``KeyError`` exception is raised.
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var index = rawGet(t, key, hc)
|
||||
if index >= 0: result = t.data[index].val
|
||||
else:
|
||||
@@ -204,7 +204,7 @@ proc mget*[A, B](t: var Table[A, B], key: A): var B =
|
||||
|
||||
iterator allValues*[A, B](t: Table[A, B]; key: A): B =
|
||||
## iterates over any value in the table `t` that belongs to the given `key`.
|
||||
var h: THash = hash(key) and high(t.data)
|
||||
var h: Hash = hash(key) and high(t.data)
|
||||
while isFilled(t.data[h].hcode):
|
||||
if t.data[h].key == key:
|
||||
yield t.data[h].val
|
||||
@@ -212,7 +212,7 @@ iterator allValues*[A, B](t: Table[A, B]; key: A): B =
|
||||
|
||||
proc hasKey*[A, B](t: Table[A, B], key: A): bool =
|
||||
## returns true iff `key` is in the table `t`.
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
result = rawGet(t, key, hc) >= 0
|
||||
|
||||
proc contains*[A, B](t: Table[A, B], key: A): bool =
|
||||
@@ -220,7 +220,7 @@ proc contains*[A, B](t: Table[A, B], key: A): bool =
|
||||
return hasKey[A, B](t, key)
|
||||
|
||||
proc rawInsert[A, B](t: var Table[A, B], data: var KeyValuePairSeq[A, B],
|
||||
key: A, val: B, hc: THash, h: THash) =
|
||||
key: A, val: B, hc: Hash, h: Hash) =
|
||||
rawInsertImpl()
|
||||
|
||||
proc enlarge[A, B](t: var Table[A, B]) =
|
||||
@@ -234,7 +234,7 @@ proc enlarge[A, B](t: var Table[A, B]) =
|
||||
|
||||
template addImpl() {.dirty.} =
|
||||
if mustRehash(len(t.data), t.counter): enlarge(t)
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var j = rawGetDeep(t, key, hc)
|
||||
rawInsert(t, t.data, key, val, hc, j)
|
||||
inc(t.counter)
|
||||
@@ -248,19 +248,19 @@ template maybeRehashPutImpl() {.dirty.} =
|
||||
inc(t.counter)
|
||||
|
||||
template putImpl() {.dirty.} =
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var index = rawGet(t, key, hc)
|
||||
if index >= 0: t.data[index].val = val
|
||||
else: maybeRehashPutImpl()
|
||||
|
||||
template mgetOrPutImpl() {.dirty.} =
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var index = rawGet(t, key, hc)
|
||||
if index < 0: maybeRehashPutImpl() # not present: insert (flipping index)
|
||||
result = t.data[index].val # either way return modifiable val
|
||||
|
||||
template hasKeyOrPutImpl() {.dirty.} =
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var index = rawGet(t, key, hc)
|
||||
if index < 0:
|
||||
result = false
|
||||
@@ -291,7 +291,7 @@ template doWhile(a: expr, b: stmt): stmt =
|
||||
|
||||
proc del*[A, B](t: var Table[A, B], key: A) =
|
||||
## deletes `key` from hash table `t`.
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var i = rawGet(t, key, hc)
|
||||
let msk = high(t.data)
|
||||
if i >= 0:
|
||||
@@ -460,7 +460,7 @@ proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): TableRef[C, B]
|
||||
|
||||
type
|
||||
OrderedKeyValuePair[A, B] = tuple[
|
||||
hcode: THash, next: int, key: A, val: B]
|
||||
hcode: Hash, next: int, key: A, val: B]
|
||||
OrderedKeyValuePairSeq[A, B] = seq[OrderedKeyValuePair[A, B]]
|
||||
OrderedTable* {.
|
||||
myShallow.}[A, B] = object ## table that remembers insertion order
|
||||
@@ -509,13 +509,13 @@ iterator mvalues*[A, B](t: var OrderedTable[A, B]): var B =
|
||||
forAllOrderedPairs:
|
||||
yield t.data[h].val
|
||||
|
||||
proc rawGetKnownHC[A, B](t: OrderedTable[A, B], key: A, hc: THash): int =
|
||||
proc rawGetKnownHC[A, B](t: OrderedTable[A, B], key: A, hc: Hash): int =
|
||||
rawGetKnownHCImpl()
|
||||
|
||||
proc rawGetDeep[A, B](t: OrderedTable[A, B], key: A, hc: var THash): int {.inline.} =
|
||||
proc rawGetDeep[A, B](t: OrderedTable[A, B], key: A, hc: var Hash): int {.inline.} =
|
||||
rawGetDeepImpl()
|
||||
|
||||
proc rawGet[A, B](t: OrderedTable[A, B], key: A, hc: var THash): int =
|
||||
proc rawGet[A, B](t: OrderedTable[A, B], key: A, hc: var Hash): int =
|
||||
rawGetImpl()
|
||||
|
||||
proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B =
|
||||
@@ -523,21 +523,21 @@ proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B =
|
||||
## default empty value for the type `B` is returned
|
||||
## and no exception is raised. One can check with ``hasKey`` whether the key
|
||||
## exists.
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var index = rawGet(t, key, hc)
|
||||
if index >= 0: result = t.data[index].val
|
||||
|
||||
proc mget*[A, B](t: var OrderedTable[A, B], key: A): var B =
|
||||
## retrieves the value at ``t[key]``. The value can be modified.
|
||||
## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var index = rawGet(t, key, hc)
|
||||
if index >= 0: result = t.data[index].val
|
||||
else: raise newException(KeyError, "key not found: " & $key)
|
||||
|
||||
proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool =
|
||||
## returns true iff `key` is in the table `t`.
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
result = rawGet(t, key, hc) >= 0
|
||||
|
||||
proc contains*[A, B](t: OrderedTable[A, B], key: A): bool =
|
||||
@@ -546,7 +546,7 @@ proc contains*[A, B](t: OrderedTable[A, B], key: A): bool =
|
||||
|
||||
proc rawInsert[A, B](t: var OrderedTable[A, B],
|
||||
data: var OrderedKeyValuePairSeq[A, B],
|
||||
key: A, val: B, hc: THash, h: THash) =
|
||||
key: A, val: B, hc: Hash, h: Hash) =
|
||||
rawInsertImpl()
|
||||
data[h].next = -1
|
||||
if t.first < 0: t.first = h
|
||||
@@ -796,7 +796,7 @@ iterator mvalues*[A](t: CountTable[A]): var int =
|
||||
if t.data[h].val != 0: yield t.data[h].val
|
||||
|
||||
proc rawGet[A](t: CountTable[A], key: A): int =
|
||||
var h: THash = hash(key) and high(t.data) # start with real hash value
|
||||
var h: Hash = hash(key) and high(t.data) # start with real hash value
|
||||
while t.data[h].val != 0:
|
||||
if t.data[h].key == key: return h
|
||||
h = nextTry(h, high(t.data))
|
||||
@@ -826,7 +826,7 @@ proc contains*[A](t: CountTable[A], key: A): bool =
|
||||
|
||||
proc rawInsert[A](t: CountTable[A], data: var seq[tuple[key: A, val: int]],
|
||||
key: A, val: int) =
|
||||
var h: THash = hash(key) and high(data)
|
||||
var h: Hash = hash(key) and high(data)
|
||||
while data[h].val != 0: h = nextTry(h, high(data))
|
||||
data[h].key = key
|
||||
data[h].val = val
|
||||
@@ -1032,7 +1032,7 @@ when isMainModule:
|
||||
Person = object
|
||||
firstName, lastName: string
|
||||
|
||||
proc hash(x: Person): THash =
|
||||
proc hash(x: Person): Hash =
|
||||
## Piggyback on the already available string hash proc.
|
||||
##
|
||||
## Without this proc nothing works!
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
when defined(windows):
|
||||
import winlean, os, strutils, math
|
||||
|
||||
proc `-`(a, b: TFILETIME): int64 = a.rdFileTime - b.rdFileTime
|
||||
proc `-`(a, b: FILETIME): int64 = a.rdFileTime - b.rdFileTime
|
||||
elif defined(linux):
|
||||
from cpuinfo import countProcessors
|
||||
|
||||
@@ -25,16 +25,16 @@ type
|
||||
|
||||
ThreadPoolState* = object
|
||||
when defined(windows):
|
||||
prevSysKernel, prevSysUser, prevProcKernel, prevProcUser: TFILETIME
|
||||
prevSysKernel, prevSysUser, prevProcKernel, prevProcUser: FILETIME
|
||||
calls*: int
|
||||
|
||||
proc advice*(s: var ThreadPoolState): ThreadPoolAdvice =
|
||||
when defined(windows):
|
||||
var
|
||||
sysIdle, sysKernel, sysUser,
|
||||
procCreation, procExit, procKernel, procUser: TFILETIME
|
||||
procCreation, procExit, procKernel, procUser: FILETIME
|
||||
if getSystemTimes(sysIdle, sysKernel, sysUser) == 0 or
|
||||
getProcessTimes(THandle(-1), procCreation, procExit,
|
||||
getProcessTimes(Handle(-1), procCreation, procExit,
|
||||
procKernel, procUser) == 0:
|
||||
return doNothing
|
||||
if s.calls > 0:
|
||||
@@ -57,7 +57,7 @@ proc advice*(s: var ThreadPoolState): ThreadPoolAdvice =
|
||||
s.prevProcKernel = procKernel
|
||||
s.prevProcUser = procUser
|
||||
elif defined(linux):
|
||||
proc fscanf(c: File, frmt: cstring) {.varargs, importc,
|
||||
proc fscanf(c: File, frmt: cstring) {.varargs, importc,
|
||||
header: "<stdio.h>".}
|
||||
|
||||
var f = open("/proc/loadavg")
|
||||
|
||||
@@ -18,8 +18,8 @@ import cpuinfo, cpuload, locks
|
||||
|
||||
type
|
||||
Semaphore = object
|
||||
c: TCond
|
||||
L: TLock
|
||||
c: Cond
|
||||
L: Lock
|
||||
counter: int
|
||||
|
||||
proc createSemaphore(): Semaphore =
|
||||
@@ -113,7 +113,7 @@ type
|
||||
|
||||
ToFreeQueue = object
|
||||
len: int
|
||||
lock: TLock
|
||||
lock: Lock
|
||||
empty: Semaphore
|
||||
data: array[128, pointer]
|
||||
|
||||
@@ -221,11 +221,17 @@ proc awaitAndThen*[T](fv: FlowVar[T]; action: proc (x: T) {.closure.}) =
|
||||
action(fv.blob)
|
||||
finished(fv)
|
||||
|
||||
proc `^`*[T](fv: FlowVar[ref T]): foreign ptr T =
|
||||
proc unsafeRead*[T](fv: FlowVar[ref T]): foreign ptr T =
|
||||
## blocks until the value is available and then returns this value.
|
||||
await(fv)
|
||||
result = cast[foreign ptr T](fv.data)
|
||||
|
||||
proc `^`*[T](fv: FlowVar[ref T]): ref T =
|
||||
## blocks until the value is available and then returns this value.
|
||||
await(fv)
|
||||
let src = cast[ref T](fv.data)
|
||||
deepCopy result, src
|
||||
|
||||
proc `^`*[T](fv: FlowVar[T]): T =
|
||||
## blocks until the value is available and then returns this value.
|
||||
await(fv)
|
||||
@@ -284,7 +290,8 @@ proc slave(w: ptr Worker) {.thread.} =
|
||||
readyWorker = w
|
||||
signal(gSomeReady)
|
||||
await(w.taskArrived)
|
||||
assert(not w.ready)
|
||||
# XXX Somebody needs to look into this (why does this assertion fail in Visual Studio?)
|
||||
when not defined(vcc): assert(not w.ready)
|
||||
w.f(w, w.data)
|
||||
if w.q.len != 0: w.cleanFlowVars
|
||||
if w.shutdown:
|
||||
@@ -349,7 +356,7 @@ proc parallel*(body: stmt) {.magic: "Parallel".}
|
||||
|
||||
var
|
||||
state: ThreadPoolState
|
||||
stateLock: TLock
|
||||
stateLock: Lock
|
||||
|
||||
initLock stateLock
|
||||
|
||||
|
||||
@@ -211,12 +211,13 @@ when defined(windows):
|
||||
when false:
|
||||
# not needed yet:
|
||||
type
|
||||
TCpInfo = object
|
||||
CpInfo = object
|
||||
maxCharSize: int32
|
||||
defaultChar: array[0..1, char]
|
||||
leadByte: array[0..12-1, char]
|
||||
{.deprecated: [TCpInfo: CpInfo].}
|
||||
|
||||
proc getCPInfo(codePage: CodePage, lpCPInfo: var TCpInfo): int32 {.
|
||||
proc getCPInfo(codePage: CodePage, lpCPInfo: var CpInfo): int32 {.
|
||||
stdcall, importc: "GetCPInfo", dynlib: "kernel32".}
|
||||
|
||||
proc nameToCodePage(name: string): CodePage =
|
||||
|
||||
23
lib/pure/etcpriv.nim
Normal file
23
lib/pure/etcpriv.nim
Normal file
@@ -0,0 +1,23 @@
|
||||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2015 Nim Authors
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module contains utils that are less then easy to categorize and
|
||||
## don't really warrant a specific module. They are private to compiler
|
||||
## and stdlib usage, and should not be used outside of that - they may
|
||||
## change or disappear at any time.
|
||||
|
||||
|
||||
# Used by pure/hashes.nim, and the compiler parsing
|
||||
const magicIdentSeparatorRuneByteWidth* = 3
|
||||
|
||||
# Used by pure/hashes.nim, and the compiler parsing
|
||||
proc isMagicIdentSeparatorRune*(cs: cstring, i: int): bool {. inline } =
|
||||
result = cs[i] == '\226' and
|
||||
cs[i + 1] == '\128' and
|
||||
cs[i + 2] == '\147' # en-dash # 145 = nb-hyphen
|
||||
@@ -108,7 +108,7 @@ proc del*(monitor: FSMonitor, wd: cint) =
|
||||
|
||||
proc getEvent(m: FSMonitor, fd: cint): seq[MonitorEvent] =
|
||||
result = @[]
|
||||
let size = (sizeof(TINotifyEvent)+2000)*MaxEvents
|
||||
let size = (sizeof(INotifyEvent)+2000)*MaxEvents
|
||||
var buffer = newString(size)
|
||||
|
||||
let le = read(fd, addr(buffer[0]), size)
|
||||
@@ -117,7 +117,7 @@ proc getEvent(m: FSMonitor, fd: cint): seq[MonitorEvent] =
|
||||
|
||||
var i = 0
|
||||
while i < le:
|
||||
var event = cast[ptr TINotifyEvent](addr(buffer[i]))
|
||||
var event = cast[ptr INotifyEvent](addr(buffer[i]))
|
||||
var mev: MonitorEvent
|
||||
mev.wd = event.wd
|
||||
if event.len.int != 0:
|
||||
@@ -129,7 +129,7 @@ proc getEvent(m: FSMonitor, fd: cint): seq[MonitorEvent] =
|
||||
if (event.mask.int and IN_MOVED_FROM) != 0:
|
||||
# Moved from event, add to m's collection
|
||||
movedFrom.add(event.cookie.cint, (mev.wd, mev.name))
|
||||
inc(i, sizeof(TINotifyEvent) + event.len.int)
|
||||
inc(i, sizeof(INotifyEvent) + event.len.int)
|
||||
continue
|
||||
elif (event.mask.int and IN_MOVED_TO) != 0:
|
||||
mev.kind = MonitorMoved
|
||||
@@ -159,7 +159,7 @@ proc getEvent(m: FSMonitor, fd: cint): seq[MonitorEvent] =
|
||||
mev.fullname = ""
|
||||
|
||||
result.add(mev)
|
||||
inc(i, sizeof(TINotifyEvent) + event.len.int)
|
||||
inc(i, sizeof(INotifyEvent) + event.len.int)
|
||||
|
||||
# If movedFrom events have not been matched with a moveTo. File has
|
||||
# been moved to an unwatched location, emit a MonitorDelete.
|
||||
|
||||
@@ -18,20 +18,22 @@ import
|
||||
os, hashes, strutils
|
||||
|
||||
type
|
||||
TGenTableMode* = enum ## describes the table's key matching mode
|
||||
GenTableMode* = enum ## describes the table's key matching mode
|
||||
modeCaseSensitive, ## case sensitive matching of keys
|
||||
modeCaseInsensitive, ## case insensitive matching of keys
|
||||
modeStyleInsensitive ## style sensitive matching of keys
|
||||
|
||||
TGenKeyValuePair[T] = tuple[key: string, val: T]
|
||||
TGenKeyValuePairSeq[T] = seq[TGenKeyValuePair[T]]
|
||||
TGenTable*[T] = object of RootObj
|
||||
GenKeyValuePair[T] = tuple[key: string, val: T]
|
||||
GenKeyValuePairSeq[T] = seq[GenKeyValuePair[T]]
|
||||
GenTable*[T] = object of RootObj
|
||||
counter: int
|
||||
data: TGenKeyValuePairSeq[T]
|
||||
mode: TGenTableMode
|
||||
data: GenKeyValuePairSeq[T]
|
||||
mode: GenTableMode
|
||||
|
||||
PGenTable*[T] = ref TGenTable[T] ## use this type to declare hash tables
|
||||
PGenTable*[T] = ref GenTable[T] ## use this type to declare hash tables
|
||||
|
||||
{.deprecated: [TGenTableMode: GenTableMode, TGenKeyValuePair: GenKeyValuePair,
|
||||
TGenKeyValuePairSeq: GenKeyValuePairSeq, TGenTable: GenTable].}
|
||||
|
||||
const
|
||||
growthFactor = 2
|
||||
@@ -48,7 +50,7 @@ iterator pairs*[T](tbl: PGenTable[T]): tuple[key: string, value: T] =
|
||||
if not isNil(tbl.data[h].key):
|
||||
yield (tbl.data[h].key, tbl.data[h].val)
|
||||
|
||||
proc myhash[T](tbl: PGenTable[T], key: string): THash =
|
||||
proc myhash[T](tbl: PGenTable[T], key: string): Hash =
|
||||
case tbl.mode
|
||||
of modeCaseSensitive: result = hashes.hash(key)
|
||||
of modeCaseInsensitive: result = hashes.hashIgnoreCase(key)
|
||||
@@ -64,18 +66,18 @@ proc mustRehash(length, counter: int): bool =
|
||||
assert(length > counter)
|
||||
result = (length * 2 < counter * 3) or (length - counter < 4)
|
||||
|
||||
proc newGenTable*[T](mode: TGenTableMode): PGenTable[T] =
|
||||
proc newGenTable*[T](mode: GenTableMode): PGenTable[T] =
|
||||
## creates a new generic hash table that is empty.
|
||||
new(result)
|
||||
result.mode = mode
|
||||
result.counter = 0
|
||||
newSeq(result.data, startSize)
|
||||
|
||||
proc nextTry(h, maxHash: THash): THash {.inline.} =
|
||||
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
|
||||
result = ((5 * h) + 1) and maxHash
|
||||
|
||||
proc rawGet[T](tbl: PGenTable[T], key: string): int =
|
||||
var h: THash
|
||||
var h: Hash
|
||||
h = myhash(tbl, key) and high(tbl.data) # start with real hash value
|
||||
while not isNil(tbl.data[h].key):
|
||||
if myCmp(tbl, tbl.data[h].key, key):
|
||||
@@ -83,9 +85,9 @@ proc rawGet[T](tbl: PGenTable[T], key: string): int =
|
||||
h = nextTry(h, high(tbl.data))
|
||||
result = - 1
|
||||
|
||||
proc rawInsert[T](tbl: PGenTable[T], data: var TGenKeyValuePairSeq[T],
|
||||
proc rawInsert[T](tbl: PGenTable[T], data: var GenKeyValuePairSeq[T],
|
||||
key: string, val: T) =
|
||||
var h: THash
|
||||
var h: Hash
|
||||
h = myhash(tbl, key) and high(data)
|
||||
while not isNil(data[h].key):
|
||||
h = nextTry(h, high(data))
|
||||
@@ -93,7 +95,7 @@ proc rawInsert[T](tbl: PGenTable[T], data: var TGenKeyValuePairSeq[T],
|
||||
data[h].val = val
|
||||
|
||||
proc enlarge[T](tbl: PGenTable[T]) =
|
||||
var n: TGenKeyValuePairSeq[T]
|
||||
var n: GenKeyValuePairSeq[T]
|
||||
newSeq(n, len(tbl.data) * growthFactor)
|
||||
for i in countup(0, high(tbl.data)):
|
||||
if not isNil(tbl.data[i].key):
|
||||
@@ -146,19 +148,20 @@ when isMainModule:
|
||||
# Verify a table of user-defined types
|
||||
#
|
||||
type
|
||||
TMyType = tuple[first, second: string] # a pair of strings
|
||||
MyType = tuple[first, second: string] # a pair of strings
|
||||
{.deprecated: [TMyType: MyType].}
|
||||
|
||||
var y = newGenTable[TMyType](modeCaseInsensitive) # hash table where each
|
||||
# value is TMyType tuple
|
||||
var y = newGenTable[MyType](modeCaseInsensitive) # hash table where each
|
||||
# value is MyType tuple
|
||||
|
||||
#var junk: TMyType = ("OK", "Here")
|
||||
#var junk: MyType = ("OK", "Here")
|
||||
|
||||
#echo junk.first, " ", junk.second
|
||||
|
||||
y["Hello"] = ("Hello", "World")
|
||||
y["Goodbye"] = ("Goodbye", "Everyone")
|
||||
#y["Hello"] = TMyType( ("Hello", "World") )
|
||||
#y["Goodbye"] = TMyType( ("Goodbye", "Everyone") )
|
||||
#y["Hello"] = MyType( ("Hello", "World") )
|
||||
#y["Goodbye"] = MyType( ("Goodbye", "Everyone") )
|
||||
|
||||
assert( not isNil(y["Hello"].first) )
|
||||
assert( y["Hello"].first == "Hello" )
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
## code:
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## proc hash(x: Something): THash =
|
||||
## ## Computes a THash from `x`.
|
||||
## var h: THash = 0
|
||||
## proc hash(x: Something): Hash =
|
||||
## ## Computes a Hash from `x`.
|
||||
## var h: Hash = 0
|
||||
## # Iterate over parts of `x`.
|
||||
## for xAtom in x:
|
||||
## # Mix the atom with the partial hash.
|
||||
@@ -30,38 +30,39 @@
|
||||
## together the hash value of the individual fields:
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## proc hash(x: Something): THash =
|
||||
## ## Computes a THash from `x`.
|
||||
## var h: THash = 0
|
||||
## proc hash(x: Something): Hash =
|
||||
## ## Computes a Hash from `x`.
|
||||
## var h: Hash = 0
|
||||
## h = h !& hash(x.foo)
|
||||
## h = h !& hash(x.bar)
|
||||
## result = !$h
|
||||
|
||||
import
|
||||
strutils
|
||||
strutils, etcpriv
|
||||
|
||||
type
|
||||
THash* = int ## a hash value; hash tables using these values should
|
||||
Hash* = int ## a hash value; hash tables using these values should
|
||||
## always have a size of a power of two and can use the ``and``
|
||||
## operator instead of ``mod`` for truncation of the hash value.
|
||||
{.deprecated: [THash: Hash].}
|
||||
|
||||
proc `!&`*(h: THash, val: int): THash {.inline.} =
|
||||
proc `!&`*(h: Hash, val: int): Hash {.inline.} =
|
||||
## mixes a hash value `h` with `val` to produce a new hash value. This is
|
||||
## only needed if you need to implement a hash proc for a new datatype.
|
||||
result = h +% val
|
||||
result = result +% result shl 10
|
||||
result = result xor (result shr 6)
|
||||
|
||||
proc `!$`*(h: THash): THash {.inline.} =
|
||||
proc `!$`*(h: Hash): Hash {.inline.} =
|
||||
## finishes the computation of the hash value. This is
|
||||
## only needed if you need to implement a hash proc for a new datatype.
|
||||
result = h +% h shl 3
|
||||
result = result xor (result shr 11)
|
||||
result = result +% result shl 15
|
||||
|
||||
proc hashData*(data: pointer, size: int): THash =
|
||||
proc hashData*(data: pointer, size: int): Hash =
|
||||
## hashes an array of bytes of size `size`
|
||||
var h: THash = 0
|
||||
var h: Hash = 0
|
||||
when defined(js):
|
||||
var p: cstring
|
||||
asm """`p` = `Data`;"""
|
||||
@@ -78,7 +79,7 @@ proc hashData*(data: pointer, size: int): THash =
|
||||
when defined(js):
|
||||
var objectID = 0
|
||||
|
||||
proc hash*(x: pointer): THash {.inline.} =
|
||||
proc hash*(x: pointer): Hash {.inline.} =
|
||||
## efficient hashing of pointers
|
||||
when defined(js):
|
||||
asm """
|
||||
@@ -92,50 +93,57 @@ proc hash*(x: pointer): THash {.inline.} =
|
||||
}
|
||||
"""
|
||||
else:
|
||||
result = (cast[THash](x)) shr 3 # skip the alignment
|
||||
result = (cast[Hash](x)) shr 3 # skip the alignment
|
||||
|
||||
when not defined(booting):
|
||||
proc hash*[T: proc](x: T): THash {.inline.} =
|
||||
proc hash*[T: proc](x: T): Hash {.inline.} =
|
||||
## efficient hashing of proc vars; closures are supported too.
|
||||
when T is "closure":
|
||||
result = hash(rawProc(x)) !& hash(rawEnv(x))
|
||||
else:
|
||||
result = hash(pointer(x))
|
||||
|
||||
proc hash*(x: int): THash {.inline.} =
|
||||
proc hash*(x: int): Hash {.inline.} =
|
||||
## efficient hashing of integers
|
||||
result = x
|
||||
|
||||
proc hash*(x: int64): THash {.inline.} =
|
||||
proc hash*(x: int64): Hash {.inline.} =
|
||||
## efficient hashing of integers
|
||||
result = toU32(x)
|
||||
|
||||
proc hash*(x: char): THash {.inline.} =
|
||||
proc hash*(x: char): Hash {.inline.} =
|
||||
## efficient hashing of characters
|
||||
result = ord(x)
|
||||
|
||||
proc hash*(x: string): THash =
|
||||
proc hash*(x: string): Hash =
|
||||
## efficient hashing of strings
|
||||
var h: THash = 0
|
||||
var h: Hash = 0
|
||||
for i in 0..x.len-1:
|
||||
h = h !& ord(x[i])
|
||||
result = !$h
|
||||
|
||||
proc hashIgnoreStyle*(x: string): THash =
|
||||
proc hashIgnoreStyle*(x: string): Hash =
|
||||
## efficient hashing of strings; style is ignored
|
||||
var h: THash = 0
|
||||
for i in 0..x.len-1:
|
||||
var h: Hash = 0
|
||||
var i = 0
|
||||
let xLen = x.len
|
||||
while i < xLen:
|
||||
var c = x[i]
|
||||
if c == '_':
|
||||
continue # skip _
|
||||
if c in {'A'..'Z'}:
|
||||
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
|
||||
h = h !& ord(c)
|
||||
inc(i)
|
||||
elif isMagicIdentSeparatorRune(cstring(x), i):
|
||||
inc(i, magicIdentSeparatorRuneByteWidth)
|
||||
else:
|
||||
if c in {'A'..'Z'}:
|
||||
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
|
||||
h = h !& ord(c)
|
||||
inc(i)
|
||||
|
||||
result = !$h
|
||||
|
||||
proc hashIgnoreCase*(x: string): THash =
|
||||
proc hashIgnoreCase*(x: string): Hash =
|
||||
## efficient hashing of strings; case is ignored
|
||||
var h: THash = 0
|
||||
var h: Hash = 0
|
||||
for i in 0..x.len-1:
|
||||
var c = x[i]
|
||||
if c in {'A'..'Z'}:
|
||||
@@ -143,28 +151,28 @@ proc hashIgnoreCase*(x: string): THash =
|
||||
h = h !& ord(c)
|
||||
result = !$h
|
||||
|
||||
proc hash*(x: float): THash {.inline.} =
|
||||
proc hash*(x: float): Hash {.inline.} =
|
||||
var y = x + 1.0
|
||||
result = cast[ptr THash](addr(y))[]
|
||||
result = cast[ptr Hash](addr(y))[]
|
||||
|
||||
|
||||
# Forward declarations before methods that hash containers. This allows
|
||||
# containers to contain other containers
|
||||
proc hash*[A](x: openArray[A]): THash
|
||||
proc hash*[A](x: set[A]): THash
|
||||
proc hash*[A](x: openArray[A]): Hash
|
||||
proc hash*[A](x: set[A]): Hash
|
||||
|
||||
|
||||
proc hash*[T: tuple](x: T): THash =
|
||||
proc hash*[T: tuple](x: T): Hash =
|
||||
## efficient hashing of tuples.
|
||||
for f in fields(x):
|
||||
result = result !& hash(f)
|
||||
result = !$result
|
||||
|
||||
proc hash*[A](x: openArray[A]): THash =
|
||||
proc hash*[A](x: openArray[A]): Hash =
|
||||
for it in items(x): result = result !& hash(it)
|
||||
result = !$result
|
||||
|
||||
proc hash*[A](x: set[A]): THash =
|
||||
proc hash*[A](x: set[A]): Hash =
|
||||
for it in items(x): result = result !& hash(it)
|
||||
result = !$result
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
import strutils, streams, parsexml, xmltree, unicode, strtabs
|
||||
|
||||
type
|
||||
THtmlTag* = enum ## list of all supported HTML tags; order will always be
|
||||
HtmlTag* = enum ## list of all supported HTML tags; order will always be
|
||||
## alphabetically
|
||||
tagUnknown, ## unknown HTML element
|
||||
tagA, ## the HTML ``a`` element
|
||||
@@ -178,6 +178,7 @@ type
|
||||
tagVar, ## the HTML ``var`` element
|
||||
tagVideo, ## the HTML ``video`` element
|
||||
tagWbr ## the HTML ``wbr`` element
|
||||
{.deprecated: [THtmlTag: HtmlTag].}
|
||||
|
||||
const
|
||||
tagToStr* = [
|
||||
@@ -295,7 +296,7 @@ proc allLower(s: string): bool =
|
||||
if c < 'a' or c > 'z': return false
|
||||
return true
|
||||
|
||||
proc toHtmlTag(s: string): THtmlTag =
|
||||
proc toHtmlTag(s: string): HtmlTag =
|
||||
case s
|
||||
of "a": tagA
|
||||
of "abbr": tagAbbr
|
||||
@@ -422,14 +423,14 @@ proc toHtmlTag(s: string): THtmlTag =
|
||||
of "wbr": tagWbr
|
||||
else: tagUnknown
|
||||
|
||||
proc htmlTag*(n: XmlNode): THtmlTag =
|
||||
## gets `n`'s tag as a ``THtmlTag``.
|
||||
proc htmlTag*(n: XmlNode): HtmlTag =
|
||||
## gets `n`'s tag as a ``HtmlTag``.
|
||||
if n.clientData == 0:
|
||||
n.clientData = toHtmlTag(n.tag).ord
|
||||
result = THtmlTag(n.clientData)
|
||||
result = HtmlTag(n.clientData)
|
||||
|
||||
proc htmlTag*(s: string): THtmlTag =
|
||||
## converts `s` to a ``THtmlTag``. If `s` is no HTML tag, ``tagUnknown`` is
|
||||
proc htmlTag*(s: string): HtmlTag =
|
||||
## converts `s` to a ``HtmlTag``. If `s` is no HTML tag, ``tagUnknown`` is
|
||||
## returned.
|
||||
let s = if allLower(s): s else: s.toLower
|
||||
result = toHtmlTag(s)
|
||||
|
||||
@@ -569,7 +569,7 @@ proc downloadFile*(url: string, outputFilename: string,
|
||||
fileError("Unable to open file")
|
||||
|
||||
proc generateHeaders(r: Uri, httpMethod: string,
|
||||
headers: StringTableRef): string =
|
||||
headers: StringTableRef, body: string): string =
|
||||
# TODO: Use this in the blocking HttpClient once it supports proxies.
|
||||
result = substr(httpMethod, len("http"))
|
||||
# TODO: Proxies
|
||||
@@ -582,6 +582,8 @@ proc generateHeaders(r: Uri, httpMethod: string,
|
||||
|
||||
add(result, "Host: " & r.hostname & "\c\L")
|
||||
add(result, "Connection: Keep-Alive\c\L")
|
||||
if body.len > 0 and not headers.hasKey("Content-Length"):
|
||||
add(result, "Content-Length: " & $body.len & "\c\L")
|
||||
for key, val in headers:
|
||||
add(result, key & ": " & val & "\c\L")
|
||||
|
||||
@@ -786,7 +788,7 @@ proc request*(client: AsyncHttpClient, url: string, httpMethod: string,
|
||||
if not client.headers.hasKey("user-agent") and client.userAgent != "":
|
||||
client.headers["User-Agent"] = client.userAgent
|
||||
|
||||
var headers = generateHeaders(r, $httpMethod, client.headers)
|
||||
var headers = generateHeaders(r, $httpMethod, client.headers, body)
|
||||
|
||||
await client.socket.send(headers)
|
||||
if body != "":
|
||||
|
||||
@@ -106,9 +106,10 @@ proc serveFile*(client: Socket, filename: string) =
|
||||
when false:
|
||||
# TODO: Fix this, or get rid of it.
|
||||
type
|
||||
TRequestMethod = enum reqGet, reqPost
|
||||
RequestMethod = enum reqGet, reqPost
|
||||
{.deprecated: [TRequestMethod: RequestMethod].}
|
||||
|
||||
proc executeCgi(client: Socket, path, query: string, meth: TRequestMethod) =
|
||||
proc executeCgi(client: Socket, path, query: string, meth: RequestMethod) =
|
||||
var env = newStringTable(modeCaseInsensitive)
|
||||
var contentLength = -1
|
||||
case meth
|
||||
@@ -208,7 +209,7 @@ when false:
|
||||
executeCgi(client, path, query, meth)
|
||||
|
||||
type
|
||||
TServer* = object of RootObj ## contains the current server state
|
||||
Server* = object of RootObj ## contains the current server state
|
||||
socket: Socket
|
||||
port: Port
|
||||
client*: Socket ## the socket to write the file data to
|
||||
@@ -218,11 +219,12 @@ type
|
||||
body*: string ## only set with POST requests
|
||||
ip*: string ## ip address of the requesting client
|
||||
|
||||
PAsyncHTTPServer* = ref TAsyncHTTPServer
|
||||
TAsyncHTTPServer = object of TServer
|
||||
PAsyncHTTPServer* = ref AsyncHTTPServer
|
||||
AsyncHTTPServer = object of Server
|
||||
asyncSocket: AsyncSocket
|
||||
{.deprecated: [TAsyncHTTPServer: AsyncHTTPServer, TServer: Server].}
|
||||
|
||||
proc open*(s: var TServer, port = Port(80), reuseAddr = false) =
|
||||
proc open*(s: var Server, port = Port(80), reuseAddr = false) =
|
||||
## creates a new server at port `port`. If ``port == 0`` a free port is
|
||||
## acquired that can be accessed later by the ``port`` proc.
|
||||
s.socket = socket(AF_INET)
|
||||
@@ -243,11 +245,11 @@ proc open*(s: var TServer, port = Port(80), reuseAddr = false) =
|
||||
s.query = ""
|
||||
s.headers = {:}.newStringTable()
|
||||
|
||||
proc port*(s: var TServer): Port =
|
||||
proc port*(s: var Server): Port =
|
||||
## get the port number the server has acquired.
|
||||
result = s.port
|
||||
|
||||
proc next*(s: var TServer) =
|
||||
proc next*(s: var Server) =
|
||||
## proceed to the first/next request.
|
||||
var client: Socket
|
||||
new(client)
|
||||
@@ -354,7 +356,7 @@ proc next*(s: var TServer) =
|
||||
s.query = ""
|
||||
s.path = data.substr(i, last-1)
|
||||
|
||||
proc close*(s: TServer) =
|
||||
proc close*(s: Server) =
|
||||
## closes the server (and the socket the server uses).
|
||||
close(s.socket)
|
||||
|
||||
@@ -362,7 +364,7 @@ proc run*(handleRequest: proc (client: Socket,
|
||||
path, query: string): bool {.closure.},
|
||||
port = Port(80)) =
|
||||
## encapsulates the server object and main loop
|
||||
var s: TServer
|
||||
var s: Server
|
||||
open(s, port, reuseAddr = true)
|
||||
#echo("httpserver running on port ", s.port)
|
||||
while true:
|
||||
@@ -517,7 +519,7 @@ proc close*(h: PAsyncHTTPServer) =
|
||||
when not defined(testing) and isMainModule:
|
||||
var counter = 0
|
||||
|
||||
var s: TServer
|
||||
var s: Server
|
||||
open(s, Port(0))
|
||||
echo("httpserver running on port ", s.port)
|
||||
while true:
|
||||
|
||||
@@ -68,7 +68,7 @@ type
|
||||
jsonArrayStart, ## start of an array: the ``[`` token
|
||||
jsonArrayEnd ## start of an array: the ``]`` token
|
||||
|
||||
TTokKind = enum # must be synchronized with TJsonEventKind!
|
||||
TokKind = enum # must be synchronized with TJsonEventKind!
|
||||
tkError,
|
||||
tkEof,
|
||||
tkString,
|
||||
@@ -103,14 +103,14 @@ type
|
||||
|
||||
JsonParser* = object of BaseLexer ## the parser object.
|
||||
a: string
|
||||
tok: TTokKind
|
||||
tok: TokKind
|
||||
kind: JsonEventKind
|
||||
err: JsonError
|
||||
state: seq[ParserState]
|
||||
filename: string
|
||||
|
||||
{.deprecated: [TJsonEventKind: JsonEventKind, TJsonError: JsonError,
|
||||
TJsonParser: JsonParser].}
|
||||
TJsonParser: JsonParser, TTokKind: TokKind].}
|
||||
|
||||
const
|
||||
errorMessages: array [JsonError, string] = [
|
||||
@@ -126,7 +126,7 @@ const
|
||||
"EOF expected",
|
||||
"expression expected"
|
||||
]
|
||||
tokToStr: array [TTokKind, string] = [
|
||||
tokToStr: array [TokKind, string] = [
|
||||
"invalid token",
|
||||
"EOF",
|
||||
"string literal",
|
||||
@@ -203,7 +203,7 @@ proc handleHexChar(c: char, x: var int): bool =
|
||||
of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
|
||||
else: result = false # error
|
||||
|
||||
proc parseString(my: var JsonParser): TTokKind =
|
||||
proc parseString(my: var JsonParser): TokKind =
|
||||
result = tkString
|
||||
var pos = my.bufpos + 1
|
||||
var buf = my.buf
|
||||
@@ -359,7 +359,7 @@ proc parseName(my: var JsonParser) =
|
||||
inc(pos)
|
||||
my.bufpos = pos
|
||||
|
||||
proc getTok(my: var JsonParser): TTokKind =
|
||||
proc getTok(my: var JsonParser): TokKind =
|
||||
setLen(my.a, 0)
|
||||
skip(my) # skip whitespace, comments
|
||||
case my.buf[my.bufpos]
|
||||
@@ -734,7 +734,7 @@ proc `==`* (a,b: JsonNode): bool =
|
||||
of JObject:
|
||||
a.fields == b.fields
|
||||
|
||||
proc hash* (n:JsonNode): THash =
|
||||
proc hash* (n:JsonNode): Hash =
|
||||
## Compute the hash for a JSON node
|
||||
case n.kind
|
||||
of JArray:
|
||||
@@ -1016,7 +1016,7 @@ iterator mpairs*(node: var JsonNode): var tuple[key: string, val: JsonNode] =
|
||||
for keyVal in mitems(node.fields):
|
||||
yield keyVal
|
||||
|
||||
proc eat(p: var JsonParser, tok: TTokKind) =
|
||||
proc eat(p: var JsonParser, tok: TokKind) =
|
||||
if p.tok == tok: discard getTok(p)
|
||||
else: raiseParseErr(p, tokToStr[tok])
|
||||
|
||||
@@ -1091,8 +1091,10 @@ when not defined(js):
|
||||
else:
|
||||
from math import `mod`
|
||||
type
|
||||
TJSObject = object
|
||||
proc parseNativeJson(x: cstring): TJSObject {.importc: "JSON.parse".}
|
||||
JSObject = object
|
||||
{.deprecated: [TJSObject: JSObject].}
|
||||
|
||||
proc parseNativeJson(x: cstring): JSObject {.importc: "JSON.parse".}
|
||||
|
||||
proc getVarType(x): JsonNodeKind =
|
||||
result = JNull
|
||||
@@ -1111,25 +1113,25 @@ else:
|
||||
of "[object String]": return JString
|
||||
else: assert false
|
||||
|
||||
proc len(x: TJSObject): int =
|
||||
proc len(x: JSObject): int =
|
||||
assert x.getVarType == JArray
|
||||
asm """
|
||||
return `x`.length;
|
||||
"""
|
||||
|
||||
proc `[]`(x: TJSObject, y: string): TJSObject =
|
||||
proc `[]`(x: JSObject, y: string): JSObject =
|
||||
assert x.getVarType == JObject
|
||||
asm """
|
||||
return `x`[`y`];
|
||||
"""
|
||||
|
||||
proc `[]`(x: TJSObject, y: int): TJSObject =
|
||||
proc `[]`(x: JSObject, y: int): JSObject =
|
||||
assert x.getVarType == JArray
|
||||
asm """
|
||||
return `x`[`y`];
|
||||
"""
|
||||
|
||||
proc convertObject(x: TJSObject): JsonNode =
|
||||
proc convertObject(x: JSObject): JsonNode =
|
||||
case getVarType(x)
|
||||
of JArray:
|
||||
result = newJArray()
|
||||
@@ -1141,7 +1143,7 @@ else:
|
||||
if (`x`.hasOwnProperty(property)) {
|
||||
"""
|
||||
var nimProperty: cstring
|
||||
var nimValue: TJSObject
|
||||
var nimValue: JSObject
|
||||
asm "`nimProperty` = property; `nimValue` = `x`[property];"
|
||||
result[$nimProperty] = nimValue.convertObject()
|
||||
asm "}}"
|
||||
|
||||
@@ -39,7 +39,7 @@ type
|
||||
{.deprecated: [TBaseLexer: BaseLexer].}
|
||||
|
||||
proc open*(L: var BaseLexer, input: Stream, bufLen: int = 8192)
|
||||
## inits the TBaseLexer with a stream to read from
|
||||
## inits the BaseLexer with a stream to read from
|
||||
|
||||
proc close*(L: var BaseLexer)
|
||||
## closes the base lexer. This closes `L`'s associated stream too.
|
||||
|
||||
@@ -19,7 +19,12 @@
|
||||
## ============ =======================
|
||||
## $date Current date
|
||||
## $time Current time
|
||||
## $datetime $dateT$time
|
||||
## $app ``os.getAppFilename()``
|
||||
## $appname base name of $app
|
||||
## $appdir directory name of $app
|
||||
## $levelid first letter of log level
|
||||
## $levelname log level name
|
||||
## ============ =======================
|
||||
##
|
||||
##
|
||||
@@ -59,8 +64,8 @@ const
|
||||
"DEBUG", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "NONE"
|
||||
]
|
||||
|
||||
defaultFmtStr* = "" ## default string between log level and message per logger
|
||||
verboseFmtStr* = "$date $time "
|
||||
defaultFmtStr* = "$levelname " ## default format string
|
||||
verboseFmtStr* = "$levelid, [$datetime] -- $appname: "
|
||||
|
||||
type
|
||||
Logger* = ref object of RootObj ## abstract logger; the base type of all loggers
|
||||
@@ -82,16 +87,16 @@ type
|
||||
baseName: string # initial filename
|
||||
baseMode: FileMode # initial file mode
|
||||
logFiles: int # how many log files already created, e.g. basename.1, basename.2...
|
||||
bufSize: int # size of output buffer (-1: use system defaults, 0: unbuffered, >0: fixed buffer size)
|
||||
|
||||
{.deprecated: [TLevel: Level, PLogger: Logger, PConsoleLogger: ConsoleLogger,
|
||||
PFileLogger: FileLogger, PRollingFileLogger: RollingFileLogger].}
|
||||
|
||||
proc substituteLog(frmt: string): string =
|
||||
## converts $date to the current date
|
||||
## converts $time to the current time
|
||||
## converts $app to getAppFilename()
|
||||
## converts
|
||||
result = newStringOfCap(frmt.len + 20)
|
||||
proc substituteLog(frmt: string, level: Level, args: varargs[string, `$`]): string =
|
||||
var msgLen = 0
|
||||
for arg in args:
|
||||
msgLen += arg.len
|
||||
result = newStringOfCap(frmt.len + msgLen + 20)
|
||||
var i = 0
|
||||
while i < frmt.len:
|
||||
if frmt[i] != '$':
|
||||
@@ -107,32 +112,32 @@ proc substituteLog(frmt: string): string =
|
||||
case v
|
||||
of "date": result.add(getDateStr())
|
||||
of "time": result.add(getClockStr())
|
||||
of "datetime": result.add(getDateStr() & "T" & getClockStr())
|
||||
of "app": result.add(app)
|
||||
of "appdir": result.add(app.splitFile.dir)
|
||||
of "appname": result.add(app.splitFile.name)
|
||||
of "levelid": result.add(LevelNames[level][0])
|
||||
of "levelname": result.add(LevelNames[level])
|
||||
else: discard
|
||||
for arg in args:
|
||||
result.add(arg)
|
||||
|
||||
method log*(logger: Logger, level: Level,
|
||||
frmt: string, args: varargs[string, `$`]) {.
|
||||
method log*(logger: Logger, level: Level, args: varargs[string, `$`]) {.
|
||||
raises: [Exception],
|
||||
tags: [TimeEffect, WriteIOEffect, ReadIOEffect].} =
|
||||
## Override this method in custom loggers. Default implementation does
|
||||
## nothing.
|
||||
discard
|
||||
|
||||
method log*(logger: ConsoleLogger, level: Level,
|
||||
frmt: string, args: varargs[string, `$`]) =
|
||||
method log*(logger: ConsoleLogger, level: Level, args: varargs[string, `$`]) =
|
||||
## Logs to the console using ``logger`` only.
|
||||
if level >= logger.levelThreshold:
|
||||
writeln(stdout, LevelNames[level], " ", substituteLog(logger.fmtStr),
|
||||
frmt % args)
|
||||
writeln(stdout, substituteLog(logger.fmtStr, level, args))
|
||||
|
||||
method log*(logger: FileLogger, level: Level,
|
||||
frmt: string, args: varargs[string, `$`]) =
|
||||
method log*(logger: FileLogger, level: Level, args: varargs[string, `$`]) =
|
||||
## Logs to a file using ``logger`` only.
|
||||
if level >= logger.levelThreshold:
|
||||
writeln(logger.f, LevelNames[level], " ",
|
||||
substituteLog(logger.fmtStr), frmt % args)
|
||||
writeln(logger.f, substituteLog(logger.fmtStr, level, args))
|
||||
|
||||
proc defaultFilename*(): string =
|
||||
## Returns the default filename for a logger.
|
||||
@@ -148,11 +153,14 @@ proc newConsoleLogger*(levelThreshold = lvlAll, fmtStr = defaultFmtStr): Console
|
||||
proc newFileLogger*(filename = defaultFilename(),
|
||||
mode: FileMode = fmAppend,
|
||||
levelThreshold = lvlAll,
|
||||
fmtStr = defaultFmtStr): FileLogger =
|
||||
fmtStr = defaultFmtStr,
|
||||
bufSize: int = -1): FileLogger =
|
||||
## Creates a new file logger. This logger logs to a file.
|
||||
## Use ``bufSize`` as size of the output buffer when writing the file
|
||||
## (-1: use system defaults, 0: unbuffered, >0: fixed buffer size).
|
||||
new(result)
|
||||
result.levelThreshold = levelThreshold
|
||||
result.f = open(filename, mode)
|
||||
result.f = open(filename, mode, bufSize = bufSize)
|
||||
result.fmtStr = fmtStr
|
||||
|
||||
# ------
|
||||
@@ -181,14 +189,18 @@ proc newRollingFileLogger*(filename = defaultFilename(),
|
||||
mode: FileMode = fmReadWrite,
|
||||
levelThreshold = lvlAll,
|
||||
fmtStr = defaultFmtStr,
|
||||
maxLines = 1000): RollingFileLogger =
|
||||
maxLines = 1000,
|
||||
bufSize: int = -1): RollingFileLogger =
|
||||
## Creates a new rolling file logger. Once a file reaches ``maxLines`` lines
|
||||
## a new log file will be started and the old will be renamed.
|
||||
## Use ``bufSize`` as size of the output buffer when writing the file
|
||||
## (-1: use system defaults, 0: unbuffered, >0: fixed buffer size).
|
||||
new(result)
|
||||
result.levelThreshold = levelThreshold
|
||||
result.fmtStr = fmtStr
|
||||
result.maxLines = maxLines
|
||||
result.f = open(filename, mode)
|
||||
result.bufSize = bufSize
|
||||
result.f = open(filename, mode, bufSize=result.bufSize)
|
||||
result.curLine = 0
|
||||
result.baseName = filename
|
||||
result.baseMode = mode
|
||||
@@ -206,8 +218,7 @@ proc rotate(logger: RollingFileLogger) =
|
||||
moveFile(dir / (name & ext & srcSuff),
|
||||
dir / (name & ext & ExtSep & $(i+1)))
|
||||
|
||||
method log*(logger: RollingFileLogger, level: Level,
|
||||
frmt: string, args: varargs[string, `$`]) =
|
||||
method log*(logger: RollingFileLogger, level: Level, args: varargs[string, `$`]) =
|
||||
## Logs to a file using rolling ``logger`` only.
|
||||
if level >= logger.levelThreshold:
|
||||
if logger.curLine >= logger.maxLines:
|
||||
@@ -215,9 +226,9 @@ method log*(logger: RollingFileLogger, level: Level,
|
||||
rotate(logger)
|
||||
logger.logFiles.inc
|
||||
logger.curLine = 0
|
||||
logger.f = open(logger.baseName, logger.baseMode)
|
||||
logger.f = open(logger.baseName, logger.baseMode, bufSize = logger.bufSize)
|
||||
|
||||
writeln(logger.f, LevelNames[level], " ",substituteLog(logger.fmtStr), frmt % args)
|
||||
writeln(logger.f, substituteLog(logger.fmtStr, level, args))
|
||||
logger.curLine.inc
|
||||
|
||||
# --------
|
||||
@@ -225,39 +236,39 @@ method log*(logger: RollingFileLogger, level: Level,
|
||||
var level {.threadvar.}: Level ## global log filter
|
||||
var handlers {.threadvar.}: seq[Logger] ## handlers with their own log levels
|
||||
|
||||
proc logLoop(level: Level, frmt: string, args: varargs[string, `$`]) =
|
||||
proc logLoop(level: Level, args: varargs[string, `$`]) =
|
||||
for logger in items(handlers):
|
||||
if level >= logger.levelThreshold:
|
||||
log(logger, level, frmt, args)
|
||||
log(logger, level, args)
|
||||
|
||||
template log*(level: Level, frmt: string, args: varargs[string, `$`]) =
|
||||
template log*(level: Level, args: varargs[string, `$`]) =
|
||||
## Logs a message to all registered handlers at the given level.
|
||||
bind logLoop
|
||||
bind `%`
|
||||
bind logging.level
|
||||
|
||||
if level >= logging.level:
|
||||
logLoop(level, frmt, args)
|
||||
logLoop(level, args)
|
||||
|
||||
template debug*(frmt: string, args: varargs[string, `$`]) =
|
||||
template debug*(args: varargs[string, `$`]) =
|
||||
## Logs a debug message to all registered handlers.
|
||||
log(lvlDebug, frmt, args)
|
||||
log(lvlDebug, args)
|
||||
|
||||
template info*(frmt: string, args: varargs[string, `$`]) =
|
||||
template info*(args: varargs[string, `$`]) =
|
||||
## Logs an info message to all registered handlers.
|
||||
log(lvlInfo, frmt, args)
|
||||
log(lvlInfo, args)
|
||||
|
||||
template warn*(frmt: string, args: varargs[string, `$`]) =
|
||||
template warn*(args: varargs[string, `$`]) =
|
||||
## Logs a warning message to all registered handlers.
|
||||
log(lvlWarn, frmt, args)
|
||||
log(lvlWarn, args)
|
||||
|
||||
template error*(frmt: string, args: varargs[string, `$`]) =
|
||||
template error*(args: varargs[string, `$`]) =
|
||||
## Logs an error message to all registered handlers.
|
||||
log(lvlError, frmt, args)
|
||||
log(lvlError, args)
|
||||
|
||||
template fatal*(frmt: string, args: varargs[string, `$`]) =
|
||||
template fatal*(args: varargs[string, `$`]) =
|
||||
## Logs a fatal error message to all registered handlers.
|
||||
log(lvlFatal, frmt, args)
|
||||
log(lvlFatal, args)
|
||||
|
||||
proc addHandler*(handler: Logger) =
|
||||
## Adds ``handler`` to the list of handlers.
|
||||
@@ -286,6 +297,4 @@ when not defined(testing) and isMainModule:
|
||||
addHandler(fL)
|
||||
addHandler(rL)
|
||||
for i in 0 .. 25:
|
||||
info("hello" & $i, [])
|
||||
|
||||
|
||||
info("hello", i)
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## type
|
||||
## TA = object
|
||||
## TB = object of TA
|
||||
## A = object
|
||||
## B = object of A
|
||||
## f: int
|
||||
##
|
||||
## var
|
||||
## a: ref TA
|
||||
## b: ref TB
|
||||
## a: ref A
|
||||
## b: ref B
|
||||
##
|
||||
## new(b)
|
||||
## a = b
|
||||
@@ -36,7 +36,7 @@ import streams, typeinfo, json, intsets, tables
|
||||
proc ptrToInt(x: pointer): int {.inline.} =
|
||||
result = cast[int](x) # don't skip alignment
|
||||
|
||||
proc storeAny(s: Stream, a: TAny, stored: var IntSet) =
|
||||
proc storeAny(s: Stream, a: Any, stored: var IntSet) =
|
||||
case a.kind
|
||||
of akNone: assert false
|
||||
of akBool: s.write($getBool(a))
|
||||
@@ -96,7 +96,7 @@ proc storeAny(s: Stream, a: TAny, stored: var IntSet) =
|
||||
of akInt..akInt64, akUInt..akUInt64: s.write($getBiggestInt(a))
|
||||
of akFloat..akFloat128: s.write($getBiggestFloat(a))
|
||||
|
||||
proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||
proc loadAny(p: var JsonParser, a: Any, t: var Table[BiggestInt, pointer]) =
|
||||
case a.kind
|
||||
of akNone: assert false
|
||||
of akBool:
|
||||
@@ -222,7 +222,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||
raiseParseErr(p, "float expected")
|
||||
of akRange: loadAny(p, a.skipRange, t)
|
||||
|
||||
proc loadAny(s: Stream, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||
proc loadAny(s: Stream, a: Any, t: var Table[BiggestInt, pointer]) =
|
||||
var p: JsonParser
|
||||
open(p, s, "unknown file")
|
||||
next(p)
|
||||
@@ -278,10 +278,11 @@ when not defined(testing) and isMainModule:
|
||||
else:
|
||||
nil
|
||||
|
||||
PNode = ref TNode
|
||||
TNode = object
|
||||
PNode = ref Node
|
||||
Node = object
|
||||
next, prev: PNode
|
||||
data: string
|
||||
{.deprecated: [TNode: Node].}
|
||||
|
||||
proc buildList(): PNode =
|
||||
new(result)
|
||||
@@ -317,14 +318,15 @@ when not defined(testing) and isMainModule:
|
||||
testit(test7)
|
||||
|
||||
type
|
||||
TA {.inheritable.} = object
|
||||
TB = object of TA
|
||||
A {.inheritable.} = object
|
||||
B = object of A
|
||||
f: int
|
||||
|
||||
var
|
||||
a: ref TA
|
||||
b: ref TB
|
||||
a: ref A
|
||||
b: ref B
|
||||
new(b)
|
||||
a = b
|
||||
echo($$a[]) # produces "{}", not "{f: 0}"
|
||||
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@ proc open*(filename: string, mode: FileMode = fmRead,
|
||||
if mappedSize != -1:
|
||||
result.size = mappedSize
|
||||
else:
|
||||
var stat: TStat
|
||||
var stat: Stat
|
||||
if fstat(result.handle, stat) != -1:
|
||||
# XXX: Hmm, this could be unsafe
|
||||
# Why is mmap taking int anyway?
|
||||
|
||||
@@ -86,7 +86,7 @@ type
|
||||
IPv6, ## IPv6 address
|
||||
IPv4 ## IPv4 address
|
||||
|
||||
TIpAddress* = object ## stores an arbitrary IP address
|
||||
IpAddress* = object ## stores an arbitrary IP address
|
||||
case family*: IpAddressFamily ## the type of the IP address (IPv4 or IPv6)
|
||||
of IpAddressFamily.IPv6:
|
||||
address_v6*: array[0..15, uint8] ## Contains the IP address in bytes in
|
||||
@@ -94,9 +94,10 @@ type
|
||||
of IpAddressFamily.IPv4:
|
||||
address_v4*: array[0..3, uint8] ## Contains the IP address in bytes in
|
||||
## case of IPv4
|
||||
{.deprecated: [TIpAddress: IpAddress].}
|
||||
|
||||
proc isIpAddress*(address_str: string): bool {.tags: [].}
|
||||
proc parseIpAddress*(address_str: string): TIpAddress
|
||||
proc parseIpAddress*(address_str: string): IpAddress
|
||||
|
||||
proc isDisconnectionError*(flags: set[SocketFlag],
|
||||
lastError: OSErrorCode): bool =
|
||||
@@ -118,13 +119,13 @@ proc toOSFlags*(socketFlags: set[SocketFlag]): cint =
|
||||
result = result or MSG_PEEK
|
||||
of SocketFlag.SafeDisconn: continue
|
||||
|
||||
proc newSocket(fd: SocketHandle, isBuff: bool): Socket =
|
||||
proc newSocket*(fd: SocketHandle, buffered = true): Socket =
|
||||
## Creates a new socket as specified by the params.
|
||||
assert fd != osInvalidSocket
|
||||
new(result)
|
||||
result.fd = fd
|
||||
result.isBuffered = isBuff
|
||||
if isBuff:
|
||||
result.isBuffered = buffered
|
||||
if buffered:
|
||||
result.currPos = 0
|
||||
|
||||
proc newSocket*(domain, typ, protocol: cint, buffered = true): Socket =
|
||||
@@ -395,7 +396,7 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string,
|
||||
|
||||
when false: #defined(ssl):
|
||||
proc acceptAddrSSL*(server: Socket, client: var Socket,
|
||||
address: var string): TSSLAcceptResult {.
|
||||
address: var string): SSLAcceptResult {.
|
||||
tags: [ReadIOEffect].} =
|
||||
## This procedure should only be used for non-blocking **SSL** sockets.
|
||||
## It will immediately return with one of the following values:
|
||||
@@ -992,39 +993,39 @@ proc isSsl*(socket: Socket): bool =
|
||||
proc getFd*(socket: Socket): SocketHandle = return socket.fd
|
||||
## Returns the socket's file descriptor
|
||||
|
||||
proc IPv4_any*(): TIpAddress =
|
||||
proc IPv4_any*(): IpAddress =
|
||||
## Returns the IPv4 any address, which can be used to listen on all available
|
||||
## network adapters
|
||||
result = TIpAddress(
|
||||
result = IpAddress(
|
||||
family: IpAddressFamily.IPv4,
|
||||
address_v4: [0'u8, 0, 0, 0])
|
||||
|
||||
proc IPv4_loopback*(): TIpAddress =
|
||||
proc IPv4_loopback*(): IpAddress =
|
||||
## Returns the IPv4 loopback address (127.0.0.1)
|
||||
result = TIpAddress(
|
||||
result = IpAddress(
|
||||
family: IpAddressFamily.IPv4,
|
||||
address_v4: [127'u8, 0, 0, 1])
|
||||
|
||||
proc IPv4_broadcast*(): TIpAddress =
|
||||
proc IPv4_broadcast*(): IpAddress =
|
||||
## Returns the IPv4 broadcast address (255.255.255.255)
|
||||
result = TIpAddress(
|
||||
result = IpAddress(
|
||||
family: IpAddressFamily.IPv4,
|
||||
address_v4: [255'u8, 255, 255, 255])
|
||||
|
||||
proc IPv6_any*(): TIpAddress =
|
||||
proc IPv6_any*(): IpAddress =
|
||||
## Returns the IPv6 any address (::0), which can be used
|
||||
## to listen on all available network adapters
|
||||
result = TIpAddress(
|
||||
result = IpAddress(
|
||||
family: IpAddressFamily.IPv6,
|
||||
address_v6: [0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
|
||||
|
||||
proc IPv6_loopback*(): TIpAddress =
|
||||
proc IPv6_loopback*(): IpAddress =
|
||||
## Returns the IPv6 loopback address (::1)
|
||||
result = TIpAddress(
|
||||
result = IpAddress(
|
||||
family: IpAddressFamily.IPv6,
|
||||
address_v6: [0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])
|
||||
|
||||
proc `==`*(lhs, rhs: TIpAddress): bool =
|
||||
proc `==`*(lhs, rhs: IpAddress): bool =
|
||||
## Compares two IpAddresses for Equality. Returns two if the addresses are equal
|
||||
if lhs.family != rhs.family: return false
|
||||
if lhs.family == IpAddressFamily.IPv4:
|
||||
@@ -1035,8 +1036,8 @@ proc `==`*(lhs, rhs: TIpAddress): bool =
|
||||
if lhs.address_v6[i] != rhs.address_v6[i]: return false
|
||||
return true
|
||||
|
||||
proc `$`*(address: TIpAddress): string =
|
||||
## Converts an TIpAddress into the textual representation
|
||||
proc `$`*(address: IpAddress): string =
|
||||
## Converts an IpAddress into the textual representation
|
||||
result = ""
|
||||
case address.family
|
||||
of IpAddressFamily.IPv4:
|
||||
@@ -1095,7 +1096,7 @@ proc `$`*(address: TIpAddress): string =
|
||||
mask = mask shr 4
|
||||
printedLastGroup = true
|
||||
|
||||
proc parseIPv4Address(address_str: string): TIpAddress =
|
||||
proc parseIPv4Address(address_str: string): IpAddress =
|
||||
## Parses IPv4 adresses
|
||||
## Raises EInvalidValue on errors
|
||||
var
|
||||
@@ -1129,7 +1130,7 @@ proc parseIPv4Address(address_str: string): TIpAddress =
|
||||
raise newException(ValueError, "Invalid IP Address")
|
||||
result.address_v4[byteCount] = cast[uint8](currentByte)
|
||||
|
||||
proc parseIPv6Address(address_str: string): TIpAddress =
|
||||
proc parseIPv6Address(address_str: string): IpAddress =
|
||||
## Parses IPv6 adresses
|
||||
## Raises EInvalidValue on errors
|
||||
result.family = IpAddressFamily.IPv6
|
||||
@@ -1250,7 +1251,7 @@ proc parseIPv6Address(address_str: string): TIpAddress =
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. The address consists of too many groups")
|
||||
|
||||
proc parseIpAddress(address_str: string): TIpAddress =
|
||||
proc parseIpAddress(address_str: string): IpAddress =
|
||||
## Parses an IP address
|
||||
## Raises EInvalidValue on error
|
||||
if address_str == nil:
|
||||
|
||||
@@ -26,17 +26,19 @@ const
|
||||
withThreads = compileOption("threads")
|
||||
tickCountCorrection = 50_000
|
||||
|
||||
when not declared(system.TStackTrace):
|
||||
type TStackTrace = array [0..20, cstring]
|
||||
when not declared(system.StackTrace):
|
||||
type StackTrace = array [0..20, cstring]
|
||||
{.deprecated: [TStackTrace: StackTrace].}
|
||||
|
||||
# We use a simple hash table of bounded size to keep track of the stack traces:
|
||||
type
|
||||
TProfileEntry = object
|
||||
ProfileEntry = object
|
||||
total: int
|
||||
st: TStackTrace
|
||||
TProfileData = array [0..64*1024-1, ptr TProfileEntry]
|
||||
st: StackTrace
|
||||
ProfileData = array [0..64*1024-1, ptr ProfileEntry]
|
||||
{.deprecated: [TProfileEntry: ProfileEntry, TProfileData: ProfileData].}
|
||||
|
||||
proc `==`(a, b: TStackTrace): bool =
|
||||
proc `==`(a, b: StackTrace): bool =
|
||||
for i in 0 .. high(a):
|
||||
if a[i] != b[i]: return false
|
||||
result = true
|
||||
@@ -44,13 +46,13 @@ proc `==`(a, b: TStackTrace): bool =
|
||||
# XXX extract this data structure; it is generally useful ;-)
|
||||
# However a chain length of over 3000 is suspicious...
|
||||
var
|
||||
profileData: TProfileData
|
||||
profileData: ProfileData
|
||||
emptySlots = profileData.len * 3 div 2
|
||||
maxChainLen = 0
|
||||
totalCalls = 0
|
||||
|
||||
when not defined(memProfiler):
|
||||
var interval: TNanos = 5_000_000 - tickCountCorrection # 5ms
|
||||
var interval: Nanos = 5_000_000 - tickCountCorrection # 5ms
|
||||
|
||||
proc setSamplingFrequency*(intervalInUs: int) =
|
||||
## set this to change the sampling frequency. Default value is 5ms.
|
||||
@@ -62,11 +64,11 @@ when not defined(memProfiler):
|
||||
when withThreads:
|
||||
import locks
|
||||
var
|
||||
profilingLock: TLock
|
||||
profilingLock: Lock
|
||||
|
||||
initLock profilingLock
|
||||
|
||||
proc hookAux(st: TStackTrace, costs: int) =
|
||||
proc hookAux(st: StackTrace, costs: int) =
|
||||
# this is quite performance sensitive!
|
||||
when withThreads: acquire profilingLock
|
||||
inc totalCalls
|
||||
@@ -94,8 +96,8 @@ proc hookAux(st: TStackTrace, costs: int) =
|
||||
var chain = 0
|
||||
while true:
|
||||
if profileData[h] == nil:
|
||||
profileData[h] = cast[ptr TProfileEntry](
|
||||
allocShared0(sizeof(TProfileEntry)))
|
||||
profileData[h] = cast[ptr ProfileEntry](
|
||||
allocShared0(sizeof(ProfileEntry)))
|
||||
profileData[h].total = costs
|
||||
profileData[h].st = st
|
||||
dec emptySlots
|
||||
@@ -115,7 +117,7 @@ when defined(memProfiler):
|
||||
var
|
||||
gTicker {.threadvar.}: int
|
||||
|
||||
proc hook(st: TStackTrace, size: int) {.nimcall.} =
|
||||
proc hook(st: StackTrace, size: int) {.nimcall.} =
|
||||
if gTicker == 0:
|
||||
gTicker = -1
|
||||
when defined(ignoreAllocationSize):
|
||||
@@ -127,26 +129,26 @@ when defined(memProfiler):
|
||||
|
||||
else:
|
||||
var
|
||||
t0 {.threadvar.}: TTicks
|
||||
t0 {.threadvar.}: Ticks
|
||||
|
||||
proc hook(st: TStackTrace) {.nimcall.} =
|
||||
proc hook(st: StackTrace) {.nimcall.} =
|
||||
if interval == 0:
|
||||
hookAux(st, 1)
|
||||
elif int64(t0) == 0 or getTicks() - t0 > interval:
|
||||
hookAux(st, 1)
|
||||
t0 = getTicks()
|
||||
|
||||
proc getTotal(x: ptr TProfileEntry): int =
|
||||
proc getTotal(x: ptr ProfileEntry): int =
|
||||
result = if isNil(x): 0 else: x.total
|
||||
|
||||
proc cmpEntries(a, b: ptr TProfileEntry): int =
|
||||
proc cmpEntries(a, b: ptr ProfileEntry): int =
|
||||
result = b.getTotal - a.getTotal
|
||||
|
||||
proc `//`(a, b: int): string =
|
||||
result = format("$1/$2 = $3%", a, b, formatFloat(a / b * 100.0, ffDefault, 2))
|
||||
|
||||
proc writeProfile() {.noconv.} =
|
||||
when declared(system.TStackTrace):
|
||||
when declared(system.StackTrace):
|
||||
system.profilerHook = nil
|
||||
const filename = "profile_results.txt"
|
||||
echo "writing " & filename & "..."
|
||||
@@ -161,7 +163,7 @@ proc writeProfile() {.noconv.} =
|
||||
var perProc = initCountTable[string]()
|
||||
for i in 0..entries-1:
|
||||
var dups = initSet[string]()
|
||||
for ii in 0..high(TStackTrace):
|
||||
for ii in 0..high(StackTrace):
|
||||
let procname = profileData[i].st[ii]
|
||||
if isNil(procname): break
|
||||
let p = $procname
|
||||
@@ -176,7 +178,7 @@ proc writeProfile() {.noconv.} =
|
||||
writeln(f, "Entry: ", i+1, "/", entries, " Calls: ",
|
||||
profileData[i].total // totalCalls, " [sum: ", sum, "; ",
|
||||
sum // totalCalls, "]")
|
||||
for ii in 0..high(TStackTrace):
|
||||
for ii in 0..high(StackTrace):
|
||||
let procname = profileData[i].st[ii]
|
||||
if isNil(procname): break
|
||||
writeln(f, " ", procname, " ", perProc[$procname] // totalCalls)
|
||||
@@ -189,16 +191,16 @@ var
|
||||
disabled: int
|
||||
|
||||
proc disableProfiling*() =
|
||||
when declared(system.TStackTrace):
|
||||
when declared(system.StackTrace):
|
||||
atomicDec disabled
|
||||
system.profilerHook = nil
|
||||
|
||||
proc enableProfiling*() =
|
||||
when declared(system.TStackTrace):
|
||||
when declared(system.StackTrace):
|
||||
if atomicInc(disabled) >= 0:
|
||||
system.profilerHook = hook
|
||||
|
||||
when declared(system.TStackTrace):
|
||||
when declared(system.StackTrace):
|
||||
system.profilerHook = hook
|
||||
addQuitProc(writeProfile)
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user