Merge branch 'devel' into gogc

This commit is contained in:
Stefan Talpalaru
2015-06-15 08:44:02 +02:00
212 changed files with 8304 additions and 7492 deletions

0
bootstrap.sh Normal file → Executable file
View File

View 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"

View File

@@ -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

View File

@@ -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)

View File

@@ -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", [])

View File

@@ -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:

View File

@@ -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)

View File

@@ -88,3 +88,4 @@ proc initDefines*() =
defineSymbol("nimalias")
defineSymbol("nimlocks")
defineSymbol("nimnode")
defineSymbol("nimnomagic64")

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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.} =

View File

@@ -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}

View File

@@ -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 =

View File

@@ -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"

View File

@@ -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]);

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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".}

View File

@@ -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

View File

@@ -4,3 +4,5 @@ proc findNodeJs*(): string =
result = findExe("nodejs")
if result == "":
result = findExe("node")
if result == "":
result = findExe("iojs")

View File

@@ -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

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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
View 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)

View File

@@ -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:

View File

@@ -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)

View File

@@ -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):

View File

@@ -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 =

View File

@@ -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)

View File

@@ -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)

View File

@@ -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:

View File

@@ -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",

View File

@@ -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>`_

View File

@@ -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

View File

@@ -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 dashstyle 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``).

View File

@@ -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
-----------

View File

@@ -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

View File

@@ -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.

View File

@@ -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"

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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
View 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 its 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 doesnt match any special characters.
## Incompatible with the Extra flag (``X``).
str.replace(SpecialCharMatcher, "\\$1")

9
lib/impure/nre/.gitignore vendored Normal file
View File

@@ -0,0 +1,9 @@
# all executables
*
!*/
!*.*
*.exe
# Wildcard patterns.
*.swp
nimcache

View 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

View File

@@ -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)

View File

@@ -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]")``.

View File

@@ -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]

View File

@@ -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")

View File

@@ -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)

View File

@@ -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

View File

@@ -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 =

View File

@@ -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

View File

@@ -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>".}

View File

@@ -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

View File

@@ -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>".}

View File

@@ -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)

View File

@@ -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

View File

@@ -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.
##

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)))

View File

@@ -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)

View File

@@ -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

View File

@@ -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!

View File

@@ -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")

View File

@@ -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

View File

@@ -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
View 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

View File

@@ -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.

View File

@@ -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" )

View File

@@ -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

View File

@@ -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)

View File

@@ -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 != "":

View File

@@ -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:

View File

@@ -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 "}}"

View File

@@ -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.

View File

@@ -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)

View File

@@ -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}"

View File

@@ -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?

View File

@@ -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:

View File

@@ -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