mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-05 13:10:50 +00:00
Merge branch 'devel' of https://github.com/Araq/Nim into add-nre
* 'devel' of https://github.com/Araq/Nim: Fix #964, fix #1384 Don't inspect typedescs
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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
|
||||
@@ -842,7 +843,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
|
||||
|
||||
@@ -1345,7 +1346,7 @@ proc propagateToOwner*(owner, elem: PType) =
|
||||
owner.flags.incl tfHasAsgn
|
||||
|
||||
if owner.kind notin {tyProc, tyGenericInst, tyGenericBody,
|
||||
tyGenericInvocation}:
|
||||
tyGenericInvocation, tyPtr}:
|
||||
let elemB = elem.skipTypes({tyGenericInst})
|
||||
if elemB.isGCedMem or tfHasGCedMem in elemB.flags:
|
||||
# for simplicity, we propagate this flag even to generics. We then
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
import
|
||||
ast, hashes, intsets, strutils, options, msgs, ropes, idents, rodutils
|
||||
|
||||
proc hashNode*(p: RootRef): THash
|
||||
proc hashNode*(p: RootRef): Hash
|
||||
proc treeToYaml*(n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope
|
||||
# Convert a tree into its YAML representation; this is used by the
|
||||
# YAML code generator and it is invaluable for debugging purposes.
|
||||
@@ -49,7 +49,7 @@ proc strTableGet*(t: TStrTable, name: PIdent): PSym
|
||||
|
||||
type
|
||||
TTabIter*{.final.} = object # consider all fields here private
|
||||
h*: THash # current hash
|
||||
h*: Hash # current hash
|
||||
|
||||
proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym
|
||||
proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym
|
||||
@@ -65,7 +65,7 @@ proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym
|
||||
|
||||
type
|
||||
TIdentIter*{.final.} = object # iterator over all syms with same identifier
|
||||
h*: THash # current hash
|
||||
h*: Hash # current hash
|
||||
name*: PIdent
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ proc getSymFromList*(list: PNode, ident: PIdent, start: int = 0): PSym
|
||||
proc lookupInRecord*(n: PNode, field: PIdent): PSym
|
||||
proc getModule*(s: PSym): PSym
|
||||
proc mustRehash*(length, counter: int): bool
|
||||
proc nextTry*(h, maxHash: THash): THash {.inline.}
|
||||
proc nextTry*(h, maxHash: Hash): Hash {.inline.}
|
||||
|
||||
# ------------- table[int, int] ---------------------------------------------
|
||||
const
|
||||
@@ -196,7 +196,7 @@ proc getSymFromList(list: PNode, ident: PIdent, start: int = 0): PSym =
|
||||
else: internalError(list.info, "getSymFromList")
|
||||
result = nil
|
||||
|
||||
proc hashNode(p: RootRef): THash =
|
||||
proc hashNode(p: RootRef): Hash =
|
||||
result = hash(cast[pointer](p))
|
||||
|
||||
proc mustRehash(length, counter: int): bool =
|
||||
@@ -466,7 +466,7 @@ proc debug(n: PNode) =
|
||||
const
|
||||
EmptySeq = @[]
|
||||
|
||||
proc nextTry(h, maxHash: THash): THash =
|
||||
proc nextTry(h, maxHash: Hash): Hash =
|
||||
result = ((5 * h) + 1) and maxHash
|
||||
# For any initial h in range(maxHash), repeating that maxHash times
|
||||
# generates each int in range(maxHash) exactly once (see any text on
|
||||
@@ -474,7 +474,7 @@ proc nextTry(h, maxHash: THash): THash =
|
||||
|
||||
proc objectSetContains(t: TObjectSet, obj: RootRef): bool =
|
||||
# returns true whether n is in t
|
||||
var h: THash = hashNode(obj) and high(t.data) # start with real hash value
|
||||
var h: Hash = hashNode(obj) and high(t.data) # start with real hash value
|
||||
while t.data[h] != nil:
|
||||
if t.data[h] == obj:
|
||||
return true
|
||||
@@ -482,7 +482,7 @@ proc objectSetContains(t: TObjectSet, obj: RootRef): bool =
|
||||
result = false
|
||||
|
||||
proc objectSetRawInsert(data: var TObjectSeq, obj: RootRef) =
|
||||
var h: THash = hashNode(obj) and high(data)
|
||||
var h: Hash = hashNode(obj) and high(data)
|
||||
while data[h] != nil:
|
||||
assert(data[h] != obj)
|
||||
h = nextTry(h, high(data))
|
||||
@@ -503,7 +503,7 @@ proc objectSetIncl(t: var TObjectSet, obj: RootRef) =
|
||||
|
||||
proc objectSetContainsOrIncl(t: var TObjectSet, obj: RootRef): bool =
|
||||
# returns true if obj is already in the string table:
|
||||
var h: THash = hashNode(obj) and high(t.data)
|
||||
var h: Hash = hashNode(obj) and high(t.data)
|
||||
while true:
|
||||
var it = t.data[h]
|
||||
if it == nil: break
|
||||
@@ -520,7 +520,7 @@ proc objectSetContainsOrIncl(t: var TObjectSet, obj: RootRef): bool =
|
||||
result = false
|
||||
|
||||
proc tableRawGet(t: TTable, key: RootRef): int =
|
||||
var h: THash = hashNode(key) and high(t.data) # start with real hash value
|
||||
var h: Hash = hashNode(key) and high(t.data) # start with real hash value
|
||||
while t.data[h].key != nil:
|
||||
if t.data[h].key == key:
|
||||
return h
|
||||
@@ -529,7 +529,7 @@ proc tableRawGet(t: TTable, key: RootRef): int =
|
||||
|
||||
proc tableSearch(t: TTable, key, closure: RootRef,
|
||||
comparator: TCmpProc): RootRef =
|
||||
var h: THash = hashNode(key) and high(t.data) # start with real hash value
|
||||
var h: Hash = hashNode(key) and high(t.data) # start with real hash value
|
||||
while t.data[h].key != nil:
|
||||
if t.data[h].key == key:
|
||||
if comparator(t.data[h].val, closure):
|
||||
@@ -544,7 +544,7 @@ proc tableGet(t: TTable, key: RootRef): RootRef =
|
||||
else: result = nil
|
||||
|
||||
proc tableRawInsert(data: var TPairSeq, key, val: RootRef) =
|
||||
var h: THash = hashNode(key) and high(data)
|
||||
var h: Hash = hashNode(key) and high(data)
|
||||
while data[h].key != nil:
|
||||
assert(data[h].key != key)
|
||||
h = nextTry(h, high(data))
|
||||
@@ -569,7 +569,7 @@ proc tablePut(t: var TTable, key, val: RootRef) =
|
||||
inc(t.counter)
|
||||
|
||||
proc strTableContains(t: TStrTable, n: PSym): bool =
|
||||
var h: THash = n.name.h and high(t.data) # start with real hash value
|
||||
var h: Hash = n.name.h and high(t.data) # start with real hash value
|
||||
while t.data[h] != nil:
|
||||
if (t.data[h] == n):
|
||||
return true
|
||||
@@ -577,7 +577,7 @@ proc strTableContains(t: TStrTable, n: PSym): bool =
|
||||
result = false
|
||||
|
||||
proc strTableRawInsert(data: var TSymSeq, n: PSym) =
|
||||
var h: THash = n.name.h and high(data)
|
||||
var h: Hash = n.name.h and high(data)
|
||||
if sfImmediate notin n.flags:
|
||||
# fast path:
|
||||
while data[h] != nil:
|
||||
@@ -606,7 +606,7 @@ proc strTableRawInsert(data: var TSymSeq, n: PSym) =
|
||||
|
||||
proc symTabReplaceRaw(data: var TSymSeq, prevSym: PSym, newSym: PSym) =
|
||||
assert prevSym.name.h == newSym.name.h
|
||||
var h: THash = prevSym.name.h and high(data)
|
||||
var h: Hash = prevSym.name.h and high(data)
|
||||
while data[h] != nil:
|
||||
if data[h] == prevSym:
|
||||
data[h] = newSym
|
||||
@@ -640,7 +640,7 @@ proc strTableIncl*(t: var TStrTable, n: PSym): bool {.discardable.} =
|
||||
# It is essential that `n` is written nevertheless!
|
||||
# This way the newest redefinition is picked by the semantic analyses!
|
||||
assert n.name != nil
|
||||
var h: THash = n.name.h and high(t.data)
|
||||
var h: Hash = n.name.h and high(t.data)
|
||||
var replaceSlot = -1
|
||||
while true:
|
||||
var it = t.data[h]
|
||||
@@ -666,7 +666,7 @@ proc strTableIncl*(t: var TStrTable, n: PSym): bool {.discardable.} =
|
||||
result = false
|
||||
|
||||
proc strTableGet(t: TStrTable, name: PIdent): PSym =
|
||||
var h: THash = name.h and high(t.data)
|
||||
var h: Hash = name.h and high(t.data)
|
||||
while true:
|
||||
result = t.data[h]
|
||||
if result == nil: break
|
||||
@@ -694,7 +694,7 @@ proc nextIdentIter(ti: var TIdentIter, tab: TStrTable): PSym =
|
||||
|
||||
proc nextIdentExcluding*(ti: var TIdentIter, tab: TStrTable,
|
||||
excluding: IntSet): PSym =
|
||||
var h: THash = ti.h and high(tab.data)
|
||||
var h: Hash = ti.h and high(tab.data)
|
||||
var start = h
|
||||
result = tab.data[h]
|
||||
while result != nil:
|
||||
@@ -743,7 +743,7 @@ proc hasEmptySlot(data: TIdPairSeq): bool =
|
||||
result = false
|
||||
|
||||
proc idTableRawGet(t: TIdTable, key: int): int =
|
||||
var h: THash
|
||||
var h: Hash
|
||||
h = key and high(t.data) # start with real hash value
|
||||
while t.data[h].key != nil:
|
||||
if t.data[h].key.id == key:
|
||||
@@ -772,7 +772,7 @@ iterator pairs*(t: TIdTable): tuple[key: int, value: RootRef] =
|
||||
yield (t.data[i].key.id, t.data[i].val)
|
||||
|
||||
proc idTableRawInsert(data: var TIdPairSeq, key: PIdObj, val: RootRef) =
|
||||
var h: THash
|
||||
var h: Hash
|
||||
h = key.id and high(data)
|
||||
while data[h].key != nil:
|
||||
assert(data[h].key.id != key.id)
|
||||
@@ -805,7 +805,7 @@ iterator idTablePairs*(t: TIdTable): tuple[key: PIdObj, val: RootRef] =
|
||||
if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val)
|
||||
|
||||
proc idNodeTableRawGet(t: TIdNodeTable, key: PIdObj): int =
|
||||
var h: THash
|
||||
var h: Hash
|
||||
h = key.id and high(t.data) # start with real hash value
|
||||
while t.data[h].key != nil:
|
||||
if t.data[h].key.id == key.id:
|
||||
@@ -824,7 +824,7 @@ proc idNodeTableGetLazy*(t: TIdNodeTable, key: PIdObj): PNode =
|
||||
result = idNodeTableGet(t, key)
|
||||
|
||||
proc idNodeTableRawInsert(data: var TIdNodePairSeq, key: PIdObj, val: PNode) =
|
||||
var h: THash
|
||||
var h: Hash
|
||||
h = key.id and high(data)
|
||||
while data[h].key != nil:
|
||||
assert(data[h].key.id != key.id)
|
||||
@@ -863,7 +863,7 @@ proc initIITable(x: var TIITable) =
|
||||
for i in countup(0, StartSize - 1): x.data[i].key = InvalidKey
|
||||
|
||||
proc iiTableRawGet(t: TIITable, key: int): int =
|
||||
var h: THash
|
||||
var h: Hash
|
||||
h = key and high(t.data) # start with real hash value
|
||||
while t.data[h].key != InvalidKey:
|
||||
if t.data[h].key == key: return h
|
||||
@@ -876,7 +876,7 @@ proc iiTableGet(t: TIITable, key: int): int =
|
||||
else: result = InvalidKey
|
||||
|
||||
proc iiTableRawInsert(data: var TIIPairSeq, key, val: int) =
|
||||
var h: THash
|
||||
var h: Hash
|
||||
h = key and high(data)
|
||||
while data[h].key != InvalidKey:
|
||||
assert(data[h].key != key)
|
||||
|
||||
@@ -2150,7 +2150,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", [])
|
||||
|
||||
@@ -721,6 +721,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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -986,6 +986,15 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
else: internalError(n.sons[0].info, "expr(nkBracketExpr, " & $ty.kind & ')')
|
||||
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 +1030,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)
|
||||
@@ -1394,6 +1398,9 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
of mCopyStrLast: ternaryExpr(p, n, r, "", "($1.slice($2, ($3)+1).concat(0))")
|
||||
of mNewString: unaryExpr(p, n, r, "mnewString", "mnewString($1)")
|
||||
of mNewStringOfCap: unaryExpr(p, n, r, "mnewString", "mnewString(0)")
|
||||
of mDotDot:
|
||||
genProcForSymIfNeeded(p, n.sons[0].sym)
|
||||
genCall(p, n, r)
|
||||
else:
|
||||
genCall(p, n, r)
|
||||
#else internalError(e.info, 'genMagic: ' + magicToStr[op]);
|
||||
|
||||
@@ -946,7 +946,11 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode =
|
||||
proc liftLambdas*(fn: PSym, body: PNode): PNode =
|
||||
# XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs
|
||||
# the transformation even when compiling to JS ...
|
||||
if body.kind == nkEmpty or gCmd == cmdCompileToJS or
|
||||
|
||||
# However we can do lifting for the stuff which is *only* compiletime.
|
||||
let isCompileTime = sfCompileTime in fn.flags or fn.kind == skMacro
|
||||
|
||||
if body.kind == nkEmpty or (gCmd == cmdCompileToJS and not isCompileTime) or
|
||||
fn.skipGenericOwner.kind != skModule:
|
||||
# ignore forward declaration:
|
||||
result = body
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
import
|
||||
hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream,
|
||||
wordrecg
|
||||
wordrecg, etcpriv
|
||||
|
||||
const
|
||||
MaxLineLength* = 80 # lines longer than this lead to a warning
|
||||
@@ -140,10 +140,12 @@ proc isKeyword*(kind: TTokType): bool =
|
||||
proc isNimIdentifier*(s: string): bool =
|
||||
if s[0] in SymStartChars:
|
||||
var i = 1
|
||||
while i < s.len:
|
||||
var sLen = s.len
|
||||
while i < sLen:
|
||||
if s[i] == '_':
|
||||
inc(i)
|
||||
if s[i] notin SymChars: return
|
||||
elif isMagicIdentSeparatorRune(cstring s, i):
|
||||
inc(i, magicIdentSeparatorRuneByteWidth)
|
||||
if s[i] notin SymChars: return
|
||||
inc(i)
|
||||
result = true
|
||||
@@ -229,23 +231,6 @@ proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") =
|
||||
var info = newLineInfo(L.fileIdx, L.lineNumber, pos - L.lineStart)
|
||||
L.dispMessage(info, msg, arg)
|
||||
|
||||
proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: set[char]) =
|
||||
var pos = L.bufpos # use registers for pos, buf
|
||||
var buf = L.buf
|
||||
while true:
|
||||
if buf[pos] in chars:
|
||||
add(tok.literal, buf[pos])
|
||||
inc(pos)
|
||||
else:
|
||||
break
|
||||
if buf[pos] == '_':
|
||||
if buf[pos+1] notin chars:
|
||||
lexMessage(L, errInvalidToken, "_")
|
||||
break
|
||||
add(tok.literal, '_')
|
||||
inc(pos)
|
||||
L.bufpos = pos
|
||||
|
||||
proc matchTwoChars(L: TLexer, first: char, second: set[char]): bool =
|
||||
result = (L.buf[L.bufpos] == first) and (L.buf[L.bufpos + 1] in second)
|
||||
|
||||
@@ -268,136 +253,195 @@ 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 =
|
||||
var
|
||||
pos, endpos: int
|
||||
startpos, endpos: int
|
||||
xi: BiggestInt
|
||||
# get the base:
|
||||
const literalishChars = { 'A'..'F', 'a'..'f', '0'..'9', 'X', 'x', 'o', 'c',
|
||||
'C', 'b', 'B', '_', '.', '\''}
|
||||
const literalishCharsNoDot = literalishChars - {'.'}
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
var isAFloatLiteral = false
|
||||
# First stage: find out base, make verifications, build token literal string
|
||||
if L.buf[L.bufpos] == '0' and
|
||||
L.buf[L.bufpos + 1] in {'X', 'x', 'o', 'O', 'c', 'C', 'b', 'B'}:
|
||||
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'}):
|
||||
isAFloatLiteral = true
|
||||
eatChar(L, result, '.')
|
||||
matchUnderscoreChars(L, result, {'0'..'9'})
|
||||
if L.buf[L.bufpos] in {'e', 'E'}:
|
||||
isAFloatLiteral = true
|
||||
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 postfix 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 literalishCharsNoDot 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'}):
|
||||
inc(pos, 2)
|
||||
xi = 0 # it may be a base prefix
|
||||
xi = 0 # it is a base prefix
|
||||
case L.buf[pos - 1] # now look at the optional type suffix:
|
||||
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,8 +452,10 @@ 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))))
|
||||
@@ -425,7 +471,7 @@ proc getNumber(L: var TLexer): TToken =
|
||||
# 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
|
||||
elif isAFloatLiteral or (result.tokType == tkFloat32Lit) or
|
||||
(result.tokType == tkFloat64Lit):
|
||||
result.fNumber = parseFloat(result.literal)
|
||||
if result.tokType == tkIntLit: result.tokType = tkFloatLit
|
||||
@@ -441,18 +487,18 @@ proc getNumber(L: var TLexer): TToken =
|
||||
if result.tokType == tkIntLit:
|
||||
result.tokType = tkInt64Lit
|
||||
elif result.tokType in {tkInt8Lit, tkInt16Lit, tkInt32Lit}:
|
||||
lexMessage(L, errNumberOutOfRange, result.literal)
|
||||
lexMessageLitNum(L, errNumberOutOfRange, startpos)
|
||||
elif result.tokType == tkInt8Lit and
|
||||
(result.iNumber < int8.low or result.iNumber > int8.high):
|
||||
lexMessage(L, errNumberOutOfRange, result.literal)
|
||||
lexMessageLitNum(L, errNumberOutOfRange, startpos)
|
||||
elif result.tokType == tkInt16Lit and
|
||||
(result.iNumber < int16.low or result.iNumber > int16.high):
|
||||
lexMessage(L, errNumberOutOfRange, result.literal)
|
||||
lexMessageLitNum(L, errNumberOutOfRange, startpos)
|
||||
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 +671,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 +709,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 +719,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
|
||||
|
||||
@@ -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\'",
|
||||
|
||||
@@ -10,8 +10,10 @@
|
||||
## Nimfix is a tool that helps to convert old-style Nimrod code to Nim code.
|
||||
|
||||
import strutils, os, parseopt
|
||||
import options, commands, modules, sem, passes, passaux, pretty, msgs, nimconf,
|
||||
extccomp, condsyms, lists
|
||||
import compiler/options, compiler/commands, compiler/modules, compiler/sem,
|
||||
compiler/passes, compiler/passaux, compiler/nimfix/pretty,
|
||||
compiler/msgs, compiler/nimconf,
|
||||
compiler/extccomp, compiler/condsyms, compiler/lists
|
||||
|
||||
const Usage = """
|
||||
Nimfix - Tool to patch Nim code
|
||||
@@ -24,7 +26,7 @@ Options:
|
||||
--wholeProject overwrite every processed file.
|
||||
--checkExtern:on|off style check also extern names
|
||||
--styleCheck:on|off|auto performs style checking for identifiers
|
||||
and suggests an alternative spelling;
|
||||
and suggests an alternative spelling;
|
||||
'auto' corrects the spelling.
|
||||
--bestEffort try to fix the code even when there
|
||||
are errors.
|
||||
@@ -48,11 +50,11 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
|
||||
var p = parseopt.initOptParser(cmd)
|
||||
var argsCount = 0
|
||||
gOnlyMainfile = true
|
||||
while true:
|
||||
while true:
|
||||
parseopt.next(p)
|
||||
case p.kind
|
||||
of cmdEnd: break
|
||||
of cmdLongoption, cmdShortOption:
|
||||
of cmdEnd: break
|
||||
of cmdLongoption, cmdShortOption:
|
||||
case p.key.normalize
|
||||
of "overwritefiles":
|
||||
case p.val.normalize
|
||||
|
||||
@@ -10,9 +10,11 @@
|
||||
## This module implements the code "prettifier". This is part of the toolchain
|
||||
## to convert Nim code into a consistent style.
|
||||
|
||||
import
|
||||
strutils, os, options, ast, astalgo, msgs, ropes, idents,
|
||||
intsets, strtabs, semdata, prettybase
|
||||
import
|
||||
strutils, os, intsets, strtabs
|
||||
|
||||
import compiler/options, compiler/ast, compiler/astalgo, compiler/msgs,
|
||||
compiler/semdata, compiler/nimfix/prettybase, compiler/ropes, compiler/idents
|
||||
|
||||
type
|
||||
StyleCheck* {.pure.} = enum None, Warn, Auto
|
||||
@@ -92,7 +94,7 @@ proc beautifyName(s: string, k: TSymKind): string =
|
||||
|
||||
proc replaceInFile(info: TLineInfo; newName: string) =
|
||||
loadFile(info)
|
||||
|
||||
|
||||
let line = gSourceFiles[info.fileIndex].lines[info.line-1]
|
||||
var first = min(info.col.int, line.len)
|
||||
if first < 0: return
|
||||
@@ -100,18 +102,18 @@ proc replaceInFile(info: TLineInfo; newName: string) =
|
||||
while first > 0 and line[first-1] in prettybase.Letters: dec first
|
||||
if first < 0: return
|
||||
if line[first] == '`': inc first
|
||||
|
||||
|
||||
let last = first+identLen(line, first)-1
|
||||
if differ(line, first, last, newName):
|
||||
# last-first+1 != newName.len or
|
||||
var x = line.substr(0, first-1) & newName & line.substr(last+1)
|
||||
# last-first+1 != newName.len or
|
||||
var x = line.substr(0, first-1) & newName & line.substr(last+1)
|
||||
system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x)
|
||||
gSourceFiles[info.fileIndex].dirty = true
|
||||
|
||||
proc checkStyle(info: TLineInfo, s: string, k: TSymKind; sym: PSym) =
|
||||
let beau = beautifyName(s, k)
|
||||
if s != beau:
|
||||
if gStyleCheck == StyleCheck.Auto:
|
||||
if gStyleCheck == StyleCheck.Auto:
|
||||
sym.name = getIdent(beau)
|
||||
replaceInFile(info, beau)
|
||||
else:
|
||||
@@ -137,7 +139,7 @@ proc styleCheckUseImpl(info: TLineInfo; s: PSym) =
|
||||
if info.fileIndex < 0: return
|
||||
# we simply convert it to what it looks like in the definition
|
||||
# for consistency
|
||||
|
||||
|
||||
# operators stay as they are:
|
||||
if s.kind in {skResult, skTemp} or s.name.s[0] notin prettybase.Letters:
|
||||
return
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
import ast, msgs, strutils, idents, lexbase, streams
|
||||
import strutils, lexbase, streams
|
||||
import compiler/ast, compiler/msgs, compiler/idents
|
||||
from os import splitFile
|
||||
|
||||
type
|
||||
@@ -39,7 +40,7 @@ proc loadFile*(info: TLineInfo) =
|
||||
var pos = lex.bufpos
|
||||
while true:
|
||||
case lex.buf[pos]
|
||||
of '\c':
|
||||
of '\c':
|
||||
gSourceFiles[i].newline = "\c\L"
|
||||
break
|
||||
of '\L', '\0':
|
||||
@@ -70,7 +71,7 @@ proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) =
|
||||
while first > 0 and line[first-1] in Letters: dec first
|
||||
if first < 0: return
|
||||
if line[first] == '`': inc first
|
||||
|
||||
|
||||
let last = first+identLen(line, first)-1
|
||||
if cmpIgnoreStyle(line[first..last], oldSym.s) == 0:
|
||||
var x = line.substr(0, first-1) & newSym.s & line.substr(last+1)
|
||||
|
||||
@@ -7,330 +7,6 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Nimsuggest is a tool that helps to give editors IDE like capabilities.
|
||||
## Nimsuggest has been moved to https://github.com/nim-lang/nimsuggest
|
||||
|
||||
import strutils, os, parseopt, parseutils, sequtils, net
|
||||
# Do NOT import suggest. It will lead to wierd bugs with
|
||||
# suggestionResultHook, because suggest.nim is included by sigmatch.
|
||||
# So we import that one instead.
|
||||
import options, commands, modules, sem, passes, passaux, msgs, nimconf,
|
||||
extccomp, condsyms, lists, net, rdstdin, sexp, sigmatch, ast
|
||||
|
||||
when defined(windows):
|
||||
import winlean
|
||||
else:
|
||||
import posix
|
||||
|
||||
const Usage = """
|
||||
Nimsuggest - Tool to give every editor IDE like capabilities for Nim
|
||||
Usage:
|
||||
nimsuggest [options] projectfile.nim
|
||||
|
||||
Options:
|
||||
--port:PORT port, by default 6000
|
||||
--address:HOST binds to that address, by default ""
|
||||
--stdin read commands from stdin and write results to
|
||||
stdout instead of using sockets
|
||||
--epc use emacs epc mode
|
||||
|
||||
The server then listens to the connection and takes line-based commands.
|
||||
|
||||
In addition, all command line options of Nim that do not affect code generation
|
||||
are supported.
|
||||
"""
|
||||
type
|
||||
Mode = enum mstdin, mtcp, mepc
|
||||
|
||||
var
|
||||
gPort = 6000.Port
|
||||
gAddress = ""
|
||||
gMode: Mode
|
||||
|
||||
const
|
||||
seps = {':', ';', ' ', '\t'}
|
||||
Help = "usage: sug|con|def|use file.nim[;dirtyfile.nim]:line:col\n"&
|
||||
"type 'quit' to quit\n" &
|
||||
"type 'debug' to toggle debug mode on/off\n" &
|
||||
"type 'terse' to toggle terse mode on/off"
|
||||
|
||||
type
|
||||
EUnexpectedCommand = object of Exception
|
||||
|
||||
proc parseQuoted(cmd: string; outp: var string; start: int): int =
|
||||
var i = start
|
||||
i += skipWhitespace(cmd, i)
|
||||
if cmd[i] == '"':
|
||||
i += parseUntil(cmd, outp, '"', i+1)+2
|
||||
else:
|
||||
i += parseUntil(cmd, outp, seps, i)
|
||||
result = i
|
||||
|
||||
proc sexp(s: IdeCmd): SexpNode = sexp($s)
|
||||
|
||||
proc sexp(s: TSymKind): SexpNode = sexp($s)
|
||||
|
||||
proc sexp(s: Suggest): SexpNode =
|
||||
# If you change the oder here, make sure to change it over in
|
||||
# nim-mode.el too.
|
||||
result = convertSexp([
|
||||
s.section,
|
||||
s.symkind,
|
||||
s.qualifiedPath.map(newSString),
|
||||
s.filePath,
|
||||
s.forth,
|
||||
s.line,
|
||||
s.column,
|
||||
s.doc
|
||||
])
|
||||
|
||||
proc sexp(s: seq[Suggest]): SexpNode =
|
||||
result = newSList()
|
||||
for sug in s:
|
||||
result.add(sexp(sug))
|
||||
|
||||
proc listEPC(): SexpNode =
|
||||
let
|
||||
argspecs = sexp("file line column dirtyfile".split(" ").map(newSSymbol))
|
||||
docstring = sexp("line starts at 1, column at 0, dirtyfile is optional")
|
||||
result = newSList()
|
||||
for command in ["sug", "con", "def", "use"]:
|
||||
let
|
||||
cmd = sexp(command)
|
||||
methodDesc = newSList()
|
||||
methodDesc.add(cmd)
|
||||
methodDesc.add(argspecs)
|
||||
methodDesc.add(docstring)
|
||||
result.add(methodDesc)
|
||||
|
||||
proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int) =
|
||||
gIdeCmd = cmd
|
||||
if cmd == ideUse:
|
||||
modules.resetAllModules()
|
||||
var isKnownFile = true
|
||||
let dirtyIdx = file.fileInfoIdx(isKnownFile)
|
||||
|
||||
if dirtyfile.len != 0: msgs.setDirtyFile(dirtyIdx, dirtyfile)
|
||||
else: msgs.setDirtyFile(dirtyIdx, nil)
|
||||
|
||||
resetModule dirtyIdx
|
||||
if dirtyIdx != gProjectMainIdx:
|
||||
resetModule gProjectMainIdx
|
||||
|
||||
gTrackPos = newLineInfo(dirtyIdx, line, col)
|
||||
gErrorCounter = 0
|
||||
if not isKnownFile:
|
||||
compileProject()
|
||||
compileProject(dirtyIdx)
|
||||
|
||||
proc executeEPC(cmd: IdeCmd, args: SexpNode) =
|
||||
let
|
||||
file = args[0].getStr
|
||||
line = args[1].getNum
|
||||
column = args[2].getNum
|
||||
var dirtyfile = ""
|
||||
if len(args) > 3:
|
||||
dirtyfile = args[3].getStr(nil)
|
||||
execute(cmd, file, dirtyfile, int(line), int(column))
|
||||
|
||||
proc returnEPC(socket: var Socket, uid: BiggestInt, s: SexpNode, return_symbol = "return") =
|
||||
let response = $convertSexp([newSSymbol(return_symbol), uid, s])
|
||||
socket.send(toHex(len(response), 6))
|
||||
socket.send(response)
|
||||
|
||||
proc connectToNextFreePort(server: Socket, host: string, start = 30000): int =
|
||||
result = start
|
||||
while true:
|
||||
try:
|
||||
server.bindaddr(Port(result), host)
|
||||
return
|
||||
except OsError:
|
||||
when defined(windows):
|
||||
let checkFor = WSAEADDRINUSE.OSErrorCode
|
||||
else:
|
||||
let checkFor = EADDRINUSE.OSErrorCode
|
||||
if osLastError() != checkFor:
|
||||
raise getCurrentException()
|
||||
else:
|
||||
result += 1
|
||||
|
||||
proc parseCmdLine(cmd: string) =
|
||||
template toggle(sw) =
|
||||
if sw in gGlobalOptions:
|
||||
excl(gGlobalOptions, sw)
|
||||
else:
|
||||
incl(gGlobalOptions, sw)
|
||||
return
|
||||
|
||||
template err() =
|
||||
echo Help
|
||||
return
|
||||
|
||||
var opc = ""
|
||||
var i = parseIdent(cmd, opc, 0)
|
||||
case opc.normalize
|
||||
of "sug": gIdeCmd = ideSug
|
||||
of "con": gIdeCmd = ideCon
|
||||
of "def": gIdeCmd = ideDef
|
||||
of "use": gIdeCmd = ideUse
|
||||
of "quit": quit()
|
||||
of "debug": toggle optIdeDebug
|
||||
of "terse": toggle optIdeTerse
|
||||
else: err()
|
||||
var dirtyfile = ""
|
||||
var orig = ""
|
||||
i = parseQuoted(cmd, orig, i)
|
||||
if cmd[i] == ';':
|
||||
i = parseQuoted(cmd, dirtyfile, i+1)
|
||||
i += skipWhile(cmd, seps, i)
|
||||
var line = -1
|
||||
var col = 0
|
||||
i += parseInt(cmd, line, i)
|
||||
i += skipWhile(cmd, seps, i)
|
||||
i += parseInt(cmd, col, i)
|
||||
|
||||
execute(gIdeCmd, orig, dirtyfile, line, col-1)
|
||||
|
||||
proc serve() =
|
||||
case gMode:
|
||||
of mstdin:
|
||||
echo Help
|
||||
var line = ""
|
||||
while readLineFromStdin("> ", line):
|
||||
parseCmdLine line
|
||||
echo ""
|
||||
flushFile(stdout)
|
||||
of mtcp:
|
||||
var server = newSocket()
|
||||
server.bindAddr(gPort, gAddress)
|
||||
var inp = "".TaintedString
|
||||
server.listen()
|
||||
|
||||
while true:
|
||||
var stdoutSocket = newSocket()
|
||||
msgs.writelnHook = proc (line: string) =
|
||||
stdoutSocket.send(line & "\c\L")
|
||||
|
||||
accept(server, stdoutSocket)
|
||||
|
||||
stdoutSocket.readLine(inp)
|
||||
parseCmdLine inp.string
|
||||
|
||||
stdoutSocket.send("\c\L")
|
||||
stdoutSocket.close()
|
||||
of mepc:
|
||||
var server = newSocket()
|
||||
let port = connectToNextFreePort(server, "localhost")
|
||||
var inp = "".TaintedString
|
||||
server.listen()
|
||||
echo(port)
|
||||
var client = newSocket()
|
||||
# Wait for connection
|
||||
accept(server, client)
|
||||
while true:
|
||||
var sizeHex = ""
|
||||
if client.recv(sizeHex, 6) != 6:
|
||||
raise newException(ValueError, "didn't get all the hexbytes")
|
||||
var size = 0
|
||||
if parseHex(sizeHex, size) == 0:
|
||||
raise newException(ValueError, "invalid size hex: " & $sizeHex)
|
||||
var messageBuffer = ""
|
||||
if client.recv(messageBuffer, size) != size:
|
||||
raise newException(ValueError, "didn't get all the bytes")
|
||||
let
|
||||
message = parseSexp($messageBuffer)
|
||||
messageType = message[0].getSymbol
|
||||
case messageType:
|
||||
of "call":
|
||||
var results: seq[Suggest] = @[]
|
||||
suggestionResultHook = proc (s: Suggest) =
|
||||
results.add(s)
|
||||
|
||||
let
|
||||
uid = message[1].getNum
|
||||
cmd = parseIdeCmd(message[2].getSymbol)
|
||||
args = message[3]
|
||||
executeEPC(cmd, args)
|
||||
returnEPC(client, uid, sexp(results))
|
||||
of "return":
|
||||
raise newException(EUnexpectedCommand, "no return expected")
|
||||
of "return-error":
|
||||
raise newException(EUnexpectedCommand, "no return expected")
|
||||
of "epc-error":
|
||||
stderr.writeln("recieved epc error: " & $messageBuffer)
|
||||
raise newException(IOError, "epc error")
|
||||
of "methods":
|
||||
returnEPC(client, message[1].getNum, listEPC())
|
||||
else:
|
||||
raise newException(EUnexpectedCommand, "unexpected call: " & messageType)
|
||||
|
||||
proc mainCommand =
|
||||
registerPass verbosePass
|
||||
registerPass semPass
|
||||
gCmd = cmdIdeTools
|
||||
incl gGlobalOptions, optCaasEnabled
|
||||
isServing = true
|
||||
wantMainModule()
|
||||
appendStr(searchPaths, options.libpath)
|
||||
if gProjectFull.len != 0:
|
||||
# current path is always looked first for modules
|
||||
prependStr(searchPaths, gProjectPath)
|
||||
|
||||
# do not stop after the first error:
|
||||
msgs.gErrorMax = high(int)
|
||||
compileProject()
|
||||
serve()
|
||||
|
||||
proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
|
||||
var p = parseopt.initOptParser(cmd)
|
||||
while true:
|
||||
parseopt.next(p)
|
||||
case p.kind
|
||||
of cmdEnd: break
|
||||
of cmdLongoption, cmdShortOption:
|
||||
case p.key.normalize
|
||||
of "port":
|
||||
gPort = parseInt(p.val).Port
|
||||
gMode = mtcp
|
||||
of "address":
|
||||
gAddress = p.val
|
||||
gMode = mtcp
|
||||
of "stdin": gMode = mstdin
|
||||
of "epc":
|
||||
gMode = mepc
|
||||
gVerbosity = 0 # Port number gotta be first.
|
||||
else: processSwitch(pass, p)
|
||||
of cmdArgument:
|
||||
options.gProjectName = unixToNativePath(p.key)
|
||||
# if processArgument(pass, p, argsCount): break
|
||||
|
||||
proc handleCmdLine() =
|
||||
if paramCount() == 0:
|
||||
stdout.writeln(Usage)
|
||||
else:
|
||||
processCmdLine(passCmd1, "")
|
||||
if gProjectName != "":
|
||||
try:
|
||||
gProjectFull = canonicalizePath(gProjectName)
|
||||
except OSError:
|
||||
gProjectFull = gProjectName
|
||||
var p = splitFile(gProjectFull)
|
||||
gProjectPath = p.dir
|
||||
gProjectName = p.name
|
||||
else:
|
||||
gProjectPath = getCurrentDir()
|
||||
loadConfigs(DefaultConfig) # load all config files
|
||||
# now process command line arguments again, because some options in the
|
||||
# command line can overwite the config file's settings
|
||||
extccomp.initVars()
|
||||
processCmdLine(passCmd2, "")
|
||||
mainCommand()
|
||||
|
||||
when false:
|
||||
proc quitCalled() {.noconv.} =
|
||||
writeStackTrace()
|
||||
|
||||
addQuitProc(quitCalled)
|
||||
|
||||
condsyms.initDefines()
|
||||
defineSymbol "nimsuggest"
|
||||
handleCmdline()
|
||||
{.error: "This project has moved to the following repo: https://github.com/nim-lang/nimsuggest".}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
# Special configuration file for the Nim project
|
||||
|
||||
gc:markAndSweep
|
||||
|
||||
hint[XDeclaredButNotUsed]:off
|
||||
path:"$projectPath/../.."
|
||||
|
||||
path:"$lib/packages/docutils"
|
||||
path:"../../compiler"
|
||||
|
||||
define:useStdoutAsStdmsg
|
||||
define:nimsuggest
|
||||
|
||||
cs:partial
|
||||
#define:useNodeIds
|
||||
define:booting
|
||||
#define:noDocgen
|
||||
@@ -64,6 +64,7 @@ proc setBaseFlags*(n: PNode, base: TNumericalBase)
|
||||
proc parseSymbol*(p: var TParser, allowNil = false): PNode
|
||||
proc parseTry(p: var TParser; isExpr: bool): PNode
|
||||
proc parseCase(p: var TParser): PNode
|
||||
proc parseStmtPragma(p: var TParser): PNode
|
||||
# implementation
|
||||
|
||||
proc getTok(p: var TParser) =
|
||||
@@ -499,10 +500,13 @@ proc parsePar(p: var TParser): PNode =
|
||||
#| parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try'
|
||||
#| | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let'
|
||||
#| | 'when' | 'var' | 'mixin'
|
||||
#| par = '(' optInd (&parKeyw complexOrSimpleStmt ^+ ';'
|
||||
#| | simpleExpr ('=' expr (';' complexOrSimpleStmt ^+ ';' )? )?
|
||||
#| | (':' expr)? (',' (exprColonEqExpr comma?)*)? )?
|
||||
#| optPar ')'
|
||||
#| par = '(' optInd
|
||||
#| ( &parKeyw complexOrSimpleStmt ^+ ';'
|
||||
#| | ';' complexOrSimpleStmt ^+ ';'
|
||||
#| | pragmaStmt
|
||||
#| | simpleExpr ( ('=' expr (';' complexOrSimpleStmt ^+ ';' )? )
|
||||
#| | (':' expr (',' exprColonEqExpr ^+ ',' )? ) ) )
|
||||
#| optPar ')'
|
||||
#
|
||||
# unfortunately it's ambiguous: (expr: expr) vs (exprStmt); however a
|
||||
# leading ';' could be used to enforce a 'stmt' context ...
|
||||
@@ -521,6 +525,8 @@ proc parsePar(p: var TParser): PNode =
|
||||
getTok(p)
|
||||
optInd(p, result)
|
||||
semiStmtList(p, result)
|
||||
elif p.tok.tokType == tkCurlyDotLe:
|
||||
result.add(parseStmtPragma(p))
|
||||
elif p.tok.tokType != tkParRi:
|
||||
var a = simpleExpr(p)
|
||||
if p.tok.tokType == tkEquals:
|
||||
|
||||
@@ -130,7 +130,9 @@ proc matchNested(c: PPatternContext, p, n: PNode, rpn: bool): bool =
|
||||
|
||||
proc matches(c: PPatternContext, p, n: PNode): bool =
|
||||
# hidden conversions (?)
|
||||
if isPatternParam(c, p):
|
||||
if nfNoRewrite in n.flags:
|
||||
result = false
|
||||
elif isPatternParam(c, p):
|
||||
result = bindOrCheck(c, p.sym, n)
|
||||
elif n.kind == nkSym and p.kind == nkIdent:
|
||||
result = p.ident.id == n.sym.name.id
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
|
||||
## The builtin 'system.locals' implemented as a plugin.
|
||||
|
||||
import plugins, ast, astalgo, magicsys, lookups, semdata, lowerings
|
||||
import compiler/plugins, compiler/ast, compiler/astalgo, compiler/magicsys,
|
||||
compiler/lookups, compiler/semdata, compiler/lowerings
|
||||
|
||||
proc semLocals(c: PContext, n: PNode): PNode =
|
||||
var counter = 0
|
||||
|
||||
@@ -37,7 +37,7 @@ const
|
||||
wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
|
||||
wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises,
|
||||
wTags, wLocks, wGcSafe}
|
||||
exprPragmas* = {wLine, wLocks}
|
||||
exprPragmas* = {wLine, wLocks, wNoRewrite}
|
||||
stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks,
|
||||
wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
|
||||
wLinedir, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError,
|
||||
@@ -859,6 +859,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||
c.module.flags.incl sfExperimental
|
||||
else:
|
||||
localError(it.info, "'experimental' pragma only valid as toplevel statement")
|
||||
of wNoRewrite:
|
||||
noVal(it)
|
||||
else: invalidPragma(it)
|
||||
else: invalidPragma(it)
|
||||
else: processNote(c, it)
|
||||
|
||||
@@ -767,7 +767,8 @@ proc gasm(g: var TSrcGen, n: PNode) =
|
||||
putWithSpace(g, tkAsm, "asm")
|
||||
gsub(g, n.sons[0])
|
||||
gcoms(g)
|
||||
gsub(g, n.sons[1])
|
||||
if n.sons.len > 1:
|
||||
gsub(g, n.sons[1])
|
||||
|
||||
proc gident(g: var TSrcGen, n: PNode) =
|
||||
if g.checkAnon and n.kind == nkSym and sfAnon in n.sym.flags: return
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -207,9 +207,9 @@ proc markGcUnsafe(a: PEffects; reason: PNode) =
|
||||
a.owner.gcUnsafetyReason = newSym(skUnknown, getIdent("<unknown>"),
|
||||
a.owner, reason.info)
|
||||
|
||||
proc listGcUnsafety(s: PSym; onlyWarning: bool) =
|
||||
proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet) =
|
||||
let u = s.gcUnsafetyReason
|
||||
if u != nil:
|
||||
if u != nil and not cycleCheck.containsOrIncl(u.id):
|
||||
let msgKind = if onlyWarning: warnGcUnsafe2 else: errGenerated
|
||||
if u.kind in {skLet, skVar}:
|
||||
message(s.info, msgKind,
|
||||
@@ -218,7 +218,7 @@ proc listGcUnsafety(s: PSym; onlyWarning: bool) =
|
||||
elif u.kind in routineKinds:
|
||||
# recursive call *always* produces only a warning so the full error
|
||||
# message is printed:
|
||||
listGcUnsafety(u, true)
|
||||
listGcUnsafety(u, true, cycleCheck)
|
||||
message(s.info, msgKind,
|
||||
"'$#' is not GC-safe as it calls '$#'" %
|
||||
[s.name.s, u.name.s])
|
||||
@@ -227,6 +227,10 @@ proc listGcUnsafety(s: PSym; onlyWarning: bool) =
|
||||
message(u.info, msgKind,
|
||||
"'$#' is not GC-safe as it performs an indirect call here" % s.name.s)
|
||||
|
||||
proc listGcUnsafety(s: PSym; onlyWarning: bool) =
|
||||
var cycleCheck = initIntSet()
|
||||
listGcUnsafety(s, onlyWarning, cycleCheck)
|
||||
|
||||
proc useVar(a: PEffects, n: PNode) =
|
||||
let s = n.sym
|
||||
if isLocalVar(a, s):
|
||||
|
||||
@@ -1268,6 +1268,8 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode =
|
||||
of wLocks:
|
||||
result = n
|
||||
result.typ = n.sons[1].typ
|
||||
of wNoRewrite:
|
||||
incl(result.flags, nfNoRewrite)
|
||||
else: discard
|
||||
|
||||
proc semStaticStmt(c: PContext, n: PNode): PNode =
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
import
|
||||
hashes, ast, astalgo, types
|
||||
|
||||
proc hashTree(n: PNode): THash =
|
||||
proc hashTree(n: PNode): Hash =
|
||||
if n == nil: return
|
||||
result = ord(n.kind)
|
||||
case n.kind
|
||||
@@ -53,8 +53,8 @@ proc treesEquivalent(a, b: PNode): bool =
|
||||
result = true
|
||||
if result: result = sameTypeOrNil(a.typ, b.typ)
|
||||
|
||||
proc nodeTableRawGet(t: TNodeTable, k: THash, key: PNode): int =
|
||||
var h: THash = k and high(t.data)
|
||||
proc nodeTableRawGet(t: TNodeTable, k: Hash, key: PNode): int =
|
||||
var h: Hash = k and high(t.data)
|
||||
while t.data[h].key != nil:
|
||||
if (t.data[h].h == k) and treesEquivalent(t.data[h].key, key):
|
||||
return h
|
||||
@@ -66,9 +66,9 @@ proc nodeTableGet*(t: TNodeTable, key: PNode): int =
|
||||
if index >= 0: result = t.data[index].val
|
||||
else: result = low(int)
|
||||
|
||||
proc nodeTableRawInsert(data: var TNodePairSeq, k: THash, key: PNode,
|
||||
proc nodeTableRawInsert(data: var TNodePairSeq, k: Hash, key: PNode,
|
||||
val: int) =
|
||||
var h: THash = k and high(data)
|
||||
var h: Hash = k and high(data)
|
||||
while data[h].key != nil: h = nextTry(h, high(data))
|
||||
assert(data[h].key == nil)
|
||||
data[h].h = k
|
||||
@@ -77,7 +77,7 @@ proc nodeTableRawInsert(data: var TNodePairSeq, k: THash, key: PNode,
|
||||
|
||||
proc nodeTablePut*(t: var TNodeTable, key: PNode, val: int) =
|
||||
var n: TNodePairSeq
|
||||
var k: THash = hashTree(key)
|
||||
var k: Hash = hashTree(key)
|
||||
var index = nodeTableRawGet(t, k, key)
|
||||
if index >= 0:
|
||||
assert(t.data[index].key != nil)
|
||||
@@ -94,7 +94,7 @@ proc nodeTablePut*(t: var TNodeTable, key: PNode, val: int) =
|
||||
|
||||
proc nodeTableTestOrSet*(t: var TNodeTable, key: PNode, val: int): int =
|
||||
var n: TNodePairSeq
|
||||
var k: THash = hashTree(key)
|
||||
var k: Hash = hashTree(key)
|
||||
var index = nodeTableRawGet(t, k, key)
|
||||
if index >= 0:
|
||||
assert(t.data[index].key != nil)
|
||||
|
||||
@@ -55,7 +55,7 @@ type
|
||||
wFloatchecks, wNanChecks, wInfChecks,
|
||||
wAssertions, wPatterns, wWarnings,
|
||||
wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects, wTags,
|
||||
wDeadCodeElim, wSafecode, wNoForward,
|
||||
wDeadCodeElim, wSafecode, wNoForward, wNoRewrite,
|
||||
wPragma,
|
||||
wCompileTime, wNoInit,
|
||||
wPassc, wPassl, wBorrow, wDiscardable,
|
||||
@@ -139,7 +139,7 @@ const
|
||||
|
||||
"assertions", "patterns", "warnings", "hints",
|
||||
"optimization", "raises", "writes", "reads", "size", "effects", "tags",
|
||||
"deadcodeelim", "safecode", "noforward",
|
||||
"deadcodeelim", "safecode", "noforward", "norewrite",
|
||||
"pragma",
|
||||
"compiletime", "noinit",
|
||||
"passc", "passl", "borrow", "discardable", "fieldchecks",
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
* `FloatInvalidOpError <system.html#FloatInvalidOpError>`_
|
||||
* `FloatOverflowError <system.html#FloatOverflowError>`_
|
||||
* `FloatUnderflowError <system.html#FloatUnderflowError>`_
|
||||
* `FieldError <system.html#InvalidFieldError>`_
|
||||
* `IndexError <system.html#InvalidIndexError>`_
|
||||
* `FieldError <system.html#FieldError>`_
|
||||
* `IndexError <system.html#IndexError>`_
|
||||
* `ObjectAssignmentError <system.html#ObjectAssignmentError>`_
|
||||
* `ObjectConversionError <system.html#ObjectConversionError>`_
|
||||
* `ValueError <system.html#ValueError>`_
|
||||
|
||||
@@ -123,11 +123,6 @@ String handling
|
||||
Ropes can represent very long strings efficiently; especially concatenation
|
||||
is done in O(1) instead of O(n).
|
||||
|
||||
* `unidecode <unidecode.html>`_
|
||||
This module provides Unicode to ASCII transliterations:
|
||||
It finds the sequence of ASCII characters that is the closest approximation
|
||||
to the Unicode string.
|
||||
|
||||
* `matchers <matchers.html>`_
|
||||
This module contains various string matchers for email addresses, etc.
|
||||
|
||||
|
||||
@@ -276,7 +276,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 +297,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 +333,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
|
||||
@@ -344,8 +349,8 @@ is approximately 1.72826e35 according to the IEEE floating point standard.
|
||||
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 +360,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``).
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
@@ -12,56 +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
|
||||
|
||||
LockEffect* {.deprecated.} = object of RootEffect ## \
|
||||
## effect that denotes that some lock operation
|
||||
## is performed. Deprecated, do not use anymore!
|
||||
AquireEffect* {.deprecated.} = object of LockEffect ## \
|
||||
## effect that denotes that some lock is
|
||||
## acquired. Deprecated, do not use anymore!
|
||||
ReleaseEffect* {.deprecated.} = object of LockEffect ## \
|
||||
## effect that denotes that some lock is
|
||||
## released. Deprecated, do not use anymore!
|
||||
{.deprecated: [FLock: LockEffect, FAquireLock: AquireEffect,
|
||||
FReleaseLock: ReleaseEffect].}
|
||||
|
||||
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.} =
|
||||
## waits on the condition variable `cond`.
|
||||
proc wait*(cond: var Cond, lock: var Lock) {.inline.} =
|
||||
## waits on the condition variable `cond`.
|
||||
waitSysCond(cond, lock)
|
||||
|
||||
proc signal*(cond: var TCond) {.inline.} =
|
||||
## sends a signal to the condition variable `cond`.
|
||||
signalSysCond(cond)
|
||||
|
||||
proc signal*(cond: var Cond) {.inline.} =
|
||||
## sends a signal to the condition variable `cond`.
|
||||
signalSysCond(cond)
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
## This module implements an interface to Nim's `runtime type information`:idx:
|
||||
## (`RTTI`:idx:).
|
||||
## Note that even though ``TAny`` and its operations hide the nasty low level
|
||||
## Note that even though ``Any`` and its operations hide the nasty low level
|
||||
## details from its clients, it remains inherently unsafe!
|
||||
##
|
||||
## See the `marshal <marshal.html>`_ module for what this module allows you
|
||||
@@ -23,7 +23,7 @@ include "system/hti.nim"
|
||||
{.pop.}
|
||||
|
||||
type
|
||||
TAnyKind* = enum ## what kind of ``any`` it is
|
||||
AnyKind* = enum ## what kind of ``any`` it is
|
||||
akNone = 0, ## invalid any
|
||||
akBool = 1, ## any represents a ``bool``
|
||||
akChar = 2, ## any represents a ``char``
|
||||
@@ -55,9 +55,9 @@ type
|
||||
akUInt32 = 43, ## any represents an unsigned int32
|
||||
akUInt64 = 44, ## any represents an unsigned int64
|
||||
|
||||
TAny* = object ## can represent any nim value; NOTE: the wrapped
|
||||
Any* = object ## can represent any nim value; NOTE: the wrapped
|
||||
## value can be modified with its wrapper! This means
|
||||
## that ``TAny`` keeps a non-traced pointer to its
|
||||
## that ``Any`` keeps a non-traced pointer to its
|
||||
## wrapped value and **must not** live longer than
|
||||
## its wrapped value.
|
||||
value: pointer
|
||||
@@ -69,6 +69,7 @@ type
|
||||
TGenericSeq {.importc.} = object
|
||||
len, space: int
|
||||
PGenSeq = ptr TGenericSeq
|
||||
{.deprecated: [TAny: Any, TAnyKind: AnyKind].}
|
||||
|
||||
const
|
||||
GenericSeqSize = (2 * sizeof(int))
|
||||
@@ -103,58 +104,58 @@ proc selectBranch(aa: pointer, n: ptr TNimNode): ptr TNimNode =
|
||||
else:
|
||||
result = n.sons[n.len]
|
||||
|
||||
proc newAny(value: pointer, rawType: PNimType): TAny =
|
||||
proc newAny(value: pointer, rawType: PNimType): Any =
|
||||
result.value = value
|
||||
result.rawType = rawType
|
||||
|
||||
when declared(system.TVarSlot):
|
||||
proc toAny*(x: TVarSlot): TAny {.inline.} =
|
||||
## constructs a ``TAny`` object from a variable slot ``x``.
|
||||
when declared(system.VarSlot):
|
||||
proc toAny*(x: VarSlot): Any {.inline.} =
|
||||
## constructs a ``Any`` object from a variable slot ``x``.
|
||||
## This captures `x`'s address, so `x` can be modified with its
|
||||
## ``TAny`` wrapper! The client needs to ensure that the wrapper
|
||||
## ``Any`` wrapper! The client needs to ensure that the wrapper
|
||||
## **does not** live longer than `x`!
|
||||
## This is provided for easier reflection capabilities of a debugger.
|
||||
result.value = x.address
|
||||
result.rawType = x.typ
|
||||
|
||||
proc toAny*[T](x: var T): TAny {.inline.} =
|
||||
## constructs a ``TAny`` object from `x`. This captures `x`'s address, so
|
||||
## `x` can be modified with its ``TAny`` wrapper! The client needs to ensure
|
||||
proc toAny*[T](x: var T): Any {.inline.} =
|
||||
## constructs a ``Any`` object from `x`. This captures `x`'s address, so
|
||||
## `x` can be modified with its ``Any`` wrapper! The client needs to ensure
|
||||
## that the wrapper **does not** live longer than `x`!
|
||||
result.value = addr(x)
|
||||
result.rawType = cast[PNimType](getTypeInfo(x))
|
||||
|
||||
proc kind*(x: TAny): TAnyKind {.inline.} =
|
||||
proc kind*(x: Any): AnyKind {.inline.} =
|
||||
## get the type kind
|
||||
result = TAnyKind(ord(x.rawType.kind))
|
||||
result = AnyKind(ord(x.rawType.kind))
|
||||
|
||||
proc size*(x: TAny): int {.inline.} =
|
||||
proc size*(x: Any): int {.inline.} =
|
||||
## returns the size of `x`'s type.
|
||||
result = x.rawType.size
|
||||
|
||||
proc baseTypeKind*(x: TAny): TAnyKind {.inline.} =
|
||||
proc baseTypeKind*(x: Any): AnyKind {.inline.} =
|
||||
## get the base type's kind; ``akNone`` is returned if `x` has no base type.
|
||||
if x.rawType.base != nil:
|
||||
result = TAnyKind(ord(x.rawType.base.kind))
|
||||
result = AnyKind(ord(x.rawType.base.kind))
|
||||
|
||||
proc baseTypeSize*(x: TAny): int {.inline.} =
|
||||
proc baseTypeSize*(x: Any): int {.inline.} =
|
||||
## returns the size of `x`'s basetype.
|
||||
if x.rawType.base != nil:
|
||||
result = x.rawType.base.size
|
||||
|
||||
proc invokeNew*(x: TAny) =
|
||||
proc invokeNew*(x: Any) =
|
||||
## performs ``new(x)``. `x` needs to represent a ``ref``.
|
||||
assert x.rawType.kind == tyRef
|
||||
var z = newObj(x.rawType, x.rawType.base.size)
|
||||
genericAssign(x.value, addr(z), x.rawType)
|
||||
|
||||
proc invokeNewSeq*(x: TAny, len: int) =
|
||||
proc invokeNewSeq*(x: Any, len: int) =
|
||||
## performs ``newSeq(x, len)``. `x` needs to represent a ``seq``.
|
||||
assert x.rawType.kind == tySequence
|
||||
var z = newSeq(x.rawType, len)
|
||||
genericShallowAssign(x.value, addr(z), x.rawType)
|
||||
|
||||
proc extendSeq*(x: TAny) =
|
||||
proc extendSeq*(x: Any) =
|
||||
## performs ``setLen(x, x.len+1)``. `x` needs to represent a ``seq``.
|
||||
assert x.rawType.kind == tySequence
|
||||
var y = cast[ptr PGenSeq](x.value)[]
|
||||
@@ -164,7 +165,7 @@ proc extendSeq*(x: TAny) =
|
||||
cast[ppointer](x.value)[] = z
|
||||
#genericShallowAssign(x.value, addr(z), x.rawType)
|
||||
|
||||
proc setObjectRuntimeType*(x: TAny) =
|
||||
proc setObjectRuntimeType*(x: Any) =
|
||||
## this needs to be called to set `x`'s runtime object type field.
|
||||
assert x.rawType.kind == tyObject
|
||||
objectInit(x.value, x.rawType)
|
||||
@@ -173,7 +174,7 @@ proc skipRange(x: PNimType): PNimType {.inline.} =
|
||||
result = x
|
||||
if result.kind == tyRange: result = result.base
|
||||
|
||||
proc `[]`*(x: TAny, i: int): TAny =
|
||||
proc `[]`*(x: Any, i: int): Any =
|
||||
## accessor for an any `x` that represents an array or a sequence.
|
||||
case x.rawType.kind
|
||||
of tyArray:
|
||||
@@ -190,7 +191,7 @@ proc `[]`*(x: TAny, i: int): TAny =
|
||||
return newAny(s +!! (GenericSeqSize+i*bs), x.rawType.base)
|
||||
else: assert false
|
||||
|
||||
proc `[]=`*(x: TAny, i: int, y: TAny) =
|
||||
proc `[]=`*(x: Any, i: int, y: Any) =
|
||||
## accessor for an any `x` that represents an array or a sequence.
|
||||
case x.rawType.kind
|
||||
of tyArray:
|
||||
@@ -209,7 +210,7 @@ proc `[]=`*(x: TAny, i: int, y: TAny) =
|
||||
genericAssign(s +!! (GenericSeqSize+i*bs), y.value, y.rawType)
|
||||
else: assert false
|
||||
|
||||
proc len*(x: TAny): int =
|
||||
proc len*(x: Any): int =
|
||||
## len for an any `x` that represents an array or a sequence.
|
||||
case x.rawType.kind
|
||||
of tyArray: result = x.rawType.size div x.rawType.base.size
|
||||
@@ -217,20 +218,20 @@ proc len*(x: TAny): int =
|
||||
else: assert false
|
||||
|
||||
|
||||
proc base*(x: TAny): TAny =
|
||||
## returns base TAny (useful for inherited object types).
|
||||
proc base*(x: Any): Any =
|
||||
## returns base Any (useful for inherited object types).
|
||||
result.rawType = x.rawType.base
|
||||
result.value = x.value
|
||||
|
||||
|
||||
proc isNil*(x: TAny): bool =
|
||||
proc isNil*(x: Any): bool =
|
||||
## `isNil` for an any `x` that represents a sequence, string, cstring,
|
||||
## proc or some pointer type.
|
||||
assert x.rawType.kind in {tyString, tyCString, tyRef, tyPtr, tyPointer,
|
||||
tySequence, tyProc}
|
||||
result = isNil(cast[ppointer](x.value)[])
|
||||
|
||||
proc getPointer*(x: TAny): pointer =
|
||||
proc getPointer*(x: Any): pointer =
|
||||
## retrieve the pointer value out of `x`. ``x`` needs to be of kind
|
||||
## ``akString``, ``akCString``, ``akProc``, ``akRef``, ``akPtr``,
|
||||
## ``akPointer``, ``akSequence``.
|
||||
@@ -238,7 +239,7 @@ proc getPointer*(x: TAny): pointer =
|
||||
tySequence, tyProc}
|
||||
result = cast[ppointer](x.value)[]
|
||||
|
||||
proc setPointer*(x: TAny, y: pointer) =
|
||||
proc setPointer*(x: Any, y: pointer) =
|
||||
## sets the pointer value of `x`. ``x`` needs to be of kind
|
||||
## ``akString``, ``akCString``, ``akProc``, ``akRef``, ``akPtr``,
|
||||
## ``akPointer``, ``akSequence``.
|
||||
@@ -247,7 +248,7 @@ proc setPointer*(x: TAny, y: pointer) =
|
||||
cast[ppointer](x.value)[] = y
|
||||
|
||||
proc fieldsAux(p: pointer, n: ptr TNimNode,
|
||||
ret: var seq[tuple[name: cstring, any: TAny]]) =
|
||||
ret: var seq[tuple[name: cstring, any: Any]]) =
|
||||
case n.kind
|
||||
of nkNone: assert(false)
|
||||
of nkSlot:
|
||||
@@ -260,7 +261,7 @@ proc fieldsAux(p: pointer, n: ptr TNimNode,
|
||||
ret.add((n.name, newAny(p +!! n.offset, n.typ)))
|
||||
if m != nil: fieldsAux(p, m, ret)
|
||||
|
||||
iterator fields*(x: TAny): tuple[name: string, any: TAny] =
|
||||
iterator fields*(x: Any): tuple[name: string, any: Any] =
|
||||
## iterates over every active field of the any `x` that represents an object
|
||||
## or a tuple.
|
||||
assert x.rawType.kind in {tyTuple, tyObject}
|
||||
@@ -269,7 +270,7 @@ iterator fields*(x: TAny): tuple[name: string, any: TAny] =
|
||||
# XXX BUG: does not work yet, however is questionable anyway
|
||||
when false:
|
||||
if x.rawType.kind == tyObject: t = cast[ptr PNimType](x.value)[]
|
||||
var ret: seq[tuple[name: cstring, any: TAny]] = @[]
|
||||
var ret: seq[tuple[name: cstring, any: Any]] = @[]
|
||||
if t.kind == tyObject:
|
||||
while true:
|
||||
fieldsAux(p, t.node, ret)
|
||||
@@ -314,7 +315,7 @@ proc getFieldNode(p: pointer, n: ptr TNimNode,
|
||||
var m = selectBranch(p, n)
|
||||
if m != nil: result = getFieldNode(p, m, name)
|
||||
|
||||
proc `[]=`*(x: TAny, fieldName: string, value: TAny) =
|
||||
proc `[]=`*(x: Any, fieldName: string, value: Any) =
|
||||
## sets a field of `x`; `x` represents an object or a tuple.
|
||||
var t = x.rawType
|
||||
# XXX BUG: does not work yet, however is questionable anyway
|
||||
@@ -328,7 +329,7 @@ proc `[]=`*(x: TAny, fieldName: string, value: TAny) =
|
||||
else:
|
||||
raise newException(ValueError, "invalid field name: " & fieldName)
|
||||
|
||||
proc `[]`*(x: TAny, fieldName: string): TAny =
|
||||
proc `[]`*(x: Any, fieldName: string): Any =
|
||||
## gets a field of `x`; `x` represents an object or a tuple.
|
||||
var t = x.rawType
|
||||
# XXX BUG: does not work yet, however is questionable anyway
|
||||
@@ -339,47 +340,49 @@ proc `[]`*(x: TAny, fieldName: string): TAny =
|
||||
if n != nil:
|
||||
result.value = x.value +!! n.offset
|
||||
result.rawType = n.typ
|
||||
elif x.rawType.kind == tyObject and x.rawType.base != nil:
|
||||
return `[]`(TAny(value: x.value, rawType: x.rawType.base), fieldName)
|
||||
else:
|
||||
raise newException(ValueError, "invalid field name: " & fieldName)
|
||||
|
||||
proc `[]`*(x: TAny): TAny =
|
||||
proc `[]`*(x: Any): Any =
|
||||
## dereference operation for the any `x` that represents a ptr or a ref.
|
||||
assert x.rawType.kind in {tyRef, tyPtr}
|
||||
result.value = cast[ppointer](x.value)[]
|
||||
result.rawType = x.rawType.base
|
||||
|
||||
proc `[]=`*(x, y: TAny) =
|
||||
proc `[]=`*(x, y: Any) =
|
||||
## dereference operation for the any `x` that represents a ptr or a ref.
|
||||
assert x.rawType.kind in {tyRef, tyPtr}
|
||||
assert y.rawType == x.rawType.base
|
||||
genericAssign(cast[ppointer](x.value)[], y.value, y.rawType)
|
||||
|
||||
proc getInt*(x: TAny): int =
|
||||
proc getInt*(x: Any): int =
|
||||
## retrieve the int value out of `x`. `x` needs to represent an int.
|
||||
assert skipRange(x.rawType).kind == tyInt
|
||||
result = cast[ptr int](x.value)[]
|
||||
|
||||
proc getInt8*(x: TAny): int8 =
|
||||
proc getInt8*(x: Any): int8 =
|
||||
## retrieve the int8 value out of `x`. `x` needs to represent an int8.
|
||||
assert skipRange(x.rawType).kind == tyInt8
|
||||
result = cast[ptr int8](x.value)[]
|
||||
|
||||
proc getInt16*(x: TAny): int16 =
|
||||
proc getInt16*(x: Any): int16 =
|
||||
## retrieve the int16 value out of `x`. `x` needs to represent an int16.
|
||||
assert skipRange(x.rawType).kind == tyInt16
|
||||
result = cast[ptr int16](x.value)[]
|
||||
|
||||
proc getInt32*(x: TAny): int32 =
|
||||
proc getInt32*(x: Any): int32 =
|
||||
## retrieve the int32 value out of `x`. `x` needs to represent an int32.
|
||||
assert skipRange(x.rawType).kind == tyInt32
|
||||
result = cast[ptr int32](x.value)[]
|
||||
|
||||
proc getInt64*(x: TAny): int64 =
|
||||
proc getInt64*(x: Any): int64 =
|
||||
## retrieve the int64 value out of `x`. `x` needs to represent an int64.
|
||||
assert skipRange(x.rawType).kind == tyInt64
|
||||
result = cast[ptr int64](x.value)[]
|
||||
|
||||
proc getBiggestInt*(x: TAny): BiggestInt =
|
||||
proc getBiggestInt*(x: Any): BiggestInt =
|
||||
## retrieve the integer value out of `x`. `x` needs to represent
|
||||
## some integer, a bool, a char, an enum or a small enough bit set.
|
||||
## The value might be sign-extended to ``BiggestInt``.
|
||||
@@ -405,7 +408,7 @@ proc getBiggestInt*(x: TAny): BiggestInt =
|
||||
of tyUInt32: result = BiggestInt(cast[ptr uint32](x.value)[])
|
||||
else: assert false
|
||||
|
||||
proc setBiggestInt*(x: TAny, y: BiggestInt) =
|
||||
proc setBiggestInt*(x: Any, y: BiggestInt) =
|
||||
## sets the integer value of `x`. `x` needs to represent
|
||||
## some integer, a bool, a char, an enum or a small enough bit set.
|
||||
var t = skipRange(x.rawType)
|
||||
@@ -430,36 +433,36 @@ proc setBiggestInt*(x: TAny, y: BiggestInt) =
|
||||
of tyUInt32: cast[ptr uint32](x.value)[] = uint32(y)
|
||||
else: assert false
|
||||
|
||||
proc getUInt*(x: TAny): uint =
|
||||
proc getUInt*(x: Any): uint =
|
||||
## retrieve the uint value out of `x`, `x` needs to represent an uint.
|
||||
assert skipRange(x.rawType).kind == tyUInt
|
||||
result = cast[ptr uint](x.value)[]
|
||||
|
||||
proc getUInt8*(x: TAny): uint8 =
|
||||
proc getUInt8*(x: Any): uint8 =
|
||||
## retrieve the uint8 value out of `x`, `x` needs to represent an
|
||||
## uint8.
|
||||
assert skipRange(x.rawType).kind == tyUInt8
|
||||
result = cast[ptr uint8](x.value)[]
|
||||
|
||||
proc getUInt16*(x: TAny): uint16 =
|
||||
proc getUInt16*(x: Any): uint16 =
|
||||
## retrieve the uint16 value out of `x`, `x` needs to represent an
|
||||
## uint16.
|
||||
assert skipRange(x.rawType).kind == tyUInt16
|
||||
result = cast[ptr uint16](x.value)[]
|
||||
|
||||
proc getUInt32*(x: TAny): uint32 =
|
||||
proc getUInt32*(x: Any): uint32 =
|
||||
## retrieve the uint32 value out of `x`, `x` needs to represent an
|
||||
## uint32.
|
||||
assert skipRange(x.rawType).kind == tyUInt32
|
||||
result = cast[ptr uint32](x.value)[]
|
||||
|
||||
proc getUInt64*(x: TAny): uint64 =
|
||||
proc getUInt64*(x: Any): uint64 =
|
||||
## retrieve the uint64 value out of `x`, `x` needs to represent an
|
||||
## uint64.
|
||||
assert skipRange(x.rawType).kind == tyUInt64
|
||||
result = cast[ptr uint64](x.value)[]
|
||||
|
||||
proc getBiggestUint*(x: TAny): uint64 =
|
||||
proc getBiggestUint*(x: Any): uint64 =
|
||||
## retrieve the unsigned integer value out of `x`. `x` needs to
|
||||
## represent an unsigned integer.
|
||||
var t = skipRange(x.rawType)
|
||||
@@ -471,7 +474,7 @@ proc getBiggestUint*(x: TAny): uint64 =
|
||||
of tyUInt64: result = uint64(cast[ptr uint64](x.value)[])
|
||||
else: assert false
|
||||
|
||||
proc setBiggestUint*(x: TAny; y: uint64) =
|
||||
proc setBiggestUint*(x: Any; y: uint64) =
|
||||
## sets the unsigned integer value of `c`. `c` needs to represent an
|
||||
## unsigned integer.
|
||||
var t = skipRange(x.rawType)
|
||||
@@ -483,25 +486,25 @@ proc setBiggestUint*(x: TAny; y: uint64) =
|
||||
of tyUInt64: cast[ptr uint64](x.value)[] = uint64(y)
|
||||
else: assert false
|
||||
|
||||
proc getChar*(x: TAny): char =
|
||||
proc getChar*(x: Any): char =
|
||||
## retrieve the char value out of `x`. `x` needs to represent a char.
|
||||
var t = skipRange(x.rawType)
|
||||
assert t.kind == tyChar
|
||||
result = cast[ptr char](x.value)[]
|
||||
|
||||
proc getBool*(x: TAny): bool =
|
||||
proc getBool*(x: Any): bool =
|
||||
## retrieve the bool value out of `x`. `x` needs to represent a bool.
|
||||
var t = skipRange(x.rawType)
|
||||
assert t.kind == tyBool
|
||||
result = cast[ptr bool](x.value)[]
|
||||
|
||||
proc skipRange*(x: TAny): TAny =
|
||||
proc skipRange*(x: Any): Any =
|
||||
## skips the range information of `x`.
|
||||
assert x.rawType.kind == tyRange
|
||||
result.rawType = x.rawType.base
|
||||
result.value = x.value
|
||||
|
||||
proc getEnumOrdinal*(x: TAny, name: string): int =
|
||||
proc getEnumOrdinal*(x: Any, name: string): int =
|
||||
## gets the enum field ordinal from `name`. `x` needs to represent an enum
|
||||
## but is only used to access the type information. In case of an error
|
||||
## ``low(int)`` is returned.
|
||||
@@ -517,7 +520,7 @@ proc getEnumOrdinal*(x: TAny, name: string): int =
|
||||
return s[i].offset
|
||||
result = low(int)
|
||||
|
||||
proc getEnumField*(x: TAny, ordinalValue: int): string =
|
||||
proc getEnumField*(x: Any, ordinalValue: int): string =
|
||||
## gets the enum field name as a string. `x` needs to represent an enum
|
||||
## but is only used to access the type information. The field name of
|
||||
## `ordinalValue` is returned.
|
||||
@@ -535,26 +538,26 @@ proc getEnumField*(x: TAny, ordinalValue: int): string =
|
||||
if s[i].offset == e: return $s[i].name
|
||||
result = $e
|
||||
|
||||
proc getEnumField*(x: TAny): string =
|
||||
proc getEnumField*(x: Any): string =
|
||||
## gets the enum field name as a string. `x` needs to represent an enum.
|
||||
result = getEnumField(x, getBiggestInt(x).int)
|
||||
|
||||
proc getFloat*(x: TAny): float =
|
||||
proc getFloat*(x: Any): float =
|
||||
## retrieve the float value out of `x`. `x` needs to represent an float.
|
||||
assert skipRange(x.rawType).kind == tyFloat
|
||||
result = cast[ptr float](x.value)[]
|
||||
|
||||
proc getFloat32*(x: TAny): float32 =
|
||||
proc getFloat32*(x: Any): float32 =
|
||||
## retrieve the float32 value out of `x`. `x` needs to represent an float32.
|
||||
assert skipRange(x.rawType).kind == tyFloat32
|
||||
result = cast[ptr float32](x.value)[]
|
||||
|
||||
proc getFloat64*(x: TAny): float64 =
|
||||
proc getFloat64*(x: Any): float64 =
|
||||
## retrieve the float64 value out of `x`. `x` needs to represent an float64.
|
||||
assert skipRange(x.rawType).kind == tyFloat64
|
||||
result = cast[ptr float64](x.value)[]
|
||||
|
||||
proc getBiggestFloat*(x: TAny): BiggestFloat =
|
||||
proc getBiggestFloat*(x: Any): BiggestFloat =
|
||||
## retrieve the float value out of `x`. `x` needs to represent
|
||||
## some float. The value is extended to ``BiggestFloat``.
|
||||
case skipRange(x.rawType).kind
|
||||
@@ -563,7 +566,7 @@ proc getBiggestFloat*(x: TAny): BiggestFloat =
|
||||
of tyFloat64: result = BiggestFloat(cast[ptr float64](x.value)[])
|
||||
else: assert false
|
||||
|
||||
proc setBiggestFloat*(x: TAny, y: BiggestFloat) =
|
||||
proc setBiggestFloat*(x: Any, y: BiggestFloat) =
|
||||
## sets the float value of `x`. `x` needs to represent
|
||||
## some float.
|
||||
case skipRange(x.rawType).kind
|
||||
@@ -572,29 +575,29 @@ proc setBiggestFloat*(x: TAny, y: BiggestFloat) =
|
||||
of tyFloat64: cast[ptr float64](x.value)[] = y
|
||||
else: assert false
|
||||
|
||||
proc getString*(x: TAny): string =
|
||||
proc getString*(x: Any): string =
|
||||
## retrieve the string value out of `x`. `x` needs to represent a string.
|
||||
assert x.rawType.kind == tyString
|
||||
if not isNil(cast[ptr pointer](x.value)[]):
|
||||
result = cast[ptr string](x.value)[]
|
||||
|
||||
proc setString*(x: TAny, y: string) =
|
||||
proc setString*(x: Any, y: string) =
|
||||
## sets the string value of `x`. `x` needs to represent a string.
|
||||
assert x.rawType.kind == tyString
|
||||
cast[ptr string](x.value)[] = y
|
||||
|
||||
proc getCString*(x: TAny): cstring =
|
||||
proc getCString*(x: Any): cstring =
|
||||
## retrieve the cstring value out of `x`. `x` needs to represent a cstring.
|
||||
assert x.rawType.kind == tyCString
|
||||
result = cast[ptr cstring](x.value)[]
|
||||
|
||||
proc assign*(x, y: TAny) =
|
||||
## copies the value of `y` to `x`. The assignment operator for ``TAny``
|
||||
proc assign*(x, y: Any) =
|
||||
## copies the value of `y` to `x`. The assignment operator for ``Any``
|
||||
## does NOT do this; it performs a shallow copy instead!
|
||||
assert y.rawType == x.rawType
|
||||
genericAssign(x.value, y.value, y.rawType)
|
||||
|
||||
iterator elements*(x: TAny): int =
|
||||
iterator elements*(x: Any): int =
|
||||
## iterates over every element of `x` that represents a Nim bitset.
|
||||
assert x.rawType.kind == tySet
|
||||
var typ = x.rawType
|
||||
@@ -616,7 +619,7 @@ iterator elements*(x: TAny): int =
|
||||
if (u and (1'i64 shl int64(i))) != 0'i64:
|
||||
yield i+typ.node.len
|
||||
|
||||
proc inclSetElement*(x: TAny, elem: int) =
|
||||
proc inclSetElement*(x: Any, elem: int) =
|
||||
## includes an element `elem` in `x`. `x` needs to represent a Nim bitset.
|
||||
assert x.rawType.kind == tySet
|
||||
var typ = x.rawType
|
||||
|
||||
@@ -13,27 +13,28 @@
|
||||
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.
|
||||
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 +49,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 +66,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 +79,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 +104,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 +127,10 @@ iterator fastRows*(db: TDbConn, query: TSqlQuery,
|
||||
yield result
|
||||
properFreeResult(sqlres, row)
|
||||
|
||||
proc getRow*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
|
||||
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 +146,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 +169,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 +184,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 +194,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 +209,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 +231,8 @@ proc open*(connection, user, password, database: string): TDbConn {.
|
||||
db_mysql.close(result)
|
||||
dbError(errmsg)
|
||||
|
||||
proc setEncoding*(connection: TDbConn, encoding: string): bool {.
|
||||
proc setEncoding*(connection: DbConn, encoding: string): bool {.
|
||||
tags: [FDb].} =
|
||||
## sets the encoding of a database connection, returns true for
|
||||
## success, false for failure.
|
||||
result = mysql.set_character_set(connection, encoding) == 0
|
||||
result = mysql.set_character_set(connection, encoding) == 0
|
||||
|
||||
@@ -13,28 +13,30 @@
|
||||
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.
|
||||
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 +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)):
|
||||
@@ -69,7 +71,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 +81,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 +91,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 +100,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 +112,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 +120,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 +135,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 +148,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 +159,44 @@ iterator fastRows*(db: TDbConn, stmtName: TSqlPrepared,
|
||||
yield result
|
||||
pqClear(res)
|
||||
|
||||
proc getRow*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): TRow {.tags: [FReadDB].} =
|
||||
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 +204,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 +226,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 +237,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 +263,8 @@ proc open*(connection, user, password, database: string): TDbConn {.
|
||||
result = pqsetdbLogin(nil, nil, nil, nil, database, user, password)
|
||||
if pqStatus(result) != CONNECTION_OK: dbError(result) # result = nil
|
||||
|
||||
proc setEncoding*(connection: TDbConn, encoding: string): bool {.
|
||||
proc setEncoding*(connection: DbConn, encoding: string): bool {.
|
||||
tags: [FDb].} =
|
||||
## sets the encoding of a database connection, returns true for
|
||||
## success, false for failure.
|
||||
return pqsetClientEncoding(connection, encoding) == 0
|
||||
return pqsetClientEncoding(connection, encoding) == 0
|
||||
|
||||
@@ -13,27 +13,28 @@
|
||||
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.
|
||||
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 +56,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 +66,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 +75,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 +109,10 @@ 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].} =
|
||||
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 +120,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 +149,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 +163,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 +172,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 +180,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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -15,11 +15,12 @@
|
||||
import openssl, strutils, os
|
||||
|
||||
type
|
||||
TSecureSocket* = object
|
||||
SecureSocket* = object
|
||||
ssl: SslPtr
|
||||
bio: BIO
|
||||
{.deprecated: [TSecureSocket: SecureSocket].}
|
||||
|
||||
proc connect*(sock: var TSecureSocket, address: string,
|
||||
proc connect*(sock: var SecureSocket, address: string,
|
||||
port: int): int =
|
||||
## Connects to the specified `address` on the specified `port`.
|
||||
## Returns the result of the certificate validation.
|
||||
@@ -52,7 +53,7 @@ proc connect*(sock: var TSecureSocket, address: string,
|
||||
|
||||
result = SSL_get_verify_result(sock.ssl)
|
||||
|
||||
proc recvLine*(sock: TSecureSocket, line: var TaintedString): bool =
|
||||
proc recvLine*(sock: SecureSocket, line: var TaintedString): bool =
|
||||
## Acts in a similar fashion to the `recvLine` in the sockets module.
|
||||
## Returns false when no data is available to be read.
|
||||
## `Line` must be initialized and not nil!
|
||||
@@ -71,19 +72,19 @@ proc recvLine*(sock: TSecureSocket, line: var TaintedString): bool =
|
||||
add(line.string, c)
|
||||
|
||||
|
||||
proc send*(sock: TSecureSocket, data: string) =
|
||||
proc send*(sock: SecureSocket, data: string) =
|
||||
## Writes `data` to the socket.
|
||||
if BIO_write(sock.bio, data, data.len.cint) <= 0:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc close*(sock: TSecureSocket) =
|
||||
proc close*(sock: SecureSocket) =
|
||||
## Closes the socket
|
||||
if BIO_free(sock.bio) <= 0:
|
||||
ERR_print_errors_fp(stderr)
|
||||
raiseOSError(osLastError())
|
||||
|
||||
when not defined(testing) and isMainModule:
|
||||
var s: TSecureSocket
|
||||
var s: SecureSocket
|
||||
echo connect(s, "smtp.gmail.com", 465)
|
||||
|
||||
#var buffer: array[0..255, char]
|
||||
|
||||
@@ -13,18 +13,18 @@ import
|
||||
streams, libzip, times, os, strutils
|
||||
|
||||
type
|
||||
TZipArchive* = object of RootObj ## represents a zip archive
|
||||
ZipArchive* = object of RootObj ## represents a zip archive
|
||||
mode: FileMode
|
||||
w: PZip
|
||||
{.deprecated: [TZipArchive: ZipArchive].}
|
||||
|
||||
|
||||
proc zipError(z: var TZipArchive) =
|
||||
proc zipError(z: var ZipArchive) =
|
||||
var e: ref IOError
|
||||
new(e)
|
||||
e.msg = $zip_strerror(z.w)
|
||||
raise e
|
||||
|
||||
proc open*(z: var TZipArchive, filename: string, mode: FileMode = fmRead): bool =
|
||||
proc open*(z: var ZipArchive, filename: string, mode: FileMode = fmRead): bool =
|
||||
## Opens a zip file for reading, writing or appending. All file modes are
|
||||
## supported. Returns true iff successful, false otherwise.
|
||||
var err, flags: int32
|
||||
@@ -38,11 +38,11 @@ proc open*(z: var TZipArchive, filename: string, mode: FileMode = fmRead): bool
|
||||
z.mode = mode
|
||||
result = z.w != nil
|
||||
|
||||
proc close*(z: var TZipArchive) =
|
||||
proc close*(z: var ZipArchive) =
|
||||
## Closes a zip file.
|
||||
zip_close(z.w)
|
||||
|
||||
proc createDir*(z: var TZipArchive, dir: string) =
|
||||
proc createDir*(z: var ZipArchive, dir: string) =
|
||||
## Creates a directory within the `z` archive. This does not fail if the
|
||||
## directory already exists. Note that for adding a file like
|
||||
## ``"path1/path2/filename"`` it is not necessary
|
||||
@@ -52,7 +52,7 @@ proc createDir*(z: var TZipArchive, dir: string) =
|
||||
discard zip_add_dir(z.w, dir)
|
||||
zip_error_clear(z.w)
|
||||
|
||||
proc addFile*(z: var TZipArchive, dest, src: string) =
|
||||
proc addFile*(z: var ZipArchive, dest, src: string) =
|
||||
## Adds the file `src` to the archive `z` with the name `dest`. `dest`
|
||||
## may contain a path that will be created.
|
||||
assert(z.mode != fmRead)
|
||||
@@ -67,13 +67,13 @@ proc addFile*(z: var TZipArchive, dest, src: string) =
|
||||
zip_source_free(zipsrc)
|
||||
zipError(z)
|
||||
|
||||
proc addFile*(z: var TZipArchive, file: string) =
|
||||
proc addFile*(z: var ZipArchive, file: string) =
|
||||
## A shortcut for ``addFile(z, file, file)``, i.e. the name of the source is
|
||||
## the name of the destination.
|
||||
addFile(z, file, file)
|
||||
|
||||
proc mySourceCallback(state, data: pointer, len: int,
|
||||
cmd: TZipSourceCmd): int {.cdecl.} =
|
||||
cmd: ZipSourceCmd): int {.cdecl.} =
|
||||
var src = cast[Stream](state)
|
||||
case cmd
|
||||
of ZIP_SOURCE_OPEN:
|
||||
@@ -86,7 +86,7 @@ proc mySourceCallback(state, data: pointer, len: int,
|
||||
zip_stat_init(stat)
|
||||
stat.size = high(int32)-1 # we don't know the size
|
||||
stat.mtime = getTime()
|
||||
result = sizeof(TZipStat)
|
||||
result = sizeof(ZipStat)
|
||||
of ZIP_SOURCE_ERROR:
|
||||
var err = cast[ptr array[0..1, cint]](data)
|
||||
err[0] = ZIP_ER_INTERNAL
|
||||
@@ -95,7 +95,7 @@ proc mySourceCallback(state, data: pointer, len: int,
|
||||
of constZIP_SOURCE_FREE: GC_unref(src)
|
||||
else: assert(false)
|
||||
|
||||
proc addFile*(z: var TZipArchive, dest: string, src: Stream) =
|
||||
proc addFile*(z: var ZipArchive, dest: string, src: Stream) =
|
||||
## Adds a file named with `dest` to the archive `z`. `dest`
|
||||
## may contain a path. The file's content is read from the `src` stream.
|
||||
assert(z.mode != fmRead)
|
||||
@@ -134,7 +134,7 @@ proc newZipFileStream(f: PZipFile): PZipFileStream =
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
proc getStream*(z: var TZipArchive, filename: string): PZipFileStream =
|
||||
proc getStream*(z: var ZipArchive, filename: string): PZipFileStream =
|
||||
## returns a stream that can be used to read the file named `filename`
|
||||
## from the archive `z`. Returns nil in case of an error.
|
||||
## The returned stream does not support the `setPosition`, `getPosition`,
|
||||
@@ -142,7 +142,7 @@ proc getStream*(z: var TZipArchive, filename: string): PZipFileStream =
|
||||
var x = zip_fopen(z.w, filename, 0'i32)
|
||||
if x != nil: result = newZipFileStream(x)
|
||||
|
||||
iterator walkFiles*(z: var TZipArchive): string =
|
||||
iterator walkFiles*(z: var ZipArchive): string =
|
||||
## walks over all files in the archive `z` and returns the filename
|
||||
## (including the path).
|
||||
var i = 0'i32
|
||||
@@ -152,7 +152,7 @@ iterator walkFiles*(z: var TZipArchive): string =
|
||||
inc(i)
|
||||
|
||||
|
||||
proc extractFile*(z: var TZipArchive, srcFile: string, dest: Stream) =
|
||||
proc extractFile*(z: var ZipArchive, srcFile: string, dest: Stream) =
|
||||
## extracts a file from the zip archive `z` to the destination stream.
|
||||
var strm = getStream(z, srcFile)
|
||||
while true:
|
||||
@@ -162,13 +162,13 @@ proc extractFile*(z: var TZipArchive, srcFile: string, dest: Stream) =
|
||||
dest.flush()
|
||||
strm.close()
|
||||
|
||||
proc extractFile*(z: var TZipArchive, srcFile: string, dest: string) =
|
||||
proc extractFile*(z: var ZipArchive, srcFile: string, dest: string) =
|
||||
## extracts a file from the zip archive `z` to the destination filename.
|
||||
var file = newFileStream(dest, fmWrite)
|
||||
extractFile(z, srcFile, file)
|
||||
file.close()
|
||||
|
||||
proc extractAll*(z: var TZipArchive, dest: string) =
|
||||
proc extractAll*(z: var ZipArchive, dest: string) =
|
||||
## extracts all files from archive `z` to the destination directory.
|
||||
for file in walkFiles(z):
|
||||
if file.endsWith("/"):
|
||||
@@ -177,7 +177,7 @@ proc extractAll*(z: var TZipArchive, dest: string) =
|
||||
extractFile(z, file, dest / file)
|
||||
|
||||
when not defined(testing) and isMainModule:
|
||||
var zip: TZipArchive
|
||||
var zip: ZipArchive
|
||||
if not zip.open("nim-0.11.0.zip"):
|
||||
raise newException(IOError, "opening zip failed")
|
||||
zip.extractAll("test")
|
||||
|
||||
@@ -15,7 +15,7 @@ import
|
||||
strutils
|
||||
|
||||
type
|
||||
TTokenClass* = enum
|
||||
TokenClass* = enum
|
||||
gtEof, gtNone, gtWhitespace, gtDecNumber, gtBinNumber, gtHexNumber,
|
||||
gtOctNumber, gtFloatNumber, gtIdentifier, gtKeyword, gtStringLit,
|
||||
gtLongStringLit, gtCharLit, gtEscapeSequence, # escape sequence like \xff
|
||||
@@ -23,20 +23,22 @@ type
|
||||
gtTagStart, gtTagEnd, gtKey, gtValue, gtRawData, gtAssembler,
|
||||
gtPreprocessor, gtDirective, gtCommand, gtRule, gtHyperlink, gtLabel,
|
||||
gtReference, gtOther
|
||||
TGeneralTokenizer* = object of RootObj
|
||||
kind*: TTokenClass
|
||||
GeneralTokenizer* = object of RootObj
|
||||
kind*: TokenClass
|
||||
start*, length*: int
|
||||
buf: cstring
|
||||
pos: int
|
||||
state: TTokenClass
|
||||
state: TokenClass
|
||||
|
||||
TSourceLanguage* = enum
|
||||
SourceLanguage* = enum
|
||||
langNone, langNim, langNimrod, langCpp, langCsharp, langC, langJava
|
||||
{.deprecated: [TSourceLanguage: SourceLanguage, TTokenClass: TokenClass,
|
||||
TGeneralTokenizer: GeneralTokenizer].}
|
||||
|
||||
const
|
||||
sourceLanguageToStr*: array[TSourceLanguage, string] = ["none",
|
||||
sourceLanguageToStr*: array[SourceLanguage, string] = ["none",
|
||||
"Nim", "Nimrod", "C++", "C#", "C", "Java"]
|
||||
tokenClassToStr*: array[TTokenClass, string] = ["Eof", "None", "Whitespace",
|
||||
tokenClassToStr*: array[TokenClass, string] = ["Eof", "None", "Whitespace",
|
||||
"DecNumber", "BinNumber", "HexNumber", "OctNumber", "FloatNumber",
|
||||
"Identifier", "Keyword", "StringLit", "LongStringLit", "CharLit",
|
||||
"EscapeSequence", "Operator", "Punctuation", "Comment", "LongComment",
|
||||
@@ -58,29 +60,29 @@ const
|
||||
"template", "try", "tuple", "type", "using", "var", "when", "while", "with",
|
||||
"without", "xor", "yield"]
|
||||
|
||||
proc getSourceLanguage*(name: string): TSourceLanguage =
|
||||
for i in countup(succ(low(TSourceLanguage)), high(TSourceLanguage)):
|
||||
proc getSourceLanguage*(name: string): SourceLanguage =
|
||||
for i in countup(succ(low(SourceLanguage)), high(SourceLanguage)):
|
||||
if cmpIgnoreStyle(name, sourceLanguageToStr[i]) == 0:
|
||||
return i
|
||||
result = langNone
|
||||
|
||||
proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: cstring) =
|
||||
proc initGeneralTokenizer*(g: var GeneralTokenizer, buf: cstring) =
|
||||
g.buf = buf
|
||||
g.kind = low(TTokenClass)
|
||||
g.kind = low(TokenClass)
|
||||
g.start = 0
|
||||
g.length = 0
|
||||
g.state = low(TTokenClass)
|
||||
g.state = low(TokenClass)
|
||||
var pos = 0 # skip initial whitespace:
|
||||
while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos)
|
||||
g.pos = pos
|
||||
|
||||
proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: string) =
|
||||
proc initGeneralTokenizer*(g: var GeneralTokenizer, buf: string) =
|
||||
initGeneralTokenizer(g, cstring(buf))
|
||||
|
||||
proc deinitGeneralTokenizer*(g: var TGeneralTokenizer) =
|
||||
proc deinitGeneralTokenizer*(g: var GeneralTokenizer) =
|
||||
discard
|
||||
|
||||
proc nimGetKeyword(id: string): TTokenClass =
|
||||
proc nimGetKeyword(id: string): TokenClass =
|
||||
for k in nimKeywords:
|
||||
if cmpIgnoreStyle(id, k) == 0: return gtKeyword
|
||||
result = gtIdentifier
|
||||
@@ -92,7 +94,7 @@ proc nimGetKeyword(id: string): TTokenClass =
|
||||
else:
|
||||
result = gtIdentifier
|
||||
|
||||
proc nimNumberPostfix(g: var TGeneralTokenizer, position: int): int =
|
||||
proc nimNumberPostfix(g: var GeneralTokenizer, position: int): int =
|
||||
var pos = position
|
||||
if g.buf[pos] == '\'':
|
||||
inc(pos)
|
||||
@@ -110,7 +112,7 @@ proc nimNumberPostfix(g: var TGeneralTokenizer, position: int): int =
|
||||
discard
|
||||
result = pos
|
||||
|
||||
proc nimNumber(g: var TGeneralTokenizer, position: int): int =
|
||||
proc nimNumber(g: var GeneralTokenizer, position: int): int =
|
||||
const decChars = {'0'..'9', '_'}
|
||||
var pos = position
|
||||
g.kind = gtDecNumber
|
||||
@@ -130,7 +132,7 @@ const
|
||||
OpChars = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', '.',
|
||||
'|', '=', '%', '&', '$', '@', '~', ':', '\x80'..'\xFF'}
|
||||
|
||||
proc nimNextToken(g: var TGeneralTokenizer) =
|
||||
proc nimNextToken(g: var GeneralTokenizer) =
|
||||
const
|
||||
hexChars = {'0'..'9', 'A'..'F', 'a'..'f', '_'}
|
||||
octChars = {'0'..'7', '_'}
|
||||
@@ -278,7 +280,7 @@ proc nimNextToken(g: var TGeneralTokenizer) =
|
||||
assert false, "nimNextToken: produced an empty token"
|
||||
g.pos = pos
|
||||
|
||||
proc generalNumber(g: var TGeneralTokenizer, position: int): int =
|
||||
proc generalNumber(g: var GeneralTokenizer, position: int): int =
|
||||
const decChars = {'0'..'9'}
|
||||
var pos = position
|
||||
g.kind = gtDecNumber
|
||||
@@ -294,7 +296,7 @@ proc generalNumber(g: var TGeneralTokenizer, position: int): int =
|
||||
while g.buf[pos] in decChars: inc(pos)
|
||||
result = pos
|
||||
|
||||
proc generalStrLit(g: var TGeneralTokenizer, position: int): int =
|
||||
proc generalStrLit(g: var GeneralTokenizer, position: int): int =
|
||||
const
|
||||
decChars = {'0'..'9'}
|
||||
hexChars = {'0'..'9', 'A'..'F', 'a'..'f'}
|
||||
@@ -355,12 +357,13 @@ proc isKeywordIgnoreCase(x: openArray[string], y: string): int =
|
||||
result = - 1
|
||||
|
||||
type
|
||||
TTokenizerFlag = enum
|
||||
TokenizerFlag = enum
|
||||
hasPreprocessor, hasNestedComments
|
||||
TTokenizerFlags = set[TTokenizerFlag]
|
||||
TokenizerFlags = set[TokenizerFlag]
|
||||
{.deprecated: [TTokenizerFlag: TokenizerFlag, TTokenizerFlags: TokenizerFlags].}
|
||||
|
||||
proc clikeNextToken(g: var TGeneralTokenizer, keywords: openArray[string],
|
||||
flags: TTokenizerFlags) =
|
||||
proc clikeNextToken(g: var GeneralTokenizer, keywords: openArray[string],
|
||||
flags: TokenizerFlags) =
|
||||
const
|
||||
hexChars = {'0'..'9', 'A'..'F', 'a'..'f'}
|
||||
octChars = {'0'..'7'}
|
||||
@@ -493,7 +496,7 @@ proc clikeNextToken(g: var TGeneralTokenizer, keywords: openArray[string],
|
||||
assert false, "clikeNextToken: produced an empty token"
|
||||
g.pos = pos
|
||||
|
||||
proc cNextToken(g: var TGeneralTokenizer) =
|
||||
proc cNextToken(g: var GeneralTokenizer) =
|
||||
const
|
||||
keywords: array[0..36, string] = ["_Bool", "_Complex", "_Imaginary", "auto",
|
||||
"break", "case", "char", "const", "continue", "default", "do", "double",
|
||||
@@ -503,7 +506,7 @@ proc cNextToken(g: var TGeneralTokenizer) =
|
||||
"volatile", "while"]
|
||||
clikeNextToken(g, keywords, {hasPreprocessor})
|
||||
|
||||
proc cppNextToken(g: var TGeneralTokenizer) =
|
||||
proc cppNextToken(g: var GeneralTokenizer) =
|
||||
const
|
||||
keywords: array[0..47, string] = ["asm", "auto", "break", "case", "catch",
|
||||
"char", "class", "const", "continue", "default", "delete", "do", "double",
|
||||
@@ -514,7 +517,7 @@ proc cppNextToken(g: var TGeneralTokenizer) =
|
||||
"union", "unsigned", "virtual", "void", "volatile", "while"]
|
||||
clikeNextToken(g, keywords, {hasPreprocessor})
|
||||
|
||||
proc csharpNextToken(g: var TGeneralTokenizer) =
|
||||
proc csharpNextToken(g: var GeneralTokenizer) =
|
||||
const
|
||||
keywords: array[0..76, string] = ["abstract", "as", "base", "bool", "break",
|
||||
"byte", "case", "catch", "char", "checked", "class", "const", "continue",
|
||||
@@ -529,7 +532,7 @@ proc csharpNextToken(g: var TGeneralTokenizer) =
|
||||
"virtual", "void", "volatile", "while"]
|
||||
clikeNextToken(g, keywords, {hasPreprocessor})
|
||||
|
||||
proc javaNextToken(g: var TGeneralTokenizer) =
|
||||
proc javaNextToken(g: var GeneralTokenizer) =
|
||||
const
|
||||
keywords: array[0..52, string] = ["abstract", "assert", "boolean", "break",
|
||||
"byte", "case", "catch", "char", "class", "const", "continue", "default",
|
||||
@@ -541,7 +544,7 @@ proc javaNextToken(g: var TGeneralTokenizer) =
|
||||
"try", "void", "volatile", "while"]
|
||||
clikeNextToken(g, keywords, {})
|
||||
|
||||
proc getNextToken*(g: var TGeneralTokenizer, lang: TSourceLanguage) =
|
||||
proc getNextToken*(g: var GeneralTokenizer, lang: SourceLanguage) =
|
||||
case lang
|
||||
of langNone: assert false
|
||||
of langNim, langNimrod: nimNextToken(g)
|
||||
|
||||
@@ -15,7 +15,7 @@ import
|
||||
os, strutils, rstast
|
||||
|
||||
type
|
||||
TRstParseOption* = enum ## options for the RST parser
|
||||
RstParseOption* = enum ## options for the RST parser
|
||||
roSkipPounds, ## skip ``#`` at line beginning (documentation
|
||||
## embedded in Nim comments)
|
||||
roSupportSmilies, ## make the RST parser support smilies like ``:)``
|
||||
@@ -23,14 +23,14 @@ type
|
||||
## it for sandboxing)
|
||||
roSupportMarkdown ## support additional features of markdown
|
||||
|
||||
TRstParseOptions* = set[TRstParseOption]
|
||||
RstParseOptions* = set[RstParseOption]
|
||||
|
||||
TMsgClass* = enum
|
||||
MsgClass* = enum
|
||||
mcHint = "Hint",
|
||||
mcWarning = "Warning",
|
||||
mcError = "Error"
|
||||
|
||||
TMsgKind* = enum ## the possible messages
|
||||
MsgKind* = enum ## the possible messages
|
||||
meCannotOpenFile,
|
||||
meExpected,
|
||||
meGridTableNotImplemented,
|
||||
@@ -42,12 +42,14 @@ type
|
||||
mwUnsupportedLanguage,
|
||||
mwUnsupportedField
|
||||
|
||||
TMsgHandler* = proc (filename: string, line, col: int, msgKind: TMsgKind,
|
||||
MsgHandler* = proc (filename: string, line, col: int, msgKind: MsgKind,
|
||||
arg: string) {.nimcall.} ## what to do in case of an error
|
||||
TFindFileHandler* = proc (filename: string): string {.nimcall.}
|
||||
FindFileHandler* = proc (filename: string): string {.nimcall.}
|
||||
{.deprecated: [TRstParseOptions: RstParseOptions, TRstParseOption: RstParseOption,
|
||||
TMsgKind: MsgKind].}
|
||||
|
||||
const
|
||||
messages: array [TMsgKind, string] = [
|
||||
messages: array [MsgKind, string] = [
|
||||
meCannotOpenFile: "cannot open '$1'",
|
||||
meExpected: "'$1' expected",
|
||||
meGridTableNotImplemented: "grid table is not implemented",
|
||||
@@ -111,23 +113,24 @@ const
|
||||
}
|
||||
|
||||
type
|
||||
TTokType = enum
|
||||
TokType = enum
|
||||
tkEof, tkIndent, tkWhite, tkWord, tkAdornment, tkPunct, tkOther
|
||||
TToken = object # a RST token
|
||||
kind*: TTokType # the type of the token
|
||||
Token = object # a RST token
|
||||
kind*: TokType # the type of the token
|
||||
ival*: int # the indentation or parsed integer value
|
||||
symbol*: string # the parsed symbol as string
|
||||
line*, col*: int # line and column of the token
|
||||
|
||||
TTokenSeq = seq[TToken]
|
||||
TLexer = object of RootObj
|
||||
TokenSeq = seq[Token]
|
||||
Lexer = object of RootObj
|
||||
buf*: cstring
|
||||
bufpos*: int
|
||||
line*, col*, baseIndent*: int
|
||||
skipPounds*: bool
|
||||
{.deprecated: [TTokType: TokType, TToken: Token, TTokenSeq: TokenSeq,
|
||||
TLexer: Lexer].}
|
||||
|
||||
|
||||
proc getThing(L: var TLexer, tok: var TToken, s: set[char]) =
|
||||
proc getThing(L: var Lexer, tok: var Token, s: set[char]) =
|
||||
tok.kind = tkWord
|
||||
tok.line = L.line
|
||||
tok.col = L.col
|
||||
@@ -139,7 +142,7 @@ proc getThing(L: var TLexer, tok: var TToken, s: set[char]) =
|
||||
inc(L.col, pos - L.bufpos)
|
||||
L.bufpos = pos
|
||||
|
||||
proc getAdornment(L: var TLexer, tok: var TToken) =
|
||||
proc getAdornment(L: var Lexer, tok: var Token) =
|
||||
tok.kind = tkAdornment
|
||||
tok.line = L.line
|
||||
tok.col = L.col
|
||||
@@ -152,7 +155,7 @@ proc getAdornment(L: var TLexer, tok: var TToken) =
|
||||
inc(L.col, pos - L.bufpos)
|
||||
L.bufpos = pos
|
||||
|
||||
proc getIndentAux(L: var TLexer, start: int): int =
|
||||
proc getIndentAux(L: var Lexer, start: int): int =
|
||||
var pos = start
|
||||
var buf = L.buf
|
||||
# skip the newline (but include it in the token!)
|
||||
@@ -181,7 +184,7 @@ proc getIndentAux(L: var TLexer, start: int): int =
|
||||
result = getIndentAux(L, pos)
|
||||
L.bufpos = pos # no need to set back buf
|
||||
|
||||
proc getIndent(L: var TLexer, tok: var TToken) =
|
||||
proc getIndent(L: var Lexer, tok: var Token) =
|
||||
tok.col = 0
|
||||
tok.kind = tkIndent # skip the newline (but include it in the token!)
|
||||
tok.ival = getIndentAux(L, L.bufpos)
|
||||
@@ -191,7 +194,7 @@ proc getIndent(L: var TLexer, tok: var TToken) =
|
||||
tok.ival = max(tok.ival - L.baseIndent, 0)
|
||||
tok.symbol = "\n" & spaces(tok.ival)
|
||||
|
||||
proc rawGetTok(L: var TLexer, tok: var TToken) =
|
||||
proc rawGetTok(L: var Lexer, tok: var Token) =
|
||||
tok.symbol = ""
|
||||
tok.ival = 0
|
||||
var c = L.buf[L.bufpos]
|
||||
@@ -222,8 +225,8 @@ proc rawGetTok(L: var TLexer, tok: var TToken) =
|
||||
inc(L.col)
|
||||
tok.col = max(tok.col - L.baseIndent, 0)
|
||||
|
||||
proc getTokens(buffer: string, skipPounds: bool, tokens: var TTokenSeq): int =
|
||||
var L: TLexer
|
||||
proc getTokens(buffer: string, skipPounds: bool, tokens: var TokenSeq): int =
|
||||
var L: Lexer
|
||||
var length = len(tokens)
|
||||
L.buf = cstring(buffer)
|
||||
L.line = 0 # skip UTF-8 BOM
|
||||
@@ -253,31 +256,31 @@ proc getTokens(buffer: string, skipPounds: bool, tokens: var TTokenSeq): int =
|
||||
tokens[0].kind = tkIndent
|
||||
|
||||
type
|
||||
TLevelMap = array[char, int]
|
||||
TSubstitution = object
|
||||
LevelMap = array[char, int]
|
||||
Substitution = object
|
||||
key*: string
|
||||
value*: PRstNode
|
||||
|
||||
TSharedState = object
|
||||
options: TRstParseOptions # parsing options
|
||||
SharedState = object
|
||||
options: RstParseOptions # parsing options
|
||||
uLevel, oLevel: int # counters for the section levels
|
||||
subs: seq[TSubstitution] # substitutions
|
||||
refs: seq[TSubstitution] # references
|
||||
underlineToLevel: TLevelMap # Saves for each possible title adornment
|
||||
subs: seq[Substitution] # substitutions
|
||||
refs: seq[Substitution] # references
|
||||
underlineToLevel: LevelMap # Saves for each possible title adornment
|
||||
# character its level in the
|
||||
# current document.
|
||||
# This is for single underline adornments.
|
||||
overlineToLevel: TLevelMap # Saves for each possible title adornment
|
||||
overlineToLevel: LevelMap # Saves for each possible title adornment
|
||||
# character its level in the current
|
||||
# document.
|
||||
# This is for over-underline adornments.
|
||||
msgHandler: TMsgHandler # How to handle errors.
|
||||
findFile: TFindFileHandler # How to find files.
|
||||
msgHandler: MsgHandler # How to handle errors.
|
||||
findFile: FindFileHandler # How to find files.
|
||||
|
||||
PSharedState = ref TSharedState
|
||||
TRstParser = object of RootObj
|
||||
PSharedState = ref SharedState
|
||||
RstParser = object of RootObj
|
||||
idx*: int
|
||||
tok*: TTokenSeq
|
||||
tok*: TokenSeq
|
||||
s*: PSharedState
|
||||
indentStack*: seq[int]
|
||||
filename*: string
|
||||
@@ -285,8 +288,12 @@ type
|
||||
hasToc*: bool
|
||||
|
||||
EParseError* = object of ValueError
|
||||
{.deprecated: [TLevelMap: LevelMap, TSubstitution: Substitution,
|
||||
TSharedState: SharedState, TRstParser: RstParser,
|
||||
TMsgHandler: MsgHandler, TFindFileHandler: FindFileHandler,
|
||||
TMsgClass: MsgClass].}
|
||||
|
||||
proc whichMsgClass*(k: TMsgKind): TMsgClass =
|
||||
proc whichMsgClass*(k: MsgKind): MsgClass =
|
||||
## returns which message class `k` belongs to.
|
||||
case ($k)[1]
|
||||
of 'e', 'E': result = mcError
|
||||
@@ -294,7 +301,7 @@ proc whichMsgClass*(k: TMsgKind): TMsgClass =
|
||||
of 'h', 'H': result = mcHint
|
||||
else: assert false, "msgkind does not fit naming scheme"
|
||||
|
||||
proc defaultMsgHandler*(filename: string, line, col: int, msgkind: TMsgKind,
|
||||
proc defaultMsgHandler*(filename: string, line, col: int, msgkind: MsgKind,
|
||||
arg: string) {.procvar.} =
|
||||
let mc = msgkind.whichMsgClass
|
||||
let a = messages[msgkind] % arg
|
||||
@@ -306,9 +313,9 @@ proc defaultFindFile*(filename: string): string {.procvar.} =
|
||||
if existsFile(filename): result = filename
|
||||
else: result = ""
|
||||
|
||||
proc newSharedState(options: TRstParseOptions,
|
||||
findFile: TFindFileHandler,
|
||||
msgHandler: TMsgHandler): PSharedState =
|
||||
proc newSharedState(options: RstParseOptions,
|
||||
findFile: FindFileHandler,
|
||||
msgHandler: MsgHandler): PSharedState =
|
||||
new(result)
|
||||
result.subs = @[]
|
||||
result.refs = @[]
|
||||
@@ -316,34 +323,34 @@ proc newSharedState(options: TRstParseOptions,
|
||||
result.msgHandler = if not isNil(msgHandler): msgHandler else: defaultMsgHandler
|
||||
result.findFile = if not isNil(findFile): findFile else: defaultFindFile
|
||||
|
||||
proc rstMessage(p: TRstParser, msgKind: TMsgKind, arg: string) =
|
||||
proc rstMessage(p: RstParser, msgKind: MsgKind, arg: string) =
|
||||
p.s.msgHandler(p.filename, p.line + p.tok[p.idx].line,
|
||||
p.col + p.tok[p.idx].col, msgKind, arg)
|
||||
|
||||
proc rstMessage(p: TRstParser, msgKind: TMsgKind, arg: string, line, col: int) =
|
||||
proc rstMessage(p: RstParser, msgKind: MsgKind, arg: string, line, col: int) =
|
||||
p.s.msgHandler(p.filename, p.line + line,
|
||||
p.col + col, msgKind, arg)
|
||||
|
||||
proc rstMessage(p: TRstParser, msgKind: TMsgKind) =
|
||||
proc rstMessage(p: RstParser, msgKind: MsgKind) =
|
||||
p.s.msgHandler(p.filename, p.line + p.tok[p.idx].line,
|
||||
p.col + p.tok[p.idx].col, msgKind,
|
||||
p.tok[p.idx].symbol)
|
||||
|
||||
when false:
|
||||
proc corrupt(p: TRstParser) =
|
||||
proc corrupt(p: RstParser) =
|
||||
assert p.indentStack[0] == 0
|
||||
for i in 1 .. high(p.indentStack): assert p.indentStack[i] < 1_000
|
||||
|
||||
proc currInd(p: TRstParser): int =
|
||||
proc currInd(p: RstParser): int =
|
||||
result = p.indentStack[high(p.indentStack)]
|
||||
|
||||
proc pushInd(p: var TRstParser, ind: int) =
|
||||
proc pushInd(p: var RstParser, ind: int) =
|
||||
add(p.indentStack, ind)
|
||||
|
||||
proc popInd(p: var TRstParser) =
|
||||
proc popInd(p: var RstParser) =
|
||||
if len(p.indentStack) > 1: setLen(p.indentStack, len(p.indentStack) - 1)
|
||||
|
||||
proc initParser(p: var TRstParser, sharedState: PSharedState) =
|
||||
proc initParser(p: var RstParser, sharedState: PSharedState) =
|
||||
p.indentStack = @[0]
|
||||
p.tok = @[]
|
||||
p.idx = 0
|
||||
@@ -393,7 +400,7 @@ proc rstnodeToRefname(n: PRstNode): string =
|
||||
var b = false
|
||||
rstnodeToRefnameAux(n, result, b)
|
||||
|
||||
proc findSub(p: var TRstParser, n: PRstNode): int =
|
||||
proc findSub(p: var RstParser, n: PRstNode): int =
|
||||
var key = addNodes(n)
|
||||
# the spec says: if no exact match, try one without case distinction:
|
||||
for i in countup(0, high(p.s.subs)):
|
||||
@@ -404,7 +411,7 @@ proc findSub(p: var TRstParser, n: PRstNode): int =
|
||||
return i
|
||||
result = -1
|
||||
|
||||
proc setSub(p: var TRstParser, key: string, value: PRstNode) =
|
||||
proc setSub(p: var RstParser, key: string, value: PRstNode) =
|
||||
var length = len(p.s.subs)
|
||||
for i in countup(0, length - 1):
|
||||
if key == p.s.subs[i].key:
|
||||
@@ -414,7 +421,7 @@ proc setSub(p: var TRstParser, key: string, value: PRstNode) =
|
||||
p.s.subs[length].key = key
|
||||
p.s.subs[length].value = value
|
||||
|
||||
proc setRef(p: var TRstParser, key: string, value: PRstNode) =
|
||||
proc setRef(p: var RstParser, key: string, value: PRstNode) =
|
||||
var length = len(p.s.refs)
|
||||
for i in countup(0, length - 1):
|
||||
if key == p.s.refs[i].key:
|
||||
@@ -427,15 +434,15 @@ proc setRef(p: var TRstParser, key: string, value: PRstNode) =
|
||||
p.s.refs[length].key = key
|
||||
p.s.refs[length].value = value
|
||||
|
||||
proc findRef(p: var TRstParser, key: string): PRstNode =
|
||||
proc findRef(p: var RstParser, key: string): PRstNode =
|
||||
for i in countup(0, high(p.s.refs)):
|
||||
if key == p.s.refs[i].key:
|
||||
return p.s.refs[i].value
|
||||
|
||||
proc newLeaf(p: var TRstParser): PRstNode =
|
||||
proc newLeaf(p: var RstParser): PRstNode =
|
||||
result = newRstNode(rnLeaf, p.tok[p.idx].symbol)
|
||||
|
||||
proc getReferenceName(p: var TRstParser, endStr: string): PRstNode =
|
||||
proc getReferenceName(p: var RstParser, endStr: string): PRstNode =
|
||||
var res = newRstNode(rnInner)
|
||||
while true:
|
||||
case p.tok[p.idx].kind
|
||||
@@ -453,17 +460,17 @@ proc getReferenceName(p: var TRstParser, endStr: string): PRstNode =
|
||||
inc(p.idx)
|
||||
result = res
|
||||
|
||||
proc untilEol(p: var TRstParser): PRstNode =
|
||||
proc untilEol(p: var RstParser): PRstNode =
|
||||
result = newRstNode(rnInner)
|
||||
while not (p.tok[p.idx].kind in {tkIndent, tkEof}):
|
||||
add(result, newLeaf(p))
|
||||
inc(p.idx)
|
||||
|
||||
proc expect(p: var TRstParser, tok: string) =
|
||||
proc expect(p: var RstParser, tok: string) =
|
||||
if p.tok[p.idx].symbol == tok: inc(p.idx)
|
||||
else: rstMessage(p, meExpected, tok)
|
||||
|
||||
proc isInlineMarkupEnd(p: TRstParser, markup: string): bool =
|
||||
proc isInlineMarkupEnd(p: RstParser, markup: string): bool =
|
||||
result = p.tok[p.idx].symbol == markup
|
||||
if not result:
|
||||
return # Rule 3:
|
||||
@@ -480,7 +487,7 @@ proc isInlineMarkupEnd(p: TRstParser, markup: string): bool =
|
||||
if (markup != "``") and (p.tok[p.idx - 1].symbol == "\\"):
|
||||
result = false
|
||||
|
||||
proc isInlineMarkupStart(p: TRstParser, markup: string): bool =
|
||||
proc isInlineMarkupStart(p: RstParser, markup: string): bool =
|
||||
var d: char
|
||||
result = p.tok[p.idx].symbol == markup
|
||||
if not result:
|
||||
@@ -507,7 +514,7 @@ proc isInlineMarkupStart(p: TRstParser, markup: string): bool =
|
||||
else: d = '\0'
|
||||
if d != '\0': result = p.tok[p.idx + 1].symbol[0] != d
|
||||
|
||||
proc match(p: TRstParser, start: int, expr: string): bool =
|
||||
proc match(p: RstParser, start: int, expr: string): bool =
|
||||
# regular expressions are:
|
||||
# special char exact match
|
||||
# 'w' tkWord
|
||||
@@ -562,7 +569,7 @@ proc fixupEmbeddedRef(n, a, b: PRstNode) =
|
||||
for i in countup(0, sep - incr): add(a, n.sons[i])
|
||||
for i in countup(sep + 1, len(n) - 2): add(b, n.sons[i])
|
||||
|
||||
proc parsePostfix(p: var TRstParser, n: PRstNode): PRstNode =
|
||||
proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode =
|
||||
result = n
|
||||
if isInlineMarkupEnd(p, "_") or isInlineMarkupEnd(p, "__"):
|
||||
inc(p.idx)
|
||||
@@ -606,7 +613,7 @@ proc parsePostfix(p: var TRstParser, n: PRstNode): PRstNode =
|
||||
add(result, newRstNode(rnLeaf, p.tok[p.idx + 1].symbol))
|
||||
inc(p.idx, 3)
|
||||
|
||||
proc matchVerbatim(p: TRstParser, start: int, expr: string): int =
|
||||
proc matchVerbatim(p: RstParser, start: int, expr: string): int =
|
||||
result = start
|
||||
var j = 0
|
||||
while j < expr.len and result < p.tok.len and
|
||||
@@ -615,7 +622,7 @@ proc matchVerbatim(p: TRstParser, start: int, expr: string): int =
|
||||
inc result
|
||||
if j < expr.len: result = 0
|
||||
|
||||
proc parseSmiley(p: var TRstParser): PRstNode =
|
||||
proc parseSmiley(p: var RstParser): PRstNode =
|
||||
if p.tok[p.idx].symbol[0] notin SmileyStartChars: return
|
||||
for key, val in items(Smilies):
|
||||
let m = matchVerbatim(p, p.idx, key)
|
||||
@@ -631,12 +638,12 @@ when false:
|
||||
'$', '(', ')', '~', '_', '?', '+', '-', '=', '\\', '.', '&',
|
||||
'\128'..'\255'}
|
||||
|
||||
proc isUrl(p: TRstParser, i: int): bool =
|
||||
proc isUrl(p: RstParser, i: int): bool =
|
||||
result = (p.tok[i+1].symbol == ":") and (p.tok[i+2].symbol == "//") and
|
||||
(p.tok[i+3].kind == tkWord) and
|
||||
(p.tok[i].symbol in ["http", "https", "ftp", "telnet", "file"])
|
||||
|
||||
proc parseUrl(p: var TRstParser, father: PRstNode) =
|
||||
proc parseUrl(p: var RstParser, father: PRstNode) =
|
||||
#if p.tok[p.idx].symbol[strStart] == '<':
|
||||
if isUrl(p, p.idx):
|
||||
var n = newRstNode(rnStandaloneHyperlink)
|
||||
@@ -656,7 +663,7 @@ proc parseUrl(p: var TRstParser, father: PRstNode) =
|
||||
if p.tok[p.idx].symbol == "_": n = parsePostfix(p, n)
|
||||
add(father, n)
|
||||
|
||||
proc parseBackslash(p: var TRstParser, father: PRstNode) =
|
||||
proc parseBackslash(p: var RstParser, father: PRstNode) =
|
||||
assert(p.tok[p.idx].kind == tkPunct)
|
||||
if p.tok[p.idx].symbol == "\\\\":
|
||||
add(father, newRstNode(rnLeaf, "\\"))
|
||||
@@ -671,7 +678,7 @@ proc parseBackslash(p: var TRstParser, father: PRstNode) =
|
||||
inc(p.idx)
|
||||
|
||||
when false:
|
||||
proc parseAdhoc(p: var TRstParser, father: PRstNode, verbatim: bool) =
|
||||
proc parseAdhoc(p: var RstParser, father: PRstNode, verbatim: bool) =
|
||||
if not verbatim and isURL(p, p.idx):
|
||||
var n = newRstNode(rnStandaloneHyperlink)
|
||||
while true:
|
||||
@@ -694,7 +701,7 @@ when false:
|
||||
if p.tok[p.idx].symbol == "_": n = parsePostfix(p, n)
|
||||
add(father, n)
|
||||
|
||||
proc parseUntil(p: var TRstParser, father: PRstNode, postfix: string,
|
||||
proc parseUntil(p: var RstParser, father: PRstNode, postfix: string,
|
||||
interpretBackslash: bool) =
|
||||
let
|
||||
line = p.tok[p.idx].line
|
||||
@@ -725,7 +732,7 @@ proc parseUntil(p: var TRstParser, father: PRstNode, postfix: string,
|
||||
inc(p.idx)
|
||||
else: rstMessage(p, meExpected, postfix, line, col)
|
||||
|
||||
proc parseMarkdownCodeblock(p: var TRstParser): PRstNode =
|
||||
proc parseMarkdownCodeblock(p: var RstParser): PRstNode =
|
||||
var args = newRstNode(rnDirArg)
|
||||
if p.tok[p.idx].kind == tkWord:
|
||||
add(args, newLeaf(p))
|
||||
@@ -755,7 +762,7 @@ proc parseMarkdownCodeblock(p: var TRstParser): PRstNode =
|
||||
add(result, nil)
|
||||
add(result, lb)
|
||||
|
||||
proc parseInline(p: var TRstParser, father: PRstNode) =
|
||||
proc parseInline(p: var RstParser, father: PRstNode) =
|
||||
case p.tok[p.idx].kind
|
||||
of tkPunct:
|
||||
if isInlineMarkupStart(p, "***"):
|
||||
@@ -810,7 +817,7 @@ proc parseInline(p: var TRstParser, father: PRstNode) =
|
||||
inc(p.idx)
|
||||
else: discard
|
||||
|
||||
proc getDirective(p: var TRstParser): string =
|
||||
proc getDirective(p: var RstParser): string =
|
||||
if p.tok[p.idx].kind == tkWhite and p.tok[p.idx+1].kind == tkWord:
|
||||
var j = p.idx
|
||||
inc(p.idx)
|
||||
@@ -830,7 +837,7 @@ proc getDirective(p: var TRstParser): string =
|
||||
else:
|
||||
result = ""
|
||||
|
||||
proc parseComment(p: var TRstParser): PRstNode =
|
||||
proc parseComment(p: var RstParser): PRstNode =
|
||||
case p.tok[p.idx].kind
|
||||
of tkIndent, tkEof:
|
||||
if p.tok[p.idx].kind != tkEof and p.tok[p.idx + 1].kind == tkIndent:
|
||||
@@ -851,34 +858,35 @@ proc parseComment(p: var TRstParser): PRstNode =
|
||||
result = nil
|
||||
|
||||
type
|
||||
TDirKind = enum # must be ordered alphabetically!
|
||||
DirKind = enum # must be ordered alphabetically!
|
||||
dkNone, dkAuthor, dkAuthors, dkCode, dkCodeBlock, dkContainer, dkContents,
|
||||
dkFigure, dkImage, dkInclude, dkIndex, dkRaw, dkTitle
|
||||
{.deprecated: [TDirKind: DirKind].}
|
||||
|
||||
const
|
||||
DirIds: array[0..12, string] = ["", "author", "authors", "code",
|
||||
"code-block", "container", "contents", "figure", "image", "include",
|
||||
"index", "raw", "title"]
|
||||
|
||||
proc getDirKind(s: string): TDirKind =
|
||||
proc getDirKind(s: string): DirKind =
|
||||
let i = find(DirIds, s)
|
||||
if i >= 0: result = TDirKind(i)
|
||||
if i >= 0: result = DirKind(i)
|
||||
else: result = dkNone
|
||||
|
||||
proc parseLine(p: var TRstParser, father: PRstNode) =
|
||||
proc parseLine(p: var RstParser, father: PRstNode) =
|
||||
while true:
|
||||
case p.tok[p.idx].kind
|
||||
of tkWhite, tkWord, tkOther, tkPunct: parseInline(p, father)
|
||||
else: break
|
||||
|
||||
proc parseUntilNewline(p: var TRstParser, father: PRstNode) =
|
||||
proc parseUntilNewline(p: var RstParser, father: PRstNode) =
|
||||
while true:
|
||||
case p.tok[p.idx].kind
|
||||
of tkWhite, tkWord, tkAdornment, tkOther, tkPunct: parseInline(p, father)
|
||||
of tkEof, tkIndent: break
|
||||
|
||||
proc parseSection(p: var TRstParser, result: PRstNode) {.gcsafe.}
|
||||
proc parseField(p: var TRstParser): PRstNode =
|
||||
proc parseSection(p: var RstParser, result: PRstNode) {.gcsafe.}
|
||||
proc parseField(p: var RstParser): PRstNode =
|
||||
## Returns a parsed rnField node.
|
||||
##
|
||||
## rnField nodes have two children nodes, a rnFieldName and a rnFieldBody.
|
||||
@@ -897,7 +905,7 @@ proc parseField(p: var TRstParser): PRstNode =
|
||||
add(result, fieldname)
|
||||
add(result, fieldbody)
|
||||
|
||||
proc parseFields(p: var TRstParser): PRstNode =
|
||||
proc parseFields(p: var RstParser): PRstNode =
|
||||
## Parses fields for a section or directive block.
|
||||
##
|
||||
## This proc may return nil if the parsing doesn't find anything of value,
|
||||
@@ -947,8 +955,8 @@ proc getArgument(n: PRstNode): string =
|
||||
if n.sons[0] == nil: result = ""
|
||||
else: result = addNodes(n.sons[0])
|
||||
|
||||
proc parseDotDot(p: var TRstParser): PRstNode {.gcsafe.}
|
||||
proc parseLiteralBlock(p: var TRstParser): PRstNode =
|
||||
proc parseDotDot(p: var RstParser): PRstNode {.gcsafe.}
|
||||
proc parseLiteralBlock(p: var RstParser): PRstNode =
|
||||
result = newRstNode(rnLiteralBlock)
|
||||
var n = newRstNode(rnLeaf, "")
|
||||
if p.tok[p.idx].kind == tkIndent:
|
||||
@@ -974,13 +982,13 @@ proc parseLiteralBlock(p: var TRstParser): PRstNode =
|
||||
inc(p.idx)
|
||||
add(result, n)
|
||||
|
||||
proc getLevel(map: var TLevelMap, lvl: var int, c: char): int =
|
||||
proc getLevel(map: var LevelMap, lvl: var int, c: char): int =
|
||||
if map[c] == 0:
|
||||
inc(lvl)
|
||||
map[c] = lvl
|
||||
result = map[c]
|
||||
|
||||
proc tokenAfterNewline(p: TRstParser): int =
|
||||
proc tokenAfterNewline(p: RstParser): int =
|
||||
result = p.idx
|
||||
while true:
|
||||
case p.tok[result].kind
|
||||
@@ -991,28 +999,28 @@ proc tokenAfterNewline(p: TRstParser): int =
|
||||
break
|
||||
else: inc(result)
|
||||
|
||||
proc isLineBlock(p: TRstParser): bool =
|
||||
proc isLineBlock(p: RstParser): bool =
|
||||
var j = tokenAfterNewline(p)
|
||||
result = (p.tok[p.idx].col == p.tok[j].col) and (p.tok[j].symbol == "|") or
|
||||
(p.tok[j].col > p.tok[p.idx].col)
|
||||
|
||||
proc predNL(p: TRstParser): bool =
|
||||
proc predNL(p: RstParser): bool =
|
||||
result = true
|
||||
if p.idx > 0:
|
||||
result = p.tok[p.idx-1].kind == tkIndent and
|
||||
p.tok[p.idx-1].ival == currInd(p)
|
||||
|
||||
proc isDefList(p: TRstParser): bool =
|
||||
proc isDefList(p: RstParser): bool =
|
||||
var j = tokenAfterNewline(p)
|
||||
result = (p.tok[p.idx].col < p.tok[j].col) and
|
||||
(p.tok[j].kind in {tkWord, tkOther, tkPunct}) and
|
||||
(p.tok[j - 2].symbol != "::")
|
||||
|
||||
proc isOptionList(p: TRstParser): bool =
|
||||
proc isOptionList(p: RstParser): bool =
|
||||
result = match(p, p.idx, "-w") or match(p, p.idx, "--w") or
|
||||
match(p, p.idx, "/w") or match(p, p.idx, "//w")
|
||||
|
||||
proc whichSection(p: TRstParser): TRstNodeKind =
|
||||
proc whichSection(p: RstParser): RstNodeKind =
|
||||
case p.tok[p.idx].kind
|
||||
of tkAdornment:
|
||||
if match(p, p.idx + 1, "ii"): result = rnTransition
|
||||
@@ -1053,7 +1061,7 @@ proc whichSection(p: TRstParser): TRstNodeKind =
|
||||
else: result = rnParagraph
|
||||
else: result = rnLeaf
|
||||
|
||||
proc parseLineBlock(p: var TRstParser): PRstNode =
|
||||
proc parseLineBlock(p: var RstParser): PRstNode =
|
||||
result = nil
|
||||
if p.tok[p.idx + 1].kind == tkWhite:
|
||||
var col = p.tok[p.idx].col
|
||||
@@ -1072,7 +1080,7 @@ proc parseLineBlock(p: var TRstParser): PRstNode =
|
||||
break
|
||||
popInd(p)
|
||||
|
||||
proc parseParagraph(p: var TRstParser, result: PRstNode) =
|
||||
proc parseParagraph(p: var RstParser, result: PRstNode) =
|
||||
while true:
|
||||
case p.tok[p.idx].kind
|
||||
of tkIndent:
|
||||
@@ -1103,7 +1111,7 @@ proc parseParagraph(p: var TRstParser, result: PRstNode) =
|
||||
parseInline(p, result)
|
||||
else: break
|
||||
|
||||
proc parseHeadline(p: var TRstParser): PRstNode =
|
||||
proc parseHeadline(p: var RstParser): PRstNode =
|
||||
result = newRstNode(rnHeadline)
|
||||
parseUntilNewline(p, result)
|
||||
assert(p.tok[p.idx].kind == tkIndent)
|
||||
@@ -1113,12 +1121,13 @@ proc parseHeadline(p: var TRstParser): PRstNode =
|
||||
result.level = getLevel(p.s.underlineToLevel, p.s.uLevel, c)
|
||||
|
||||
type
|
||||
TIntSeq = seq[int]
|
||||
IntSeq = seq[int]
|
||||
{.deprecated: [TIntSeq: IntSeq].}
|
||||
|
||||
proc tokEnd(p: TRstParser): int =
|
||||
proc tokEnd(p: RstParser): int =
|
||||
result = p.tok[p.idx].col + len(p.tok[p.idx].symbol) - 1
|
||||
|
||||
proc getColumns(p: var TRstParser, cols: var TIntSeq) =
|
||||
proc getColumns(p: var RstParser, cols: var IntSeq) =
|
||||
var L = 0
|
||||
while true:
|
||||
inc(L)
|
||||
@@ -1133,15 +1142,15 @@ proc getColumns(p: var TRstParser, cols: var TIntSeq) =
|
||||
# last column has no limit:
|
||||
cols[L - 1] = 32000
|
||||
|
||||
proc parseDoc(p: var TRstParser): PRstNode {.gcsafe.}
|
||||
proc parseDoc(p: var RstParser): PRstNode {.gcsafe.}
|
||||
|
||||
proc parseSimpleTable(p: var TRstParser): PRstNode =
|
||||
proc parseSimpleTable(p: var RstParser): PRstNode =
|
||||
var
|
||||
cols: TIntSeq
|
||||
cols: IntSeq
|
||||
row: seq[string]
|
||||
i, last, line: int
|
||||
c: char
|
||||
q: TRstParser
|
||||
q: RstParser
|
||||
a, b: PRstNode
|
||||
result = newRstNode(rnTable)
|
||||
cols = @[]
|
||||
@@ -1188,13 +1197,13 @@ proc parseSimpleTable(p: var TRstParser): PRstNode =
|
||||
add(a, b)
|
||||
add(result, a)
|
||||
|
||||
proc parseTransition(p: var TRstParser): PRstNode =
|
||||
proc parseTransition(p: var RstParser): PRstNode =
|
||||
result = newRstNode(rnTransition)
|
||||
inc(p.idx)
|
||||
if p.tok[p.idx].kind == tkIndent: inc(p.idx)
|
||||
if p.tok[p.idx].kind == tkIndent: inc(p.idx)
|
||||
|
||||
proc parseOverline(p: var TRstParser): PRstNode =
|
||||
proc parseOverline(p: var RstParser): PRstNode =
|
||||
var c = p.tok[p.idx].symbol[0]
|
||||
inc(p.idx, 2)
|
||||
result = newRstNode(rnOverline)
|
||||
@@ -1213,7 +1222,7 @@ proc parseOverline(p: var TRstParser): PRstNode =
|
||||
inc(p.idx) # XXX: check?
|
||||
if p.tok[p.idx].kind == tkIndent: inc(p.idx)
|
||||
|
||||
proc parseBulletList(p: var TRstParser): PRstNode =
|
||||
proc parseBulletList(p: var RstParser): PRstNode =
|
||||
result = nil
|
||||
if p.tok[p.idx + 1].kind == tkWhite:
|
||||
var bullet = p.tok[p.idx].symbol
|
||||
@@ -1233,7 +1242,7 @@ proc parseBulletList(p: var TRstParser): PRstNode =
|
||||
break
|
||||
popInd(p)
|
||||
|
||||
proc parseOptionList(p: var TRstParser): PRstNode =
|
||||
proc parseOptionList(p: var RstParser): PRstNode =
|
||||
result = newRstNode(rnOptionList)
|
||||
while true:
|
||||
if isOptionList(p):
|
||||
@@ -1262,7 +1271,7 @@ proc parseOptionList(p: var TRstParser): PRstNode =
|
||||
else:
|
||||
break
|
||||
|
||||
proc parseDefinitionList(p: var TRstParser): PRstNode =
|
||||
proc parseDefinitionList(p: var RstParser): PRstNode =
|
||||
result = nil
|
||||
var j = tokenAfterNewline(p) - 1
|
||||
if (j >= 1) and (p.tok[j].kind == tkIndent) and
|
||||
@@ -1298,7 +1307,7 @@ proc parseDefinitionList(p: var TRstParser): PRstNode =
|
||||
break
|
||||
if len(result) == 0: result = nil
|
||||
|
||||
proc parseEnumList(p: var TRstParser): PRstNode =
|
||||
proc parseEnumList(p: var RstParser): PRstNode =
|
||||
const
|
||||
wildcards: array[0..2, string] = ["(e) ", "e) ", "e. "]
|
||||
wildpos: array[0..2, int] = [1, 0, 0]
|
||||
@@ -1328,11 +1337,11 @@ proc parseEnumList(p: var TRstParser): PRstNode =
|
||||
dec(p.idx, wildpos[w] + 3)
|
||||
result = nil
|
||||
|
||||
proc sonKind(father: PRstNode, i: int): TRstNodeKind =
|
||||
proc sonKind(father: PRstNode, i: int): RstNodeKind =
|
||||
result = rnLeaf
|
||||
if i < len(father): result = father.sons[i].kind
|
||||
|
||||
proc parseSection(p: var TRstParser, result: PRstNode) =
|
||||
proc parseSection(p: var RstParser, result: PRstNode) =
|
||||
while true:
|
||||
var leave = false
|
||||
assert(p.idx >= 0)
|
||||
@@ -1380,16 +1389,16 @@ proc parseSection(p: var TRstParser, result: PRstNode) =
|
||||
if sonKind(result, 0) == rnParagraph and sonKind(result, 1) != rnParagraph:
|
||||
result.sons[0].kind = rnInner
|
||||
|
||||
proc parseSectionWrapper(p: var TRstParser): PRstNode =
|
||||
proc parseSectionWrapper(p: var RstParser): PRstNode =
|
||||
result = newRstNode(rnInner)
|
||||
parseSection(p, result)
|
||||
while (result.kind == rnInner) and (len(result) == 1):
|
||||
result = result.sons[0]
|
||||
|
||||
proc `$`(t: TToken): string =
|
||||
proc `$`(t: Token): string =
|
||||
result = $t.kind & ' ' & (if isNil(t.symbol): "NIL" else: t.symbol)
|
||||
|
||||
proc parseDoc(p: var TRstParser): PRstNode =
|
||||
proc parseDoc(p: var RstParser): PRstNode =
|
||||
result = parseSectionWrapper(p)
|
||||
if p.tok[p.idx].kind != tkEof:
|
||||
when false:
|
||||
@@ -1403,12 +1412,14 @@ proc parseDoc(p: var TRstParser): PRstNode =
|
||||
rstMessage(p, meGeneralParseError)
|
||||
|
||||
type
|
||||
TDirFlag = enum
|
||||
DirFlag = enum
|
||||
hasArg, hasOptions, argIsFile, argIsWord
|
||||
TDirFlags = set[TDirFlag]
|
||||
TSectionParser = proc (p: var TRstParser): PRstNode {.nimcall.}
|
||||
DirFlags = set[DirFlag]
|
||||
SectionParser = proc (p: var RstParser): PRstNode {.nimcall.}
|
||||
{.deprecated: [TDirFlag: DirFlag, TDirFlags: DirFlags,
|
||||
TSectionParser: SectionParser].}
|
||||
|
||||
proc parseDirective(p: var TRstParser, flags: TDirFlags): PRstNode =
|
||||
proc parseDirective(p: var RstParser, flags: DirFlags): PRstNode =
|
||||
## Parses arguments and options for a directive block.
|
||||
##
|
||||
## A directive block will always have three sons: the arguments for the
|
||||
@@ -1446,11 +1457,11 @@ proc parseDirective(p: var TRstParser, flags: TDirFlags): PRstNode =
|
||||
options = parseFields(p)
|
||||
add(result, options)
|
||||
|
||||
proc indFollows(p: TRstParser): bool =
|
||||
proc indFollows(p: RstParser): bool =
|
||||
result = p.tok[p.idx].kind == tkIndent and p.tok[p.idx].ival > currInd(p)
|
||||
|
||||
proc parseDirective(p: var TRstParser, flags: TDirFlags,
|
||||
contentParser: TSectionParser): PRstNode =
|
||||
proc parseDirective(p: var RstParser, flags: DirFlags,
|
||||
contentParser: SectionParser): PRstNode =
|
||||
## Returns a generic rnDirective tree.
|
||||
##
|
||||
## The children are rnDirArg, rnFieldList and rnLineBlock. Any might be nil.
|
||||
@@ -1463,13 +1474,13 @@ proc parseDirective(p: var TRstParser, flags: TDirFlags,
|
||||
else:
|
||||
add(result, nil)
|
||||
|
||||
proc parseDirBody(p: var TRstParser, contentParser: TSectionParser): PRstNode =
|
||||
proc parseDirBody(p: var RstParser, contentParser: SectionParser): PRstNode =
|
||||
if indFollows(p):
|
||||
pushInd(p, p.tok[p.idx].ival)
|
||||
result = contentParser(p)
|
||||
popInd(p)
|
||||
|
||||
proc dirInclude(p: var TRstParser): PRstNode =
|
||||
proc dirInclude(p: var RstParser): PRstNode =
|
||||
#
|
||||
#The following options are recognized:
|
||||
#
|
||||
@@ -1498,7 +1509,7 @@ proc dirInclude(p: var TRstParser): PRstNode =
|
||||
result = newRstNode(rnLiteralBlock)
|
||||
add(result, newRstNode(rnLeaf, readFile(path)))
|
||||
else:
|
||||
var q: TRstParser
|
||||
var q: RstParser
|
||||
initParser(q, p.s)
|
||||
q.filename = filename
|
||||
q.col += getTokens(readFile(path), false, q.tok)
|
||||
@@ -1507,7 +1518,7 @@ proc dirInclude(p: var TRstParser): PRstNode =
|
||||
# InternalError("Too many binary zeros in include file")
|
||||
result = parseDoc(q)
|
||||
|
||||
proc dirCodeBlock(p: var TRstParser, nimrodExtension = false): PRstNode =
|
||||
proc dirCodeBlock(p: var RstParser, nimrodExtension = false): PRstNode =
|
||||
## Parses a code block.
|
||||
##
|
||||
## Code blocks are rnDirective trees with a `kind` of rnCodeBlock. See the
|
||||
@@ -1548,35 +1559,35 @@ proc dirCodeBlock(p: var TRstParser, nimrodExtension = false): PRstNode =
|
||||
|
||||
result.kind = rnCodeBlock
|
||||
|
||||
proc dirContainer(p: var TRstParser): PRstNode =
|
||||
proc dirContainer(p: var RstParser): PRstNode =
|
||||
result = parseDirective(p, {hasArg}, parseSectionWrapper)
|
||||
assert(result.kind == rnDirective)
|
||||
assert(len(result) == 3)
|
||||
result.kind = rnContainer
|
||||
|
||||
proc dirImage(p: var TRstParser): PRstNode =
|
||||
proc dirImage(p: var RstParser): PRstNode =
|
||||
result = parseDirective(p, {hasOptions, hasArg, argIsFile}, nil)
|
||||
result.kind = rnImage
|
||||
|
||||
proc dirFigure(p: var TRstParser): PRstNode =
|
||||
proc dirFigure(p: var RstParser): PRstNode =
|
||||
result = parseDirective(p, {hasOptions, hasArg, argIsFile},
|
||||
parseSectionWrapper)
|
||||
result.kind = rnFigure
|
||||
|
||||
proc dirTitle(p: var TRstParser): PRstNode =
|
||||
proc dirTitle(p: var RstParser): PRstNode =
|
||||
result = parseDirective(p, {hasArg}, nil)
|
||||
result.kind = rnTitle
|
||||
|
||||
proc dirContents(p: var TRstParser): PRstNode =
|
||||
proc dirContents(p: var RstParser): PRstNode =
|
||||
result = parseDirective(p, {hasArg}, nil)
|
||||
result.kind = rnContents
|
||||
|
||||
proc dirIndex(p: var TRstParser): PRstNode =
|
||||
proc dirIndex(p: var RstParser): PRstNode =
|
||||
result = parseDirective(p, {}, parseSectionWrapper)
|
||||
result.kind = rnIndex
|
||||
|
||||
proc dirRawAux(p: var TRstParser, result: var PRstNode, kind: TRstNodeKind,
|
||||
contentParser: TSectionParser) =
|
||||
proc dirRawAux(p: var RstParser, result: var PRstNode, kind: RstNodeKind,
|
||||
contentParser: SectionParser) =
|
||||
var filename = getFieldValue(result, "file")
|
||||
if filename.len > 0:
|
||||
var path = p.s.findFile(filename)
|
||||
@@ -1590,7 +1601,7 @@ proc dirRawAux(p: var TRstParser, result: var PRstNode, kind: TRstNodeKind,
|
||||
result.kind = kind
|
||||
add(result, parseDirBody(p, contentParser))
|
||||
|
||||
proc dirRaw(p: var TRstParser): PRstNode =
|
||||
proc dirRaw(p: var RstParser): PRstNode =
|
||||
#
|
||||
#The following options are recognized:
|
||||
#
|
||||
@@ -1610,7 +1621,7 @@ proc dirRaw(p: var TRstParser): PRstNode =
|
||||
else:
|
||||
dirRawAux(p, result, rnRaw, parseSectionWrapper)
|
||||
|
||||
proc parseDotDot(p: var TRstParser): PRstNode =
|
||||
proc parseDotDot(p: var RstParser): PRstNode =
|
||||
result = nil
|
||||
var col = p.tok[p.idx].col
|
||||
inc(p.idx)
|
||||
@@ -1667,7 +1678,7 @@ proc parseDotDot(p: var TRstParser): PRstNode =
|
||||
else:
|
||||
result = parseComment(p)
|
||||
|
||||
proc resolveSubs(p: var TRstParser, n: PRstNode): PRstNode =
|
||||
proc resolveSubs(p: var RstParser, n: PRstNode): PRstNode =
|
||||
result = n
|
||||
if n == nil: return
|
||||
case n.kind
|
||||
@@ -1696,10 +1707,10 @@ proc resolveSubs(p: var TRstParser, n: PRstNode): PRstNode =
|
||||
|
||||
proc rstParse*(text, filename: string,
|
||||
line, column: int, hasToc: var bool,
|
||||
options: TRstParseOptions,
|
||||
findFile: TFindFileHandler = nil,
|
||||
msgHandler: TMsgHandler = nil): PRstNode =
|
||||
var p: TRstParser
|
||||
options: RstParseOptions,
|
||||
findFile: FindFileHandler = nil,
|
||||
msgHandler: MsgHandler = nil): PRstNode =
|
||||
var p: RstParser
|
||||
initParser(p, newSharedState(options, findFile, msgHandler))
|
||||
p.filename = filename
|
||||
p.line = line
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
import strutils, json
|
||||
|
||||
type
|
||||
TRstNodeKind* = enum ## the possible node kinds of an PRstNode
|
||||
RstNodeKind* = enum ## the possible node kinds of an PRstNode
|
||||
rnInner, # an inner node or a root
|
||||
rnHeadline, # a headline
|
||||
rnOverline, # an over- and underlined headline
|
||||
@@ -62,24 +62,26 @@ type
|
||||
# leaf val
|
||||
|
||||
|
||||
PRstNode* = ref TRstNode ## an RST node
|
||||
TRstNodeSeq* = seq[PRstNode]
|
||||
TRstNode* {.acyclic, final.} = object ## an RST node's description
|
||||
kind*: TRstNodeKind ## the node's kind
|
||||
PRstNode* = ref RstNode ## an RST node
|
||||
RstNodeSeq* = seq[PRstNode]
|
||||
RstNode* {.acyclic, final.} = object ## an RST node's description
|
||||
kind*: RstNodeKind ## the node's kind
|
||||
text*: string ## valid for leafs in the AST; and the title of
|
||||
## the document or the section
|
||||
level*: int ## valid for some node kinds
|
||||
sons*: TRstNodeSeq ## the node's sons
|
||||
sons*: RstNodeSeq ## the node's sons
|
||||
{.deprecated: [TRstNodeKind: RstNodeKind, TRstNodeSeq: RstNodeSeq,
|
||||
TRstNode: RstNode].}
|
||||
|
||||
proc len*(n: PRstNode): int =
|
||||
result = len(n.sons)
|
||||
|
||||
proc newRstNode*(kind: TRstNodeKind): PRstNode =
|
||||
proc newRstNode*(kind: RstNodeKind): PRstNode =
|
||||
new(result)
|
||||
result.sons = @[]
|
||||
result.kind = kind
|
||||
|
||||
proc newRstNode*(kind: TRstNodeKind, s: string): PRstNode =
|
||||
proc newRstNode*(kind: RstNodeKind, s: string): PRstNode =
|
||||
result = newRstNode(kind)
|
||||
result.text = s
|
||||
|
||||
@@ -94,18 +96,19 @@ proc addIfNotNil*(father, son: PRstNode) =
|
||||
|
||||
|
||||
type
|
||||
TRenderContext {.pure.} = object
|
||||
RenderContext {.pure.} = object
|
||||
indent: int
|
||||
verbatim: int
|
||||
{.deprecated: [TRenderContext: RenderContext].}
|
||||
|
||||
proc renderRstToRst(d: var TRenderContext, n: PRstNode,
|
||||
proc renderRstToRst(d: var RenderContext, n: PRstNode,
|
||||
result: var string) {.gcsafe.}
|
||||
|
||||
proc renderRstSons(d: var TRenderContext, n: PRstNode, result: var string) =
|
||||
proc renderRstSons(d: var RenderContext, n: PRstNode, result: var string) =
|
||||
for i in countup(0, len(n) - 1):
|
||||
renderRstToRst(d, n.sons[i], result)
|
||||
|
||||
proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) =
|
||||
proc renderRstToRst(d: var RenderContext, n: PRstNode, result: var string) =
|
||||
# this is needed for the index generation; it may also be useful for
|
||||
# debugging, but most code is already debugged...
|
||||
const
|
||||
@@ -284,7 +287,7 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) =
|
||||
|
||||
proc renderRstToRst*(n: PRstNode, result: var string) =
|
||||
## renders `n` into its string representation and appends to `result`.
|
||||
var d: TRenderContext
|
||||
var d: RenderContext
|
||||
renderRstToRst(d, n, result)
|
||||
|
||||
proc renderRstToJsonNode(node: PRstNode): JsonNode =
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
## document you provide yourself, so it won't contain the usual ``<header>`` or
|
||||
## ``<body>`` parts.
|
||||
##
|
||||
## You can also create a ``TRstGenerator`` structure and populate it with the
|
||||
## You can also create a ``RstGenerator`` structure and populate it with the
|
||||
## other lower level methods to finally build complete documents. This requires
|
||||
## many options and tweaking, but you are not limited to snippets and can
|
||||
## generate `LaTeX documents <https://en.wikipedia.org/wiki/LaTeX>`_ too.
|
||||
@@ -31,29 +31,29 @@ const
|
||||
IndexExt* = ".idx"
|
||||
|
||||
type
|
||||
TOutputTarget* = enum ## which document type to generate
|
||||
OutputTarget* = enum ## which document type to generate
|
||||
outHtml, # output is HTML
|
||||
outLatex # output is Latex
|
||||
|
||||
TTocEntry = object
|
||||
TocEntry = object
|
||||
n*: PRstNode
|
||||
refname*, header*: string
|
||||
|
||||
TMetaEnum* = enum
|
||||
MetaEnum* = enum
|
||||
metaNone, metaTitle, metaSubtitle, metaAuthor, metaVersion
|
||||
|
||||
TRstGenerator* = object of RootObj
|
||||
target*: TOutputTarget
|
||||
RstGenerator* = object of RootObj
|
||||
target*: OutputTarget
|
||||
config*: StringTableRef
|
||||
splitAfter*: int # split too long entries in the TOC
|
||||
tocPart*: seq[TTocEntry]
|
||||
tocPart*: seq[TocEntry]
|
||||
hasToc*: bool
|
||||
theIndex: string # Contents of the index file to be dumped at the end.
|
||||
options*: TRstParseOptions
|
||||
findFile*: TFindFileHandler
|
||||
msgHandler*: TMsgHandler
|
||||
options*: RstParseOptions
|
||||
findFile*: FindFileHandler
|
||||
msgHandler*: MsgHandler
|
||||
filename*: string
|
||||
meta*: array[TMetaEnum, string]
|
||||
meta*: array[MetaEnum, string]
|
||||
currentSection: string ## \
|
||||
## Stores the empty string or the last headline/overline found in the rst
|
||||
## document, so it can be used as a prettier name for term index generation.
|
||||
@@ -61,14 +61,15 @@ type
|
||||
## Keeps count of same text index terms to generate different identifiers
|
||||
## for hyperlinks. See renderIndexTerm proc for details.
|
||||
|
||||
PDoc = var TRstGenerator ## Alias to type less.
|
||||
PDoc = var RstGenerator ## Alias to type less.
|
||||
|
||||
CodeBlockParams = object ## Stores code block params.
|
||||
numberLines: bool ## True if the renderer has to show line numbers.
|
||||
startLine: int ## The starting line of the code block, by default 1.
|
||||
langStr: string ## Input string used to specify the language.
|
||||
lang: TSourceLanguage ## Type of highlighting, by default none.
|
||||
|
||||
lang: SourceLanguage ## Type of highlighting, by default none.
|
||||
{.deprecated: [TRstGenerator: RstGenerator, TTocEntry: TocEntry,
|
||||
TOutputTarget: OutputTarget, TMetaEnum: MetaEnum].}
|
||||
|
||||
proc init(p: var CodeBlockParams) =
|
||||
## Default initialisation of CodeBlockParams to sane values.
|
||||
@@ -76,14 +77,14 @@ proc init(p: var CodeBlockParams) =
|
||||
p.lang = langNone
|
||||
p.langStr = ""
|
||||
|
||||
proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget,
|
||||
proc initRstGenerator*(g: var RstGenerator, target: OutputTarget,
|
||||
config: StringTableRef, filename: string,
|
||||
options: TRstParseOptions,
|
||||
findFile: TFindFileHandler=nil,
|
||||
msgHandler: TMsgHandler=nil) =
|
||||
## Initializes a ``TRstGenerator``.
|
||||
options: RstParseOptions,
|
||||
findFile: FindFileHandler=nil,
|
||||
msgHandler: MsgHandler=nil) =
|
||||
## Initializes a ``RstGenerator``.
|
||||
##
|
||||
## You need to call this before using a ``TRstGenerator`` with any other
|
||||
## You need to call this before using a ``RstGenerator`` with any other
|
||||
## procs in this module. Pass a non ``nil`` ``StringTableRef`` value as
|
||||
## `config` with parameters used by the HTML output generator. If you don't
|
||||
## know what to use, pass the results of the `defaultConfig()
|
||||
@@ -96,7 +97,7 @@ proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget,
|
||||
## filename``. This default title can be overriden by the embedded rst, but
|
||||
## it helps to prettify the generated index if no title is found.
|
||||
##
|
||||
## The ``TRstParseOptions``, ``TFindFileHandler`` and ``TMsgHandler`` types
|
||||
## The ``RstParseOptions``, ``FindFileHandler`` and ``MsgHandler`` types
|
||||
## are defined in the the `packages/docutils/rst module <rst.html>`_.
|
||||
## ``options`` selects the behaviour of the rst parser.
|
||||
##
|
||||
@@ -120,7 +121,7 @@ proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget,
|
||||
##
|
||||
## import packages/docutils/rstgen
|
||||
##
|
||||
## var gen: TRstGenerator
|
||||
## var gen: RstGenerator
|
||||
## gen.initRstGenerator(outHtml, defaultConfig(), "filename", {})
|
||||
g.config = config
|
||||
g.target = target
|
||||
@@ -141,7 +142,7 @@ proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget,
|
||||
if s != "": g.splitAfter = parseInt(s)
|
||||
for i in low(g.meta)..high(g.meta): g.meta[i] = ""
|
||||
|
||||
proc writeIndexFile*(g: var TRstGenerator, outfile: string) =
|
||||
proc writeIndexFile*(g: var RstGenerator, outfile: string) =
|
||||
## Writes the current index buffer to the specified output file.
|
||||
##
|
||||
## You previously need to add entries to the index with the `setIndexTerm()
|
||||
@@ -183,7 +184,7 @@ proc addTexChar(dest: var string, c: char) =
|
||||
|
||||
var splitter*: string = "<wbr />"
|
||||
|
||||
proc escChar*(target: TOutputTarget, dest: var string, c: char) {.inline.} =
|
||||
proc escChar*(target: OutputTarget, dest: var string, c: char) {.inline.} =
|
||||
case target
|
||||
of outHtml: addXmlChar(dest, c)
|
||||
of outLatex: addTexChar(dest, c)
|
||||
@@ -200,7 +201,7 @@ proc nextSplitPoint*(s: string, start: int): int =
|
||||
inc(result)
|
||||
dec(result) # last valid index
|
||||
|
||||
proc esc*(target: TOutputTarget, s: string, splitAfter = -1): string =
|
||||
proc esc*(target: OutputTarget, s: string, splitAfter = -1): string =
|
||||
result = ""
|
||||
if splitAfter >= 0:
|
||||
var partLen = 0
|
||||
@@ -217,16 +218,16 @@ proc esc*(target: TOutputTarget, s: string, splitAfter = -1): string =
|
||||
for i in countup(0, len(s) - 1): escChar(target, result, s[i])
|
||||
|
||||
|
||||
proc disp(target: TOutputTarget, xml, tex: string): string =
|
||||
proc disp(target: OutputTarget, xml, tex: string): string =
|
||||
if target != outLatex: result = xml
|
||||
else: result = tex
|
||||
|
||||
proc dispF(target: TOutputTarget, xml, tex: string,
|
||||
proc dispF(target: OutputTarget, xml, tex: string,
|
||||
args: varargs[string]): string =
|
||||
if target != outLatex: result = xml % args
|
||||
else: result = tex % args
|
||||
|
||||
proc dispA(target: TOutputTarget, dest: var string,
|
||||
proc dispA(target: OutputTarget, dest: var string,
|
||||
xml, tex: string, args: varargs[string]) =
|
||||
if target != outLatex: addf(dest, xml, args)
|
||||
else: addf(dest, tex, args)
|
||||
@@ -234,10 +235,10 @@ proc dispA(target: TOutputTarget, dest: var string,
|
||||
proc `or`(x, y: string): string {.inline.} =
|
||||
result = if x.isNil: y else: x
|
||||
|
||||
proc renderRstToOut*(d: var TRstGenerator, n: PRstNode, result: var string)
|
||||
proc renderRstToOut*(d: var RstGenerator, n: PRstNode, result: var string)
|
||||
## Writes into ``result`` the rst ast ``n`` using the ``d`` configuration.
|
||||
##
|
||||
## Before using this proc you need to initialise a ``TRstGenerator`` with
|
||||
## Before using this proc you need to initialise a ``RstGenerator`` with
|
||||
## ``initRstGenerator`` and parse a rst file with ``rstParse`` from the
|
||||
## `packages/docutils/rst module <rst.html>`_. Example:
|
||||
##
|
||||
@@ -277,7 +278,7 @@ proc unquoteIndexColumn(text: string): string =
|
||||
## Returns the unquoted version generated by ``quoteIndexColumn``.
|
||||
result = text.replace("\\t", "\t").replace("\\n", "\n").replace("\\\\", "\\")
|
||||
|
||||
proc setIndexTerm*(d: var TRstGenerator, id, term: string,
|
||||
proc setIndexTerm*(d: var RstGenerator, id, term: string,
|
||||
linkTitle, linkDesc = "") =
|
||||
## Adds a `term` to the index using the specified hyperlink identifier.
|
||||
##
|
||||
@@ -351,30 +352,30 @@ proc renderIndexTerm*(d: PDoc, n: PRstNode, result: var string) =
|
||||
[id, term])
|
||||
|
||||
type
|
||||
TIndexEntry = object
|
||||
IndexEntry = object
|
||||
keyword: string
|
||||
link: string
|
||||
linkTitle: string ## If not nil, contains a prettier text for the href
|
||||
linkDesc: string ## If not nil, the title attribute of the final href
|
||||
|
||||
TIndexedDocs = Table[TIndexEntry, seq[TIndexEntry]] ## \
|
||||
IndexedDocs = Table[IndexEntry, seq[IndexEntry]] ## \
|
||||
## Contains the index sequences for doc types.
|
||||
##
|
||||
## The key is a *fake* TIndexEntry which will contain the title of the
|
||||
## The key is a *fake* IndexEntry which will contain the title of the
|
||||
## document in the `keyword` field and `link` will contain the html
|
||||
## filename for the document. `linkTitle` and `linkDesc` will be nil.
|
||||
##
|
||||
## The value indexed by this TIndexEntry is a sequence with the real index
|
||||
## The value indexed by this IndexEntry is a sequence with the real index
|
||||
## entries found in the ``.idx`` file.
|
||||
{.deprecated: [TIndexEntry: IndexEntry, TIndexedDocs: IndexedDocs].}
|
||||
|
||||
|
||||
proc cmp(a, b: TIndexEntry): int =
|
||||
## Sorts two ``TIndexEntry`` first by `keyword` field, then by `link`.
|
||||
proc cmp(a, b: IndexEntry): int =
|
||||
## Sorts two ``IndexEntry`` first by `keyword` field, then by `link`.
|
||||
result = cmpIgnoreStyle(a.keyword, b.keyword)
|
||||
if result == 0:
|
||||
result = cmpIgnoreStyle(a.link, b.link)
|
||||
|
||||
proc hash(x: TIndexEntry): THash =
|
||||
proc hash(x: IndexEntry): Hash =
|
||||
## Returns the hash for the combined fields of the type.
|
||||
##
|
||||
## The hash is computed as the chained hash of the individual string hashes.
|
||||
@@ -385,7 +386,7 @@ proc hash(x: TIndexEntry): THash =
|
||||
result = result !& (x.linkDesc or "").hash
|
||||
result = !$result
|
||||
|
||||
proc `<-`(a: var TIndexEntry, b: TIndexEntry) =
|
||||
proc `<-`(a: var IndexEntry, b: IndexEntry) =
|
||||
shallowCopy a.keyword, b.keyword
|
||||
shallowCopy a.link, b.link
|
||||
if b.linkTitle.isNil: a.linkTitle = nil
|
||||
@@ -393,7 +394,7 @@ proc `<-`(a: var TIndexEntry, b: TIndexEntry) =
|
||||
if b.linkDesc.isNil: a.linkDesc = nil
|
||||
else: shallowCopy a.linkDesc, b.linkDesc
|
||||
|
||||
proc sortIndex(a: var openArray[TIndexEntry]) =
|
||||
proc sortIndex(a: var openArray[IndexEntry]) =
|
||||
# we use shellsort here; fast and simple
|
||||
let n = len(a)
|
||||
var h = 1
|
||||
@@ -403,7 +404,7 @@ proc sortIndex(a: var openArray[TIndexEntry]) =
|
||||
while true:
|
||||
h = h div 3
|
||||
for i in countup(h, n - 1):
|
||||
var v: TIndexEntry
|
||||
var v: IndexEntry
|
||||
v <- a[i]
|
||||
var j = i
|
||||
while cmp(a[j-h], v) >= 0:
|
||||
@@ -413,7 +414,7 @@ proc sortIndex(a: var openArray[TIndexEntry]) =
|
||||
a[j] <- v
|
||||
if h == 1: break
|
||||
|
||||
proc generateSymbolIndex(symbols: seq[TIndexEntry]): string =
|
||||
proc generateSymbolIndex(symbols: seq[IndexEntry]): string =
|
||||
result = ""
|
||||
var i = 0
|
||||
while i < symbols.len:
|
||||
@@ -466,7 +467,7 @@ proc indentToLevel(level: var int, newLevel: int): string =
|
||||
result = repeat("</ul>", level - newLevel)
|
||||
level = newLevel
|
||||
|
||||
proc generateDocumentationTOC(entries: seq[TIndexEntry]): string =
|
||||
proc generateDocumentationTOC(entries: seq[IndexEntry]): string =
|
||||
## Returns the sequence of index entries in an HTML hierarchical list.
|
||||
result = ""
|
||||
# Build a list of levels and extracted titles to make processing easier.
|
||||
@@ -507,12 +508,12 @@ proc generateDocumentationTOC(entries: seq[TIndexEntry]): string =
|
||||
assert(not titleRef.isNil,
|
||||
"Can't use this proc on an API index, docs always have a title entry")
|
||||
|
||||
proc generateDocumentationIndex(docs: TIndexedDocs): string =
|
||||
proc generateDocumentationIndex(docs: IndexedDocs): string =
|
||||
## Returns all the documentation TOCs in an HTML hierarchical list.
|
||||
result = ""
|
||||
|
||||
# Sort the titles to generate their toc in alphabetical order.
|
||||
var titles = toSeq(keys[TIndexEntry, seq[TIndexEntry]](docs))
|
||||
var titles = toSeq(keys[IndexEntry, seq[IndexEntry]](docs))
|
||||
sort(titles, cmp)
|
||||
|
||||
for title in titles:
|
||||
@@ -520,12 +521,12 @@ proc generateDocumentationIndex(docs: TIndexedDocs): string =
|
||||
result.add("<ul><li><a href=\"" &
|
||||
title.link & "\">" & title.keyword & "</a>\n" & tocList & "</ul>\n")
|
||||
|
||||
proc generateDocumentationJumps(docs: TIndexedDocs): string =
|
||||
proc generateDocumentationJumps(docs: IndexedDocs): string =
|
||||
## Returns a plain list of hyperlinks to documentation TOCs in HTML.
|
||||
result = "Documents: "
|
||||
|
||||
# Sort the titles to generate their toc in alphabetical order.
|
||||
var titles = toSeq(keys[TIndexEntry, seq[TIndexEntry]](docs))
|
||||
var titles = toSeq(keys[IndexEntry, seq[IndexEntry]](docs))
|
||||
sort(titles, cmp)
|
||||
|
||||
var chunks: seq[string] = @[]
|
||||
@@ -545,14 +546,14 @@ proc generateModuleJumps(modules: seq[string]): string =
|
||||
result.add(chunks.join(", ") & ".<br>")
|
||||
|
||||
proc readIndexDir(dir: string):
|
||||
tuple[modules: seq[string], symbols: seq[TIndexEntry], docs: TIndexedDocs] =
|
||||
## Walks `dir` reading ``.idx`` files converting them in TIndexEntry items.
|
||||
tuple[modules: seq[string], symbols: seq[IndexEntry], docs: IndexedDocs] =
|
||||
## Walks `dir` reading ``.idx`` files converting them in IndexEntry items.
|
||||
##
|
||||
## Returns the list of found module names, the list of free symbol entries
|
||||
## and the different documentation indexes. The list of modules is sorted.
|
||||
## See the documentation of ``mergeIndexes`` for details.
|
||||
result.modules = @[]
|
||||
result.docs = initTable[TIndexEntry, seq[TIndexEntry]](32)
|
||||
result.docs = initTable[IndexEntry, seq[IndexEntry]](32)
|
||||
newSeq(result.symbols, 15_000)
|
||||
setLen(result.symbols, 0)
|
||||
var L = 0
|
||||
@@ -560,8 +561,8 @@ proc readIndexDir(dir: string):
|
||||
for kind, path in walkDir(dir):
|
||||
if kind == pcFile and path.endsWith(IndexExt):
|
||||
var
|
||||
fileEntries: seq[TIndexEntry]
|
||||
title: TIndexEntry
|
||||
fileEntries: seq[IndexEntry]
|
||||
title: IndexEntry
|
||||
F = 0
|
||||
newSeq(fileEntries, 500)
|
||||
setLen(fileEntries, 0)
|
||||
@@ -662,7 +663,7 @@ proc mergeIndexes*(dir: string): string =
|
||||
proc stripTOCHTML(s: string): string =
|
||||
## Ugly quick hack to remove HTML tags from TOC titles.
|
||||
##
|
||||
## A TTocEntry.header field already contains rendered HTML tags. Instead of
|
||||
## A TocEntry.header field already contains rendered HTML tags. Instead of
|
||||
## implementing a proper version of renderRstToOut() which recursively
|
||||
## renders an rst tree to plain text, we simply remove text found between
|
||||
## angled brackets. Given the limited possibilities of rst inside TOC titles
|
||||
@@ -728,12 +729,12 @@ proc renderOverline(d: PDoc, n: PRstNode, result: var string) =
|
||||
rstnodeToRefname(n), tmp, $chr(n.level - 1 + ord('A'))])
|
||||
|
||||
|
||||
proc renderTocEntry(d: PDoc, e: TTocEntry, result: var string) =
|
||||
proc renderTocEntry(d: PDoc, e: TocEntry, result: var string) =
|
||||
dispA(d.target, result,
|
||||
"<li><a class=\"reference\" id=\"$1_toc\" href=\"#$1\">$2</a></li>\n",
|
||||
"\\item\\label{$1_toc} $2\\ref{$1}\n", [e.refname, e.header])
|
||||
|
||||
proc renderTocEntries*(d: var TRstGenerator, j: var int, lvl: int,
|
||||
proc renderTocEntries*(d: var RstGenerator, j: var int, lvl: int,
|
||||
result: var string) =
|
||||
var tmp = ""
|
||||
while j <= high(d.tocPart):
|
||||
@@ -878,7 +879,7 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) =
|
||||
d.msgHandler(d.filename, 1, 0, mwUnsupportedLanguage, params.langStr)
|
||||
for letter in m.text: escChar(d.target, result, letter)
|
||||
else:
|
||||
var g: TGeneralTokenizer
|
||||
var g: GeneralTokenizer
|
||||
initGeneralTokenizer(g, m.text)
|
||||
while true:
|
||||
getNextToken(g, params.lang)
|
||||
@@ -1214,7 +1215,7 @@ $content
|
||||
|
||||
# ---------- forum ---------------------------------------------------------
|
||||
|
||||
proc rstToHtml*(s: string, options: TRstParseOptions,
|
||||
proc rstToHtml*(s: string, options: RstParseOptions,
|
||||
config: StringTableRef): string =
|
||||
## Converts an input rst string into embeddable HTML.
|
||||
##
|
||||
@@ -1233,7 +1234,7 @@ proc rstToHtml*(s: string, options: TRstParseOptions,
|
||||
## # --> <em>Hello</em> <strong>world</strong>!
|
||||
##
|
||||
## If you need to allow the rst ``include`` directive or tweak the generated
|
||||
## output you have to create your own ``TRstGenerator`` with
|
||||
## output you have to create your own ``RstGenerator`` with
|
||||
## ``initRstGenerator`` and related procs.
|
||||
|
||||
proc myFindFile(filename: string): string =
|
||||
@@ -1241,7 +1242,7 @@ proc rstToHtml*(s: string, options: TRstParseOptions,
|
||||
result = ""
|
||||
|
||||
const filen = "input"
|
||||
var d: TRstGenerator
|
||||
var d: RstGenerator
|
||||
initRstGenerator(d, outHtml, config, filen, options, myFindFile,
|
||||
rst.defaultMsgHandler)
|
||||
var dummyHasToc = false
|
||||
|
||||
@@ -12,14 +12,15 @@
|
||||
# Get the platform-dependent flags.
|
||||
# Structure describing an inotify event.
|
||||
type
|
||||
Tinotify_event*{.pure, final, importc: "struct inotify_event",
|
||||
InotifyEvent*{.pure, final, importc: "struct inotify_event",
|
||||
header: "<sys/inotify.h>".} = object
|
||||
wd*{.importc: "wd".}: cint # Watch descriptor.
|
||||
mask*{.importc: "mask".}: uint32 # Watch mask.
|
||||
cookie*{.importc: "cookie".}: uint32 # Cookie to synchronize two events.
|
||||
len*{.importc: "len".}: uint32 # Length (including NULs) of name.
|
||||
name*{.importc: "name".}: char # Name.
|
||||
|
||||
{.deprecated: [Tinotify_event: InotifyEvent].}
|
||||
|
||||
# Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH.
|
||||
const
|
||||
IN_ACCESS* = 0x00000001 # File was accessed.
|
||||
@@ -69,4 +70,4 @@ proc inotify_add_watch*(fd: cint; name: cstring; mask: uint32): cint{.
|
||||
cdecl, importc: "inotify_add_watch", header: "<sys/inotify.h>".}
|
||||
# Remove the watch specified by WD from the inotify instance FD.
|
||||
proc inotify_rm_watch*(fd: cint; wd: cint): cint{.cdecl,
|
||||
importc: "inotify_rm_watch", header: "<sys/inotify.h>".}
|
||||
importc: "inotify_rm_watch", header: "<sys/inotify.h>".}
|
||||
|
||||
@@ -24,5 +24,5 @@ const
|
||||
|
||||
# fn should be of type proc (a2: pointer): void {.cdecl.}
|
||||
proc clone*(fn: pointer; child_stack: pointer; flags: cint;
|
||||
arg: pointer; ptid: ptr TPid; tls: pointer;
|
||||
ctid: ptr TPid): cint {.importc, header: "<sched.h>".}
|
||||
arg: pointer; ptid: ptr Pid; tls: pointer;
|
||||
ctid: ptr Pid): cint {.importc, header: "<sched.h>".}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,17 +12,18 @@ import posix
|
||||
|
||||
type
|
||||
Speed* = cuint
|
||||
Tcflag* = cuint
|
||||
Cflag* = cuint
|
||||
{.deprecated: [Tcflag: Cflag].}
|
||||
|
||||
const
|
||||
NCCS* = 32
|
||||
|
||||
type
|
||||
Termios* {.importc: "struct termios", header: "<termios.h>".} = object
|
||||
c_iflag*: Tcflag # input mode flags
|
||||
c_oflag*: Tcflag # output mode flags
|
||||
c_cflag*: Tcflag # control mode flags
|
||||
c_lflag*: Tcflag # local mode flags
|
||||
c_iflag*: Cflag # input mode flags
|
||||
c_oflag*: Cflag # output mode flags
|
||||
c_cflag*: Cflag # control mode flags
|
||||
c_lflag*: Cflag # local mode flags
|
||||
c_line*: cuchar # line discipline
|
||||
c_cc*: array[NCCS, cuchar] # control characters
|
||||
|
||||
@@ -258,4 +259,4 @@ proc tcFlow*(fd: cint; action: cint): cint {.importc: "tcflow",
|
||||
header: "<termios.h>".}
|
||||
# Get process group ID for session leader for controlling terminal FD.
|
||||
|
||||
proc tcGetSid*(fd: cint): TPid {.importc: "tcgetsid", header: "<termios.h>".}
|
||||
proc tcGetSid*(fd: cint): Pid {.importc: "tcgetsid", header: "<termios.h>".}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## var
|
||||
## a: TActorPool[int, void]
|
||||
## a: ActorPool[int, void]
|
||||
## createActorPool(a)
|
||||
## for i in 0 .. < 300:
|
||||
## a.spawn(i, proc (x: int) {.thread.} = echo x)
|
||||
@@ -30,75 +30,76 @@
|
||||
from os import sleep
|
||||
|
||||
type
|
||||
TTask*[TIn, TOut] = object{.pure, final.} ## a task
|
||||
when TOut isnot void:
|
||||
receiver*: ptr TChannel[TOut] ## the receiver channel of the response
|
||||
action*: proc (x: TIn): TOut {.thread.} ## action to execute;
|
||||
Task*[In, Out] = object{.pure, final.} ## a task
|
||||
when Out isnot void:
|
||||
receiver*: ptr Channel[Out] ## the receiver channel of the response
|
||||
action*: proc (x: In): Out {.thread.} ## action to execute;
|
||||
## sometimes useful
|
||||
shutDown*: bool ## set to tell an actor to shut-down
|
||||
data*: TIn ## the data to process
|
||||
data*: In ## the data to process
|
||||
|
||||
TActor[TIn, TOut] = object{.pure, final.}
|
||||
i: TChannel[TTask[TIn, TOut]]
|
||||
t: TThread[ptr TActor[TIn, TOut]]
|
||||
Actor[In, Out] = object{.pure, final.}
|
||||
i: Channel[Task[In, Out]]
|
||||
t: TThread[ptr Actor[In, Out]]
|
||||
|
||||
PActor*[TIn, TOut] = ptr TActor[TIn, TOut] ## an actor
|
||||
|
||||
proc spawn*[TIn, TOut](action: proc(
|
||||
self: PActor[TIn, TOut]){.thread.}): PActor[TIn, TOut] =
|
||||
PActor*[In, Out] = ptr Actor[In, Out] ## an actor
|
||||
{.deprecated: [TTask: Task, TActor: Actor].}
|
||||
|
||||
proc spawn*[In, Out](action: proc(
|
||||
self: PActor[In, Out]){.thread.}): PActor[In, Out] =
|
||||
## creates an actor; that is a thread with an inbox. The caller MUST call
|
||||
## ``join`` because that also frees the actor's associated resources.
|
||||
result = cast[PActor[TIn, TOut]](allocShared0(sizeof(result[])))
|
||||
result = cast[PActor[In, Out]](allocShared0(sizeof(result[])))
|
||||
open(result.i)
|
||||
createThread(result.t, action, result)
|
||||
|
||||
proc inbox*[TIn, TOut](self: PActor[TIn, TOut]): ptr TChannel[TIn] =
|
||||
proc inbox*[In, Out](self: PActor[In, Out]): ptr Channel[In] =
|
||||
## gets a pointer to the associated inbox of the actor `self`.
|
||||
result = addr(self.i)
|
||||
|
||||
proc running*[TIn, TOut](a: PActor[TIn, TOut]): bool =
|
||||
proc running*[In, Out](a: PActor[In, Out]): bool =
|
||||
## returns true if the actor `a` is running.
|
||||
result = running(a.t)
|
||||
|
||||
proc ready*[TIn, TOut](a: PActor[TIn, TOut]): bool =
|
||||
proc ready*[In, Out](a: PActor[In, Out]): bool =
|
||||
## returns true if the actor `a` is ready to process new messages.
|
||||
result = ready(a.i)
|
||||
|
||||
proc join*[TIn, TOut](a: PActor[TIn, TOut]) =
|
||||
proc join*[In, Out](a: PActor[In, Out]) =
|
||||
## joins an actor.
|
||||
joinThread(a.t)
|
||||
close(a.i)
|
||||
deallocShared(a)
|
||||
|
||||
proc recv*[TIn, TOut](a: PActor[TIn, TOut]): TTask[TIn, TOut] =
|
||||
proc recv*[In, Out](a: PActor[In, Out]): Task[In, Out] =
|
||||
## receives a task from `a`'s inbox.
|
||||
result = recv(a.i)
|
||||
|
||||
proc send*[TIn, TOut, X, Y](receiver: PActor[TIn, TOut], msg: TIn,
|
||||
proc send*[In, Out, X, Y](receiver: PActor[In, Out], msg: In,
|
||||
sender: PActor[X, Y]) =
|
||||
## sends a message to `a`'s inbox.
|
||||
var t: TTask[TIn, TOut]
|
||||
var t: Task[In, Out]
|
||||
t.receiver = addr(sender.i)
|
||||
shallowCopy(t.data, msg)
|
||||
send(receiver.i, t)
|
||||
|
||||
proc send*[TIn, TOut](receiver: PActor[TIn, TOut], msg: TIn,
|
||||
sender: ptr TChannel[TOut] = nil) =
|
||||
proc send*[In, Out](receiver: PActor[In, Out], msg: In,
|
||||
sender: ptr Channel[Out] = nil) =
|
||||
## sends a message to `receiver`'s inbox.
|
||||
var t: TTask[TIn, TOut]
|
||||
var t: Task[In, Out]
|
||||
t.receiver = sender
|
||||
shallowCopy(t.data, msg)
|
||||
send(receiver.i, t)
|
||||
|
||||
proc sendShutdown*[TIn, TOut](receiver: PActor[TIn, TOut]) =
|
||||
proc sendShutdown*[In, Out](receiver: PActor[In, Out]) =
|
||||
## send a shutdown message to `receiver`.
|
||||
var t: TTask[TIn, TOut]
|
||||
var t: Task[In, Out]
|
||||
t.shutdown = true
|
||||
send(receiver.i, t)
|
||||
|
||||
proc reply*[TIn, TOut](t: TTask[TIn, TOut], m: TOut) =
|
||||
proc reply*[In, Out](t: Task[In, Out], m: Out) =
|
||||
## sends a message to io's output message box.
|
||||
when TOut is void:
|
||||
when Out is void:
|
||||
{.error: "you cannot reply to a void outbox".}
|
||||
assert t.receiver != nil
|
||||
send(t.receiver[], m)
|
||||
@@ -107,34 +108,35 @@ proc reply*[TIn, TOut](t: TTask[TIn, TOut], m: TOut) =
|
||||
# ----------------- actor pools ----------------------------------------------
|
||||
|
||||
type
|
||||
TActorPool*[TIn, TOut] = object{.pure, final.} ## an actor pool
|
||||
actors: seq[PActor[TIn, TOut]]
|
||||
when TOut isnot void:
|
||||
outputs: TChannel[TOut]
|
||||
ActorPool*[In, Out] = object{.pure, final.} ## an actor pool
|
||||
actors: seq[PActor[In, Out]]
|
||||
when Out isnot void:
|
||||
outputs: Channel[Out]
|
||||
{.deprecated: [TActorPool: ActorPool].}
|
||||
|
||||
proc `^`*[T](f: ptr TChannel[T]): T =
|
||||
proc `^`*[T](f: ptr Channel[T]): T =
|
||||
## alias for 'recv'.
|
||||
result = recv(f[])
|
||||
|
||||
proc poolWorker[TIn, TOut](self: PActor[TIn, TOut]) {.thread.} =
|
||||
proc poolWorker[In, Out](self: PActor[In, Out]) {.thread.} =
|
||||
while true:
|
||||
var m = self.recv
|
||||
if m.shutDown: break
|
||||
when TOut is void:
|
||||
when Out is void:
|
||||
m.action(m.data)
|
||||
else:
|
||||
send(m.receiver[], m.action(m.data))
|
||||
#self.reply()
|
||||
|
||||
proc createActorPool*[TIn, TOut](a: var TActorPool[TIn, TOut], poolSize = 4) =
|
||||
proc createActorPool*[In, Out](a: var ActorPool[In, Out], poolSize = 4) =
|
||||
## creates an actor pool.
|
||||
newSeq(a.actors, poolSize)
|
||||
when TOut isnot void:
|
||||
when Out isnot void:
|
||||
open(a.outputs)
|
||||
for i in 0 .. < a.actors.len:
|
||||
a.actors[i] = spawn(poolWorker[TIn, TOut])
|
||||
a.actors[i] = spawn(poolWorker[In, Out])
|
||||
|
||||
proc sync*[TIn, TOut](a: var TActorPool[TIn, TOut], polling=50) =
|
||||
proc sync*[In, Out](a: var ActorPool[In, Out], polling=50) =
|
||||
## waits for every actor of `a` to finish with its work. Currently this is
|
||||
## implemented as polling every `polling` ms and has a slight chance
|
||||
## of failing since we check for every actor to be in `ready` state and not
|
||||
@@ -157,18 +159,18 @@ proc sync*[TIn, TOut](a: var TActorPool[TIn, TOut], polling=50) =
|
||||
if allReadyCount > 1: break
|
||||
sleep(polling)
|
||||
|
||||
proc terminate*[TIn, TOut](a: var TActorPool[TIn, TOut]) =
|
||||
proc terminate*[In, Out](a: var ActorPool[In, Out]) =
|
||||
## terminates each actor in the actor pool `a` and frees the
|
||||
## resources attached to `a`.
|
||||
var t: TTask[TIn, TOut]
|
||||
var t: Task[In, Out]
|
||||
t.shutdown = true
|
||||
for i in 0.. <a.actors.len: send(a.actors[i].i, t)
|
||||
for i in 0.. <a.actors.len: join(a.actors[i])
|
||||
when TOut isnot void:
|
||||
when Out isnot void:
|
||||
close(a.outputs)
|
||||
a.actors = nil
|
||||
|
||||
proc join*[TIn, TOut](a: var TActorPool[TIn, TOut]) =
|
||||
proc join*[In, Out](a: var ActorPool[In, Out]) =
|
||||
## short-cut for `sync` and then `terminate`.
|
||||
sync(a)
|
||||
terminate(a)
|
||||
@@ -202,28 +204,28 @@ template schedule =
|
||||
else:
|
||||
raise newException(DeadThreadError, "cannot send message; thread died")
|
||||
|
||||
proc spawn*[TIn, TOut](p: var TActorPool[TIn, TOut], input: TIn,
|
||||
action: proc (input: TIn): TOut {.thread.}
|
||||
): ptr TChannel[TOut] =
|
||||
proc spawn*[In, Out](p: var ActorPool[In, Out], input: In,
|
||||
action: proc (input: In): Out {.thread.}
|
||||
): ptr Channel[Out] =
|
||||
## uses the actor pool to run ``action(input)`` concurrently.
|
||||
## `spawn` is guaranteed to not block.
|
||||
var t: TTask[TIn, TOut]
|
||||
var t: Task[In, Out]
|
||||
setupTask()
|
||||
result = addr(p.outputs)
|
||||
t.receiver = result
|
||||
schedule()
|
||||
|
||||
proc spawn*[TIn](p: var TActorPool[TIn, void], input: TIn,
|
||||
action: proc (input: TIn) {.thread.}) =
|
||||
proc spawn*[In](p: var ActorPool[In, void], input: In,
|
||||
action: proc (input: In) {.thread.}) =
|
||||
## uses the actor pool to run ``action(input)`` concurrently.
|
||||
## `spawn` is guaranteed to not block.
|
||||
var t: TTask[TIn, void]
|
||||
var t: Task[In, void]
|
||||
setupTask()
|
||||
schedule()
|
||||
|
||||
when not defined(testing) and isMainModule:
|
||||
var
|
||||
a: TActorPool[int, void]
|
||||
a: ActorPool[int, void]
|
||||
createActorPool(a)
|
||||
for i in 0 .. < 300:
|
||||
a.spawn(i, proc (x: int) {.thread.} = echo x)
|
||||
|
||||
@@ -99,16 +99,13 @@ proc lowerBound*[T](a: openArray[T], key: T, cmp: proc(x,y: T): int {.closure.})
|
||||
## arr.insert(4, arr.lowerBound(4))
|
||||
## `after running the above arr is `[1,2,3,4,5,6,7,8,9]`
|
||||
result = a.low
|
||||
var pos = result
|
||||
var count, step: int
|
||||
count = a.high - a.low + 1
|
||||
var count = a.high - a.low + 1
|
||||
var step, pos: int
|
||||
while count != 0:
|
||||
pos = result
|
||||
step = count div 2
|
||||
pos += step
|
||||
pos = result + step
|
||||
if cmp(a[pos], key) < 0:
|
||||
pos.inc
|
||||
result = pos
|
||||
result = pos + 1
|
||||
count -= step + 1
|
||||
else:
|
||||
count = step
|
||||
@@ -331,3 +328,16 @@ proc prevPermutation*[T](x: var openarray[T]): bool {.discardable.} =
|
||||
swap x[i-1], x[j]
|
||||
|
||||
result = true
|
||||
|
||||
when isMainModule:
|
||||
# Tests for lowerBound
|
||||
var arr = @[1,2,3,5,6,7,8,9]
|
||||
assert arr.lowerBound(0) == 0
|
||||
assert arr.lowerBound(4) == 3
|
||||
assert arr.lowerBound(5) == 3
|
||||
assert arr.lowerBound(10) == 8
|
||||
arr = @[1,5,10]
|
||||
assert arr.lowerBound(4) == 1
|
||||
assert arr.lowerBound(5) == 1
|
||||
assert arr.lowerBound(6) == 2
|
||||
|
||||
|
||||
@@ -323,32 +323,34 @@ proc processTimers(p: PDispatcherBase) =
|
||||
when defined(windows) or defined(nimdoc):
|
||||
import winlean, sets, hashes
|
||||
type
|
||||
TCompletionKey = Dword
|
||||
CompletionKey = Dword
|
||||
|
||||
TCompletionData* = object
|
||||
fd*: TAsyncFD # TODO: Rename this.
|
||||
cb*: proc (fd: TAsyncFD, bytesTransferred: Dword,
|
||||
CompletionData* = object
|
||||
fd*: AsyncFD # TODO: Rename this.
|
||||
cb*: proc (fd: AsyncFD, bytesTransferred: Dword,
|
||||
errcode: OSErrorCode) {.closure,gcsafe.}
|
||||
|
||||
PDispatcher* = ref object of PDispatcherBase
|
||||
ioPort: THandle
|
||||
handles: HashSet[TAsyncFD]
|
||||
ioPort: Handle
|
||||
handles: HashSet[AsyncFD]
|
||||
|
||||
TCustomOverlapped = object of TOVERLAPPED
|
||||
data*: TCompletionData
|
||||
CustomOverlapped = object of TOVERLAPPED
|
||||
data*: CompletionData
|
||||
|
||||
PCustomOverlapped* = ref TCustomOverlapped
|
||||
PCustomOverlapped* = ref CustomOverlapped
|
||||
|
||||
TAsyncFD* = distinct int
|
||||
AsyncFD* = distinct int
|
||||
{.deprecated: [TCompletionKey: CompletionKey, TAsyncFD: AsyncFD,
|
||||
TCustomOverlapped: CustomOverlapped, TCompletionData: CompletionData].}
|
||||
|
||||
proc hash(x: TAsyncFD): THash {.borrow.}
|
||||
proc `==`*(x: TAsyncFD, y: TAsyncFD): bool {.borrow.}
|
||||
proc hash(x: AsyncFD): Hash {.borrow.}
|
||||
proc `==`*(x: AsyncFD, y: AsyncFD): bool {.borrow.}
|
||||
|
||||
proc newDispatcher*(): PDispatcher =
|
||||
## Creates a new Dispatcher instance.
|
||||
new result
|
||||
result.ioPort = createIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1)
|
||||
result.handles = initSet[TAsyncFD]()
|
||||
result.handles = initSet[AsyncFD]()
|
||||
result.timers = @[]
|
||||
|
||||
var gDisp{.threadvar.}: PDispatcher ## Global dispatcher
|
||||
@@ -357,15 +359,15 @@ when defined(windows) or defined(nimdoc):
|
||||
if gDisp.isNil: gDisp = newDispatcher()
|
||||
result = gDisp
|
||||
|
||||
proc register*(fd: TAsyncFD) =
|
||||
proc register*(fd: AsyncFD) =
|
||||
## Registers ``fd`` with the dispatcher.
|
||||
let p = getGlobalDispatcher()
|
||||
if createIoCompletionPort(fd.THandle, p.ioPort,
|
||||
cast[TCompletionKey](fd), 1) == 0:
|
||||
if createIoCompletionPort(fd.Handle, p.ioPort,
|
||||
cast[CompletionKey](fd), 1) == 0:
|
||||
raiseOSError(osLastError())
|
||||
p.handles.incl(fd)
|
||||
|
||||
proc verifyPresence(fd: TAsyncFD) =
|
||||
proc verifyPresence(fd: AsyncFD) =
|
||||
## Ensures that file descriptor has been registered with the dispatcher.
|
||||
let p = getGlobalDispatcher()
|
||||
if fd notin p.handles:
|
||||
@@ -394,7 +396,7 @@ when defined(windows) or defined(nimdoc):
|
||||
# TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html
|
||||
if res:
|
||||
# This is useful for ensuring the reliability of the overlapped struct.
|
||||
assert customOverlapped.data.fd == lpCompletionKey.TAsyncFD
|
||||
assert customOverlapped.data.fd == lpCompletionKey.AsyncFD
|
||||
|
||||
customOverlapped.data.cb(customOverlapped.data.fd,
|
||||
lpNumberOfBytesTransferred, OSErrorCode(-1))
|
||||
@@ -402,7 +404,7 @@ when defined(windows) or defined(nimdoc):
|
||||
else:
|
||||
let errCode = osLastError()
|
||||
if customOverlapped != nil:
|
||||
assert customOverlapped.data.fd == lpCompletionKey.TAsyncFD
|
||||
assert customOverlapped.data.fd == lpCompletionKey.AsyncFD
|
||||
customOverlapped.data.cb(customOverlapped.data.fd,
|
||||
lpNumberOfBytesTransferred, errCode)
|
||||
GC_unref(customOverlapped)
|
||||
@@ -480,7 +482,7 @@ when defined(windows) or defined(nimdoc):
|
||||
dwRemoteAddressLength, LocalSockaddr, LocalSockaddrLength,
|
||||
RemoteSockaddr, RemoteSockaddrLength)
|
||||
|
||||
proc connect*(socket: TAsyncFD, address: string, port: Port,
|
||||
proc connect*(socket: AsyncFD, address: string, port: Port,
|
||||
af = AF_INET): Future[void] =
|
||||
## Connects ``socket`` to server at ``address:port``.
|
||||
##
|
||||
@@ -506,8 +508,8 @@ when defined(windows) or defined(nimdoc):
|
||||
# http://blogs.msdn.com/b/oldnewthing/archive/2011/02/02/10123392.aspx
|
||||
var ol = PCustomOverlapped()
|
||||
GC_ref(ol)
|
||||
ol.data = TCompletionData(fd: socket, cb:
|
||||
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
ol.data = CompletionData(fd: socket, cb:
|
||||
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
if not retFuture.finished:
|
||||
if errcode == OSErrorCode(-1):
|
||||
retFuture.complete()
|
||||
@@ -542,7 +544,7 @@ when defined(windows) or defined(nimdoc):
|
||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||
return retFuture
|
||||
|
||||
proc recv*(socket: TAsyncFD, size: int,
|
||||
proc recv*(socket: AsyncFD, size: int,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[string] =
|
||||
## Reads **up to** ``size`` bytes from ``socket``. Returned future will
|
||||
## complete once all the data requested is read, a part of the data has been
|
||||
@@ -570,8 +572,8 @@ when defined(windows) or defined(nimdoc):
|
||||
var flagsio = flags.toOSFlags().Dword
|
||||
var ol = PCustomOverlapped()
|
||||
GC_ref(ol)
|
||||
ol.data = TCompletionData(fd: socket, cb:
|
||||
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
ol.data = CompletionData(fd: socket, cb:
|
||||
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
if not retFuture.finished:
|
||||
if errcode == OSErrorCode(-1):
|
||||
if bytesCount == 0 and dataBuf.buf[0] == '\0':
|
||||
@@ -634,7 +636,7 @@ when defined(windows) or defined(nimdoc):
|
||||
# free ``ol``.
|
||||
return retFuture
|
||||
|
||||
proc recvInto*(socket: TAsyncFD, buf: cstring, size: int,
|
||||
proc recvInto*(socket: AsyncFD, buf: cstring, size: int,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[int] =
|
||||
## Reads **up to** ``size`` bytes from ``socket`` into ``buf``, which must
|
||||
## at least be of that size. Returned future will complete once all the
|
||||
@@ -665,8 +667,8 @@ when defined(windows) or defined(nimdoc):
|
||||
var flagsio = flags.toOSFlags().Dword
|
||||
var ol = PCustomOverlapped()
|
||||
GC_ref(ol)
|
||||
ol.data = TCompletionData(fd: socket, cb:
|
||||
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
ol.data = CompletionData(fd: socket, cb:
|
||||
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
if not retFuture.finished:
|
||||
if errcode == OSErrorCode(-1):
|
||||
if bytesCount == 0 and dataBuf.buf[0] == '\0':
|
||||
@@ -721,7 +723,7 @@ when defined(windows) or defined(nimdoc):
|
||||
# free ``ol``.
|
||||
return retFuture
|
||||
|
||||
proc send*(socket: TAsyncFD, data: string,
|
||||
proc send*(socket: AsyncFD, data: string,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[void] =
|
||||
## Sends ``data`` to ``socket``. The returned future will complete once all
|
||||
## data has been sent.
|
||||
@@ -735,8 +737,8 @@ when defined(windows) or defined(nimdoc):
|
||||
var bytesReceived, lowFlags: Dword
|
||||
var ol = PCustomOverlapped()
|
||||
GC_ref(ol)
|
||||
ol.data = TCompletionData(fd: socket, cb:
|
||||
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
ol.data = CompletionData(fd: socket, cb:
|
||||
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
if not retFuture.finished:
|
||||
if errcode == OSErrorCode(-1):
|
||||
retFuture.complete()
|
||||
@@ -764,8 +766,8 @@ when defined(windows) or defined(nimdoc):
|
||||
# free ``ol``.
|
||||
return retFuture
|
||||
|
||||
proc acceptAddr*(socket: TAsyncFD, flags = {SocketFlag.SafeDisconn}):
|
||||
Future[tuple[address: string, client: TAsyncFD]] =
|
||||
proc acceptAddr*(socket: AsyncFD, flags = {SocketFlag.SafeDisconn}):
|
||||
Future[tuple[address: string, client: AsyncFD]] =
|
||||
## Accepts a new connection. Returns a future containing the client socket
|
||||
## corresponding to that connection and the remote address of the client.
|
||||
## The future will complete when the connection is successfully accepted.
|
||||
@@ -778,7 +780,7 @@ when defined(windows) or defined(nimdoc):
|
||||
## flag is specified then this error will not be raised and instead
|
||||
## accept will be called again.
|
||||
verifyPresence(socket)
|
||||
var retFuture = newFuture[tuple[address: string, client: TAsyncFD]]("acceptAddr")
|
||||
var retFuture = newFuture[tuple[address: string, client: AsyncFD]]("acceptAddr")
|
||||
|
||||
var clientSock = newRawSocket()
|
||||
if clientSock == osInvalidSocket: raiseOSError(osLastError())
|
||||
@@ -803,11 +805,11 @@ when defined(windows) or defined(nimdoc):
|
||||
dwLocalAddressLength, dwRemoteAddressLength,
|
||||
addr localSockaddr, addr localLen,
|
||||
addr remoteSockaddr, addr remoteLen)
|
||||
register(clientSock.TAsyncFD)
|
||||
register(clientSock.AsyncFD)
|
||||
# TODO: IPv6. Check ``sa_family``. http://stackoverflow.com/a/9212542/492186
|
||||
retFuture.complete(
|
||||
(address: $inet_ntoa(cast[ptr Sockaddr_in](remoteSockAddr).sin_addr),
|
||||
client: clientSock.TAsyncFD)
|
||||
client: clientSock.AsyncFD)
|
||||
)
|
||||
|
||||
template failAccept(errcode): stmt =
|
||||
@@ -824,8 +826,8 @@ when defined(windows) or defined(nimdoc):
|
||||
|
||||
var ol = PCustomOverlapped()
|
||||
GC_ref(ol)
|
||||
ol.data = TCompletionData(fd: socket, cb:
|
||||
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
ol.data = CompletionData(fd: socket, cb:
|
||||
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
if not retFuture.finished:
|
||||
if errcode == OSErrorCode(-1):
|
||||
completeAccept()
|
||||
@@ -853,26 +855,26 @@ when defined(windows) or defined(nimdoc):
|
||||
|
||||
return retFuture
|
||||
|
||||
proc newAsyncRawSocket*(domain, typ, protocol: cint): TAsyncFD =
|
||||
proc newAsyncRawSocket*(domain, typ, protocol: cint): AsyncFD =
|
||||
## Creates a new socket and registers it with the dispatcher implicitly.
|
||||
result = newRawSocket(domain, typ, protocol).TAsyncFD
|
||||
result = newRawSocket(domain, typ, protocol).AsyncFD
|
||||
result.SocketHandle.setBlocking(false)
|
||||
register(result)
|
||||
|
||||
proc newAsyncRawSocket*(domain: Domain = AF_INET,
|
||||
typ: SockType = SOCK_STREAM,
|
||||
protocol: Protocol = IPPROTO_TCP): TAsyncFD =
|
||||
protocol: Protocol = IPPROTO_TCP): AsyncFD =
|
||||
## Creates a new socket and registers it with the dispatcher implicitly.
|
||||
result = newRawSocket(domain, typ, protocol).TAsyncFD
|
||||
result = newRawSocket(domain, typ, protocol).AsyncFD
|
||||
result.SocketHandle.setBlocking(false)
|
||||
register(result)
|
||||
|
||||
proc closeSocket*(socket: TAsyncFD) =
|
||||
proc closeSocket*(socket: AsyncFD) =
|
||||
## Closes a socket and ensures that it is unregistered.
|
||||
socket.SocketHandle.close()
|
||||
getGlobalDispatcher().handles.excl(socket)
|
||||
|
||||
proc unregister*(fd: TAsyncFD) =
|
||||
proc unregister*(fd: AsyncFD) =
|
||||
## Unregisters ``fd``.
|
||||
getGlobalDispatcher().handles.excl(fd)
|
||||
|
||||
@@ -892,18 +894,19 @@ else:
|
||||
MSG_NOSIGNAL
|
||||
|
||||
type
|
||||
TAsyncFD* = distinct cint
|
||||
TCallback = proc (fd: TAsyncFD): bool {.closure,gcsafe.}
|
||||
AsyncFD* = distinct cint
|
||||
Callback = proc (fd: AsyncFD): bool {.closure,gcsafe.}
|
||||
|
||||
PData* = ref object of RootRef
|
||||
fd: TAsyncFD
|
||||
readCBs: seq[TCallback]
|
||||
writeCBs: seq[TCallback]
|
||||
fd: AsyncFD
|
||||
readCBs: seq[Callback]
|
||||
writeCBs: seq[Callback]
|
||||
|
||||
PDispatcher* = ref object of PDispatcherBase
|
||||
selector: Selector
|
||||
{.deprecated: [TAsyncFD: AsyncFD, TCallback: Callback].}
|
||||
|
||||
proc `==`*(x, y: TAsyncFD): bool {.borrow.}
|
||||
proc `==`*(x, y: AsyncFD): bool {.borrow.}
|
||||
|
||||
proc newDispatcher*(): PDispatcher =
|
||||
new result
|
||||
@@ -915,18 +918,18 @@ else:
|
||||
if gDisp.isNil: gDisp = newDispatcher()
|
||||
result = gDisp
|
||||
|
||||
proc update(fd: TAsyncFD, events: set[Event]) =
|
||||
proc update(fd: AsyncFD, events: set[Event]) =
|
||||
let p = getGlobalDispatcher()
|
||||
assert fd.SocketHandle in p.selector
|
||||
discard p.selector.update(fd.SocketHandle, events)
|
||||
|
||||
proc register*(fd: TAsyncFD) =
|
||||
proc register*(fd: AsyncFD) =
|
||||
let p = getGlobalDispatcher()
|
||||
var data = PData(fd: fd, readCBs: @[], writeCBs: @[])
|
||||
p.selector.register(fd.SocketHandle, {}, data.RootRef)
|
||||
|
||||
proc newAsyncRawSocket*(domain: cint, typ: cint, protocol: cint): TAsyncFD =
|
||||
result = newRawSocket(domain, typ, protocol).TAsyncFD
|
||||
proc newAsyncRawSocket*(domain: cint, typ: cint, protocol: cint): AsyncFD =
|
||||
result = newRawSocket(domain, typ, protocol).AsyncFD
|
||||
result.SocketHandle.setBlocking(false)
|
||||
when defined(macosx):
|
||||
result.SocketHandle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1)
|
||||
@@ -934,29 +937,29 @@ else:
|
||||
|
||||
proc newAsyncRawSocket*(domain: Domain = AF_INET,
|
||||
typ: SockType = SOCK_STREAM,
|
||||
protocol: Protocol = IPPROTO_TCP): TAsyncFD =
|
||||
result = newRawSocket(domain, typ, protocol).TAsyncFD
|
||||
protocol: Protocol = IPPROTO_TCP): AsyncFD =
|
||||
result = newRawSocket(domain, typ, protocol).AsyncFD
|
||||
result.SocketHandle.setBlocking(false)
|
||||
when defined(macosx):
|
||||
result.SocketHandle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1)
|
||||
register(result)
|
||||
|
||||
proc closeSocket*(sock: TAsyncFD) =
|
||||
proc closeSocket*(sock: AsyncFD) =
|
||||
let disp = getGlobalDispatcher()
|
||||
sock.SocketHandle.close()
|
||||
disp.selector.unregister(sock.SocketHandle)
|
||||
|
||||
proc unregister*(fd: TAsyncFD) =
|
||||
proc unregister*(fd: AsyncFD) =
|
||||
getGlobalDispatcher().selector.unregister(fd.SocketHandle)
|
||||
|
||||
proc addRead*(fd: TAsyncFD, cb: TCallback) =
|
||||
proc addRead*(fd: AsyncFD, cb: Callback) =
|
||||
let p = getGlobalDispatcher()
|
||||
if fd.SocketHandle notin p.selector:
|
||||
raise newException(ValueError, "File descriptor not registered.")
|
||||
p.selector[fd.SocketHandle].data.PData.readCBs.add(cb)
|
||||
update(fd, p.selector[fd.SocketHandle].events + {EvRead})
|
||||
|
||||
proc addWrite*(fd: TAsyncFD, cb: TCallback) =
|
||||
proc addWrite*(fd: AsyncFD, cb: Callback) =
|
||||
let p = getGlobalDispatcher()
|
||||
if fd.SocketHandle notin p.selector:
|
||||
raise newException(ValueError, "File descriptor not registered.")
|
||||
@@ -967,7 +970,7 @@ else:
|
||||
let p = getGlobalDispatcher()
|
||||
for info in p.selector.select(timeout):
|
||||
let data = PData(info.key.data)
|
||||
assert data.fd == info.key.fd.TAsyncFD
|
||||
assert data.fd == info.key.fd.AsyncFD
|
||||
#echo("In poll ", data.fd.cint)
|
||||
if EvError in info.events:
|
||||
closeSocket(data.fd)
|
||||
@@ -1005,11 +1008,11 @@ else:
|
||||
|
||||
processTimers(p)
|
||||
|
||||
proc connect*(socket: TAsyncFD, address: string, port: Port,
|
||||
proc connect*(socket: AsyncFD, address: string, port: Port,
|
||||
af = AF_INET): Future[void] =
|
||||
var retFuture = newFuture[void]("connect")
|
||||
|
||||
proc cb(fd: TAsyncFD): bool =
|
||||
proc cb(fd: AsyncFD): bool =
|
||||
# We have connected.
|
||||
retFuture.complete()
|
||||
return true
|
||||
@@ -1040,13 +1043,13 @@ else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||
return retFuture
|
||||
|
||||
proc recv*(socket: TAsyncFD, size: int,
|
||||
proc recv*(socket: AsyncFD, size: int,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[string] =
|
||||
var retFuture = newFuture[string]("recv")
|
||||
|
||||
var readBuffer = newString(size)
|
||||
|
||||
proc cb(sock: TAsyncFD): bool =
|
||||
proc cb(sock: AsyncFD): bool =
|
||||
result = true
|
||||
let res = recv(sock.SocketHandle, addr readBuffer[0], size.cint,
|
||||
flags.toOSFlags())
|
||||
@@ -1070,11 +1073,11 @@ else:
|
||||
addRead(socket, cb)
|
||||
return retFuture
|
||||
|
||||
proc recvInto*(socket: TAsyncFD, buf: cstring, size: int,
|
||||
proc recvInto*(socket: AsyncFD, buf: cstring, size: int,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[int] =
|
||||
var retFuture = newFuture[int]("recvInto")
|
||||
|
||||
proc cb(sock: TAsyncFD): bool =
|
||||
proc cb(sock: AsyncFD): bool =
|
||||
result = true
|
||||
let res = recv(sock.SocketHandle, buf, size.cint,
|
||||
flags.toOSFlags())
|
||||
@@ -1094,13 +1097,13 @@ else:
|
||||
addRead(socket, cb)
|
||||
return retFuture
|
||||
|
||||
proc send*(socket: TAsyncFD, data: string,
|
||||
proc send*(socket: AsyncFD, data: string,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[void] =
|
||||
var retFuture = newFuture[void]("send")
|
||||
|
||||
var written = 0
|
||||
|
||||
proc cb(sock: TAsyncFD): bool =
|
||||
proc cb(sock: AsyncFD): bool =
|
||||
result = true
|
||||
let netSize = data.len-written
|
||||
var d = data.cstring
|
||||
@@ -1126,11 +1129,11 @@ else:
|
||||
addWrite(socket, cb)
|
||||
return retFuture
|
||||
|
||||
proc acceptAddr*(socket: TAsyncFD, flags = {SocketFlag.SafeDisconn}):
|
||||
Future[tuple[address: string, client: TAsyncFD]] =
|
||||
proc acceptAddr*(socket: AsyncFD, flags = {SocketFlag.SafeDisconn}):
|
||||
Future[tuple[address: string, client: AsyncFD]] =
|
||||
var retFuture = newFuture[tuple[address: string,
|
||||
client: TAsyncFD]]("acceptAddr")
|
||||
proc cb(sock: TAsyncFD): bool =
|
||||
client: AsyncFD]]("acceptAddr")
|
||||
proc cb(sock: AsyncFD): bool =
|
||||
result = true
|
||||
var sockAddress: SockAddr_in
|
||||
var addrLen = sizeof(sockAddress).Socklen
|
||||
@@ -1147,8 +1150,8 @@ else:
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||
else:
|
||||
register(client.TAsyncFD)
|
||||
retFuture.complete(($inet_ntoa(sockAddress.sin_addr), client.TAsyncFD))
|
||||
register(client.AsyncFD)
|
||||
retFuture.complete(($inet_ntoa(sockAddress.sin_addr), client.AsyncFD))
|
||||
addRead(socket, cb)
|
||||
return retFuture
|
||||
|
||||
@@ -1160,15 +1163,15 @@ proc sleepAsync*(ms: int): Future[void] =
|
||||
p.timers.add((epochTime() + (ms / 1000), retFuture))
|
||||
return retFuture
|
||||
|
||||
proc accept*(socket: TAsyncFD,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[TAsyncFD] =
|
||||
proc accept*(socket: AsyncFD,
|
||||
flags = {SocketFlag.SafeDisconn}): Future[AsyncFD] =
|
||||
## Accepts a new connection. Returns a future containing the client socket
|
||||
## corresponding to that connection.
|
||||
## The future will complete when the connection is successfully accepted.
|
||||
var retFut = newFuture[TAsyncFD]("accept")
|
||||
var retFut = newFuture[AsyncFD]("accept")
|
||||
var fut = acceptAddr(socket, flags)
|
||||
fut.callback =
|
||||
proc (future: Future[tuple[address: string, client: TAsyncFD]]) =
|
||||
proc (future: Future[tuple[address: string, client: AsyncFD]]) =
|
||||
assert future.finished
|
||||
if future.failed:
|
||||
retFut.fail(future.error)
|
||||
@@ -1495,7 +1498,7 @@ macro async*(prc: stmt): stmt {.immediate.} =
|
||||
#if prc[0].getName == "test":
|
||||
# echo(toStrLit(result))
|
||||
|
||||
proc recvLine*(socket: TAsyncFD): Future[string] {.async.} =
|
||||
proc recvLine*(socket: AsyncFD): Future[string] {.async.} =
|
||||
## Reads a line of data from ``socket``. Returned future will complete once
|
||||
## a full line is read or an error occurs.
|
||||
##
|
||||
|
||||
@@ -31,7 +31,7 @@ else:
|
||||
|
||||
type
|
||||
AsyncFile* = ref object
|
||||
fd: TAsyncFd
|
||||
fd: AsyncFd
|
||||
offset: int64
|
||||
|
||||
when defined(windows) or defined(nimdoc):
|
||||
@@ -72,7 +72,7 @@ proc getFileSize(f: AsyncFile): int64 =
|
||||
## Retrieves the specified file's size.
|
||||
when defined(windows) or defined(nimdoc):
|
||||
var high: DWord
|
||||
let low = getFileSize(f.fd.THandle, addr high)
|
||||
let low = getFileSize(f.fd.Handle, addr high)
|
||||
if low == INVALID_FILE_SIZE:
|
||||
raiseOSError(osLastError())
|
||||
return (high shl 32) or low
|
||||
@@ -88,13 +88,13 @@ proc openAsync*(filename: string, mode = fmRead): AsyncFile =
|
||||
when useWinUnicode:
|
||||
result.fd = createFileW(newWideCString(filename), desiredAccess,
|
||||
FILE_SHARE_READ,
|
||||
nil, creationDisposition, flags, 0).TAsyncFd
|
||||
nil, creationDisposition, flags, 0).AsyncFd
|
||||
else:
|
||||
result.fd = createFileA(filename, desiredAccess,
|
||||
FILE_SHARE_READ,
|
||||
nil, creationDisposition, flags, 0).TAsyncFd
|
||||
nil, creationDisposition, flags, 0).AsyncFd
|
||||
|
||||
if result.fd.THandle == INVALID_HANDLE_VALUE:
|
||||
if result.fd.Handle == INVALID_HANDLE_VALUE:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
register(result.fd)
|
||||
@@ -106,7 +106,7 @@ proc openAsync*(filename: string, mode = fmRead): AsyncFile =
|
||||
let flags = getPosixFlags(mode)
|
||||
# RW (Owner), RW (Group), R (Other)
|
||||
let perm = S_IRUSR or S_IWUSR or S_IRGRP or S_IWGRP or S_IROTH
|
||||
result.fd = open(filename, flags, perm).TAsyncFD
|
||||
result.fd = open(filename, flags, perm).AsyncFD
|
||||
if result.fd.cint == -1:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
@@ -125,8 +125,8 @@ proc read*(f: AsyncFile, size: int): Future[string] =
|
||||
|
||||
var ol = PCustomOverlapped()
|
||||
GC_ref(ol)
|
||||
ol.data = TCompletionData(fd: f.fd, cb:
|
||||
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
ol.data = CompletionData(fd: f.fd, cb:
|
||||
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||
if not retFuture.finished:
|
||||
if errcode == OSErrorCode(-1):
|
||||
assert bytesCount > 0
|
||||
@@ -148,7 +148,7 @@ proc read*(f: AsyncFile, size: int): Future[string] =
|
||||
ol.offsetHigh = DWord(f.offset shr 32)
|
||||
|
||||
# According to MSDN we're supposed to pass nil to lpNumberOfBytesRead.
|
||||
let ret = readFile(f.fd.THandle, buffer, size.int32, nil,
|
||||
let ret = readFile(f.fd.Handle, buffer, size.int32, nil,
|
||||
cast[POVERLAPPED](ol))
|
||||
if not ret.bool:
|
||||
let err = osLastError()
|
||||
@@ -161,7 +161,7 @@ proc read*(f: AsyncFile, size: int): Future[string] =
|
||||
else:
|
||||
# Request completed immediately.
|
||||
var bytesRead: DWord
|
||||
let overlappedRes = getOverlappedResult(f.fd.THandle,
|
||||
let overlappedRes = getOverlappedResult(f.fd.Handle,
|
||||
cast[POverlapped](ol)[], bytesRead, false.WinBool)
|
||||
if not overlappedRes.bool:
|
||||
let err = osLastError()
|
||||
@@ -179,7 +179,7 @@ proc read*(f: AsyncFile, size: int): Future[string] =
|
||||
else:
|
||||
var readBuffer = newString(size)
|
||||
|
||||
proc cb(fd: TAsyncFD): bool =
|
||||
proc cb(fd: AsyncFD): bool =
|
||||
result = true
|
||||
let res = read(fd.cint, addr readBuffer[0], size.cint)
|
||||
if res < 0:
|
||||
@@ -251,8 +251,8 @@ proc write*(f: AsyncFile, data: string): Future[void] =
|
||||
|
||||
var ol = PCustomOverlapped()
|
||||
GC_ref(ol)
|
||||
ol.data = TCompletionData(fd: f.fd, cb:
|
||||
proc (fd: TAsyncFD, bytesCount: DWord, errcode: OSErrorCode) =
|
||||
ol.data = CompletionData(fd: f.fd, cb:
|
||||
proc (fd: AsyncFD, bytesCount: DWord, errcode: OSErrorCode) =
|
||||
if not retFuture.finished:
|
||||
if errcode == OSErrorCode(-1):
|
||||
assert bytesCount == data.len.int32
|
||||
@@ -268,7 +268,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
|
||||
ol.offsetHigh = DWord(f.offset shr 32)
|
||||
|
||||
# According to MSDN we're supposed to pass nil to lpNumberOfBytesWritten.
|
||||
let ret = writeFile(f.fd.THandle, buffer, data.len.int32, nil,
|
||||
let ret = writeFile(f.fd.Handle, buffer, data.len.int32, nil,
|
||||
cast[POVERLAPPED](ol))
|
||||
if not ret.bool:
|
||||
let err = osLastError()
|
||||
@@ -281,7 +281,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
|
||||
else:
|
||||
# Request completed immediately.
|
||||
var bytesWritten: DWord
|
||||
let overlappedRes = getOverlappedResult(f.fd.THandle,
|
||||
let overlappedRes = getOverlappedResult(f.fd.Handle,
|
||||
cast[POverlapped](ol)[], bytesWritten, false.WinBool)
|
||||
if not overlappedRes.bool:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
|
||||
@@ -292,7 +292,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
|
||||
else:
|
||||
var written = 0
|
||||
|
||||
proc cb(fd: TAsyncFD): bool =
|
||||
proc cb(fd: AsyncFD): bool =
|
||||
result = true
|
||||
let remainderSize = data.len-written
|
||||
let res = write(fd.cint, addr copy[written], remainderSize.cint)
|
||||
@@ -317,7 +317,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
|
||||
proc close*(f: AsyncFile) =
|
||||
## Closes the file specified.
|
||||
when defined(windows) or defined(nimdoc):
|
||||
if not closeHandle(f.fd.THandle).bool:
|
||||
if not closeHandle(f.fd.Handle).bool:
|
||||
raiseOSError(osLastError())
|
||||
else:
|
||||
if close(f.fd.cint) == -1:
|
||||
|
||||
@@ -210,6 +210,7 @@ proc processClient(client: AsyncSocket, address: string,
|
||||
var contentLength = 0
|
||||
if parseInt(request.headers["Content-Length"], contentLength) == 0:
|
||||
await request.respond(Http400, "Bad Request. Invalid Content-Length.")
|
||||
continue
|
||||
else:
|
||||
request.body = await client.recv(contentLength)
|
||||
assert request.body.len == contentLength
|
||||
|
||||
@@ -188,8 +188,8 @@ proc asyncSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
|
||||
result.socket.setBlocking(false)
|
||||
|
||||
proc toAsyncSocket*(sock: Socket, state: SocketStatus = SockConnected): AsyncSocket =
|
||||
## Wraps an already initialized ``TSocket`` into a AsyncSocket.
|
||||
## This is useful if you want to use an already connected TSocket as an
|
||||
## Wraps an already initialized ``Socket`` into a AsyncSocket.
|
||||
## This is useful if you want to use an already connected Socket as an
|
||||
## asynchronous AsyncSocket in asyncio's event loop.
|
||||
##
|
||||
## ``state`` may be overriden, i.e. if ``sock`` is not connected it should be
|
||||
|
||||
@@ -91,13 +91,13 @@ type
|
||||
|
||||
# TODO: Save AF, domain etc info and reuse it in procs which need it like connect.
|
||||
|
||||
proc newAsyncSocket*(fd: TAsyncFD, isBuff: bool): AsyncSocket =
|
||||
proc newAsyncSocket*(fd: AsyncFD, buffered = true): AsyncSocket =
|
||||
## Creates a new ``AsyncSocket`` based on the supplied params.
|
||||
assert fd != osInvalidSocket.TAsyncFD
|
||||
assert fd != osInvalidSocket.AsyncFD
|
||||
new(result)
|
||||
result.fd = fd.SocketHandle
|
||||
result.isBuffered = isBuff
|
||||
if isBuff:
|
||||
result.isBuffered = buffered
|
||||
if buffered:
|
||||
result.currPos = 0
|
||||
|
||||
proc newAsyncSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
|
||||
@@ -142,7 +142,7 @@ when defined(ssl):
|
||||
if read < 0:
|
||||
raiseSslError()
|
||||
data.setLen(read)
|
||||
await socket.fd.TAsyncFd.send(data, flags)
|
||||
await socket.fd.AsyncFd.send(data, flags)
|
||||
|
||||
proc appeaseSsl(socket: AsyncSocket, flags: set[SocketFlag],
|
||||
sslError: cint) {.async.} =
|
||||
@@ -150,7 +150,7 @@ when defined(ssl):
|
||||
of SSL_ERROR_WANT_WRITE:
|
||||
await sendPendingSslData(socket, flags)
|
||||
of SSL_ERROR_WANT_READ:
|
||||
var data = await recv(socket.fd.TAsyncFD, BufferSize, flags)
|
||||
var data = await recv(socket.fd.AsyncFD, BufferSize, flags)
|
||||
let ret = bioWrite(socket.bioIn, addr data[0], data.len.cint)
|
||||
if ret < 0:
|
||||
raiseSSLError()
|
||||
@@ -175,7 +175,7 @@ proc connect*(socket: AsyncSocket, address: string, port: Port,
|
||||
##
|
||||
## Returns a ``Future`` which will complete when the connection succeeds
|
||||
## or an error occurs.
|
||||
await connect(socket.fd.TAsyncFD, address, port, af)
|
||||
await connect(socket.fd.AsyncFD, address, port, af)
|
||||
if socket.isSsl:
|
||||
when defined(ssl):
|
||||
let flags = {SocketFlag.SafeDisconn}
|
||||
@@ -194,7 +194,7 @@ template readInto(buf: cstring, size: int, socket: AsyncSocket,
|
||||
sslRead(socket.sslHandle, buf, size.cint))
|
||||
res = opResult
|
||||
else:
|
||||
var recvIntoFut = recvInto(socket.fd.TAsyncFD, buf, size, flags)
|
||||
var recvIntoFut = recvInto(socket.fd.AsyncFD, buf, size, flags)
|
||||
yield recvIntoFut
|
||||
# Not in SSL mode.
|
||||
res = recvIntoFut.read()
|
||||
@@ -271,7 +271,7 @@ proc send*(socket: AsyncSocket, data: string,
|
||||
sslWrite(socket.sslHandle, addr copy[0], copy.len.cint))
|
||||
await sendPendingSslData(socket, flags)
|
||||
else:
|
||||
await send(socket.fd.TAsyncFD, data, flags)
|
||||
await send(socket.fd.AsyncFD, data, flags)
|
||||
|
||||
proc acceptAddr*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn}):
|
||||
Future[tuple[address: string, client: AsyncSocket]] =
|
||||
@@ -279,9 +279,9 @@ proc acceptAddr*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn}):
|
||||
## corresponding to that connection and the remote address of the client.
|
||||
## The future will complete when the connection is successfully accepted.
|
||||
var retFuture = newFuture[tuple[address: string, client: AsyncSocket]]("asyncnet.acceptAddr")
|
||||
var fut = acceptAddr(socket.fd.TAsyncFD, flags)
|
||||
var fut = acceptAddr(socket.fd.AsyncFD, flags)
|
||||
fut.callback =
|
||||
proc (future: Future[tuple[address: string, client: TAsyncFD]]) =
|
||||
proc (future: Future[tuple[address: string, client: AsyncFD]]) =
|
||||
assert future.finished
|
||||
if future.failed:
|
||||
retFuture.fail(future.readError)
|
||||
@@ -445,7 +445,7 @@ proc bindAddr*(socket: AsyncSocket, port = Port(0), address = "") {.
|
||||
proc close*(socket: AsyncSocket) =
|
||||
## Closes the socket.
|
||||
defer:
|
||||
socket.fd.TAsyncFD.closeSocket()
|
||||
socket.fd.AsyncFD.closeSocket()
|
||||
when defined(ssl):
|
||||
if socket.isSSL:
|
||||
let res = SslShutdown(socket.sslHandle)
|
||||
|
||||
@@ -20,20 +20,20 @@ import strutils
|
||||
##
|
||||
## # Create a matrix which first rotates, then scales and at last translates
|
||||
##
|
||||
## var m:TMatrix2d=rotate(DEG90) & scale(2.0) & move(100.0,200.0)
|
||||
## var m:Matrix2d=rotate(DEG90) & scale(2.0) & move(100.0,200.0)
|
||||
##
|
||||
## # Create a 2d point at (100,0) and a vector (5,2)
|
||||
##
|
||||
## var pt:TPoint2d=point2d(100.0,0.0)
|
||||
## var pt:Point2d=point2d(100.0,0.0)
|
||||
##
|
||||
## var vec:TVector2d=vector2d(5.0,2.0)
|
||||
## var vec:Vector2d=vector2d(5.0,2.0)
|
||||
##
|
||||
##
|
||||
## pt &= m # transforms pt in place
|
||||
##
|
||||
## var pt2:TPoint2d=pt & m #concatenates pt with m and returns a new point
|
||||
## var pt2:Point2d=pt & m #concatenates pt with m and returns a new point
|
||||
##
|
||||
## var vec2:TVector2d=vec & m #concatenates vec with m and returns a new vector
|
||||
## var vec2:Vector2d=vec & m #concatenates vec with m and returns a new vector
|
||||
|
||||
|
||||
const
|
||||
@@ -57,46 +57,46 @@ const
|
||||
## used internally by DegToRad and RadToDeg
|
||||
|
||||
type
|
||||
TMatrix2d* = object
|
||||
Matrix2d* = object
|
||||
## Implements a row major 2d matrix, which means
|
||||
## transformations are applied the order they are concatenated.
|
||||
## The rightmost column of the 3x3 matrix is left out since normally
|
||||
## not used for geometric transformations in 2d.
|
||||
ax*,ay*,bx*,by*,tx*,ty*:float
|
||||
TPoint2d* = object
|
||||
Point2d* = object
|
||||
## Implements a non-homegeneous 2d point stored as
|
||||
## an `x` coordinate and an `y` coordinate.
|
||||
x*,y*:float
|
||||
TVector2d* = object
|
||||
Vector2d* = object
|
||||
## Implements a 2d **direction vector** stored as
|
||||
## an `x` coordinate and an `y` coordinate. Direction vector means,
|
||||
## that when transforming a vector with a matrix, the translational
|
||||
## part of the matrix is ignored.
|
||||
x*,y*:float
|
||||
|
||||
{.deprecated: [TMatrix2d: Matrix2d, TPoint2d: Point2d, TVector2d: Vector2d].}
|
||||
|
||||
|
||||
# Some forward declarations...
|
||||
proc matrix2d*(ax,ay,bx,by,tx,ty:float):TMatrix2d {.noInit.}
|
||||
proc matrix2d*(ax,ay,bx,by,tx,ty:float):Matrix2d {.noInit.}
|
||||
## Creates a new matrix.
|
||||
## `ax`,`ay` is the local x axis
|
||||
## `bx`,`by` is the local y axis
|
||||
## `tx`,`ty` is the translation
|
||||
proc vector2d*(x,y:float):TVector2d {.noInit,inline.}
|
||||
proc vector2d*(x,y:float):Vector2d {.noInit,inline.}
|
||||
## Returns a new vector (`x`,`y`)
|
||||
proc point2d*(x,y:float):TPoint2d {.noInit,inline.}
|
||||
proc point2d*(x,y:float):Point2d {.noInit,inline.}
|
||||
## Returns a new point (`x`,`y`)
|
||||
|
||||
|
||||
|
||||
let
|
||||
IDMATRIX*:TMatrix2d=matrix2d(1.0,0.0,0.0,1.0,0.0,0.0)
|
||||
IDMATRIX*:Matrix2d=matrix2d(1.0,0.0,0.0,1.0,0.0,0.0)
|
||||
## Quick access to an identity matrix
|
||||
ORIGO*:TPoint2d=point2d(0.0,0.0)
|
||||
ORIGO*:Point2d=point2d(0.0,0.0)
|
||||
## Quick acces to point (0,0)
|
||||
XAXIS*:TVector2d=vector2d(1.0,0.0)
|
||||
XAXIS*:Vector2d=vector2d(1.0,0.0)
|
||||
## Quick acces to an 2d x-axis unit vector
|
||||
YAXIS*:TVector2d=vector2d(0.0,1.0)
|
||||
YAXIS*:Vector2d=vector2d(0.0,1.0)
|
||||
## Quick acces to an 2d y-axis unit vector
|
||||
|
||||
|
||||
@@ -116,21 +116,21 @@ proc safeArccos(v:float):float=
|
||||
|
||||
template makeBinOpVector(s:expr)=
|
||||
## implements binary operators + , - , * and / for vectors
|
||||
proc s*(a,b:TVector2d):TVector2d {.inline,noInit.} = vector2d(s(a.x,b.x),s(a.y,b.y))
|
||||
proc s*(a:TVector2d,b:float):TVector2d {.inline,noInit.} = vector2d(s(a.x,b),s(a.y,b))
|
||||
proc s*(a:float,b:TVector2d):TVector2d {.inline,noInit.} = vector2d(s(a,b.x),s(a,b.y))
|
||||
proc s*(a,b:Vector2d):Vector2d {.inline,noInit.} = vector2d(s(a.x,b.x),s(a.y,b.y))
|
||||
proc s*(a:Vector2d,b:float):Vector2d {.inline,noInit.} = vector2d(s(a.x,b),s(a.y,b))
|
||||
proc s*(a:float,b:Vector2d):Vector2d {.inline,noInit.} = vector2d(s(a,b.x),s(a,b.y))
|
||||
|
||||
template makeBinOpAssignVector(s:expr)=
|
||||
## implements inplace binary operators += , -= , /= and *= for vectors
|
||||
proc s*(a:var TVector2d,b:TVector2d) {.inline.} = s(a.x,b.x) ; s(a.y,b.y)
|
||||
proc s*(a:var TVector2d,b:float) {.inline.} = s(a.x,b) ; s(a.y,b)
|
||||
proc s*(a:var Vector2d,b:Vector2d) {.inline.} = s(a.x,b.x) ; s(a.y,b.y)
|
||||
proc s*(a:var Vector2d,b:float) {.inline.} = s(a.x,b) ; s(a.y,b)
|
||||
|
||||
|
||||
# ***************************************
|
||||
# TMatrix2d implementation
|
||||
# Matrix2d implementation
|
||||
# ***************************************
|
||||
|
||||
proc setElements*(t:var TMatrix2d,ax,ay,bx,by,tx,ty:float) {.inline.}=
|
||||
proc setElements*(t:var Matrix2d,ax,ay,bx,by,tx,ty:float) {.inline.}=
|
||||
## Sets arbitrary elements in an existing matrix.
|
||||
t.ax=ax
|
||||
t.ay=ay
|
||||
@@ -139,10 +139,10 @@ proc setElements*(t:var TMatrix2d,ax,ay,bx,by,tx,ty:float) {.inline.}=
|
||||
t.tx=tx
|
||||
t.ty=ty
|
||||
|
||||
proc matrix2d*(ax,ay,bx,by,tx,ty:float):TMatrix2d =
|
||||
proc matrix2d*(ax,ay,bx,by,tx,ty:float):Matrix2d =
|
||||
result.setElements(ax,ay,bx,by,tx,ty)
|
||||
|
||||
proc `&`*(a,b:TMatrix2d):TMatrix2d {.noInit.} = #concatenate matrices
|
||||
proc `&`*(a,b:Matrix2d):Matrix2d {.noInit.} = #concatenate matrices
|
||||
## Concatenates matrices returning a new matrix.
|
||||
|
||||
# | a.AX a.AY 0 | | b.AX b.AY 0 |
|
||||
@@ -157,34 +157,34 @@ proc `&`*(a,b:TMatrix2d):TMatrix2d {.noInit.} = #concatenate matrices
|
||||
a.tx * b.ay + a.ty * b.by + b.ty)
|
||||
|
||||
|
||||
proc scale*(s:float):TMatrix2d {.noInit.} =
|
||||
proc scale*(s:float):Matrix2d {.noInit.} =
|
||||
## Returns a new scale matrix.
|
||||
result.setElements(s,0,0,s,0,0)
|
||||
|
||||
proc scale*(s:float,org:TPoint2d):TMatrix2d {.noInit.} =
|
||||
proc scale*(s:float,org:Point2d):Matrix2d {.noInit.} =
|
||||
## Returns a new scale matrix using, `org` as scale origin.
|
||||
result.setElements(s,0,0,s,org.x-s*org.x,org.y-s*org.y)
|
||||
|
||||
proc stretch*(sx,sy:float):TMatrix2d {.noInit.} =
|
||||
proc stretch*(sx,sy:float):Matrix2d {.noInit.} =
|
||||
## Returns new a stretch matrix, which is a
|
||||
## scale matrix with non uniform scale in x and y.
|
||||
result.setElements(sx,0,0,sy,0,0)
|
||||
|
||||
proc stretch*(sx,sy:float,org:TPoint2d):TMatrix2d {.noInit.} =
|
||||
proc stretch*(sx,sy:float,org:Point2d):Matrix2d {.noInit.} =
|
||||
## Returns a new stretch matrix, which is a
|
||||
## scale matrix with non uniform scale in x and y.
|
||||
## `org` is used as stretch origin.
|
||||
result.setElements(sx,0,0,sy,org.x-sx*org.x,org.y-sy*org.y)
|
||||
|
||||
proc move*(dx,dy:float):TMatrix2d {.noInit.} =
|
||||
proc move*(dx,dy:float):Matrix2d {.noInit.} =
|
||||
## Returns a new translation matrix.
|
||||
result.setElements(1,0,0,1,dx,dy)
|
||||
|
||||
proc move*(v:TVector2d):TMatrix2d {.noInit.} =
|
||||
proc move*(v:Vector2d):Matrix2d {.noInit.} =
|
||||
## Returns a new translation matrix from a vector.
|
||||
result.setElements(1,0,0,1,v.x,v.y)
|
||||
|
||||
proc rotate*(rad:float):TMatrix2d {.noInit.} =
|
||||
proc rotate*(rad:float):Matrix2d {.noInit.} =
|
||||
## Returns a new rotation matrix, which
|
||||
## represents a rotation by `rad` radians
|
||||
let
|
||||
@@ -192,7 +192,7 @@ proc rotate*(rad:float):TMatrix2d {.noInit.} =
|
||||
c=cos(rad)
|
||||
result.setElements(c,s,-s,c,0,0)
|
||||
|
||||
proc rotate*(rad:float,org:TPoint2d):TMatrix2d {.noInit.} =
|
||||
proc rotate*(rad:float,org:Point2d):Matrix2d {.noInit.} =
|
||||
## Returns a new rotation matrix, which
|
||||
## represents a rotation by `rad` radians around
|
||||
## the origin `org`
|
||||
@@ -201,7 +201,7 @@ proc rotate*(rad:float,org:TPoint2d):TMatrix2d {.noInit.} =
|
||||
c=cos(rad)
|
||||
result.setElements(c,s,-s,c,org.x+s*org.y-c*org.x,org.y-c*org.y-s*org.x)
|
||||
|
||||
proc mirror*(v:TVector2d):TMatrix2d {.noInit.} =
|
||||
proc mirror*(v:Vector2d):Matrix2d {.noInit.} =
|
||||
## Returns a new mirror matrix, mirroring
|
||||
## around the line that passes through origo and
|
||||
## has the direction of `v`
|
||||
@@ -220,7 +220,7 @@ proc mirror*(v:TVector2d):TMatrix2d {.noInit.} =
|
||||
xy2,-sqd,
|
||||
0.0,0.0)
|
||||
|
||||
proc mirror*(org:TPoint2d,v:TVector2d):TMatrix2d {.noInit.} =
|
||||
proc mirror*(org:Point2d,v:Vector2d):Matrix2d {.noInit.} =
|
||||
## Returns a new mirror matrix, mirroring
|
||||
## around the line that passes through `org` and
|
||||
## has the direction of `v`
|
||||
@@ -241,20 +241,20 @@ proc mirror*(org:TPoint2d,v:TVector2d):TMatrix2d {.noInit.} =
|
||||
|
||||
|
||||
|
||||
proc skew*(xskew,yskew:float):TMatrix2d {.noInit.} =
|
||||
proc skew*(xskew,yskew:float):Matrix2d {.noInit.} =
|
||||
## Returns a new skew matrix, which has its
|
||||
## x axis rotated `xskew` radians from the local x axis, and
|
||||
## y axis rotated `yskew` radians from the local y axis
|
||||
result.setElements(cos(yskew),sin(yskew),-sin(xskew),cos(xskew),0,0)
|
||||
|
||||
|
||||
proc `$`* (t:TMatrix2d):string {.noInit.} =
|
||||
proc `$`* (t:Matrix2d):string {.noInit.} =
|
||||
## Returns a string representation of the matrix
|
||||
return rtos(t.ax) & "," & rtos(t.ay) &
|
||||
"," & rtos(t.bx) & "," & rtos(t.by) &
|
||||
"," & rtos(t.tx) & "," & rtos(t.ty)
|
||||
|
||||
proc isUniform*(t:TMatrix2d,tol=1.0e-6):bool=
|
||||
proc isUniform*(t:Matrix2d,tol=1.0e-6):bool=
|
||||
## Checks if the transform is uniform, that is
|
||||
## perpendicular axes of equal length, which means (for example)
|
||||
## it cannot transform a circle into an ellipse.
|
||||
@@ -268,18 +268,18 @@ proc isUniform*(t:TMatrix2d,tol=1.0e-6):bool=
|
||||
return true
|
||||
return false
|
||||
|
||||
proc determinant*(t:TMatrix2d):float=
|
||||
proc determinant*(t:Matrix2d):float=
|
||||
## Computes the determinant of the matrix.
|
||||
|
||||
#NOTE: equivalent with perp.dot product for two 2d vectors
|
||||
return t.ax*t.by-t.bx*t.ay
|
||||
|
||||
proc isMirroring* (m:TMatrix2d):bool=
|
||||
proc isMirroring* (m:Matrix2d):bool=
|
||||
## Checks if the `m` is a mirroring matrix,
|
||||
## which means it will reverse direction of a curve transformed with it
|
||||
return m.determinant<0.0
|
||||
|
||||
proc inverse*(m:TMatrix2d):TMatrix2d {.noInit.} =
|
||||
proc inverse*(m:Matrix2d):Matrix2d {.noInit.} =
|
||||
## Returns a new matrix, which is the inverse of the matrix
|
||||
## If the matrix is not invertible (determinant=0), an EDivByZero
|
||||
## will be raised.
|
||||
@@ -293,7 +293,7 @@ proc inverse*(m:TMatrix2d):TMatrix2d {.noInit.} =
|
||||
(m.bx*m.ty-m.by*m.tx)/d,
|
||||
(m.ay*m.tx-m.ax*m.ty)/d)
|
||||
|
||||
proc equals*(m1:TMatrix2d,m2:TMatrix2d,tol=1.0e-6):bool=
|
||||
proc equals*(m1:Matrix2d,m2:Matrix2d,tol=1.0e-6):bool=
|
||||
## Checks if all elements of `m1`and `m2` is equal within
|
||||
## a given tolerance `tol`.
|
||||
return
|
||||
@@ -304,17 +304,17 @@ proc equals*(m1:TMatrix2d,m2:TMatrix2d,tol=1.0e-6):bool=
|
||||
abs(m1.tx-m2.tx)<=tol and
|
||||
abs(m1.ty-m2.ty)<=tol
|
||||
|
||||
proc `=~`*(m1,m2:TMatrix2d):bool=
|
||||
proc `=~`*(m1,m2:Matrix2d):bool=
|
||||
## Checks if `m1`and `m2` is approximately equal, using a
|
||||
## tolerance of 1e-6.
|
||||
equals(m1,m2)
|
||||
|
||||
proc isIdentity*(m:TMatrix2d,tol=1.0e-6):bool=
|
||||
proc isIdentity*(m:Matrix2d,tol=1.0e-6):bool=
|
||||
## Checks is a matrix is approximately an identity matrix,
|
||||
## using `tol` as tolerance for each element.
|
||||
return equals(m,IDMATRIX,tol)
|
||||
|
||||
proc apply*(m:TMatrix2d,x,y:var float,translate=false)=
|
||||
proc apply*(m:Matrix2d,x,y:var float,translate=false)=
|
||||
## Applies transformation `m` onto `x`,`y`, optionally
|
||||
## using the translation part of the matrix.
|
||||
if translate: # positional style transform
|
||||
@@ -329,29 +329,29 @@ proc apply*(m:TMatrix2d,x,y:var float,translate=false)=
|
||||
|
||||
|
||||
# ***************************************
|
||||
# TVector2d implementation
|
||||
# Vector2d implementation
|
||||
# ***************************************
|
||||
proc vector2d*(x,y:float):TVector2d = #forward decl.
|
||||
proc vector2d*(x,y:float):Vector2d = #forward decl.
|
||||
result.x=x
|
||||
result.y=y
|
||||
|
||||
proc polarVector2d*(ang:float,len:float):TVector2d {.noInit.} =
|
||||
proc polarVector2d*(ang:float,len:float):Vector2d {.noInit.} =
|
||||
## Returns a new vector with angle `ang` and magnitude `len`
|
||||
result.x=cos(ang)*len
|
||||
result.y=sin(ang)*len
|
||||
|
||||
proc slopeVector2d*(slope:float,len:float):TVector2d {.noInit.} =
|
||||
proc slopeVector2d*(slope:float,len:float):Vector2d {.noInit.} =
|
||||
## Returns a new vector having slope (dy/dx) given by
|
||||
## `slope`, and a magnitude of `len`
|
||||
let ang=arctan(slope)
|
||||
result.x=cos(ang)*len
|
||||
result.y=sin(ang)*len
|
||||
|
||||
proc len*(v:TVector2d):float {.inline.}=
|
||||
proc len*(v:Vector2d):float {.inline.}=
|
||||
## Returns the length of the vector.
|
||||
sqrt(v.x*v.x+v.y*v.y)
|
||||
|
||||
proc `len=`*(v:var TVector2d,newlen:float) {.noInit.} =
|
||||
proc `len=`*(v:var Vector2d,newlen:float) {.noInit.} =
|
||||
## Sets the length of the vector, keeping its angle.
|
||||
let fac=newlen/v.len
|
||||
|
||||
@@ -369,25 +369,25 @@ proc `len=`*(v:var TVector2d,newlen:float) {.noInit.} =
|
||||
v.x*=fac
|
||||
v.y*=fac
|
||||
|
||||
proc sqrLen*(v:TVector2d):float {.inline.}=
|
||||
proc sqrLen*(v:Vector2d):float {.inline.}=
|
||||
## Computes the squared length of the vector, which is
|
||||
## faster than computing the absolute length.
|
||||
v.x*v.x+v.y*v.y
|
||||
|
||||
proc angle*(v:TVector2d):float=
|
||||
proc angle*(v:Vector2d):float=
|
||||
## Returns the angle of the vector.
|
||||
## (The counter clockwise plane angle between posetive x axis and `v`)
|
||||
result=arctan2(v.y,v.x)
|
||||
if result<0.0: result+=DEG360
|
||||
|
||||
proc `$` *(v:TVector2d):string=
|
||||
proc `$` *(v:Vector2d):string=
|
||||
## String representation of `v`
|
||||
result=rtos(v.x)
|
||||
result.add(",")
|
||||
result.add(rtos(v.y))
|
||||
|
||||
|
||||
proc `&` *(v:TVector2d,m:TMatrix2d):TVector2d {.noInit.} =
|
||||
proc `&` *(v:Vector2d,m:Matrix2d):Vector2d {.noInit.} =
|
||||
## Concatenate vector `v` with a transformation matrix.
|
||||
## Transforming a vector ignores the translational part
|
||||
## of the matrix.
|
||||
@@ -399,7 +399,7 @@ proc `&` *(v:TVector2d,m:TMatrix2d):TVector2d {.noInit.} =
|
||||
result.y=v.x*m.ay+v.y*m.by
|
||||
|
||||
|
||||
proc `&=`*(v:var TVector2d,m:TMatrix2d) {.inline.}=
|
||||
proc `&=`*(v:var Vector2d,m:Matrix2d) {.inline.}=
|
||||
## Applies transformation `m` onto `v` in place.
|
||||
## Transforming a vector ignores the translational part
|
||||
## of the matrix.
|
||||
@@ -412,7 +412,7 @@ proc `&=`*(v:var TVector2d,m:TMatrix2d) {.inline.}=
|
||||
v.x=newx
|
||||
|
||||
|
||||
proc tryNormalize*(v:var TVector2d):bool=
|
||||
proc tryNormalize*(v:var Vector2d):bool=
|
||||
## Modifies `v` to have a length of 1.0, keeping its angle.
|
||||
## If `v` has zero length (and thus no angle), it is left unmodified and
|
||||
## false is returned, otherwise true is returned.
|
||||
@@ -427,13 +427,13 @@ proc tryNormalize*(v:var TVector2d):bool=
|
||||
return true
|
||||
|
||||
|
||||
proc normalize*(v:var TVector2d) {.inline.}=
|
||||
proc normalize*(v:var Vector2d) {.inline.}=
|
||||
## Modifies `v` to have a length of 1.0, keeping its angle.
|
||||
## If `v` has zero length, an EDivByZero will be raised.
|
||||
if not tryNormalize(v):
|
||||
raise newException(DivByZeroError,"Cannot normalize zero length vector")
|
||||
|
||||
proc transformNorm*(v:var TVector2d,t:TMatrix2d)=
|
||||
proc transformNorm*(v:var Vector2d,t:Matrix2d)=
|
||||
## Applies a normal direction transformation `t` onto `v` in place.
|
||||
## The resulting vector is *not* normalized. Transforming a vector ignores the
|
||||
## translational part of the matrix. If the matrix is not invertible
|
||||
@@ -452,7 +452,7 @@ proc transformNorm*(v:var TVector2d,t:TMatrix2d)=
|
||||
v.y = (t.ax*v.y-t.bx*v.x)/d
|
||||
v.x = newx
|
||||
|
||||
proc transformInv*(v:var TVector2d,t:TMatrix2d)=
|
||||
proc transformInv*(v:var Vector2d,t:Matrix2d)=
|
||||
## Applies inverse of a transformation `t` to `v` in place.
|
||||
## This is faster than creating an inverse matrix and apply() it.
|
||||
## Transforming a vector ignores the translational part
|
||||
@@ -467,7 +467,7 @@ proc transformInv*(v:var TVector2d,t:TMatrix2d)=
|
||||
v.y = (t.ax*v.y-t.ay*v.x)/d
|
||||
v.x = newx
|
||||
|
||||
proc transformNormInv*(v:var TVector2d,t:TMatrix2d)=
|
||||
proc transformNormInv*(v:var Vector2d,t:Matrix2d)=
|
||||
## Applies an inverse normal direction transformation `t` onto `v` in place.
|
||||
## This is faster than creating an inverse
|
||||
## matrix and transformNorm(...) it. Transforming a vector ignores the
|
||||
@@ -484,25 +484,25 @@ proc transformNormInv*(v:var TVector2d,t:TMatrix2d)=
|
||||
v.y=t.by*v.y+t.bx*v.x
|
||||
v.x=newx
|
||||
|
||||
proc rotate90*(v:var TVector2d) {.inline.}=
|
||||
proc rotate90*(v:var Vector2d) {.inline.}=
|
||||
## Quickly rotates vector `v` 90 degrees counter clockwise,
|
||||
## without using any trigonometrics.
|
||||
swap(v.x,v.y)
|
||||
v.x= -v.x
|
||||
|
||||
proc rotate180*(v:var TVector2d){.inline.}=
|
||||
proc rotate180*(v:var Vector2d){.inline.}=
|
||||
## Quickly rotates vector `v` 180 degrees counter clockwise,
|
||||
## without using any trigonometrics.
|
||||
v.x= -v.x
|
||||
v.y= -v.y
|
||||
|
||||
proc rotate270*(v:var TVector2d) {.inline.}=
|
||||
proc rotate270*(v:var Vector2d) {.inline.}=
|
||||
## Quickly rotates vector `v` 270 degrees counter clockwise,
|
||||
## without using any trigonometrics.
|
||||
swap(v.x,v.y)
|
||||
v.y= -v.y
|
||||
|
||||
proc rotate*(v:var TVector2d,rad:float) =
|
||||
proc rotate*(v:var Vector2d,rad:float) =
|
||||
## Rotates vector `v` `rad` radians in place.
|
||||
let
|
||||
s=sin(rad)
|
||||
@@ -511,18 +511,18 @@ proc rotate*(v:var TVector2d,rad:float) =
|
||||
v.y=c*v.y+s*v.x
|
||||
v.x=newx
|
||||
|
||||
proc scale*(v:var TVector2d,fac:float){.inline.}=
|
||||
proc scale*(v:var Vector2d,fac:float){.inline.}=
|
||||
## Scales vector `v` `rad` radians in place.
|
||||
v.x*=fac
|
||||
v.y*=fac
|
||||
|
||||
proc stretch*(v:var TVector2d,facx,facy:float){.inline.}=
|
||||
proc stretch*(v:var Vector2d,facx,facy:float){.inline.}=
|
||||
## Stretches vector `v` `facx` times horizontally,
|
||||
## and `facy` times vertically.
|
||||
v.x*=facx
|
||||
v.y*=facy
|
||||
|
||||
proc mirror*(v:var TVector2d,mirrvec:TVector2d)=
|
||||
proc mirror*(v:var Vector2d,mirrvec:Vector2d)=
|
||||
## Mirrors vector `v` using `mirrvec` as mirror direction.
|
||||
let
|
||||
sqx=mirrvec.x*mirrvec.x
|
||||
@@ -539,7 +539,7 @@ proc mirror*(v:var TVector2d,mirrvec:TVector2d)=
|
||||
v.x=newx
|
||||
|
||||
|
||||
proc `-` *(v:TVector2d):TVector2d=
|
||||
proc `-` *(v:Vector2d):Vector2d=
|
||||
## Negates a vector
|
||||
result.x= -v.x
|
||||
result.y= -v.y
|
||||
@@ -555,27 +555,27 @@ makeBinOpAssignVector(`*=`)
|
||||
makeBinOpAssignVector(`/=`)
|
||||
|
||||
|
||||
proc dot*(v1,v2:TVector2d):float=
|
||||
proc dot*(v1,v2:Vector2d):float=
|
||||
## Computes the dot product of two vectors.
|
||||
## Returns 0.0 if the vectors are perpendicular.
|
||||
return v1.x*v2.x+v1.y*v2.y
|
||||
|
||||
proc cross*(v1,v2:TVector2d):float=
|
||||
proc cross*(v1,v2:Vector2d):float=
|
||||
## Computes the cross product of two vectors, also called
|
||||
## the 'perpendicular dot product' in 2d. Returns 0.0 if the vectors
|
||||
## are parallel.
|
||||
return v1.x*v2.y-v1.y*v2.x
|
||||
|
||||
proc equals*(v1,v2:TVector2d,tol=1.0e-6):bool=
|
||||
proc equals*(v1,v2:Vector2d,tol=1.0e-6):bool=
|
||||
## Checks if two vectors approximately equals with a tolerance.
|
||||
return abs(v2.x-v1.x)<=tol and abs(v2.y-v1.y)<=tol
|
||||
|
||||
proc `=~` *(v1,v2:TVector2d):bool=
|
||||
proc `=~` *(v1,v2:Vector2d):bool=
|
||||
## Checks if two vectors approximately equals with a
|
||||
## hardcoded tolerance 1e-6
|
||||
equals(v1,v2)
|
||||
|
||||
proc angleTo*(v1,v2:TVector2d):float=
|
||||
proc angleTo*(v1,v2:Vector2d):float=
|
||||
## Returns the smallest of the two possible angles
|
||||
## between `v1` and `v2` in radians.
|
||||
var
|
||||
@@ -585,7 +585,7 @@ proc angleTo*(v1,v2:TVector2d):float=
|
||||
return 0.0 # zero length vector has zero angle to any other vector
|
||||
return safeArccos(dot(nv1,nv2))
|
||||
|
||||
proc angleCCW*(v1,v2:TVector2d):float=
|
||||
proc angleCCW*(v1,v2:Vector2d):float=
|
||||
## Returns the counter clockwise plane angle from `v1` to `v2`,
|
||||
## in range 0 - 2*PI
|
||||
let a=v1.angleTo(v2)
|
||||
@@ -593,7 +593,7 @@ proc angleCCW*(v1,v2:TVector2d):float=
|
||||
return a
|
||||
return DEG360-a
|
||||
|
||||
proc angleCW*(v1,v2:TVector2d):float=
|
||||
proc angleCW*(v1,v2:Vector2d):float=
|
||||
## Returns the clockwise plane angle from `v1` to `v2`,
|
||||
## in range 0 - 2*PI
|
||||
let a=v1.angleTo(v2)
|
||||
@@ -601,7 +601,7 @@ proc angleCW*(v1,v2:TVector2d):float=
|
||||
return a
|
||||
return DEG360-a
|
||||
|
||||
proc turnAngle*(v1,v2:TVector2d):float=
|
||||
proc turnAngle*(v1,v2:Vector2d):float=
|
||||
## Returns the amount v1 should be rotated (in radians) to equal v2,
|
||||
## in range -PI to PI
|
||||
let a=v1.angleTo(v2)
|
||||
@@ -609,7 +609,7 @@ proc turnAngle*(v1,v2:TVector2d):float=
|
||||
return -a
|
||||
return a
|
||||
|
||||
proc bisect*(v1,v2:TVector2d):TVector2d {.noInit.}=
|
||||
proc bisect*(v1,v2:Vector2d):Vector2d {.noInit.}=
|
||||
## Computes the bisector between v1 and v2 as a normalized vector.
|
||||
## If one of the input vectors has zero length, a normalized version
|
||||
## of the other is returned. If both input vectors has zero length,
|
||||
@@ -645,24 +645,24 @@ proc bisect*(v1,v2:TVector2d):TVector2d {.noInit.}=
|
||||
|
||||
|
||||
# ***************************************
|
||||
# TPoint2d implementation
|
||||
# Point2d implementation
|
||||
# ***************************************
|
||||
|
||||
proc point2d*(x,y:float):TPoint2d =
|
||||
proc point2d*(x,y:float):Point2d =
|
||||
result.x=x
|
||||
result.y=y
|
||||
|
||||
proc sqrDist*(a,b:TPoint2d):float=
|
||||
proc sqrDist*(a,b:Point2d):float=
|
||||
## Computes the squared distance between `a` and `b`
|
||||
let dx=b.x-a.x
|
||||
let dy=b.y-a.y
|
||||
result=dx*dx+dy*dy
|
||||
|
||||
proc dist*(a,b:TPoint2d):float {.inline.}=
|
||||
proc dist*(a,b:Point2d):float {.inline.}=
|
||||
## Computes the absolute distance between `a` and `b`
|
||||
result=sqrt(sqrDist(a,b))
|
||||
|
||||
proc angle*(a,b:TPoint2d):float=
|
||||
proc angle*(a,b:Point2d):float=
|
||||
## Computes the angle of the vector `b`-`a`
|
||||
let dx=b.x-a.x
|
||||
let dy=b.y-a.y
|
||||
@@ -670,13 +670,13 @@ proc angle*(a,b:TPoint2d):float=
|
||||
if result<0:
|
||||
result += DEG360
|
||||
|
||||
proc `$` *(p:TPoint2d):string=
|
||||
proc `$` *(p:Point2d):string=
|
||||
## String representation of `p`
|
||||
result=rtos(p.x)
|
||||
result.add(",")
|
||||
result.add(rtos(p.y))
|
||||
|
||||
proc `&`*(p:TPoint2d,t:TMatrix2d):TPoint2d {.noInit,inline.} =
|
||||
proc `&`*(p:Point2d,t:Matrix2d):Point2d {.noInit,inline.} =
|
||||
## Concatenates a point `p` with a transform `t`,
|
||||
## resulting in a new, transformed point.
|
||||
|
||||
@@ -686,14 +686,14 @@ proc `&`*(p:TPoint2d,t:TMatrix2d):TPoint2d {.noInit,inline.} =
|
||||
result.x=p.x*t.ax+p.y*t.bx+t.tx
|
||||
result.y=p.x*t.ay+p.y*t.by+t.ty
|
||||
|
||||
proc `&=` *(p:var TPoint2d,t:TMatrix2d) {.inline.}=
|
||||
proc `&=` *(p:var Point2d,t:Matrix2d) {.inline.}=
|
||||
## Applies transformation `t` onto `p` in place.
|
||||
let newx=p.x*t.ax+p.y*t.bx+t.tx
|
||||
p.y=p.x*t.ay+p.y*t.by+t.ty
|
||||
p.x=newx
|
||||
|
||||
|
||||
proc transformInv*(p:var TPoint2d,t:TMatrix2d){.inline.}=
|
||||
proc transformInv*(p:var Point2d,t:Matrix2d){.inline.}=
|
||||
## Applies the inverse of transformation `t` onto `p` in place.
|
||||
## If the matrix is not invertable (determinant=0) , EDivByZero will
|
||||
## be raised.
|
||||
@@ -710,48 +710,48 @@ proc transformInv*(p:var TPoint2d,t:TMatrix2d){.inline.}=
|
||||
p.x=newx
|
||||
|
||||
|
||||
proc `+`*(p:TPoint2d,v:TVector2d):TPoint2d {.noInit,inline.} =
|
||||
proc `+`*(p:Point2d,v:Vector2d):Point2d {.noInit,inline.} =
|
||||
## Adds a vector `v` to a point `p`, resulting
|
||||
## in a new point.
|
||||
result.x=p.x+v.x
|
||||
result.y=p.y+v.y
|
||||
|
||||
proc `+=`*(p:var TPoint2d,v:TVector2d) {.noInit,inline.} =
|
||||
proc `+=`*(p:var Point2d,v:Vector2d) {.noInit,inline.} =
|
||||
## Adds a vector `v` to a point `p` in place.
|
||||
p.x+=v.x
|
||||
p.y+=v.y
|
||||
|
||||
proc `-`*(p:TPoint2d,v:TVector2d):TPoint2d {.noInit,inline.} =
|
||||
proc `-`*(p:Point2d,v:Vector2d):Point2d {.noInit,inline.} =
|
||||
## Subtracts a vector `v` from a point `p`, resulting
|
||||
## in a new point.
|
||||
result.x=p.x-v.x
|
||||
result.y=p.y-v.y
|
||||
|
||||
proc `-`*(p1,p2:TPoint2d):TVector2d {.noInit,inline.} =
|
||||
proc `-`*(p1,p2:Point2d):Vector2d {.noInit,inline.} =
|
||||
## Subtracts `p2`from `p1` resulting in a difference vector.
|
||||
result.x=p1.x-p2.x
|
||||
result.y=p1.y-p2.y
|
||||
|
||||
proc `-=`*(p:var TPoint2d,v:TVector2d) {.noInit,inline.} =
|
||||
proc `-=`*(p:var Point2d,v:Vector2d) {.noInit,inline.} =
|
||||
## Subtracts a vector `v` from a point `p` in place.
|
||||
p.x-=v.x
|
||||
p.y-=v.y
|
||||
|
||||
proc equals(p1,p2:TPoint2d,tol=1.0e-6):bool {.inline.}=
|
||||
proc equals(p1,p2:Point2d,tol=1.0e-6):bool {.inline.}=
|
||||
## Checks if two points approximately equals with a tolerance.
|
||||
return abs(p2.x-p1.x)<=tol and abs(p2.y-p1.y)<=tol
|
||||
|
||||
proc `=~`*(p1,p2:TPoint2d):bool {.inline.}=
|
||||
proc `=~`*(p1,p2:Point2d):bool {.inline.}=
|
||||
## Checks if two vectors approximately equals with a
|
||||
## hardcoded tolerance 1e-6
|
||||
equals(p1,p2)
|
||||
|
||||
proc polar*(p:TPoint2d,ang,dist:float):TPoint2d {.noInit.} =
|
||||
proc polar*(p:Point2d,ang,dist:float):Point2d {.noInit.} =
|
||||
## Returns a point with a given angle and distance away from `p`
|
||||
result.x=p.x+cos(ang)*dist
|
||||
result.y=p.y+sin(ang)*dist
|
||||
|
||||
proc rotate*(p:var TPoint2d,rad:float)=
|
||||
proc rotate*(p:var Point2d,rad:float)=
|
||||
## Rotates a point in place `rad` radians around origo.
|
||||
let
|
||||
c=cos(rad)
|
||||
@@ -760,7 +760,7 @@ proc rotate*(p:var TPoint2d,rad:float)=
|
||||
p.y=p.y*c+p.x*s
|
||||
p.x=newx
|
||||
|
||||
proc rotate*(p:var TPoint2d,rad:float,org:TPoint2d)=
|
||||
proc rotate*(p:var Point2d,rad:float,org:Point2d)=
|
||||
## Rotates a point in place `rad` radians using `org` as
|
||||
## center of rotation.
|
||||
let
|
||||
@@ -770,50 +770,50 @@ proc rotate*(p:var TPoint2d,rad:float,org:TPoint2d)=
|
||||
p.y=(p.y - org.y) * c + (p.x - org.x) * s + org.y
|
||||
p.x=newx
|
||||
|
||||
proc scale*(p:var TPoint2d,fac:float) {.inline.}=
|
||||
proc scale*(p:var Point2d,fac:float) {.inline.}=
|
||||
## Scales a point in place `fac` times with world origo as origin.
|
||||
p.x*=fac
|
||||
p.y*=fac
|
||||
|
||||
proc scale*(p:var TPoint2d,fac:float,org:TPoint2d){.inline.}=
|
||||
proc scale*(p:var Point2d,fac:float,org:Point2d){.inline.}=
|
||||
## Scales the point in place `fac` times with `org` as origin.
|
||||
p.x=(p.x - org.x) * fac + org.x
|
||||
p.y=(p.y - org.y) * fac + org.y
|
||||
|
||||
proc stretch*(p:var TPoint2d,facx,facy:float){.inline.}=
|
||||
proc stretch*(p:var Point2d,facx,facy:float){.inline.}=
|
||||
## Scales a point in place non uniformly `facx` and `facy` times with
|
||||
## world origo as origin.
|
||||
p.x*=facx
|
||||
p.y*=facy
|
||||
|
||||
proc stretch*(p:var TPoint2d,facx,facy:float,org:TPoint2d){.inline.}=
|
||||
proc stretch*(p:var Point2d,facx,facy:float,org:Point2d){.inline.}=
|
||||
## Scales the point in place non uniformly `facx` and `facy` times with
|
||||
## `org` as origin.
|
||||
p.x=(p.x - org.x) * facx + org.x
|
||||
p.y=(p.y - org.y) * facy + org.y
|
||||
|
||||
proc move*(p:var TPoint2d,dx,dy:float){.inline.}=
|
||||
proc move*(p:var Point2d,dx,dy:float){.inline.}=
|
||||
## Translates a point `dx`, `dy` in place.
|
||||
p.x+=dx
|
||||
p.y+=dy
|
||||
|
||||
proc move*(p:var TPoint2d,v:TVector2d){.inline.}=
|
||||
proc move*(p:var Point2d,v:Vector2d){.inline.}=
|
||||
## Translates a point with vector `v` in place.
|
||||
p.x+=v.x
|
||||
p.y+=v.y
|
||||
|
||||
proc sgnArea*(a,b,c:TPoint2d):float=
|
||||
proc sgnArea*(a,b,c:Point2d):float=
|
||||
## Computes the signed area of the triangle thru points `a`,`b` and `c`
|
||||
## result>0.0 for counter clockwise triangle
|
||||
## result<0.0 for clockwise triangle
|
||||
## This is commonly used to determinate side of a point with respect to a line.
|
||||
return ((b.x - c.x) * (b.y - a.y)-(b.y - c.y) * (b.x - a.x))*0.5
|
||||
|
||||
proc area*(a,b,c:TPoint2d):float=
|
||||
proc area*(a,b,c:Point2d):float=
|
||||
## Computes the area of the triangle thru points `a`,`b` and `c`
|
||||
return abs(sgnArea(a,b,c))
|
||||
|
||||
proc closestPoint*(p:TPoint2d,pts:varargs[TPoint2d]):TPoint2d=
|
||||
proc closestPoint*(p:Point2d,pts:varargs[Point2d]):Point2d=
|
||||
## Returns a point selected from `pts`, that has the closest
|
||||
## euclidean distance to `p`
|
||||
assert(pts.len>0) # must have at least one point
|
||||
|
||||
@@ -25,25 +25,25 @@ import times
|
||||
##
|
||||
## # Create a matrix which first rotates, then scales and at last translates
|
||||
##
|
||||
## var m:TMatrix3d=rotate(PI,vector3d(1,1,2.5)) & scale(2.0) & move(100.0,200.0,300.0)
|
||||
## var m:Matrix3d=rotate(PI,vector3d(1,1,2.5)) & scale(2.0) & move(100.0,200.0,300.0)
|
||||
##
|
||||
## # Create a 3d point at (100,150,200) and a vector (5,2,3)
|
||||
##
|
||||
## var pt:TPoint3d=point3d(100.0,150.0,200.0)
|
||||
## var pt:Point3d=point3d(100.0,150.0,200.0)
|
||||
##
|
||||
## var vec:TVector3d=vector3d(5.0,2.0,3.0)
|
||||
## var vec:Vector3d=vector3d(5.0,2.0,3.0)
|
||||
##
|
||||
##
|
||||
## pt &= m # transforms pt in place
|
||||
##
|
||||
## var pt2:TPoint3d=pt & m #concatenates pt with m and returns a new point
|
||||
## var pt2:Point3d=pt & m #concatenates pt with m and returns a new point
|
||||
##
|
||||
## var vec2:TVector3d=vec & m #concatenates vec with m and returns a new vector
|
||||
## var vec2:Vector3d=vec & m #concatenates vec with m and returns a new vector
|
||||
|
||||
|
||||
|
||||
type
|
||||
TMatrix3d* =object
|
||||
Matrix3d* =object
|
||||
## Implements a row major 3d matrix, which means
|
||||
## transformations are applied the order they are concatenated.
|
||||
## This matrix is stored as an 4x4 matrix:
|
||||
@@ -52,31 +52,31 @@ type
|
||||
## [ cx cy cz cw ]
|
||||
## [ tx ty tz tw ]
|
||||
ax*,ay*,az*,aw*, bx*,by*,bz*,bw*, cx*,cy*,cz*,cw*, tx*,ty*,tz*,tw*:float
|
||||
TPoint3d* = object
|
||||
Point3d* = object
|
||||
## Implements a non-homegeneous 2d point stored as
|
||||
## an `x` , `y` and `z` coordinate.
|
||||
x*,y*,z*:float
|
||||
TVector3d* = object
|
||||
Vector3d* = object
|
||||
## Implements a 3d **direction vector** stored as
|
||||
## an `x` , `y` and `z` coordinate. Direction vector means,
|
||||
## that when transforming a vector with a matrix, the translational
|
||||
## part of the matrix is ignored.
|
||||
x*,y*,z*:float
|
||||
|
||||
{.deprecated: [TMatrix3d: Matrix3d, TPoint3d: Point3d, TVector3d: Vector3d].}
|
||||
|
||||
|
||||
# Some forward declarations
|
||||
proc matrix3d*(ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw:float):TMatrix3d {.noInit.}
|
||||
proc matrix3d*(ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw:float):Matrix3d {.noInit.}
|
||||
## Creates a new 4x4 3d transformation matrix.
|
||||
## `ax` , `ay` , `az` is the local x axis.
|
||||
## `bx` , `by` , `bz` is the local y axis.
|
||||
## `cx` , `cy` , `cz` is the local z axis.
|
||||
## `tx` , `ty` , `tz` is the translation.
|
||||
proc vector3d*(x,y,z:float):TVector3d {.noInit,inline.}
|
||||
proc vector3d*(x,y,z:float):Vector3d {.noInit,inline.}
|
||||
## Returns a new 3d vector (`x`,`y`,`z`)
|
||||
proc point3d*(x,y,z:float):TPoint3d {.noInit,inline.}
|
||||
proc point3d*(x,y,z:float):Point3d {.noInit,inline.}
|
||||
## Returns a new 4d point (`x`,`y`,`z`)
|
||||
proc tryNormalize*(v:var TVector3d):bool
|
||||
proc tryNormalize*(v:var Vector3d):bool
|
||||
## Modifies `v` to have a length of 1.0, keeping its angle.
|
||||
## If `v` has zero length (and thus no angle), it is left unmodified and false is
|
||||
## returned, otherwise true is returned.
|
||||
@@ -84,19 +84,19 @@ proc tryNormalize*(v:var TVector3d):bool
|
||||
|
||||
|
||||
let
|
||||
IDMATRIX*:TMatrix3d=matrix3d(
|
||||
IDMATRIX*:Matrix3d=matrix3d(
|
||||
1.0,0.0,0.0,0.0,
|
||||
0.0,1.0,0.0,0.0,
|
||||
0.0,0.0,1.0,0.0,
|
||||
0.0,0.0,0.0,1.0)
|
||||
## Quick access to a 3d identity matrix
|
||||
ORIGO*:TPoint3d=point3d(0.0,0.0,0.0)
|
||||
ORIGO*:Point3d=point3d(0.0,0.0,0.0)
|
||||
## Quick access to point (0,0)
|
||||
XAXIS*:TVector3d=vector3d(1.0,0.0,0.0)
|
||||
XAXIS*:Vector3d=vector3d(1.0,0.0,0.0)
|
||||
## Quick access to an 3d x-axis unit vector
|
||||
YAXIS*:TVector3d=vector3d(0.0,1.0,0.0)
|
||||
YAXIS*:Vector3d=vector3d(0.0,1.0,0.0)
|
||||
## Quick access to an 3d y-axis unit vector
|
||||
ZAXIS*:TVector3d=vector3d(0.0,0.0,1.0)
|
||||
ZAXIS*:Vector3d=vector3d(0.0,0.0,1.0)
|
||||
## Quick access to an 3d z-axis unit vector
|
||||
|
||||
|
||||
@@ -116,27 +116,27 @@ proc safeArccos(v:float):float=
|
||||
|
||||
template makeBinOpVector(s:expr)=
|
||||
## implements binary operators + , - , * and / for vectors
|
||||
proc s*(a,b:TVector3d):TVector3d {.inline,noInit.} =
|
||||
proc s*(a,b:Vector3d):Vector3d {.inline,noInit.} =
|
||||
vector3d(s(a.x,b.x),s(a.y,b.y),s(a.z,b.z))
|
||||
proc s*(a:TVector3d,b:float):TVector3d {.inline,noInit.} =
|
||||
proc s*(a:Vector3d,b:float):Vector3d {.inline,noInit.} =
|
||||
vector3d(s(a.x,b),s(a.y,b),s(a.z,b))
|
||||
proc s*(a:float,b:TVector3d):TVector3d {.inline,noInit.} =
|
||||
proc s*(a:float,b:Vector3d):Vector3d {.inline,noInit.} =
|
||||
vector3d(s(a,b.x),s(a,b.y),s(a,b.z))
|
||||
|
||||
template makeBinOpAssignVector(s:expr)=
|
||||
## implements inplace binary operators += , -= , /= and *= for vectors
|
||||
proc s*(a:var TVector3d,b:TVector3d) {.inline.} =
|
||||
proc s*(a:var Vector3d,b:Vector3d) {.inline.} =
|
||||
s(a.x,b.x) ; s(a.y,b.y) ; s(a.z,b.z)
|
||||
proc s*(a:var TVector3d,b:float) {.inline.} =
|
||||
proc s*(a:var Vector3d,b:float) {.inline.} =
|
||||
s(a.x,b) ; s(a.y,b) ; s(a.z,b)
|
||||
|
||||
|
||||
|
||||
# ***************************************
|
||||
# TMatrix3d implementation
|
||||
# Matrix3d implementation
|
||||
# ***************************************
|
||||
|
||||
proc setElements*(t:var TMatrix3d,ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw:float) {.inline.}=
|
||||
proc setElements*(t:var Matrix3d,ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw:float) {.inline.}=
|
||||
## Sets arbitrary elements in an exisitng matrix.
|
||||
t.ax=ax
|
||||
t.ay=ay
|
||||
@@ -155,10 +155,10 @@ proc setElements*(t:var TMatrix3d,ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,t
|
||||
t.tz=tz
|
||||
t.tw=tw
|
||||
|
||||
proc matrix3d*(ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw:float):TMatrix3d =
|
||||
proc matrix3d*(ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw:float):Matrix3d =
|
||||
result.setElements(ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw)
|
||||
|
||||
proc `&`*(a,b:TMatrix3d):TMatrix3d {.noinit.} =
|
||||
proc `&`*(a,b:Matrix3d):Matrix3d {.noinit.} =
|
||||
## Concatenates matrices returning a new matrix.
|
||||
result.setElements(
|
||||
a.aw*b.tx+a.az*b.cx+a.ay*b.bx+a.ax*b.ax,
|
||||
@@ -182,36 +182,36 @@ proc `&`*(a,b:TMatrix3d):TMatrix3d {.noinit.} =
|
||||
a.tw*b.tw+a.tz*b.cw+a.ty*b.bw+a.tx*b.aw)
|
||||
|
||||
|
||||
proc scale*(s:float):TMatrix3d {.noInit.} =
|
||||
proc scale*(s:float):Matrix3d {.noInit.} =
|
||||
## Returns a new scaling matrix.
|
||||
result.setElements(s,0,0,0, 0,s,0,0, 0,0,s,0, 0,0,0,1)
|
||||
|
||||
proc scale*(s:float,org:TPoint3d):TMatrix3d {.noInit.} =
|
||||
proc scale*(s:float,org:Point3d):Matrix3d {.noInit.} =
|
||||
## Returns a new scaling matrix using, `org` as scale origin.
|
||||
result.setElements(s,0,0,0, 0,s,0,0, 0,0,s,0,
|
||||
org.x-s*org.x,org.y-s*org.y,org.z-s*org.z,1.0)
|
||||
|
||||
proc stretch*(sx,sy,sz:float):TMatrix3d {.noInit.} =
|
||||
proc stretch*(sx,sy,sz:float):Matrix3d {.noInit.} =
|
||||
## Returns new a stretch matrix, which is a
|
||||
## scale matrix with non uniform scale in x,y and z.
|
||||
result.setElements(sx,0,0,0, 0,sy,0,0, 0,0,sz,0, 0,0,0,1)
|
||||
|
||||
proc stretch*(sx,sy,sz:float,org:TPoint3d):TMatrix3d {.noInit.} =
|
||||
proc stretch*(sx,sy,sz:float,org:Point3d):Matrix3d {.noInit.} =
|
||||
## Returns a new stretch matrix, which is a
|
||||
## scale matrix with non uniform scale in x,y and z.
|
||||
## `org` is used as stretch origin.
|
||||
result.setElements(sx,0,0,0, 0,sy,0,0, 0,0,sz,0, org.x-sx*org.x,org.y-sy*org.y,org.z-sz*org.z,1)
|
||||
|
||||
proc move*(dx,dy,dz:float):TMatrix3d {.noInit.} =
|
||||
proc move*(dx,dy,dz:float):Matrix3d {.noInit.} =
|
||||
## Returns a new translation matrix.
|
||||
result.setElements(1,0,0,0, 0,1,0,0, 0,0,1,0, dx,dy,dz,1)
|
||||
|
||||
proc move*(v:TVector3d):TMatrix3d {.noInit.} =
|
||||
proc move*(v:Vector3d):Matrix3d {.noInit.} =
|
||||
## Returns a new translation matrix from a vector.
|
||||
result.setElements(1,0,0,0, 0,1,0,0, 0,0,1,0, v.x,v.y,v.z,1)
|
||||
|
||||
|
||||
proc rotate*(angle:float,axis:TVector3d):TMatrix3d {.noInit.}=
|
||||
proc rotate*(angle:float,axis:Vector3d):Matrix3d {.noInit.}=
|
||||
## Creates a rotation matrix that rotates `angle` radians over
|
||||
## `axis`, which passes through origo.
|
||||
|
||||
@@ -242,7 +242,7 @@ proc rotate*(angle:float,axis:TVector3d):TMatrix3d {.noInit.}=
|
||||
uwomc+vsi, vwomc-usi, w2+(1.0-w2)*cs, 0.0,
|
||||
0.0,0.0,0.0,1.0)
|
||||
|
||||
proc rotate*(angle:float,org:TPoint3d,axis:TVector3d):TMatrix3d {.noInit.}=
|
||||
proc rotate*(angle:float,org:Point3d,axis:Vector3d):Matrix3d {.noInit.}=
|
||||
## Creates a rotation matrix that rotates `angle` radians over
|
||||
## `axis`, which passes through `org`.
|
||||
|
||||
@@ -282,7 +282,7 @@ proc rotate*(angle:float,org:TPoint3d,axis:TVector3d):TMatrix3d {.noInit.}=
|
||||
(c*(u2+v2)-w*(a*u+b*v))*omc+(a*v-b*u)*si,1.0)
|
||||
|
||||
|
||||
proc rotateX*(angle:float):TMatrix3d {.noInit.}=
|
||||
proc rotateX*(angle:float):Matrix3d {.noInit.}=
|
||||
## Creates a matrix that rotates around the x-axis with `angle` radians,
|
||||
## which is also called a 'roll' matrix.
|
||||
let
|
||||
@@ -294,7 +294,7 @@ proc rotateX*(angle:float):TMatrix3d {.noInit.}=
|
||||
0,-s,c,0,
|
||||
0,0,0,1)
|
||||
|
||||
proc rotateY*(angle:float):TMatrix3d {.noInit.}=
|
||||
proc rotateY*(angle:float):Matrix3d {.noInit.}=
|
||||
## Creates a matrix that rotates around the y-axis with `angle` radians,
|
||||
## which is also called a 'pitch' matrix.
|
||||
let
|
||||
@@ -306,7 +306,7 @@ proc rotateY*(angle:float):TMatrix3d {.noInit.}=
|
||||
s,0,c,0,
|
||||
0,0,0,1)
|
||||
|
||||
proc rotateZ*(angle:float):TMatrix3d {.noInit.}=
|
||||
proc rotateZ*(angle:float):Matrix3d {.noInit.}=
|
||||
## Creates a matrix that rotates around the z-axis with `angle` radians,
|
||||
## which is also called a 'yaw' matrix.
|
||||
let
|
||||
@@ -318,7 +318,7 @@ proc rotateZ*(angle:float):TMatrix3d {.noInit.}=
|
||||
0,0,1,0,
|
||||
0,0,0,1)
|
||||
|
||||
proc isUniform*(m:TMatrix3d,tol=1.0e-6):bool=
|
||||
proc isUniform*(m:Matrix3d,tol=1.0e-6):bool=
|
||||
## Checks if the transform is uniform, that is
|
||||
## perpendicular axes of equal length, which means (for example)
|
||||
## it cannot transform a sphere into an ellipsoid.
|
||||
@@ -341,7 +341,7 @@ proc isUniform*(m:TMatrix3d,tol=1.0e-6):bool=
|
||||
|
||||
|
||||
|
||||
proc mirror*(planeperp:TVector3d):TMatrix3d {.noInit.}=
|
||||
proc mirror*(planeperp:Vector3d):Matrix3d {.noInit.}=
|
||||
## Creates a matrix that mirrors over the plane that has `planeperp` as normal,
|
||||
## and passes through origo. `planeperp` does not need to be normalized.
|
||||
|
||||
@@ -365,7 +365,7 @@ proc mirror*(planeperp:TVector3d):TMatrix3d {.noInit.}=
|
||||
0,0,0,1)
|
||||
|
||||
|
||||
proc mirror*(org:TPoint3d,planeperp:TVector3d):TMatrix3d {.noInit.}=
|
||||
proc mirror*(org:Point3d,planeperp:Vector3d):Matrix3d {.noInit.}=
|
||||
## Creates a matrix that mirrors over the plane that has `planeperp` as normal,
|
||||
## and passes through `org`. `planeperp` does not need to be normalized.
|
||||
|
||||
@@ -400,7 +400,7 @@ proc mirror*(org:TPoint3d,planeperp:TVector3d):TMatrix3d {.noInit.}=
|
||||
2*(cc*tz+bc*ty+ac*tx) ,1)
|
||||
|
||||
|
||||
proc determinant*(m:TMatrix3d):float=
|
||||
proc determinant*(m:Matrix3d):float=
|
||||
## Computes the determinant of matrix `m`.
|
||||
|
||||
# This computation is gotten from ratsimp(optimize(determinant(m)))
|
||||
@@ -419,7 +419,7 @@ proc determinant*(m:TMatrix3d):float=
|
||||
(O3*m.az-O5*m.ay+O6*m.ax)*m.bw
|
||||
|
||||
|
||||
proc inverse*(m:TMatrix3d):TMatrix3d {.noInit.}=
|
||||
proc inverse*(m:Matrix3d):Matrix3d {.noInit.}=
|
||||
## Computes the inverse of matrix `m`. If the matrix
|
||||
## determinant is zero, thus not invertible, a EDivByZero
|
||||
## will be raised.
|
||||
@@ -461,7 +461,7 @@ proc inverse*(m:TMatrix3d):TMatrix3d {.noInit.}=
|
||||
(-m.ax*O7+m.ay*O14-m.az*O18)/det , (m.ax*O10-m.ay*O16+m.az*O19)/det)
|
||||
|
||||
|
||||
proc equals*(m1:TMatrix3d,m2:TMatrix3d,tol=1.0e-6):bool=
|
||||
proc equals*(m1:Matrix3d,m2:Matrix3d,tol=1.0e-6):bool=
|
||||
## Checks if all elements of `m1`and `m2` is equal within
|
||||
## a given tolerance `tol`.
|
||||
return
|
||||
@@ -482,42 +482,42 @@ proc equals*(m1:TMatrix3d,m2:TMatrix3d,tol=1.0e-6):bool=
|
||||
abs(m1.tz-m2.tz)<=tol and
|
||||
abs(m1.tw-m2.tw)<=tol
|
||||
|
||||
proc `=~`*(m1,m2:TMatrix3d):bool=
|
||||
proc `=~`*(m1,m2:Matrix3d):bool=
|
||||
## Checks if `m1` and `m2` is approximately equal, using a
|
||||
## tolerance of 1e-6.
|
||||
equals(m1,m2)
|
||||
|
||||
proc transpose*(m:TMatrix3d):TMatrix3d {.noInit.}=
|
||||
proc transpose*(m:Matrix3d):Matrix3d {.noInit.}=
|
||||
## Returns the transpose of `m`
|
||||
result.setElements(m.ax,m.bx,m.cx,m.tx,m.ay,m.by,m.cy,m.ty,m.az,m.bz,m.cz,m.tz,m.aw,m.bw,m.cw,m.tw)
|
||||
|
||||
proc getXAxis*(m:TMatrix3d):TVector3d {.noInit.}=
|
||||
proc getXAxis*(m:Matrix3d):Vector3d {.noInit.}=
|
||||
## Gets the local x axis of `m`
|
||||
result.x=m.ax
|
||||
result.y=m.ay
|
||||
result.z=m.az
|
||||
|
||||
proc getYAxis*(m:TMatrix3d):TVector3d {.noInit.}=
|
||||
proc getYAxis*(m:Matrix3d):Vector3d {.noInit.}=
|
||||
## Gets the local y axis of `m`
|
||||
result.x=m.bx
|
||||
result.y=m.by
|
||||
result.z=m.bz
|
||||
|
||||
proc getZAxis*(m:TMatrix3d):TVector3d {.noInit.}=
|
||||
proc getZAxis*(m:Matrix3d):Vector3d {.noInit.}=
|
||||
## Gets the local y axis of `m`
|
||||
result.x=m.cx
|
||||
result.y=m.cy
|
||||
result.z=m.cz
|
||||
|
||||
|
||||
proc `$`*(m:TMatrix3d):string=
|
||||
proc `$`*(m:Matrix3d):string=
|
||||
## String representation of `m`
|
||||
return rtos(m.ax) & "," & rtos(m.ay) & "," & rtos(m.az) & "," & rtos(m.aw) &
|
||||
"\n" & rtos(m.bx) & "," & rtos(m.by) & "," & rtos(m.bz) & "," & rtos(m.bw) &
|
||||
"\n" & rtos(m.cx) & "," & rtos(m.cy) & "," & rtos(m.cz) & "," & rtos(m.cw) &
|
||||
"\n" & rtos(m.tx) & "," & rtos(m.ty) & "," & rtos(m.tz) & "," & rtos(m.tw)
|
||||
|
||||
proc apply*(m:TMatrix3d, x,y,z:var float, translate=false)=
|
||||
proc apply*(m:Matrix3d, x,y,z:var float, translate=false)=
|
||||
## Applies transformation `m` onto `x` , `y` , `z` , optionally
|
||||
## using the translation part of the matrix.
|
||||
let
|
||||
@@ -535,18 +535,18 @@ proc apply*(m:TMatrix3d, x,y,z:var float, translate=false)=
|
||||
z+=m.tz
|
||||
|
||||
# ***************************************
|
||||
# TVector3d implementation
|
||||
# Vector3d implementation
|
||||
# ***************************************
|
||||
proc vector3d*(x,y,z:float):TVector3d=
|
||||
proc vector3d*(x,y,z:float):Vector3d=
|
||||
result.x=x
|
||||
result.y=y
|
||||
result.z=z
|
||||
|
||||
proc len*(v:TVector3d):float=
|
||||
proc len*(v:Vector3d):float=
|
||||
## Returns the length of the vector `v`.
|
||||
sqrt(v.x*v.x+v.y*v.y+v.z*v.z)
|
||||
|
||||
proc `len=`*(v:var TVector3d,newlen:float) {.noInit.} =
|
||||
proc `len=`*(v:var Vector3d,newlen:float) {.noInit.} =
|
||||
## Sets the length of the vector, keeping its direction.
|
||||
## If the vector has zero length before changing it's length,
|
||||
## an arbitrary vector of the requested length is returned.
|
||||
@@ -571,12 +571,12 @@ proc `len=`*(v:var TVector3d,newlen:float) {.noInit.} =
|
||||
v.z*=fac
|
||||
|
||||
|
||||
proc sqrLen*(v:TVector3d):float {.inline.}=
|
||||
proc sqrLen*(v:Vector3d):float {.inline.}=
|
||||
## Computes the squared length of the vector, which is
|
||||
## faster than computing the absolute length.
|
||||
return v.x*v.x+v.y*v.y+v.z*v.z
|
||||
|
||||
proc `$` *(v:TVector3d):string=
|
||||
proc `$` *(v:Vector3d):string=
|
||||
## String representation of `v`
|
||||
result=rtos(v.x)
|
||||
result.add(",")
|
||||
@@ -584,7 +584,7 @@ proc `$` *(v:TVector3d):string=
|
||||
result.add(",")
|
||||
result.add(rtos(v.z))
|
||||
|
||||
proc `&` *(v:TVector3d,m:TMatrix3d):TVector3d {.noInit.} =
|
||||
proc `&` *(v:Vector3d,m:Matrix3d):Vector3d {.noInit.} =
|
||||
## Concatenate vector `v` with a transformation matrix.
|
||||
## Transforming a vector ignores the translational part
|
||||
## of the matrix.
|
||||
@@ -601,7 +601,7 @@ proc `&` *(v:TVector3d,m:TMatrix3d):TVector3d {.noInit.} =
|
||||
result.x=newx
|
||||
|
||||
|
||||
proc `&=` *(v:var TVector3d,m:TMatrix3d) {.noInit.} =
|
||||
proc `&=` *(v:var Vector3d,m:Matrix3d) {.noInit.} =
|
||||
## Applies transformation `m` onto `v` in place.
|
||||
## Transforming a vector ignores the translational part
|
||||
## of the matrix.
|
||||
@@ -618,7 +618,7 @@ proc `&=` *(v:var TVector3d,m:TMatrix3d) {.noInit.} =
|
||||
v.y=newy
|
||||
v.x=newx
|
||||
|
||||
proc transformNorm*(v:var TVector3d,m:TMatrix3d)=
|
||||
proc transformNorm*(v:var Vector3d,m:Matrix3d)=
|
||||
## Applies a normal direction transformation `m` onto `v` in place.
|
||||
## The resulting vector is *not* normalized. Transforming a vector ignores the
|
||||
## translational part of the matrix. If the matrix is not invertible
|
||||
@@ -631,7 +631,7 @@ proc transformNorm*(v:var TVector3d,m:TMatrix3d)=
|
||||
# (possibly by hardware) as well as having a consistent API with the 2d version.
|
||||
v&=transpose(inverse(m))
|
||||
|
||||
proc transformInv*(v:var TVector3d,m:TMatrix3d)=
|
||||
proc transformInv*(v:var Vector3d,m:Matrix3d)=
|
||||
## Applies the inverse of `m` on vector `v`. Transforming a vector ignores
|
||||
## the translational part of the matrix. Transforming a vector ignores the
|
||||
## translational part of the matrix.
|
||||
@@ -642,7 +642,7 @@ proc transformInv*(v:var TVector3d,m:TMatrix3d)=
|
||||
# (possibly by hardware) as well as having a consistent API with the 2d version.
|
||||
v&=m.inverse
|
||||
|
||||
proc transformNormInv*(vec:var TVector3d,m:TMatrix3d)=
|
||||
proc transformNormInv*(vec:var Vector3d,m:Matrix3d)=
|
||||
## Applies an inverse normal direction transformation `m` onto `v` in place.
|
||||
## This is faster than creating an inverse
|
||||
## matrix and transformNorm(...) it. Transforming a vector ignores the
|
||||
@@ -651,7 +651,7 @@ proc transformNormInv*(vec:var TVector3d,m:TMatrix3d)=
|
||||
# see vector2d:s equivalent for a deeper look how/why this works
|
||||
vec&=m.transpose
|
||||
|
||||
proc tryNormalize*(v:var TVector3d):bool=
|
||||
proc tryNormalize*(v:var Vector3d):bool=
|
||||
## Modifies `v` to have a length of 1.0, keeping its angle.
|
||||
## If `v` has zero length (and thus no angle), it is left unmodified and false is
|
||||
## returned, otherwise true is returned.
|
||||
@@ -666,13 +666,13 @@ proc tryNormalize*(v:var TVector3d):bool=
|
||||
|
||||
return true
|
||||
|
||||
proc normalize*(v:var TVector3d) {.inline.}=
|
||||
proc normalize*(v:var Vector3d) {.inline.}=
|
||||
## Modifies `v` to have a length of 1.0, keeping its angle.
|
||||
## If `v` has zero length, an EDivByZero will be raised.
|
||||
if not tryNormalize(v):
|
||||
raise newException(DivByZeroError,"Cannot normalize zero length vector")
|
||||
|
||||
proc rotate*(vec:var TVector3d,angle:float,axis:TVector3d)=
|
||||
proc rotate*(vec:var Vector3d,angle:float,axis:Vector3d)=
|
||||
## Rotates `vec` in place, with `angle` radians over `axis`, which passes
|
||||
## through origo.
|
||||
|
||||
@@ -699,19 +699,19 @@ proc rotate*(vec:var TVector3d,angle:float,axis:TVector3d)=
|
||||
vec.y=v*uxyzomc+y*cs+(w*x-u*z)*si
|
||||
vec.z=w*uxyzomc+z*cs+(u*y-v*x)*si
|
||||
|
||||
proc scale*(v:var TVector3d,s:float)=
|
||||
proc scale*(v:var Vector3d,s:float)=
|
||||
## Scales the vector in place with factor `s`
|
||||
v.x*=s
|
||||
v.y*=s
|
||||
v.z*=s
|
||||
|
||||
proc stretch*(v:var TVector3d,sx,sy,sz:float)=
|
||||
proc stretch*(v:var Vector3d,sx,sy,sz:float)=
|
||||
## Scales the vector non uniformly with factors `sx` , `sy` , `sz`
|
||||
v.x*=sx
|
||||
v.y*=sy
|
||||
v.z*=sz
|
||||
|
||||
proc mirror*(v:var TVector3d,planeperp:TVector3d)=
|
||||
proc mirror*(v:var Vector3d,planeperp:Vector3d)=
|
||||
## Computes the mirrored vector of `v` over the plane
|
||||
## that has `planeperp` as normal direction.
|
||||
## `planeperp` does not need to be normalized.
|
||||
@@ -735,7 +735,7 @@ proc mirror*(v:var TVector3d,planeperp:TVector3d)=
|
||||
v.z= -2*(c*c*z+bc*y+ac*x)+z
|
||||
|
||||
|
||||
proc `-` *(v:TVector3d):TVector3d=
|
||||
proc `-` *(v:Vector3d):Vector3d=
|
||||
## Negates a vector
|
||||
result.x= -v.x
|
||||
result.y= -v.y
|
||||
@@ -751,12 +751,12 @@ makeBinOpAssignVector(`-=`)
|
||||
makeBinOpAssignVector(`*=`)
|
||||
makeBinOpAssignVector(`/=`)
|
||||
|
||||
proc dot*(v1,v2:TVector3d):float {.inline.}=
|
||||
proc dot*(v1,v2:Vector3d):float {.inline.}=
|
||||
## Computes the dot product of two vectors.
|
||||
## Returns 0.0 if the vectors are perpendicular.
|
||||
return v1.x*v2.x+v1.y*v2.y+v1.z*v2.z
|
||||
|
||||
proc cross*(v1,v2:TVector3d):TVector3d {.inline.}=
|
||||
proc cross*(v1,v2:Vector3d):Vector3d {.inline.}=
|
||||
## Computes the cross product of two vectors.
|
||||
## The result is a vector which is perpendicular
|
||||
## to the plane of `v1` and `v2`, which means
|
||||
@@ -766,16 +766,16 @@ proc cross*(v1,v2:TVector3d):TVector3d {.inline.}=
|
||||
result.y = (v1.z * v2.x) - (v2.z * v1.x)
|
||||
result.z = (v1.x * v2.y) - (v2.x * v1.y)
|
||||
|
||||
proc equals*(v1,v2:TVector3d,tol=1.0e-6):bool=
|
||||
proc equals*(v1,v2:Vector3d,tol=1.0e-6):bool=
|
||||
## Checks if two vectors approximately equals with a tolerance.
|
||||
return abs(v2.x-v1.x)<=tol and abs(v2.y-v1.y)<=tol and abs(v2.z-v1.z)<=tol
|
||||
|
||||
proc `=~` *(v1,v2:TVector3d):bool=
|
||||
proc `=~` *(v1,v2:Vector3d):bool=
|
||||
## Checks if two vectors approximately equals with a
|
||||
## hardcoded tolerance 1e-6
|
||||
equals(v1,v2)
|
||||
|
||||
proc angleTo*(v1,v2:TVector3d):float=
|
||||
proc angleTo*(v1,v2:Vector3d):float=
|
||||
## Returns the smallest angle between v1 and v2,
|
||||
## which is in range 0-PI
|
||||
var
|
||||
@@ -785,13 +785,13 @@ proc angleTo*(v1,v2:TVector3d):float=
|
||||
return 0.0 # zero length vector has zero angle to any other vector
|
||||
return safeArccos(dot(nv1,nv2))
|
||||
|
||||
proc arbitraryAxis*(norm:TVector3d):TMatrix3d {.noInit.}=
|
||||
proc arbitraryAxis*(norm:Vector3d):Matrix3d {.noInit.}=
|
||||
## Computes the rotation matrix that would transform
|
||||
## world z vector into `norm`. The inverse of this matrix
|
||||
## is useful to transform a planar 3d object to 2d space.
|
||||
## This is the same algorithm used to interpret DXF and DWG files.
|
||||
const lim=1.0/64.0
|
||||
var ax,ay,az:TVector3d
|
||||
var ax,ay,az:Vector3d
|
||||
if abs(norm.x)<lim and abs(norm.y)<lim:
|
||||
ax=cross(YAXIS,norm)
|
||||
else:
|
||||
@@ -808,7 +808,7 @@ proc arbitraryAxis*(norm:TVector3d):TMatrix3d {.noInit.}=
|
||||
az.x,az.y,az.z,0.0,
|
||||
0.0,0.0,0.0,1.0)
|
||||
|
||||
proc bisect*(v1,v2:TVector3d):TVector3d {.noInit.}=
|
||||
proc bisect*(v1,v2:Vector3d):Vector3d {.noInit.}=
|
||||
## Computes the bisector between v1 and v2 as a normalized vector.
|
||||
## If one of the input vectors has zero length, a normalized version
|
||||
## of the other is returned. If both input vectors has zero length,
|
||||
@@ -851,25 +851,25 @@ proc bisect*(v1,v2:TVector3d):TVector3d {.noInit.}=
|
||||
|
||||
|
||||
# ***************************************
|
||||
# TPoint3d implementation
|
||||
# Point3d implementation
|
||||
# ***************************************
|
||||
proc point3d*(x,y,z:float):TPoint3d=
|
||||
proc point3d*(x,y,z:float):Point3d=
|
||||
result.x=x
|
||||
result.y=y
|
||||
result.z=z
|
||||
|
||||
proc sqrDist*(a,b:TPoint3d):float=
|
||||
proc sqrDist*(a,b:Point3d):float=
|
||||
## Computes the squared distance between `a`and `b`
|
||||
let dx=b.x-a.x
|
||||
let dy=b.y-a.y
|
||||
let dz=b.z-a.z
|
||||
result=dx*dx+dy*dy+dz*dz
|
||||
|
||||
proc dist*(a,b:TPoint3d):float {.inline.}=
|
||||
proc dist*(a,b:Point3d):float {.inline.}=
|
||||
## Computes the absolute distance between `a`and `b`
|
||||
result=sqrt(sqrDist(a,b))
|
||||
|
||||
proc `$` *(p:TPoint3d):string=
|
||||
proc `$` *(p:Point3d):string=
|
||||
## String representation of `p`
|
||||
result=rtos(p.x)
|
||||
result.add(",")
|
||||
@@ -877,14 +877,14 @@ proc `$` *(p:TPoint3d):string=
|
||||
result.add(",")
|
||||
result.add(rtos(p.z))
|
||||
|
||||
proc `&`*(p:TPoint3d,m:TMatrix3d):TPoint3d=
|
||||
proc `&`*(p:Point3d,m:Matrix3d):Point3d=
|
||||
## Concatenates a point `p` with a transform `m`,
|
||||
## resulting in a new, transformed point.
|
||||
result.z=m.cz*p.z+m.bz*p.y+m.az*p.x+m.tz
|
||||
result.y=m.cy*p.z+m.by*p.y+m.ay*p.x+m.ty
|
||||
result.x=m.cx*p.z+m.bx*p.y+m.ax*p.x+m.tx
|
||||
|
||||
proc `&=` *(p:var TPoint3d,m:TMatrix3d)=
|
||||
proc `&=` *(p:var Point3d,m:Matrix3d)=
|
||||
## Applies transformation `m` onto `p` in place.
|
||||
let
|
||||
x=p.x
|
||||
@@ -894,7 +894,7 @@ proc `&=` *(p:var TPoint3d,m:TMatrix3d)=
|
||||
p.y=m.cy*z+m.by*y+m.ay*x+m.ty
|
||||
p.z=m.cz*z+m.bz*y+m.az*x+m.tz
|
||||
|
||||
proc transformInv*(p:var TPoint3d,m:TMatrix3d)=
|
||||
proc transformInv*(p:var Point3d,m:Matrix3d)=
|
||||
## Applies the inverse of transformation `m` onto `p` in place.
|
||||
## If the matrix is not invertable (determinant=0) , EDivByZero will
|
||||
## be raised.
|
||||
@@ -903,48 +903,48 @@ proc transformInv*(p:var TPoint3d,m:TMatrix3d)=
|
||||
p&=inverse(m)
|
||||
|
||||
|
||||
proc `+`*(p:TPoint3d,v:TVector3d):TPoint3d {.noInit,inline.} =
|
||||
proc `+`*(p:Point3d,v:Vector3d):Point3d {.noInit,inline.} =
|
||||
## Adds a vector `v` to a point `p`, resulting
|
||||
## in a new point.
|
||||
result.x=p.x+v.x
|
||||
result.y=p.y+v.y
|
||||
result.z=p.z+v.z
|
||||
|
||||
proc `+=`*(p:var TPoint3d,v:TVector3d) {.noInit,inline.} =
|
||||
proc `+=`*(p:var Point3d,v:Vector3d) {.noInit,inline.} =
|
||||
## Adds a vector `v` to a point `p` in place.
|
||||
p.x+=v.x
|
||||
p.y+=v.y
|
||||
p.z+=v.z
|
||||
|
||||
proc `-`*(p:TPoint3d,v:TVector3d):TPoint3d {.noInit,inline.} =
|
||||
proc `-`*(p:Point3d,v:Vector3d):Point3d {.noInit,inline.} =
|
||||
## Subtracts a vector `v` from a point `p`, resulting
|
||||
## in a new point.
|
||||
result.x=p.x-v.x
|
||||
result.y=p.y-v.y
|
||||
result.z=p.z-v.z
|
||||
|
||||
proc `-`*(p1,p2:TPoint3d):TVector3d {.noInit,inline.} =
|
||||
proc `-`*(p1,p2:Point3d):Vector3d {.noInit,inline.} =
|
||||
## Subtracts `p2`from `p1` resulting in a difference vector.
|
||||
result.x=p1.x-p2.x
|
||||
result.y=p1.y-p2.y
|
||||
result.z=p1.z-p2.z
|
||||
|
||||
proc `-=`*(p:var TPoint3d,v:TVector3d) {.noInit,inline.} =
|
||||
proc `-=`*(p:var Point3d,v:Vector3d) {.noInit,inline.} =
|
||||
## Subtracts a vector `v` from a point `p` in place.
|
||||
p.x-=v.x
|
||||
p.y-=v.y
|
||||
p.z-=v.z
|
||||
|
||||
proc equals(p1,p2:TPoint3d,tol=1.0e-6):bool {.inline.}=
|
||||
proc equals(p1,p2:Point3d,tol=1.0e-6):bool {.inline.}=
|
||||
## Checks if two points approximately equals with a tolerance.
|
||||
return abs(p2.x-p1.x)<=tol and abs(p2.y-p1.y)<=tol and abs(p2.z-p1.z)<=tol
|
||||
|
||||
proc `=~`*(p1,p2:TPoint3d):bool {.inline.}=
|
||||
proc `=~`*(p1,p2:Point3d):bool {.inline.}=
|
||||
## Checks if two vectors approximately equals with a
|
||||
## hardcoded tolerance 1e-6
|
||||
equals(p1,p2)
|
||||
|
||||
proc rotate*(p:var TPoint3d,rad:float,axis:TVector3d)=
|
||||
proc rotate*(p:var Point3d,rad:float,axis:Vector3d)=
|
||||
## Rotates point `p` in place `rad` radians about an axis
|
||||
## passing through origo.
|
||||
|
||||
@@ -954,7 +954,7 @@ proc rotate*(p:var TPoint3d,rad:float,axis:TVector3d)=
|
||||
p.y=v.y
|
||||
p.z=v.z
|
||||
|
||||
proc rotate*(p:var TPoint3d,angle:float,org:TPoint3d,axis:TVector3d)=
|
||||
proc rotate*(p:var Point3d,angle:float,org:Point3d,axis:Vector3d)=
|
||||
## Rotates point `p` in place `rad` radians about an axis
|
||||
## passing through `org`
|
||||
|
||||
@@ -992,26 +992,26 @@ proc rotate*(p:var TPoint3d,angle:float,org:TPoint3d,axis:TVector3d)=
|
||||
p.y=(b*(uu+ww)-v*(au+cw-uxmvymwz))*omc + y*cs + (c*u-a*w+w*x-u*z)*si
|
||||
p.z=(c*(uu+vv)-w*(au+bv-uxmvymwz))*omc + z*cs + (a*v+u*y-b*u-v*x)*si
|
||||
|
||||
proc scale*(p:var TPoint3d,fac:float) {.inline.}=
|
||||
proc scale*(p:var Point3d,fac:float) {.inline.}=
|
||||
## Scales a point in place `fac` times with world origo as origin.
|
||||
p.x*=fac
|
||||
p.y*=fac
|
||||
p.z*=fac
|
||||
|
||||
proc scale*(p:var TPoint3d,fac:float,org:TPoint3d){.inline.}=
|
||||
proc scale*(p:var Point3d,fac:float,org:Point3d){.inline.}=
|
||||
## Scales the point in place `fac` times with `org` as origin.
|
||||
p.x=(p.x - org.x) * fac + org.x
|
||||
p.y=(p.y - org.y) * fac + org.y
|
||||
p.z=(p.z - org.z) * fac + org.z
|
||||
|
||||
proc stretch*(p:var TPoint3d,facx,facy,facz:float){.inline.}=
|
||||
proc stretch*(p:var Point3d,facx,facy,facz:float){.inline.}=
|
||||
## Scales a point in place non uniformly `facx` , `facy` , `facz` times
|
||||
## with world origo as origin.
|
||||
p.x*=facx
|
||||
p.y*=facy
|
||||
p.z*=facz
|
||||
|
||||
proc stretch*(p:var TPoint3d,facx,facy,facz:float,org:TPoint3d){.inline.}=
|
||||
proc stretch*(p:var Point3d,facx,facy,facz:float,org:Point3d){.inline.}=
|
||||
## Scales the point in place non uniformly `facx` , `facy` , `facz` times
|
||||
## with `org` as origin.
|
||||
p.x=(p.x - org.x) * facx + org.x
|
||||
@@ -1019,19 +1019,19 @@ proc stretch*(p:var TPoint3d,facx,facy,facz:float,org:TPoint3d){.inline.}=
|
||||
p.z=(p.z - org.z) * facz + org.z
|
||||
|
||||
|
||||
proc move*(p:var TPoint3d,dx,dy,dz:float){.inline.}=
|
||||
proc move*(p:var Point3d,dx,dy,dz:float){.inline.}=
|
||||
## Translates a point `dx` , `dy` , `dz` in place.
|
||||
p.x+=dx
|
||||
p.y+=dy
|
||||
p.z+=dz
|
||||
|
||||
proc move*(p:var TPoint3d,v:TVector3d){.inline.}=
|
||||
proc move*(p:var Point3d,v:Vector3d){.inline.}=
|
||||
## Translates a point with vector `v` in place.
|
||||
p.x+=v.x
|
||||
p.y+=v.y
|
||||
p.z+=v.z
|
||||
|
||||
proc area*(a,b,c:TPoint3d):float {.inline.}=
|
||||
proc area*(a,b,c:Point3d):float {.inline.}=
|
||||
## Computes the area of the triangle thru points `a` , `b` and `c`
|
||||
|
||||
# The area of a planar 3d quadliteral is the magnitude of the cross
|
||||
|
||||
@@ -46,24 +46,25 @@ const
|
||||
|
||||
when sizeof(int) == 4: # 32bit
|
||||
type
|
||||
TRaw = range[0..1073741823]
|
||||
Raw = range[0..1073741823]
|
||||
## The range of uint values that can be stored directly in a value slot
|
||||
## when on a 32 bit platform
|
||||
|
||||
{.deprecated: [TRaw: Raw].}
|
||||
elif sizeof(int) == 8: # 64bit
|
||||
type
|
||||
TRaw = range[0..4611686018427387903]
|
||||
Raw = range[0..4611686018427387903]
|
||||
## The range of uint values that can be stored directly in a value slot
|
||||
## when on a 64 bit platform
|
||||
{.deprecated: [TRaw: Raw].}
|
||||
else:
|
||||
{.error: "unsupported platform".}
|
||||
|
||||
type
|
||||
TEntry = tuple
|
||||
Entry = tuple
|
||||
key: int
|
||||
value: int
|
||||
|
||||
TEntryArr = ptr array[0..10_000_000, TEntry]
|
||||
EntryArr = ptr array[0..10_000_000, Entry]
|
||||
|
||||
PConcTable[K,V] = ptr object {.pure.}
|
||||
len: int
|
||||
@@ -72,8 +73,8 @@ type
|
||||
copyIdx: int
|
||||
copyDone: int
|
||||
next: PConcTable[K,V]
|
||||
data: TEntryArr
|
||||
|
||||
data: EntryArr
|
||||
{.deprecated: [TEntry: Entry, TEntryArr: EntryArr.}
|
||||
|
||||
proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
|
||||
expVal: int, match: bool): int
|
||||
@@ -84,7 +85,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
|
||||
proc newLFTable*[K,V](size: int = minTableSize): PConcTable[K,V] =
|
||||
let
|
||||
dataLen = max(nextPowerOfTwo(size), minTableSize)
|
||||
dataSize = dataLen*sizeof(TEntry)
|
||||
dataSize = dataLen*sizeof(Entry)
|
||||
dataMem = allocShared0(dataSize)
|
||||
tableSize = 7 * intSize
|
||||
tableMem = allocShared0(tableSize)
|
||||
@@ -95,7 +96,7 @@ proc newLFTable*[K,V](size: int = minTableSize): PConcTable[K,V] =
|
||||
table.copyIdx = 0
|
||||
table.copyDone = 0
|
||||
table.next = nil
|
||||
table.data = cast[TEntryArr](dataMem)
|
||||
table.data = cast[EntryArr](dataMem)
|
||||
result = table
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
@@ -107,7 +108,7 @@ proc deleteConcTable[K,V](tbl: PConcTable[K,V]) =
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
proc `[]`[K,V](table: var PConcTable[K,V], i: int): var TEntry {.inline.} =
|
||||
proc `[]`[K,V](table: var PConcTable[K,V], i: int): var Entry {.inline.} =
|
||||
table.data[i]
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
@@ -191,7 +192,7 @@ proc resize[K,V](self: PConcTable[K,V]): PConcTable[K,V] =
|
||||
#proc keyEQ[K](key1: ptr K, key2: ptr K): bool {.inline.} =
|
||||
proc keyEQ[K](key1: int, key2: int): bool {.inline.} =
|
||||
result = false
|
||||
when K is TRaw:
|
||||
when K is Raw:
|
||||
if key1 == key2:
|
||||
result = true
|
||||
else:
|
||||
@@ -236,7 +237,7 @@ proc copySlot[K,V](idx: int, oldTbl: var PConcTable[K,V], newTbl: var PConcTable
|
||||
break
|
||||
#echo("oldVal was = ", oldVal, " set it to prime ", box)
|
||||
if isPrime(oldVal) and isTomb(oldVal):
|
||||
#when not (K is TRaw):
|
||||
#when not (K is Raw):
|
||||
# deallocShared(popPtr[K](oldKey))
|
||||
return false
|
||||
if isTomb(oldVal):
|
||||
@@ -343,7 +344,7 @@ proc helpCopy[K,V](table: var PConcTable[K,V]): PConcTable[K,V] =
|
||||
proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
|
||||
expVal: int, match: bool): int =
|
||||
#echo("-try set- in table ", " key = ", (popPtr[K](key)[]), " val = ", val)
|
||||
when K is TRaw:
|
||||
when K is Raw:
|
||||
var idx = hashInt(key)
|
||||
else:
|
||||
var idx = popPtr[K](key)[].hash
|
||||
@@ -428,7 +429,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
|
||||
|
||||
proc getVal[K,V](table: var PConcTable[K,V], key: int): int =
|
||||
#echo("-try get- key = " & $key)
|
||||
when K is TRaw:
|
||||
when K is Raw:
|
||||
var idx = hashInt(key)
|
||||
else:
|
||||
var idx = popPtr[K](key)[].hash
|
||||
@@ -468,37 +469,37 @@ proc getVal[K,V](table: var PConcTable[K,V], key: int): int =
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#proc set*(table: var PConcTable[TRaw,TRaw], key: TRaw, val: TRaw) =
|
||||
#proc set*(table: var PConcTable[Raw,Raw], key: Raw, val: Raw) =
|
||||
# discard setVal(table, pack(key), pack(key), 0, false)
|
||||
|
||||
#proc set*[V](table: var PConcTable[TRaw,V], key: TRaw, val: ptr V) =
|
||||
#proc set*[V](table: var PConcTable[Raw,V], key: Raw, val: ptr V) =
|
||||
# discard setVal(table, pack(key), cast[int](val), 0, false)
|
||||
|
||||
proc set*[K,V](table: var PConcTable[K,V], key: var K, val: var V) =
|
||||
when not (K is TRaw):
|
||||
when not (K is Raw):
|
||||
var newKey = cast[int](copyShared(key))
|
||||
else:
|
||||
var newKey = pack(key)
|
||||
when not (V is TRaw):
|
||||
when not (V is Raw):
|
||||
var newVal = cast[int](copyShared(val))
|
||||
else:
|
||||
var newVal = pack(val)
|
||||
var oldPtr = pop(setVal(table, newKey, newVal, 0, false))
|
||||
#echo("oldPtr = ", cast[int](oldPtr), " newPtr = ", cast[int](newPtr))
|
||||
when not (V is TRaw):
|
||||
when not (V is Raw):
|
||||
if newVal != oldPtr and oldPtr != 0:
|
||||
deallocShared(cast[ptr V](oldPtr))
|
||||
|
||||
|
||||
|
||||
proc get*[K,V](table: var PConcTable[K,V], key: var K): V =
|
||||
when not (V is TRaw):
|
||||
when not (K is TRaw):
|
||||
when not (V is Raw):
|
||||
when not (K is Raw):
|
||||
return popPtr[V](getVal(table, cast[int](key.addr)))[]
|
||||
else:
|
||||
return popPtr[V](getVal(table, pack(key)))[]
|
||||
else:
|
||||
when not (K is TRaw):
|
||||
when not (K is Raw):
|
||||
return popRaw(getVal(table, cast[int](key.addr)))
|
||||
else:
|
||||
return popRaw(getVal(table, pack(key)))
|
||||
@@ -535,23 +536,24 @@ when not defined(testing) and isMainModule:
|
||||
|
||||
|
||||
type
|
||||
TTestObj = tuple
|
||||
TestObj = tuple
|
||||
thr: int
|
||||
f0: int
|
||||
f1: int
|
||||
|
||||
TData = tuple[k: string,v: TTestObj]
|
||||
PDataArr = array[0..numTests-1, TData]
|
||||
Dict = PConcTable[string,TTestObj]
|
||||
Data = tuple[k: string,v: TestObj]
|
||||
PDataArr = array[0..numTests-1, Data]
|
||||
Dict = PConcTable[string,TestObj]
|
||||
{.deprecated: [TTestObj: TestObj, TData: Data].}
|
||||
|
||||
var
|
||||
thr: array[0..numThreads-1, TThread[Dict]]
|
||||
thr: array[0..numThreads-1, Thread[Dict]]
|
||||
|
||||
table = newLFTable[string,TTestObj](8)
|
||||
table = newLFTable[string,TestObj](8)
|
||||
rand = newMersenneTwister(2525)
|
||||
|
||||
proc createSampleData(len: int): PDataArr =
|
||||
#result = cast[PDataArr](allocShared0(sizeof(TData)*numTests))
|
||||
#result = cast[PDataArr](allocShared0(sizeof(Data)*numTests))
|
||||
for i in 0..len-1:
|
||||
result[i].k = "mark" & $(i+1)
|
||||
#echo("mark" & $(i+1), " ", hash("mark" & $(i+1)))
|
||||
|
||||
@@ -30,25 +30,25 @@ const
|
||||
IntMask = 1 shl IntShift - 1
|
||||
|
||||
type
|
||||
PTrunk = ref TTrunk
|
||||
TTrunk {.final.} = object
|
||||
PTrunk = ref Trunk
|
||||
Trunk {.final.} = object
|
||||
next: PTrunk # all nodes are connected with this pointer
|
||||
key: int # start address at bit 0
|
||||
bits: array[0..IntsPerTrunk - 1, BitScalar] # a bit vector
|
||||
|
||||
TTrunkSeq = seq[PTrunk]
|
||||
TrunkSeq = seq[PTrunk]
|
||||
IntSet* = object ## an efficient set of 'int' implemented as a sparse bit set
|
||||
counter, max: int
|
||||
head: PTrunk
|
||||
data: TTrunkSeq
|
||||
data: TrunkSeq
|
||||
|
||||
{.deprecated: [TIntSet: IntSet].}
|
||||
{.deprecated: [TIntSet: IntSet, TTrunk: Trunk, TTrunkSeq: TrunkSeq].}
|
||||
|
||||
proc mustRehash(length, counter: int): bool {.inline.} =
|
||||
assert(length > counter)
|
||||
result = (length * 2 < counter * 3) or (length - counter < 4)
|
||||
|
||||
proc nextTry(h, maxHash: THash): THash {.inline.} =
|
||||
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
|
||||
result = ((5 * h) + 1) and maxHash
|
||||
|
||||
proc intSetGet(t: IntSet, key: int): PTrunk =
|
||||
@@ -59,7 +59,7 @@ proc intSetGet(t: IntSet, key: int): PTrunk =
|
||||
h = nextTry(h, t.max)
|
||||
result = nil
|
||||
|
||||
proc intSetRawInsert(t: IntSet, data: var TTrunkSeq, desc: PTrunk) =
|
||||
proc intSetRawInsert(t: IntSet, data: var TrunkSeq, desc: PTrunk) =
|
||||
var h = desc.key and t.max
|
||||
while data[h] != nil:
|
||||
assert(data[h] != desc)
|
||||
@@ -68,7 +68,7 @@ proc intSetRawInsert(t: IntSet, data: var TTrunkSeq, desc: PTrunk) =
|
||||
data[h] = desc
|
||||
|
||||
proc intSetEnlarge(t: var IntSet) =
|
||||
var n: TTrunkSeq
|
||||
var n: TrunkSeq
|
||||
var oldMax = t.max
|
||||
t.max = ((t.max + 1) * 2) - 1
|
||||
newSeq(n, t.max + 1)
|
||||
|
||||
@@ -29,7 +29,7 @@ when not defined(nimhygiene):
|
||||
# codes should never be needed, and this can pack more entries per cache-line.
|
||||
# Losing hcode entirely is also possible - if some element value is forbidden.
|
||||
type
|
||||
KeyValuePair[A] = tuple[hcode: THash, key: A]
|
||||
KeyValuePair[A] = tuple[hcode: Hash, key: A]
|
||||
KeyValuePairSeq[A] = seq[KeyValuePair[A]]
|
||||
HashSet* {.myShallow.}[A] = object ## \
|
||||
## A generic hash set.
|
||||
@@ -43,10 +43,10 @@ type
|
||||
|
||||
# hcode for real keys cannot be zero. hcode==0 signifies an empty slot. These
|
||||
# two procs retain clarity of that encoding without the space cost of an enum.
|
||||
proc isEmpty(hcode: THash): bool {.inline.} =
|
||||
proc isEmpty(hcode: Hash): bool {.inline.} =
|
||||
result = hcode == 0
|
||||
|
||||
proc isFilled(hcode: THash): bool {.inline.} =
|
||||
proc isFilled(hcode: Hash): bool {.inline.} =
|
||||
result = hcode != 0
|
||||
|
||||
proc isValid*[A](s: HashSet[A]): bool =
|
||||
@@ -58,7 +58,7 @@ proc isValid*[A](s: HashSet[A]): bool =
|
||||
## initialized. Example:
|
||||
##
|
||||
## .. code-block ::
|
||||
## proc savePreferences(options: TSet[string]) =
|
||||
## proc savePreferences(options: Set[string]) =
|
||||
## assert options.isValid, "Pass an initialized set!"
|
||||
## # Do stuff here, may crash in release builds!
|
||||
result = not s.data.isNil
|
||||
@@ -72,7 +72,7 @@ proc len*[A](s: HashSet[A]): int =
|
||||
##
|
||||
## .. code-block::
|
||||
##
|
||||
## var values: TSet[int]
|
||||
## var values: Set[int]
|
||||
## assert(not values.isValid)
|
||||
## assert values.len == 0
|
||||
result = s.counter
|
||||
@@ -123,15 +123,15 @@ proc rightSize*(count: Natural): int {.inline.} =
|
||||
## Internally, we want mustRehash(rightSize(x), x) == false.
|
||||
result = nextPowerOfTwo(count * 3 div 2 + 4)
|
||||
|
||||
proc nextTry(h, maxHash: THash): THash {.inline.} =
|
||||
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
|
||||
result = (h + 1) and maxHash
|
||||
|
||||
template rawGetKnownHCImpl() {.dirty.} =
|
||||
var h: THash = hc and high(s.data) # start with real hash value
|
||||
var h: Hash = hc and high(s.data) # start with real hash value
|
||||
while isFilled(s.data[h].hcode):
|
||||
# Compare hc THEN key with boolean short circuit. This makes the common case
|
||||
# zero ==key's for missing (e.g.inserts) and exactly one ==key for present.
|
||||
# It does slow down succeeding lookups by one extra THash cmp&and..usually
|
||||
# It does slow down succeeding lookups by one extra Hash cmp&and..usually
|
||||
# just a few clock cycles, generally worth it for any non-integer-like A.
|
||||
if s.data[h].hcode == hc and s.data[h].key == key: # compare hc THEN key
|
||||
return h
|
||||
@@ -148,10 +148,10 @@ template rawInsertImpl() {.dirty.} =
|
||||
data[h].key = key
|
||||
data[h].hcode = hc
|
||||
|
||||
proc rawGetKnownHC[A](s: HashSet[A], key: A, hc: THash): int {.inline.} =
|
||||
proc rawGetKnownHC[A](s: HashSet[A], key: A, hc: Hash): int {.inline.} =
|
||||
rawGetKnownHCImpl()
|
||||
|
||||
proc rawGet[A](s: HashSet[A], key: A, hc: var THash): int {.inline.} =
|
||||
proc rawGet[A](s: HashSet[A], key: A, hc: var Hash): int {.inline.} =
|
||||
rawGetImpl()
|
||||
|
||||
proc mget*[A](s: var HashSet[A], key: A): var A =
|
||||
@@ -160,7 +160,7 @@ proc mget*[A](s: var HashSet[A], key: A): var A =
|
||||
## when one overloaded 'hash' and '==' but still needs reference semantics
|
||||
## for sharing.
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var index = rawGet(s, key, hc)
|
||||
if index >= 0: result = s.data[index].key
|
||||
else: raise newException(KeyError, "key not found: " & $key)
|
||||
@@ -178,12 +178,12 @@ proc contains*[A](s: HashSet[A], key: A): bool =
|
||||
## values.excl(2)
|
||||
## assert(not values.contains(2))
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var index = rawGet(s, key, hc)
|
||||
result = index >= 0
|
||||
|
||||
proc rawInsert[A](s: var HashSet[A], data: var KeyValuePairSeq[A], key: A,
|
||||
hc: THash, h: THash) =
|
||||
hc: Hash, h: Hash) =
|
||||
rawInsertImpl()
|
||||
|
||||
proc enlarge[A](s: var HashSet[A]) =
|
||||
@@ -196,7 +196,7 @@ proc enlarge[A](s: var HashSet[A]) =
|
||||
rawInsert(s, s.data, n[i].key, n[i].hcode, j)
|
||||
|
||||
template inclImpl() {.dirty.} =
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var index = rawGet(s, key, hc)
|
||||
if index < 0:
|
||||
if mustRehash(len(s.data), s.counter):
|
||||
@@ -206,7 +206,7 @@ template inclImpl() {.dirty.} =
|
||||
inc(s.counter)
|
||||
|
||||
template containsOrInclImpl() {.dirty.} =
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var index = rawGet(s, key, hc)
|
||||
if index >= 0:
|
||||
result = true
|
||||
@@ -261,7 +261,7 @@ proc excl*[A](s: var HashSet[A], key: A) =
|
||||
## s.excl(2)
|
||||
## assert s.len == 3
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var i = rawGet(s, key, hc)
|
||||
var msk = high(s.data)
|
||||
if i >= 0:
|
||||
@@ -323,7 +323,7 @@ proc init*[A](s: var HashSet[A], initialSize=64) =
|
||||
## existing values and calling `excl() <#excl,TSet[A],A>`_ on them. Example:
|
||||
##
|
||||
## .. code-block ::
|
||||
## var a: TSet[int]
|
||||
## var a: Set[int]
|
||||
## a.init(4)
|
||||
## a.incl(2)
|
||||
## a.init
|
||||
@@ -552,7 +552,7 @@ proc map*[A, B](data: HashSet[A], op: proc (x: A): B {.closure.}): HashSet[B] =
|
||||
|
||||
type
|
||||
OrderedKeyValuePair[A] = tuple[
|
||||
hcode: THash, next: int, key: A]
|
||||
hcode: Hash, next: int, key: A]
|
||||
OrderedKeyValuePairSeq[A] = seq[OrderedKeyValuePair[A]]
|
||||
OrderedSet* {.myShallow.}[A] = object ## \
|
||||
## A generic hash set that remembers insertion order.
|
||||
@@ -574,7 +574,7 @@ proc isValid*[A](s: OrderedSet[A]): bool =
|
||||
## correctly initialized. Example:
|
||||
##
|
||||
## .. code-block::
|
||||
## proc saveTarotCards(cards: TOrderedSet[int]) =
|
||||
## proc saveTarotCards(cards: OrderedSet[int]) =
|
||||
## assert cards.isValid, "Pass an initialized set!"
|
||||
## # Do stuff here, may crash in release builds!
|
||||
result = not s.data.isNil
|
||||
@@ -588,7 +588,7 @@ proc len*[A](s: OrderedSet[A]): int {.inline.} =
|
||||
##
|
||||
## .. code-block::
|
||||
##
|
||||
## var values: TOrderedSet[int]
|
||||
## var values: OrderedSet[int]
|
||||
## assert(not values.isValid)
|
||||
## assert values.len == 0
|
||||
result = s.counter
|
||||
@@ -629,10 +629,10 @@ iterator items*[A](s: OrderedSet[A]): A =
|
||||
forAllOrderedPairs:
|
||||
yield s.data[h].key
|
||||
|
||||
proc rawGetKnownHC[A](s: OrderedSet[A], key: A, hc: THash): int {.inline.} =
|
||||
proc rawGetKnownHC[A](s: OrderedSet[A], key: A, hc: Hash): int {.inline.} =
|
||||
rawGetKnownHCImpl()
|
||||
|
||||
proc rawGet[A](s: OrderedSet[A], key: A, hc: var THash): int {.inline.} =
|
||||
proc rawGet[A](s: OrderedSet[A], key: A, hc: var Hash): int {.inline.} =
|
||||
rawGetImpl()
|
||||
|
||||
proc contains*[A](s: OrderedSet[A], key: A): bool =
|
||||
@@ -646,12 +646,12 @@ proc contains*[A](s: OrderedSet[A], key: A): bool =
|
||||
## values.incl(2)
|
||||
## assert values.contains(2)
|
||||
assert s.isValid, "The set needs to be initialized."
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var index = rawGet(s, key, hc)
|
||||
result = index >= 0
|
||||
|
||||
proc rawInsert[A](s: var OrderedSet[A], data: var OrderedKeyValuePairSeq[A],
|
||||
key: A, hc: THash, h: THash) =
|
||||
key: A, hc: Hash, h: Hash) =
|
||||
rawInsertImpl()
|
||||
data[h].next = -1
|
||||
if s.first < 0: s.first = h
|
||||
@@ -729,7 +729,7 @@ proc init*[A](s: var OrderedSet[A], initialSize=64) =
|
||||
## from an ordered hash set. Example:
|
||||
##
|
||||
## .. code-block ::
|
||||
## var a: TOrderedSet[int]
|
||||
## var a: OrderedSet[int]
|
||||
## a.init(4)
|
||||
## a.incl(2)
|
||||
## a.init
|
||||
|
||||
@@ -24,13 +24,13 @@
|
||||
##
|
||||
## Error: type mismatch: got (Person)
|
||||
## but expected one of:
|
||||
## hashes.hash(x: openarray[A]): THash
|
||||
## hashes.hash(x: int): THash
|
||||
## hashes.hash(x: float): THash
|
||||
## hashes.hash(x: openarray[A]): Hash
|
||||
## hashes.hash(x: int): Hash
|
||||
## hashes.hash(x: float): Hash
|
||||
## …
|
||||
##
|
||||
## What is happening here is that the types used for table keys require to have
|
||||
## a ``hash()`` proc which will convert them to a `THash <hashes.html#THash>`_
|
||||
## a ``hash()`` proc which will convert them to a `Hash <hashes.html#Hash>`_
|
||||
## value, and the compiler is listing all the hash functions it knows.
|
||||
## Additionally there has to be a ``==`` operator that provides the same
|
||||
## semantics as its corresponding ``hash`` proc.
|
||||
@@ -46,7 +46,7 @@
|
||||
## Person = object
|
||||
## firstName, lastName: string
|
||||
##
|
||||
## proc hash(x: Person): THash =
|
||||
## proc hash(x: Person): Hash =
|
||||
## ## Piggyback on the already available string hash proc.
|
||||
## ##
|
||||
## ## Without this proc nothing works!
|
||||
@@ -71,7 +71,7 @@ import
|
||||
{.pragma: myShallow.}
|
||||
|
||||
type
|
||||
KeyValuePair[A, B] = tuple[hcode: THash, key: A, val: B]
|
||||
KeyValuePair[A, B] = tuple[hcode: Hash, key: A, val: B]
|
||||
KeyValuePairSeq[A, B] = seq[KeyValuePair[A, B]]
|
||||
Table* {.myShallow.}[A, B] = object ## generic hash table
|
||||
data: KeyValuePairSeq[A, B]
|
||||
@@ -85,10 +85,10 @@ when not defined(nimhygiene):
|
||||
|
||||
# hcode for real keys cannot be zero. hcode==0 signifies an empty slot. These
|
||||
# two procs retain clarity of that encoding without the space cost of an enum.
|
||||
proc isEmpty(hcode: THash): bool {.inline.} =
|
||||
proc isEmpty(hcode: Hash): bool {.inline.} =
|
||||
result = hcode == 0
|
||||
|
||||
proc isFilled(hcode: THash): bool {.inline.} =
|
||||
proc isFilled(hcode: Hash): bool {.inline.} =
|
||||
result = hcode != 0
|
||||
|
||||
proc len*[A, B](t: Table[A, B]): int =
|
||||
@@ -137,15 +137,15 @@ proc rightSize*(count: Natural): int {.inline.} =
|
||||
## Internally, we want mustRehash(rightSize(x), x) == false.
|
||||
result = nextPowerOfTwo(count * 3 div 2 + 4)
|
||||
|
||||
proc nextTry(h, maxHash: THash): THash {.inline.} =
|
||||
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
|
||||
result = (h + 1) and maxHash
|
||||
|
||||
template rawGetKnownHCImpl() {.dirty.} =
|
||||
var h: THash = hc and high(t.data) # start with real hash value
|
||||
var h: Hash = hc and high(t.data) # start with real hash value
|
||||
while isFilled(t.data[h].hcode):
|
||||
# Compare hc THEN key with boolean short circuit. This makes the common case
|
||||
# zero ==key's for missing (e.g.inserts) and exactly one ==key for present.
|
||||
# It does slow down succeeding lookups by one extra THash cmp&and..usually
|
||||
# It does slow down succeeding lookups by one extra Hash cmp&and..usually
|
||||
# just a few clock cycles, generally worth it for any non-integer-like A.
|
||||
if t.data[h].hcode == hc and t.data[h].key == key:
|
||||
return h
|
||||
@@ -162,7 +162,7 @@ template rawGetDeepImpl() {.dirty.} = # Search algo for unconditional add
|
||||
hc = hash(key)
|
||||
if hc == 0:
|
||||
hc = 314159265
|
||||
var h: THash = hc and high(t.data)
|
||||
var h: Hash = hc and high(t.data)
|
||||
while isFilled(t.data[h].hcode):
|
||||
h = nextTry(h, high(t.data))
|
||||
result = h
|
||||
@@ -172,13 +172,13 @@ template rawInsertImpl() {.dirty.} =
|
||||
data[h].val = val
|
||||
data[h].hcode = hc
|
||||
|
||||
proc rawGetKnownHC[A, B](t: Table[A, B], key: A, hc: THash): int {.inline.} =
|
||||
proc rawGetKnownHC[A, B](t: Table[A, B], key: A, hc: Hash): int {.inline.} =
|
||||
rawGetKnownHCImpl()
|
||||
|
||||
proc rawGetDeep[A, B](t: Table[A, B], key: A, hc: var THash): int {.inline.} =
|
||||
proc rawGetDeep[A, B](t: Table[A, B], key: A, hc: var Hash): int {.inline.} =
|
||||
rawGetDeepImpl()
|
||||
|
||||
proc rawGet[A, B](t: Table[A, B], key: A, hc: var THash): int {.inline.} =
|
||||
proc rawGet[A, B](t: Table[A, B], key: A, hc: var Hash): int {.inline.} =
|
||||
rawGetImpl()
|
||||
|
||||
proc `[]`*[A, B](t: Table[A, B], key: A): B =
|
||||
@@ -186,14 +186,14 @@ proc `[]`*[A, B](t: Table[A, B], key: A): B =
|
||||
## default empty value for the type `B` is returned
|
||||
## and no exception is raised. One can check with ``hasKey`` whether the key
|
||||
## exists.
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var index = rawGet(t, key, hc)
|
||||
if index >= 0: result = t.data[index].val
|
||||
|
||||
proc mget*[A, B](t: var Table[A, B], key: A): var B =
|
||||
## retrieves the value at ``t[key]``. The value can be modified.
|
||||
## If `key` is not in `t`, the ``KeyError`` exception is raised.
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var index = rawGet(t, key, hc)
|
||||
if index >= 0: result = t.data[index].val
|
||||
else:
|
||||
@@ -204,7 +204,7 @@ proc mget*[A, B](t: var Table[A, B], key: A): var B =
|
||||
|
||||
iterator allValues*[A, B](t: Table[A, B]; key: A): B =
|
||||
## iterates over any value in the table `t` that belongs to the given `key`.
|
||||
var h: THash = hash(key) and high(t.data)
|
||||
var h: Hash = hash(key) and high(t.data)
|
||||
while isFilled(t.data[h].hcode):
|
||||
if t.data[h].key == key:
|
||||
yield t.data[h].val
|
||||
@@ -212,7 +212,7 @@ iterator allValues*[A, B](t: Table[A, B]; key: A): B =
|
||||
|
||||
proc hasKey*[A, B](t: Table[A, B], key: A): bool =
|
||||
## returns true iff `key` is in the table `t`.
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
result = rawGet(t, key, hc) >= 0
|
||||
|
||||
proc contains*[A, B](t: Table[A, B], key: A): bool =
|
||||
@@ -220,7 +220,7 @@ proc contains*[A, B](t: Table[A, B], key: A): bool =
|
||||
return hasKey[A, B](t, key)
|
||||
|
||||
proc rawInsert[A, B](t: var Table[A, B], data: var KeyValuePairSeq[A, B],
|
||||
key: A, val: B, hc: THash, h: THash) =
|
||||
key: A, val: B, hc: Hash, h: Hash) =
|
||||
rawInsertImpl()
|
||||
|
||||
proc enlarge[A, B](t: var Table[A, B]) =
|
||||
@@ -234,7 +234,7 @@ proc enlarge[A, B](t: var Table[A, B]) =
|
||||
|
||||
template addImpl() {.dirty.} =
|
||||
if mustRehash(len(t.data), t.counter): enlarge(t)
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var j = rawGetDeep(t, key, hc)
|
||||
rawInsert(t, t.data, key, val, hc, j)
|
||||
inc(t.counter)
|
||||
@@ -248,19 +248,19 @@ template maybeRehashPutImpl() {.dirty.} =
|
||||
inc(t.counter)
|
||||
|
||||
template putImpl() {.dirty.} =
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var index = rawGet(t, key, hc)
|
||||
if index >= 0: t.data[index].val = val
|
||||
else: maybeRehashPutImpl()
|
||||
|
||||
template mgetOrPutImpl() {.dirty.} =
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var index = rawGet(t, key, hc)
|
||||
if index < 0: maybeRehashPutImpl() # not present: insert (flipping index)
|
||||
result = t.data[index].val # either way return modifiable val
|
||||
|
||||
template hasKeyOrPutImpl() {.dirty.} =
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var index = rawGet(t, key, hc)
|
||||
if index < 0:
|
||||
result = false
|
||||
@@ -291,7 +291,7 @@ template doWhile(a: expr, b: stmt): stmt =
|
||||
|
||||
proc del*[A, B](t: var Table[A, B], key: A) =
|
||||
## deletes `key` from hash table `t`.
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var i = rawGet(t, key, hc)
|
||||
let msk = high(t.data)
|
||||
if i >= 0:
|
||||
@@ -460,7 +460,7 @@ proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): TableRef[C, B]
|
||||
|
||||
type
|
||||
OrderedKeyValuePair[A, B] = tuple[
|
||||
hcode: THash, next: int, key: A, val: B]
|
||||
hcode: Hash, next: int, key: A, val: B]
|
||||
OrderedKeyValuePairSeq[A, B] = seq[OrderedKeyValuePair[A, B]]
|
||||
OrderedTable* {.
|
||||
myShallow.}[A, B] = object ## table that remembers insertion order
|
||||
@@ -509,13 +509,13 @@ iterator mvalues*[A, B](t: var OrderedTable[A, B]): var B =
|
||||
forAllOrderedPairs:
|
||||
yield t.data[h].val
|
||||
|
||||
proc rawGetKnownHC[A, B](t: OrderedTable[A, B], key: A, hc: THash): int =
|
||||
proc rawGetKnownHC[A, B](t: OrderedTable[A, B], key: A, hc: Hash): int =
|
||||
rawGetKnownHCImpl()
|
||||
|
||||
proc rawGetDeep[A, B](t: OrderedTable[A, B], key: A, hc: var THash): int {.inline.} =
|
||||
proc rawGetDeep[A, B](t: OrderedTable[A, B], key: A, hc: var Hash): int {.inline.} =
|
||||
rawGetDeepImpl()
|
||||
|
||||
proc rawGet[A, B](t: OrderedTable[A, B], key: A, hc: var THash): int =
|
||||
proc rawGet[A, B](t: OrderedTable[A, B], key: A, hc: var Hash): int =
|
||||
rawGetImpl()
|
||||
|
||||
proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B =
|
||||
@@ -523,21 +523,21 @@ proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B =
|
||||
## default empty value for the type `B` is returned
|
||||
## and no exception is raised. One can check with ``hasKey`` whether the key
|
||||
## exists.
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var index = rawGet(t, key, hc)
|
||||
if index >= 0: result = t.data[index].val
|
||||
|
||||
proc mget*[A, B](t: var OrderedTable[A, B], key: A): var B =
|
||||
## retrieves the value at ``t[key]``. The value can be modified.
|
||||
## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
var index = rawGet(t, key, hc)
|
||||
if index >= 0: result = t.data[index].val
|
||||
else: raise newException(KeyError, "key not found: " & $key)
|
||||
|
||||
proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool =
|
||||
## returns true iff `key` is in the table `t`.
|
||||
var hc: THash
|
||||
var hc: Hash
|
||||
result = rawGet(t, key, hc) >= 0
|
||||
|
||||
proc contains*[A, B](t: OrderedTable[A, B], key: A): bool =
|
||||
@@ -546,7 +546,7 @@ proc contains*[A, B](t: OrderedTable[A, B], key: A): bool =
|
||||
|
||||
proc rawInsert[A, B](t: var OrderedTable[A, B],
|
||||
data: var OrderedKeyValuePairSeq[A, B],
|
||||
key: A, val: B, hc: THash, h: THash) =
|
||||
key: A, val: B, hc: Hash, h: Hash) =
|
||||
rawInsertImpl()
|
||||
data[h].next = -1
|
||||
if t.first < 0: t.first = h
|
||||
@@ -796,7 +796,7 @@ iterator mvalues*[A](t: CountTable[A]): var int =
|
||||
if t.data[h].val != 0: yield t.data[h].val
|
||||
|
||||
proc rawGet[A](t: CountTable[A], key: A): int =
|
||||
var h: THash = hash(key) and high(t.data) # start with real hash value
|
||||
var h: Hash = hash(key) and high(t.data) # start with real hash value
|
||||
while t.data[h].val != 0:
|
||||
if t.data[h].key == key: return h
|
||||
h = nextTry(h, high(t.data))
|
||||
@@ -826,7 +826,7 @@ proc contains*[A](t: CountTable[A], key: A): bool =
|
||||
|
||||
proc rawInsert[A](t: CountTable[A], data: var seq[tuple[key: A, val: int]],
|
||||
key: A, val: int) =
|
||||
var h: THash = hash(key) and high(data)
|
||||
var h: Hash = hash(key) and high(data)
|
||||
while data[h].val != 0: h = nextTry(h, high(data))
|
||||
data[h].key = key
|
||||
data[h].val = val
|
||||
@@ -1032,7 +1032,7 @@ when isMainModule:
|
||||
Person = object
|
||||
firstName, lastName: string
|
||||
|
||||
proc hash(x: Person): THash =
|
||||
proc hash(x: Person): Hash =
|
||||
## Piggyback on the already available string hash proc.
|
||||
##
|
||||
## Without this proc nothing works!
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
when defined(windows):
|
||||
import winlean, os, strutils, math
|
||||
|
||||
proc `-`(a, b: TFILETIME): int64 = a.rdFileTime - b.rdFileTime
|
||||
proc `-`(a, b: FILETIME): int64 = a.rdFileTime - b.rdFileTime
|
||||
elif defined(linux):
|
||||
from cpuinfo import countProcessors
|
||||
|
||||
@@ -25,16 +25,16 @@ type
|
||||
|
||||
ThreadPoolState* = object
|
||||
when defined(windows):
|
||||
prevSysKernel, prevSysUser, prevProcKernel, prevProcUser: TFILETIME
|
||||
prevSysKernel, prevSysUser, prevProcKernel, prevProcUser: FILETIME
|
||||
calls*: int
|
||||
|
||||
proc advice*(s: var ThreadPoolState): ThreadPoolAdvice =
|
||||
when defined(windows):
|
||||
var
|
||||
sysIdle, sysKernel, sysUser,
|
||||
procCreation, procExit, procKernel, procUser: TFILETIME
|
||||
procCreation, procExit, procKernel, procUser: FILETIME
|
||||
if getSystemTimes(sysIdle, sysKernel, sysUser) == 0 or
|
||||
getProcessTimes(THandle(-1), procCreation, procExit,
|
||||
getProcessTimes(Handle(-1), procCreation, procExit,
|
||||
procKernel, procUser) == 0:
|
||||
return doNothing
|
||||
if s.calls > 0:
|
||||
@@ -57,7 +57,7 @@ proc advice*(s: var ThreadPoolState): ThreadPoolAdvice =
|
||||
s.prevProcKernel = procKernel
|
||||
s.prevProcUser = procUser
|
||||
elif defined(linux):
|
||||
proc fscanf(c: File, frmt: cstring) {.varargs, importc,
|
||||
proc fscanf(c: File, frmt: cstring) {.varargs, importc,
|
||||
header: "<stdio.h>".}
|
||||
|
||||
var f = open("/proc/loadavg")
|
||||
|
||||
@@ -18,8 +18,8 @@ import cpuinfo, cpuload, locks
|
||||
|
||||
type
|
||||
Semaphore = object
|
||||
c: TCond
|
||||
L: TLock
|
||||
c: Cond
|
||||
L: Lock
|
||||
counter: int
|
||||
|
||||
proc createSemaphore(): Semaphore =
|
||||
@@ -113,7 +113,7 @@ type
|
||||
|
||||
ToFreeQueue = object
|
||||
len: int
|
||||
lock: TLock
|
||||
lock: Lock
|
||||
empty: Semaphore
|
||||
data: array[128, pointer]
|
||||
|
||||
@@ -221,11 +221,17 @@ proc awaitAndThen*[T](fv: FlowVar[T]; action: proc (x: T) {.closure.}) =
|
||||
action(fv.blob)
|
||||
finished(fv)
|
||||
|
||||
proc `^`*[T](fv: FlowVar[ref T]): foreign ptr T =
|
||||
proc unsafeRead*[T](fv: FlowVar[ref T]): foreign ptr T =
|
||||
## blocks until the value is available and then returns this value.
|
||||
await(fv)
|
||||
result = cast[foreign ptr T](fv.data)
|
||||
|
||||
proc `^`*[T](fv: FlowVar[ref T]): ref T =
|
||||
## blocks until the value is available and then returns this value.
|
||||
await(fv)
|
||||
let src = cast[ref T](fv.data)
|
||||
deepCopy result, src
|
||||
|
||||
proc `^`*[T](fv: FlowVar[T]): T =
|
||||
## blocks until the value is available and then returns this value.
|
||||
await(fv)
|
||||
@@ -349,7 +355,7 @@ proc parallel*(body: stmt) {.magic: "Parallel".}
|
||||
|
||||
var
|
||||
state: ThreadPoolState
|
||||
stateLock: TLock
|
||||
stateLock: Lock
|
||||
|
||||
initLock stateLock
|
||||
|
||||
|
||||
@@ -211,12 +211,13 @@ when defined(windows):
|
||||
when false:
|
||||
# not needed yet:
|
||||
type
|
||||
TCpInfo = object
|
||||
CpInfo = object
|
||||
maxCharSize: int32
|
||||
defaultChar: array[0..1, char]
|
||||
leadByte: array[0..12-1, char]
|
||||
{.deprecated: [TCpInfo: CpInfo].}
|
||||
|
||||
proc getCPInfo(codePage: CodePage, lpCPInfo: var TCpInfo): int32 {.
|
||||
proc getCPInfo(codePage: CodePage, lpCPInfo: var CpInfo): int32 {.
|
||||
stdcall, importc: "GetCPInfo", dynlib: "kernel32".}
|
||||
|
||||
proc nameToCodePage(name: string): CodePage =
|
||||
@@ -262,7 +263,7 @@ else:
|
||||
else:
|
||||
const iconvDll = "(libc.so.6|libiconv.so)"
|
||||
|
||||
when defined(macosx) and defined(powerpc):
|
||||
when defined(macosx):
|
||||
const prefix = "lib"
|
||||
else:
|
||||
const prefix = ""
|
||||
|
||||
23
lib/pure/etcpriv.nim
Normal file
23
lib/pure/etcpriv.nim
Normal file
@@ -0,0 +1,23 @@
|
||||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2015 Nim Authors
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module contains utils that are less then easy to categorize and
|
||||
## don't really warrant a specific module. They are private to compiler
|
||||
## and stdlib usage, and should not be used outside of that - they may
|
||||
## change or disappear at any time.
|
||||
|
||||
|
||||
# Used by pure/hashes.nim, and the compiler parsing
|
||||
const magicIdentSeparatorRuneByteWidth* = 3
|
||||
|
||||
# Used by pure/hashes.nim, and the compiler parsing
|
||||
proc isMagicIdentSeparatorRune*(cs: cstring, i: int): bool {. inline } =
|
||||
result = cs[i] == '\226' and
|
||||
cs[i + 1] == '\128' and
|
||||
cs[i + 2] == '\147' # en-dash # 145 = nb-hyphen
|
||||
@@ -108,7 +108,7 @@ proc del*(monitor: FSMonitor, wd: cint) =
|
||||
|
||||
proc getEvent(m: FSMonitor, fd: cint): seq[MonitorEvent] =
|
||||
result = @[]
|
||||
let size = (sizeof(TINotifyEvent)+2000)*MaxEvents
|
||||
let size = (sizeof(INotifyEvent)+2000)*MaxEvents
|
||||
var buffer = newString(size)
|
||||
|
||||
let le = read(fd, addr(buffer[0]), size)
|
||||
@@ -117,7 +117,7 @@ proc getEvent(m: FSMonitor, fd: cint): seq[MonitorEvent] =
|
||||
|
||||
var i = 0
|
||||
while i < le:
|
||||
var event = cast[ptr TINotifyEvent](addr(buffer[i]))
|
||||
var event = cast[ptr INotifyEvent](addr(buffer[i]))
|
||||
var mev: MonitorEvent
|
||||
mev.wd = event.wd
|
||||
if event.len.int != 0:
|
||||
@@ -129,7 +129,7 @@ proc getEvent(m: FSMonitor, fd: cint): seq[MonitorEvent] =
|
||||
if (event.mask.int and IN_MOVED_FROM) != 0:
|
||||
# Moved from event, add to m's collection
|
||||
movedFrom.add(event.cookie.cint, (mev.wd, mev.name))
|
||||
inc(i, sizeof(TINotifyEvent) + event.len.int)
|
||||
inc(i, sizeof(INotifyEvent) + event.len.int)
|
||||
continue
|
||||
elif (event.mask.int and IN_MOVED_TO) != 0:
|
||||
mev.kind = MonitorMoved
|
||||
@@ -159,7 +159,7 @@ proc getEvent(m: FSMonitor, fd: cint): seq[MonitorEvent] =
|
||||
mev.fullname = ""
|
||||
|
||||
result.add(mev)
|
||||
inc(i, sizeof(TINotifyEvent) + event.len.int)
|
||||
inc(i, sizeof(INotifyEvent) + event.len.int)
|
||||
|
||||
# If movedFrom events have not been matched with a moveTo. File has
|
||||
# been moved to an unwatched location, emit a MonitorDelete.
|
||||
|
||||
@@ -18,20 +18,22 @@ import
|
||||
os, hashes, strutils
|
||||
|
||||
type
|
||||
TGenTableMode* = enum ## describes the table's key matching mode
|
||||
GenTableMode* = enum ## describes the table's key matching mode
|
||||
modeCaseSensitive, ## case sensitive matching of keys
|
||||
modeCaseInsensitive, ## case insensitive matching of keys
|
||||
modeStyleInsensitive ## style sensitive matching of keys
|
||||
|
||||
TGenKeyValuePair[T] = tuple[key: string, val: T]
|
||||
TGenKeyValuePairSeq[T] = seq[TGenKeyValuePair[T]]
|
||||
TGenTable*[T] = object of RootObj
|
||||
GenKeyValuePair[T] = tuple[key: string, val: T]
|
||||
GenKeyValuePairSeq[T] = seq[GenKeyValuePair[T]]
|
||||
GenTable*[T] = object of RootObj
|
||||
counter: int
|
||||
data: TGenKeyValuePairSeq[T]
|
||||
mode: TGenTableMode
|
||||
data: GenKeyValuePairSeq[T]
|
||||
mode: GenTableMode
|
||||
|
||||
PGenTable*[T] = ref TGenTable[T] ## use this type to declare hash tables
|
||||
PGenTable*[T] = ref GenTable[T] ## use this type to declare hash tables
|
||||
|
||||
{.deprecated: [TGenTableMode: GenTableMode, TGenKeyValuePair: GenKeyValuePair,
|
||||
TGenKeyValuePairSeq: GenKeyValuePairSeq, TGenTable: GenTable].}
|
||||
|
||||
const
|
||||
growthFactor = 2
|
||||
@@ -48,7 +50,7 @@ iterator pairs*[T](tbl: PGenTable[T]): tuple[key: string, value: T] =
|
||||
if not isNil(tbl.data[h].key):
|
||||
yield (tbl.data[h].key, tbl.data[h].val)
|
||||
|
||||
proc myhash[T](tbl: PGenTable[T], key: string): THash =
|
||||
proc myhash[T](tbl: PGenTable[T], key: string): Hash =
|
||||
case tbl.mode
|
||||
of modeCaseSensitive: result = hashes.hash(key)
|
||||
of modeCaseInsensitive: result = hashes.hashIgnoreCase(key)
|
||||
@@ -64,18 +66,18 @@ proc mustRehash(length, counter: int): bool =
|
||||
assert(length > counter)
|
||||
result = (length * 2 < counter * 3) or (length - counter < 4)
|
||||
|
||||
proc newGenTable*[T](mode: TGenTableMode): PGenTable[T] =
|
||||
proc newGenTable*[T](mode: GenTableMode): PGenTable[T] =
|
||||
## creates a new generic hash table that is empty.
|
||||
new(result)
|
||||
result.mode = mode
|
||||
result.counter = 0
|
||||
newSeq(result.data, startSize)
|
||||
|
||||
proc nextTry(h, maxHash: THash): THash {.inline.} =
|
||||
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
|
||||
result = ((5 * h) + 1) and maxHash
|
||||
|
||||
proc rawGet[T](tbl: PGenTable[T], key: string): int =
|
||||
var h: THash
|
||||
var h: Hash
|
||||
h = myhash(tbl, key) and high(tbl.data) # start with real hash value
|
||||
while not isNil(tbl.data[h].key):
|
||||
if myCmp(tbl, tbl.data[h].key, key):
|
||||
@@ -83,9 +85,9 @@ proc rawGet[T](tbl: PGenTable[T], key: string): int =
|
||||
h = nextTry(h, high(tbl.data))
|
||||
result = - 1
|
||||
|
||||
proc rawInsert[T](tbl: PGenTable[T], data: var TGenKeyValuePairSeq[T],
|
||||
proc rawInsert[T](tbl: PGenTable[T], data: var GenKeyValuePairSeq[T],
|
||||
key: string, val: T) =
|
||||
var h: THash
|
||||
var h: Hash
|
||||
h = myhash(tbl, key) and high(data)
|
||||
while not isNil(data[h].key):
|
||||
h = nextTry(h, high(data))
|
||||
@@ -93,7 +95,7 @@ proc rawInsert[T](tbl: PGenTable[T], data: var TGenKeyValuePairSeq[T],
|
||||
data[h].val = val
|
||||
|
||||
proc enlarge[T](tbl: PGenTable[T]) =
|
||||
var n: TGenKeyValuePairSeq[T]
|
||||
var n: GenKeyValuePairSeq[T]
|
||||
newSeq(n, len(tbl.data) * growthFactor)
|
||||
for i in countup(0, high(tbl.data)):
|
||||
if not isNil(tbl.data[i].key):
|
||||
@@ -146,19 +148,20 @@ when isMainModule:
|
||||
# Verify a table of user-defined types
|
||||
#
|
||||
type
|
||||
TMyType = tuple[first, second: string] # a pair of strings
|
||||
MyType = tuple[first, second: string] # a pair of strings
|
||||
{.deprecated: [TMyType: MyType].}
|
||||
|
||||
var y = newGenTable[TMyType](modeCaseInsensitive) # hash table where each
|
||||
# value is TMyType tuple
|
||||
var y = newGenTable[MyType](modeCaseInsensitive) # hash table where each
|
||||
# value is MyType tuple
|
||||
|
||||
#var junk: TMyType = ("OK", "Here")
|
||||
#var junk: MyType = ("OK", "Here")
|
||||
|
||||
#echo junk.first, " ", junk.second
|
||||
|
||||
y["Hello"] = ("Hello", "World")
|
||||
y["Goodbye"] = ("Goodbye", "Everyone")
|
||||
#y["Hello"] = TMyType( ("Hello", "World") )
|
||||
#y["Goodbye"] = TMyType( ("Goodbye", "Everyone") )
|
||||
#y["Hello"] = MyType( ("Hello", "World") )
|
||||
#y["Goodbye"] = MyType( ("Goodbye", "Everyone") )
|
||||
|
||||
assert( not isNil(y["Hello"].first) )
|
||||
assert( y["Hello"].first == "Hello" )
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
## code:
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## proc hash(x: Something): THash =
|
||||
## ## Computes a THash from `x`.
|
||||
## var h: THash = 0
|
||||
## proc hash(x: Something): Hash =
|
||||
## ## Computes a Hash from `x`.
|
||||
## var h: Hash = 0
|
||||
## # Iterate over parts of `x`.
|
||||
## for xAtom in x:
|
||||
## # Mix the atom with the partial hash.
|
||||
@@ -30,38 +30,39 @@
|
||||
## together the hash value of the individual fields:
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## proc hash(x: Something): THash =
|
||||
## ## Computes a THash from `x`.
|
||||
## var h: THash = 0
|
||||
## proc hash(x: Something): Hash =
|
||||
## ## Computes a Hash from `x`.
|
||||
## var h: Hash = 0
|
||||
## h = h !& hash(x.foo)
|
||||
## h = h !& hash(x.bar)
|
||||
## result = !$h
|
||||
|
||||
import
|
||||
strutils
|
||||
strutils, etcpriv
|
||||
|
||||
type
|
||||
THash* = int ## a hash value; hash tables using these values should
|
||||
Hash* = int ## a hash value; hash tables using these values should
|
||||
## always have a size of a power of two and can use the ``and``
|
||||
## operator instead of ``mod`` for truncation of the hash value.
|
||||
{.deprecated: [THash: Hash].}
|
||||
|
||||
proc `!&`*(h: THash, val: int): THash {.inline.} =
|
||||
proc `!&`*(h: Hash, val: int): Hash {.inline.} =
|
||||
## mixes a hash value `h` with `val` to produce a new hash value. This is
|
||||
## only needed if you need to implement a hash proc for a new datatype.
|
||||
result = h +% val
|
||||
result = result +% result shl 10
|
||||
result = result xor (result shr 6)
|
||||
|
||||
proc `!$`*(h: THash): THash {.inline.} =
|
||||
proc `!$`*(h: Hash): Hash {.inline.} =
|
||||
## finishes the computation of the hash value. This is
|
||||
## only needed if you need to implement a hash proc for a new datatype.
|
||||
result = h +% h shl 3
|
||||
result = result xor (result shr 11)
|
||||
result = result +% result shl 15
|
||||
|
||||
proc hashData*(data: pointer, size: int): THash =
|
||||
proc hashData*(data: pointer, size: int): Hash =
|
||||
## hashes an array of bytes of size `size`
|
||||
var h: THash = 0
|
||||
var h: Hash = 0
|
||||
when defined(js):
|
||||
var p: cstring
|
||||
asm """`p` = `Data`;"""
|
||||
@@ -78,7 +79,7 @@ proc hashData*(data: pointer, size: int): THash =
|
||||
when defined(js):
|
||||
var objectID = 0
|
||||
|
||||
proc hash*(x: pointer): THash {.inline.} =
|
||||
proc hash*(x: pointer): Hash {.inline.} =
|
||||
## efficient hashing of pointers
|
||||
when defined(js):
|
||||
asm """
|
||||
@@ -92,50 +93,57 @@ proc hash*(x: pointer): THash {.inline.} =
|
||||
}
|
||||
"""
|
||||
else:
|
||||
result = (cast[THash](x)) shr 3 # skip the alignment
|
||||
result = (cast[Hash](x)) shr 3 # skip the alignment
|
||||
|
||||
when not defined(booting):
|
||||
proc hash*[T: proc](x: T): THash {.inline.} =
|
||||
proc hash*[T: proc](x: T): Hash {.inline.} =
|
||||
## efficient hashing of proc vars; closures are supported too.
|
||||
when T is "closure":
|
||||
result = hash(rawProc(x)) !& hash(rawEnv(x))
|
||||
else:
|
||||
result = hash(pointer(x))
|
||||
|
||||
proc hash*(x: int): THash {.inline.} =
|
||||
proc hash*(x: int): Hash {.inline.} =
|
||||
## efficient hashing of integers
|
||||
result = x
|
||||
|
||||
proc hash*(x: int64): THash {.inline.} =
|
||||
proc hash*(x: int64): Hash {.inline.} =
|
||||
## efficient hashing of integers
|
||||
result = toU32(x)
|
||||
|
||||
proc hash*(x: char): THash {.inline.} =
|
||||
proc hash*(x: char): Hash {.inline.} =
|
||||
## efficient hashing of characters
|
||||
result = ord(x)
|
||||
|
||||
proc hash*(x: string): THash =
|
||||
proc hash*(x: string): Hash =
|
||||
## efficient hashing of strings
|
||||
var h: THash = 0
|
||||
var h: Hash = 0
|
||||
for i in 0..x.len-1:
|
||||
h = h !& ord(x[i])
|
||||
result = !$h
|
||||
|
||||
proc hashIgnoreStyle*(x: string): THash =
|
||||
proc hashIgnoreStyle*(x: string): Hash =
|
||||
## efficient hashing of strings; style is ignored
|
||||
var h: THash = 0
|
||||
for i in 0..x.len-1:
|
||||
var h: Hash = 0
|
||||
var i = 0
|
||||
let xLen = x.len
|
||||
while i < xLen:
|
||||
var c = x[i]
|
||||
if c == '_':
|
||||
continue # skip _
|
||||
if c in {'A'..'Z'}:
|
||||
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
|
||||
h = h !& ord(c)
|
||||
inc(i)
|
||||
elif isMagicIdentSeparatorRune(cstring(x), i):
|
||||
inc(i, magicIdentSeparatorRuneByteWidth)
|
||||
else:
|
||||
if c in {'A'..'Z'}:
|
||||
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
|
||||
h = h !& ord(c)
|
||||
inc(i)
|
||||
|
||||
result = !$h
|
||||
|
||||
proc hashIgnoreCase*(x: string): THash =
|
||||
proc hashIgnoreCase*(x: string): Hash =
|
||||
## efficient hashing of strings; case is ignored
|
||||
var h: THash = 0
|
||||
var h: Hash = 0
|
||||
for i in 0..x.len-1:
|
||||
var c = x[i]
|
||||
if c in {'A'..'Z'}:
|
||||
@@ -143,28 +151,28 @@ proc hashIgnoreCase*(x: string): THash =
|
||||
h = h !& ord(c)
|
||||
result = !$h
|
||||
|
||||
proc hash*(x: float): THash {.inline.} =
|
||||
proc hash*(x: float): Hash {.inline.} =
|
||||
var y = x + 1.0
|
||||
result = cast[ptr THash](addr(y))[]
|
||||
result = cast[ptr Hash](addr(y))[]
|
||||
|
||||
|
||||
# Forward declarations before methods that hash containers. This allows
|
||||
# containers to contain other containers
|
||||
proc hash*[A](x: openArray[A]): THash
|
||||
proc hash*[A](x: set[A]): THash
|
||||
proc hash*[A](x: openArray[A]): Hash
|
||||
proc hash*[A](x: set[A]): Hash
|
||||
|
||||
|
||||
proc hash*[T: tuple](x: T): THash =
|
||||
proc hash*[T: tuple](x: T): Hash =
|
||||
## efficient hashing of tuples.
|
||||
for f in fields(x):
|
||||
result = result !& hash(f)
|
||||
result = !$result
|
||||
|
||||
proc hash*[A](x: openArray[A]): THash =
|
||||
proc hash*[A](x: openArray[A]): Hash =
|
||||
for it in items(x): result = result !& hash(it)
|
||||
result = !$result
|
||||
|
||||
proc hash*[A](x: set[A]): THash =
|
||||
proc hash*[A](x: set[A]): Hash =
|
||||
for it in items(x): result = result !& hash(it)
|
||||
result = !$result
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
import strutils, streams, parsexml, xmltree, unicode, strtabs
|
||||
|
||||
type
|
||||
THtmlTag* = enum ## list of all supported HTML tags; order will always be
|
||||
HtmlTag* = enum ## list of all supported HTML tags; order will always be
|
||||
## alphabetically
|
||||
tagUnknown, ## unknown HTML element
|
||||
tagA, ## the HTML ``a`` element
|
||||
@@ -178,6 +178,7 @@ type
|
||||
tagVar, ## the HTML ``var`` element
|
||||
tagVideo, ## the HTML ``video`` element
|
||||
tagWbr ## the HTML ``wbr`` element
|
||||
{.deprecated: [THtmlTag: HtmlTag].}
|
||||
|
||||
const
|
||||
tagToStr* = [
|
||||
@@ -295,7 +296,7 @@ proc allLower(s: string): bool =
|
||||
if c < 'a' or c > 'z': return false
|
||||
return true
|
||||
|
||||
proc toHtmlTag(s: string): THtmlTag =
|
||||
proc toHtmlTag(s: string): HtmlTag =
|
||||
case s
|
||||
of "a": tagA
|
||||
of "abbr": tagAbbr
|
||||
@@ -422,14 +423,14 @@ proc toHtmlTag(s: string): THtmlTag =
|
||||
of "wbr": tagWbr
|
||||
else: tagUnknown
|
||||
|
||||
proc htmlTag*(n: XmlNode): THtmlTag =
|
||||
## gets `n`'s tag as a ``THtmlTag``.
|
||||
proc htmlTag*(n: XmlNode): HtmlTag =
|
||||
## gets `n`'s tag as a ``HtmlTag``.
|
||||
if n.clientData == 0:
|
||||
n.clientData = toHtmlTag(n.tag).ord
|
||||
result = THtmlTag(n.clientData)
|
||||
result = HtmlTag(n.clientData)
|
||||
|
||||
proc htmlTag*(s: string): THtmlTag =
|
||||
## converts `s` to a ``THtmlTag``. If `s` is no HTML tag, ``tagUnknown`` is
|
||||
proc htmlTag*(s: string): HtmlTag =
|
||||
## converts `s` to a ``HtmlTag``. If `s` is no HTML tag, ``tagUnknown`` is
|
||||
## returned.
|
||||
let s = if allLower(s): s else: s.toLower
|
||||
result = toHtmlTag(s)
|
||||
|
||||
@@ -106,9 +106,10 @@ proc serveFile*(client: Socket, filename: string) =
|
||||
when false:
|
||||
# TODO: Fix this, or get rid of it.
|
||||
type
|
||||
TRequestMethod = enum reqGet, reqPost
|
||||
RequestMethod = enum reqGet, reqPost
|
||||
{.deprecated: [TRequestMethod: RequestMethod].}
|
||||
|
||||
proc executeCgi(client: Socket, path, query: string, meth: TRequestMethod) =
|
||||
proc executeCgi(client: Socket, path, query: string, meth: RequestMethod) =
|
||||
var env = newStringTable(modeCaseInsensitive)
|
||||
var contentLength = -1
|
||||
case meth
|
||||
@@ -208,7 +209,7 @@ when false:
|
||||
executeCgi(client, path, query, meth)
|
||||
|
||||
type
|
||||
TServer* = object of RootObj ## contains the current server state
|
||||
Server* = object of RootObj ## contains the current server state
|
||||
socket: Socket
|
||||
port: Port
|
||||
client*: Socket ## the socket to write the file data to
|
||||
@@ -218,11 +219,12 @@ type
|
||||
body*: string ## only set with POST requests
|
||||
ip*: string ## ip address of the requesting client
|
||||
|
||||
PAsyncHTTPServer* = ref TAsyncHTTPServer
|
||||
TAsyncHTTPServer = object of TServer
|
||||
PAsyncHTTPServer* = ref AsyncHTTPServer
|
||||
AsyncHTTPServer = object of Server
|
||||
asyncSocket: AsyncSocket
|
||||
{.deprecated: [TAsyncHTTPServer: AsyncHTTPServer, TServer: Server].}
|
||||
|
||||
proc open*(s: var TServer, port = Port(80), reuseAddr = false) =
|
||||
proc open*(s: var Server, port = Port(80), reuseAddr = false) =
|
||||
## creates a new server at port `port`. If ``port == 0`` a free port is
|
||||
## acquired that can be accessed later by the ``port`` proc.
|
||||
s.socket = socket(AF_INET)
|
||||
@@ -243,11 +245,11 @@ proc open*(s: var TServer, port = Port(80), reuseAddr = false) =
|
||||
s.query = ""
|
||||
s.headers = {:}.newStringTable()
|
||||
|
||||
proc port*(s: var TServer): Port =
|
||||
proc port*(s: var Server): Port =
|
||||
## get the port number the server has acquired.
|
||||
result = s.port
|
||||
|
||||
proc next*(s: var TServer) =
|
||||
proc next*(s: var Server) =
|
||||
## proceed to the first/next request.
|
||||
var client: Socket
|
||||
new(client)
|
||||
@@ -354,7 +356,7 @@ proc next*(s: var TServer) =
|
||||
s.query = ""
|
||||
s.path = data.substr(i, last-1)
|
||||
|
||||
proc close*(s: TServer) =
|
||||
proc close*(s: Server) =
|
||||
## closes the server (and the socket the server uses).
|
||||
close(s.socket)
|
||||
|
||||
@@ -362,7 +364,7 @@ proc run*(handleRequest: proc (client: Socket,
|
||||
path, query: string): bool {.closure.},
|
||||
port = Port(80)) =
|
||||
## encapsulates the server object and main loop
|
||||
var s: TServer
|
||||
var s: Server
|
||||
open(s, port, reuseAddr = true)
|
||||
#echo("httpserver running on port ", s.port)
|
||||
while true:
|
||||
@@ -517,7 +519,7 @@ proc close*(h: PAsyncHTTPServer) =
|
||||
when not defined(testing) and isMainModule:
|
||||
var counter = 0
|
||||
|
||||
var s: TServer
|
||||
var s: Server
|
||||
open(s, Port(0))
|
||||
echo("httpserver running on port ", s.port)
|
||||
while true:
|
||||
|
||||
@@ -68,7 +68,7 @@ type
|
||||
jsonArrayStart, ## start of an array: the ``[`` token
|
||||
jsonArrayEnd ## start of an array: the ``]`` token
|
||||
|
||||
TTokKind = enum # must be synchronized with TJsonEventKind!
|
||||
TokKind = enum # must be synchronized with TJsonEventKind!
|
||||
tkError,
|
||||
tkEof,
|
||||
tkString,
|
||||
@@ -103,14 +103,14 @@ type
|
||||
|
||||
JsonParser* = object of BaseLexer ## the parser object.
|
||||
a: string
|
||||
tok: TTokKind
|
||||
tok: TokKind
|
||||
kind: JsonEventKind
|
||||
err: JsonError
|
||||
state: seq[ParserState]
|
||||
filename: string
|
||||
|
||||
{.deprecated: [TJsonEventKind: JsonEventKind, TJsonError: JsonError,
|
||||
TJsonParser: JsonParser].}
|
||||
TJsonParser: JsonParser, TTokKind: TokKind].}
|
||||
|
||||
const
|
||||
errorMessages: array [JsonError, string] = [
|
||||
@@ -126,7 +126,7 @@ const
|
||||
"EOF expected",
|
||||
"expression expected"
|
||||
]
|
||||
tokToStr: array [TTokKind, string] = [
|
||||
tokToStr: array [TokKind, string] = [
|
||||
"invalid token",
|
||||
"EOF",
|
||||
"string literal",
|
||||
@@ -203,7 +203,7 @@ proc handleHexChar(c: char, x: var int): bool =
|
||||
of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
|
||||
else: result = false # error
|
||||
|
||||
proc parseString(my: var JsonParser): TTokKind =
|
||||
proc parseString(my: var JsonParser): TokKind =
|
||||
result = tkString
|
||||
var pos = my.bufpos + 1
|
||||
var buf = my.buf
|
||||
@@ -359,7 +359,7 @@ proc parseName(my: var JsonParser) =
|
||||
inc(pos)
|
||||
my.bufpos = pos
|
||||
|
||||
proc getTok(my: var JsonParser): TTokKind =
|
||||
proc getTok(my: var JsonParser): TokKind =
|
||||
setLen(my.a, 0)
|
||||
skip(my) # skip whitespace, comments
|
||||
case my.buf[my.bufpos]
|
||||
@@ -734,7 +734,7 @@ proc `==`* (a,b: JsonNode): bool =
|
||||
of JObject:
|
||||
a.fields == b.fields
|
||||
|
||||
proc hash* (n:JsonNode): THash =
|
||||
proc hash* (n:JsonNode): Hash =
|
||||
## Compute the hash for a JSON node
|
||||
case n.kind
|
||||
of JArray:
|
||||
@@ -1016,7 +1016,7 @@ iterator mpairs*(node: var JsonNode): var tuple[key: string, val: JsonNode] =
|
||||
for keyVal in mitems(node.fields):
|
||||
yield keyVal
|
||||
|
||||
proc eat(p: var JsonParser, tok: TTokKind) =
|
||||
proc eat(p: var JsonParser, tok: TokKind) =
|
||||
if p.tok == tok: discard getTok(p)
|
||||
else: raiseParseErr(p, tokToStr[tok])
|
||||
|
||||
@@ -1091,8 +1091,10 @@ when not defined(js):
|
||||
else:
|
||||
from math import `mod`
|
||||
type
|
||||
TJSObject = object
|
||||
proc parseNativeJson(x: cstring): TJSObject {.importc: "JSON.parse".}
|
||||
JSObject = object
|
||||
{.deprecated: [TJSObject: JSObject].}
|
||||
|
||||
proc parseNativeJson(x: cstring): JSObject {.importc: "JSON.parse".}
|
||||
|
||||
proc getVarType(x): JsonNodeKind =
|
||||
result = JNull
|
||||
@@ -1111,25 +1113,25 @@ else:
|
||||
of "[object String]": return JString
|
||||
else: assert false
|
||||
|
||||
proc len(x: TJSObject): int =
|
||||
proc len(x: JSObject): int =
|
||||
assert x.getVarType == JArray
|
||||
asm """
|
||||
return `x`.length;
|
||||
"""
|
||||
|
||||
proc `[]`(x: TJSObject, y: string): TJSObject =
|
||||
proc `[]`(x: JSObject, y: string): JSObject =
|
||||
assert x.getVarType == JObject
|
||||
asm """
|
||||
return `x`[`y`];
|
||||
"""
|
||||
|
||||
proc `[]`(x: TJSObject, y: int): TJSObject =
|
||||
proc `[]`(x: JSObject, y: int): JSObject =
|
||||
assert x.getVarType == JArray
|
||||
asm """
|
||||
return `x`[`y`];
|
||||
"""
|
||||
|
||||
proc convertObject(x: TJSObject): JsonNode =
|
||||
proc convertObject(x: JSObject): JsonNode =
|
||||
case getVarType(x)
|
||||
of JArray:
|
||||
result = newJArray()
|
||||
@@ -1141,7 +1143,7 @@ else:
|
||||
if (`x`.hasOwnProperty(property)) {
|
||||
"""
|
||||
var nimProperty: cstring
|
||||
var nimValue: TJSObject
|
||||
var nimValue: JSObject
|
||||
asm "`nimProperty` = property; `nimValue` = `x`[property];"
|
||||
result[$nimProperty] = nimValue.convertObject()
|
||||
asm "}}"
|
||||
|
||||
@@ -39,7 +39,7 @@ type
|
||||
{.deprecated: [TBaseLexer: BaseLexer].}
|
||||
|
||||
proc open*(L: var BaseLexer, input: Stream, bufLen: int = 8192)
|
||||
## inits the TBaseLexer with a stream to read from
|
||||
## inits the BaseLexer with a stream to read from
|
||||
|
||||
proc close*(L: var BaseLexer)
|
||||
## closes the base lexer. This closes `L`'s associated stream too.
|
||||
|
||||
@@ -82,6 +82,7 @@ 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].}
|
||||
@@ -112,27 +113,22 @@ proc substituteLog(frmt: string): string =
|
||||
of "appname": result.add(app.splitFile.name)
|
||||
else: discard
|
||||
|
||||
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, LevelNames[level], " ", substituteLog(logger.fmtStr), 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, LevelNames[level], " ", substituteLog(logger.fmtStr), args)
|
||||
|
||||
proc defaultFilename*(): string =
|
||||
## Returns the default filename for a logger.
|
||||
@@ -148,11 +144,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 +180,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 +209,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 +217,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, LevelNames[level], " ", substituteLog(logger.fmtStr), args)
|
||||
logger.curLine.inc
|
||||
|
||||
# --------
|
||||
@@ -225,39 +227,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 +288,4 @@ when not defined(testing) and isMainModule:
|
||||
addHandler(fL)
|
||||
addHandler(rL)
|
||||
for i in 0 .. 25:
|
||||
info("hello" & $i, [])
|
||||
|
||||
|
||||
info("hello", i)
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## type
|
||||
## TA = object
|
||||
## TB = object of TA
|
||||
## A = object
|
||||
## B = object of A
|
||||
## f: int
|
||||
##
|
||||
## var
|
||||
## a: ref TA
|
||||
## b: ref TB
|
||||
## a: ref A
|
||||
## b: ref B
|
||||
##
|
||||
## new(b)
|
||||
## a = b
|
||||
@@ -36,7 +36,7 @@ import streams, typeinfo, json, intsets, tables
|
||||
proc ptrToInt(x: pointer): int {.inline.} =
|
||||
result = cast[int](x) # don't skip alignment
|
||||
|
||||
proc storeAny(s: Stream, a: TAny, stored: var IntSet) =
|
||||
proc storeAny(s: Stream, a: Any, stored: var IntSet) =
|
||||
case a.kind
|
||||
of akNone: assert false
|
||||
of akBool: s.write($getBool(a))
|
||||
@@ -96,7 +96,7 @@ proc storeAny(s: Stream, a: TAny, stored: var IntSet) =
|
||||
of akInt..akInt64, akUInt..akUInt64: s.write($getBiggestInt(a))
|
||||
of akFloat..akFloat128: s.write($getBiggestFloat(a))
|
||||
|
||||
proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||
proc loadAny(p: var JsonParser, a: Any, t: var Table[BiggestInt, pointer]) =
|
||||
case a.kind
|
||||
of akNone: assert false
|
||||
of akBool:
|
||||
@@ -222,7 +222,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||
raiseParseErr(p, "float expected")
|
||||
of akRange: loadAny(p, a.skipRange, t)
|
||||
|
||||
proc loadAny(s: Stream, a: TAny, t: var Table[BiggestInt, pointer]) =
|
||||
proc loadAny(s: Stream, a: Any, t: var Table[BiggestInt, pointer]) =
|
||||
var p: JsonParser
|
||||
open(p, s, "unknown file")
|
||||
next(p)
|
||||
@@ -278,10 +278,11 @@ when not defined(testing) and isMainModule:
|
||||
else:
|
||||
nil
|
||||
|
||||
PNode = ref TNode
|
||||
TNode = object
|
||||
PNode = ref Node
|
||||
Node = object
|
||||
next, prev: PNode
|
||||
data: string
|
||||
{.deprecated: [TNode: Node].}
|
||||
|
||||
proc buildList(): PNode =
|
||||
new(result)
|
||||
@@ -317,14 +318,15 @@ when not defined(testing) and isMainModule:
|
||||
testit(test7)
|
||||
|
||||
type
|
||||
TA {.inheritable.} = object
|
||||
TB = object of TA
|
||||
A {.inheritable.} = object
|
||||
B = object of A
|
||||
f: int
|
||||
|
||||
var
|
||||
a: ref TA
|
||||
b: ref TB
|
||||
a: ref A
|
||||
b: ref B
|
||||
new(b)
|
||||
a = b
|
||||
echo($$a[]) # produces "{}", not "{f: 0}"
|
||||
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@ proc open*(filename: string, mode: FileMode = fmRead,
|
||||
if mappedSize != -1:
|
||||
result.size = mappedSize
|
||||
else:
|
||||
var stat: TStat
|
||||
var stat: Stat
|
||||
if fstat(result.handle, stat) != -1:
|
||||
# XXX: Hmm, this could be unsafe
|
||||
# Why is mmap taking int anyway?
|
||||
|
||||
@@ -86,7 +86,7 @@ type
|
||||
IPv6, ## IPv6 address
|
||||
IPv4 ## IPv4 address
|
||||
|
||||
TIpAddress* = object ## stores an arbitrary IP address
|
||||
IpAddress* = object ## stores an arbitrary IP address
|
||||
case family*: IpAddressFamily ## the type of the IP address (IPv4 or IPv6)
|
||||
of IpAddressFamily.IPv6:
|
||||
address_v6*: array[0..15, uint8] ## Contains the IP address in bytes in
|
||||
@@ -94,9 +94,10 @@ type
|
||||
of IpAddressFamily.IPv4:
|
||||
address_v4*: array[0..3, uint8] ## Contains the IP address in bytes in
|
||||
## case of IPv4
|
||||
{.deprecated: [TIpAddress: IpAddress].}
|
||||
|
||||
proc isIpAddress*(address_str: string): bool {.tags: [].}
|
||||
proc parseIpAddress*(address_str: string): TIpAddress
|
||||
proc parseIpAddress*(address_str: string): IpAddress
|
||||
|
||||
proc isDisconnectionError*(flags: set[SocketFlag],
|
||||
lastError: OSErrorCode): bool =
|
||||
@@ -118,13 +119,13 @@ proc toOSFlags*(socketFlags: set[SocketFlag]): cint =
|
||||
result = result or MSG_PEEK
|
||||
of SocketFlag.SafeDisconn: continue
|
||||
|
||||
proc newSocket(fd: SocketHandle, isBuff: bool): Socket =
|
||||
proc newSocket*(fd: SocketHandle, buffered = true): Socket =
|
||||
## Creates a new socket as specified by the params.
|
||||
assert fd != osInvalidSocket
|
||||
new(result)
|
||||
result.fd = fd
|
||||
result.isBuffered = isBuff
|
||||
if isBuff:
|
||||
result.isBuffered = buffered
|
||||
if buffered:
|
||||
result.currPos = 0
|
||||
|
||||
proc newSocket*(domain, typ, protocol: cint, buffered = true): Socket =
|
||||
@@ -395,7 +396,7 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string,
|
||||
|
||||
when false: #defined(ssl):
|
||||
proc acceptAddrSSL*(server: Socket, client: var Socket,
|
||||
address: var string): TSSLAcceptResult {.
|
||||
address: var string): SSLAcceptResult {.
|
||||
tags: [ReadIOEffect].} =
|
||||
## This procedure should only be used for non-blocking **SSL** sockets.
|
||||
## It will immediately return with one of the following values:
|
||||
@@ -992,39 +993,39 @@ proc isSsl*(socket: Socket): bool =
|
||||
proc getFd*(socket: Socket): SocketHandle = return socket.fd
|
||||
## Returns the socket's file descriptor
|
||||
|
||||
proc IPv4_any*(): TIpAddress =
|
||||
proc IPv4_any*(): IpAddress =
|
||||
## Returns the IPv4 any address, which can be used to listen on all available
|
||||
## network adapters
|
||||
result = TIpAddress(
|
||||
result = IpAddress(
|
||||
family: IpAddressFamily.IPv4,
|
||||
address_v4: [0'u8, 0, 0, 0])
|
||||
|
||||
proc IPv4_loopback*(): TIpAddress =
|
||||
proc IPv4_loopback*(): IpAddress =
|
||||
## Returns the IPv4 loopback address (127.0.0.1)
|
||||
result = TIpAddress(
|
||||
result = IpAddress(
|
||||
family: IpAddressFamily.IPv4,
|
||||
address_v4: [127'u8, 0, 0, 1])
|
||||
|
||||
proc IPv4_broadcast*(): TIpAddress =
|
||||
proc IPv4_broadcast*(): IpAddress =
|
||||
## Returns the IPv4 broadcast address (255.255.255.255)
|
||||
result = TIpAddress(
|
||||
result = IpAddress(
|
||||
family: IpAddressFamily.IPv4,
|
||||
address_v4: [255'u8, 255, 255, 255])
|
||||
|
||||
proc IPv6_any*(): TIpAddress =
|
||||
proc IPv6_any*(): IpAddress =
|
||||
## Returns the IPv6 any address (::0), which can be used
|
||||
## to listen on all available network adapters
|
||||
result = TIpAddress(
|
||||
result = IpAddress(
|
||||
family: IpAddressFamily.IPv6,
|
||||
address_v6: [0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
|
||||
|
||||
proc IPv6_loopback*(): TIpAddress =
|
||||
proc IPv6_loopback*(): IpAddress =
|
||||
## Returns the IPv6 loopback address (::1)
|
||||
result = TIpAddress(
|
||||
result = IpAddress(
|
||||
family: IpAddressFamily.IPv6,
|
||||
address_v6: [0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])
|
||||
|
||||
proc `==`*(lhs, rhs: TIpAddress): bool =
|
||||
proc `==`*(lhs, rhs: IpAddress): bool =
|
||||
## Compares two IpAddresses for Equality. Returns two if the addresses are equal
|
||||
if lhs.family != rhs.family: return false
|
||||
if lhs.family == IpAddressFamily.IPv4:
|
||||
@@ -1035,8 +1036,8 @@ proc `==`*(lhs, rhs: TIpAddress): bool =
|
||||
if lhs.address_v6[i] != rhs.address_v6[i]: return false
|
||||
return true
|
||||
|
||||
proc `$`*(address: TIpAddress): string =
|
||||
## Converts an TIpAddress into the textual representation
|
||||
proc `$`*(address: IpAddress): string =
|
||||
## Converts an IpAddress into the textual representation
|
||||
result = ""
|
||||
case address.family
|
||||
of IpAddressFamily.IPv4:
|
||||
@@ -1095,7 +1096,7 @@ proc `$`*(address: TIpAddress): string =
|
||||
mask = mask shr 4
|
||||
printedLastGroup = true
|
||||
|
||||
proc parseIPv4Address(address_str: string): TIpAddress =
|
||||
proc parseIPv4Address(address_str: string): IpAddress =
|
||||
## Parses IPv4 adresses
|
||||
## Raises EInvalidValue on errors
|
||||
var
|
||||
@@ -1129,7 +1130,7 @@ proc parseIPv4Address(address_str: string): TIpAddress =
|
||||
raise newException(ValueError, "Invalid IP Address")
|
||||
result.address_v4[byteCount] = cast[uint8](currentByte)
|
||||
|
||||
proc parseIPv6Address(address_str: string): TIpAddress =
|
||||
proc parseIPv6Address(address_str: string): IpAddress =
|
||||
## Parses IPv6 adresses
|
||||
## Raises EInvalidValue on errors
|
||||
result.family = IpAddressFamily.IPv6
|
||||
@@ -1250,7 +1251,7 @@ proc parseIPv6Address(address_str: string): TIpAddress =
|
||||
raise newException(ValueError,
|
||||
"Invalid IP Address. The address consists of too many groups")
|
||||
|
||||
proc parseIpAddress(address_str: string): TIpAddress =
|
||||
proc parseIpAddress(address_str: string): IpAddress =
|
||||
## Parses an IP address
|
||||
## Raises EInvalidValue on error
|
||||
if address_str == nil:
|
||||
|
||||
@@ -26,17 +26,19 @@ const
|
||||
withThreads = compileOption("threads")
|
||||
tickCountCorrection = 50_000
|
||||
|
||||
when not declared(system.TStackTrace):
|
||||
type TStackTrace = array [0..20, cstring]
|
||||
when not declared(system.StackTrace):
|
||||
type StackTrace = array [0..20, cstring]
|
||||
{.deprecated: [TStackTrace: StackTrace].}
|
||||
|
||||
# We use a simple hash table of bounded size to keep track of the stack traces:
|
||||
type
|
||||
TProfileEntry = object
|
||||
ProfileEntry = object
|
||||
total: int
|
||||
st: TStackTrace
|
||||
TProfileData = array [0..64*1024-1, ptr TProfileEntry]
|
||||
st: StackTrace
|
||||
ProfileData = array [0..64*1024-1, ptr ProfileEntry]
|
||||
{.deprecated: [TProfileEntry: ProfileEntry, TProfileData: ProfileData].}
|
||||
|
||||
proc `==`(a, b: TStackTrace): bool =
|
||||
proc `==`(a, b: StackTrace): bool =
|
||||
for i in 0 .. high(a):
|
||||
if a[i] != b[i]: return false
|
||||
result = true
|
||||
@@ -44,13 +46,13 @@ proc `==`(a, b: TStackTrace): bool =
|
||||
# XXX extract this data structure; it is generally useful ;-)
|
||||
# However a chain length of over 3000 is suspicious...
|
||||
var
|
||||
profileData: TProfileData
|
||||
profileData: ProfileData
|
||||
emptySlots = profileData.len * 3 div 2
|
||||
maxChainLen = 0
|
||||
totalCalls = 0
|
||||
|
||||
when not defined(memProfiler):
|
||||
var interval: TNanos = 5_000_000 - tickCountCorrection # 5ms
|
||||
var interval: Nanos = 5_000_000 - tickCountCorrection # 5ms
|
||||
|
||||
proc setSamplingFrequency*(intervalInUs: int) =
|
||||
## set this to change the sampling frequency. Default value is 5ms.
|
||||
@@ -62,11 +64,11 @@ when not defined(memProfiler):
|
||||
when withThreads:
|
||||
import locks
|
||||
var
|
||||
profilingLock: TLock
|
||||
profilingLock: Lock
|
||||
|
||||
initLock profilingLock
|
||||
|
||||
proc hookAux(st: TStackTrace, costs: int) =
|
||||
proc hookAux(st: StackTrace, costs: int) =
|
||||
# this is quite performance sensitive!
|
||||
when withThreads: acquire profilingLock
|
||||
inc totalCalls
|
||||
@@ -94,8 +96,8 @@ proc hookAux(st: TStackTrace, costs: int) =
|
||||
var chain = 0
|
||||
while true:
|
||||
if profileData[h] == nil:
|
||||
profileData[h] = cast[ptr TProfileEntry](
|
||||
allocShared0(sizeof(TProfileEntry)))
|
||||
profileData[h] = cast[ptr ProfileEntry](
|
||||
allocShared0(sizeof(ProfileEntry)))
|
||||
profileData[h].total = costs
|
||||
profileData[h].st = st
|
||||
dec emptySlots
|
||||
@@ -115,7 +117,7 @@ when defined(memProfiler):
|
||||
var
|
||||
gTicker {.threadvar.}: int
|
||||
|
||||
proc hook(st: TStackTrace, size: int) {.nimcall.} =
|
||||
proc hook(st: StackTrace, size: int) {.nimcall.} =
|
||||
if gTicker == 0:
|
||||
gTicker = -1
|
||||
when defined(ignoreAllocationSize):
|
||||
@@ -127,26 +129,26 @@ when defined(memProfiler):
|
||||
|
||||
else:
|
||||
var
|
||||
t0 {.threadvar.}: TTicks
|
||||
t0 {.threadvar.}: Ticks
|
||||
|
||||
proc hook(st: TStackTrace) {.nimcall.} =
|
||||
proc hook(st: StackTrace) {.nimcall.} =
|
||||
if interval == 0:
|
||||
hookAux(st, 1)
|
||||
elif int64(t0) == 0 or getTicks() - t0 > interval:
|
||||
hookAux(st, 1)
|
||||
t0 = getTicks()
|
||||
|
||||
proc getTotal(x: ptr TProfileEntry): int =
|
||||
proc getTotal(x: ptr ProfileEntry): int =
|
||||
result = if isNil(x): 0 else: x.total
|
||||
|
||||
proc cmpEntries(a, b: ptr TProfileEntry): int =
|
||||
proc cmpEntries(a, b: ptr ProfileEntry): int =
|
||||
result = b.getTotal - a.getTotal
|
||||
|
||||
proc `//`(a, b: int): string =
|
||||
result = format("$1/$2 = $3%", a, b, formatFloat(a / b * 100.0, ffDefault, 2))
|
||||
|
||||
proc writeProfile() {.noconv.} =
|
||||
when declared(system.TStackTrace):
|
||||
when declared(system.StackTrace):
|
||||
system.profilerHook = nil
|
||||
const filename = "profile_results.txt"
|
||||
echo "writing " & filename & "..."
|
||||
@@ -161,7 +163,7 @@ proc writeProfile() {.noconv.} =
|
||||
var perProc = initCountTable[string]()
|
||||
for i in 0..entries-1:
|
||||
var dups = initSet[string]()
|
||||
for ii in 0..high(TStackTrace):
|
||||
for ii in 0..high(StackTrace):
|
||||
let procname = profileData[i].st[ii]
|
||||
if isNil(procname): break
|
||||
let p = $procname
|
||||
@@ -176,7 +178,7 @@ proc writeProfile() {.noconv.} =
|
||||
writeln(f, "Entry: ", i+1, "/", entries, " Calls: ",
|
||||
profileData[i].total // totalCalls, " [sum: ", sum, "; ",
|
||||
sum // totalCalls, "]")
|
||||
for ii in 0..high(TStackTrace):
|
||||
for ii in 0..high(StackTrace):
|
||||
let procname = profileData[i].st[ii]
|
||||
if isNil(procname): break
|
||||
writeln(f, " ", procname, " ", perProc[$procname] // totalCalls)
|
||||
@@ -189,16 +191,16 @@ var
|
||||
disabled: int
|
||||
|
||||
proc disableProfiling*() =
|
||||
when declared(system.TStackTrace):
|
||||
when declared(system.StackTrace):
|
||||
atomicDec disabled
|
||||
system.profilerHook = nil
|
||||
|
||||
proc enableProfiling*() =
|
||||
when declared(system.TStackTrace):
|
||||
when declared(system.StackTrace):
|
||||
if atomicInc(disabled) >= 0:
|
||||
system.profilerHook = hook
|
||||
|
||||
when declared(system.TStackTrace):
|
||||
when declared(system.StackTrace):
|
||||
system.profilerHook = hook
|
||||
addQuitProc(writeProfile)
|
||||
|
||||
|
||||
108
lib/pure/os.nim
108
lib/pure/os.nim
@@ -41,7 +41,7 @@ type
|
||||
|
||||
OSErrorCode* = distinct int32 ## Specifies an OS Error Code.
|
||||
|
||||
{.deprecated: [FReadEnv: ReadEnvEffect, FWriteEnv: WriteEnvEffect,
|
||||
{.deprecated: [FReadEnv: ReadEnvEffect, FWriteEnv: WriteEnvEffect,
|
||||
FReadDir: ReadDirEffect,
|
||||
FWriteDir: WriteDirEffect,
|
||||
TOSErrorCode: OSErrorCode
|
||||
@@ -359,7 +359,7 @@ when defined(windows):
|
||||
|
||||
template wrapBinary(varname, winApiProc, arg, arg2: expr) {.immediate.} =
|
||||
var varname = winApiProc(newWideCString(arg), arg2)
|
||||
proc findFirstFile(a: string, b: var TWIN32_FIND_DATA): THandle =
|
||||
proc findFirstFile(a: string, b: var WIN32_FIND_DATA): Handle =
|
||||
result = findFirstFileW(newWideCString(a), b)
|
||||
template findNextFile(a, b: expr): expr = findNextFileW(a, b)
|
||||
template getCommandLine(): expr = getCommandLineW()
|
||||
@@ -373,7 +373,7 @@ when defined(windows):
|
||||
|
||||
template getFilename(f: expr): expr = $f.cFilename
|
||||
|
||||
proc skipFindData(f: TWIN32_FIND_DATA): bool {.inline.} =
|
||||
proc skipFindData(f: WIN32_FIND_DATA): bool {.inline.} =
|
||||
# Note - takes advantage of null delimiter in the cstring
|
||||
const dot = ord('.')
|
||||
result = f.cFileName[0].int == dot and (f.cFileName[1].int == 0 or
|
||||
@@ -390,7 +390,7 @@ proc existsFile*(filename: string): bool {.rtl, extern: "nos$1",
|
||||
if a != -1'i32:
|
||||
result = (a and FILE_ATTRIBUTE_DIRECTORY) == 0'i32
|
||||
else:
|
||||
var res: TStat
|
||||
var res: Stat
|
||||
return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode)
|
||||
|
||||
proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect].} =
|
||||
@@ -404,7 +404,7 @@ proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect]
|
||||
if a != -1'i32:
|
||||
result = (a and FILE_ATTRIBUTE_DIRECTORY) != 0'i32
|
||||
else:
|
||||
var res: TStat
|
||||
var res: Stat
|
||||
return stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode)
|
||||
|
||||
proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
|
||||
@@ -419,7 +419,7 @@ proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
|
||||
if a != -1'i32:
|
||||
result = (a and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32
|
||||
else:
|
||||
var res: TStat
|
||||
var res: Stat
|
||||
return lstat(link, res) >= 0'i32 and S_ISLNK(res.st_mode)
|
||||
|
||||
proc fileExists*(filename: string): bool {.inline.} =
|
||||
@@ -433,11 +433,11 @@ proc dirExists*(dir: string): bool {.inline.} =
|
||||
proc getLastModificationTime*(file: string): Time {.rtl, extern: "nos$1".} =
|
||||
## Returns the `file`'s last modification time.
|
||||
when defined(posix):
|
||||
var res: TStat
|
||||
var res: Stat
|
||||
if stat(file, res) < 0'i32: raiseOSError(osLastError())
|
||||
return res.st_mtime
|
||||
else:
|
||||
var f: TWIN32_FIND_DATA
|
||||
var f: WIN32_FIND_DATA
|
||||
var h = findFirstFile(file, f)
|
||||
if h == -1'i32: raiseOSError(osLastError())
|
||||
result = winTimeToUnixTime(rdFileTime(f.ftLastWriteTime))
|
||||
@@ -446,11 +446,11 @@ proc getLastModificationTime*(file: string): Time {.rtl, extern: "nos$1".} =
|
||||
proc getLastAccessTime*(file: string): Time {.rtl, extern: "nos$1".} =
|
||||
## Returns the `file`'s last read or write access time.
|
||||
when defined(posix):
|
||||
var res: TStat
|
||||
var res: Stat
|
||||
if stat(file, res) < 0'i32: raiseOSError(osLastError())
|
||||
return res.st_atime
|
||||
else:
|
||||
var f: TWIN32_FIND_DATA
|
||||
var f: WIN32_FIND_DATA
|
||||
var h = findFirstFile(file, f)
|
||||
if h == -1'i32: raiseOSError(osLastError())
|
||||
result = winTimeToUnixTime(rdFileTime(f.ftLastAccessTime))
|
||||
@@ -461,11 +461,11 @@ proc getCreationTime*(file: string): Time {.rtl, extern: "nos$1".} =
|
||||
## Note that under posix OS's, the returned time may actually be the time at
|
||||
## which the file's attribute's were last modified.
|
||||
when defined(posix):
|
||||
var res: TStat
|
||||
var res: Stat
|
||||
if stat(file, res) < 0'i32: raiseOSError(osLastError())
|
||||
return res.st_ctime
|
||||
else:
|
||||
var f: TWIN32_FIND_DATA
|
||||
var f: WIN32_FIND_DATA
|
||||
var h = findFirstFile(file, f)
|
||||
if h == -1'i32: raiseOSError(osLastError())
|
||||
result = winTimeToUnixTime(rdFileTime(f.ftCreationTime))
|
||||
@@ -794,20 +794,20 @@ proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1".} =
|
||||
result = path[0] == '/'
|
||||
|
||||
when defined(Windows):
|
||||
proc openHandle(path: string, followSymlink=true): THandle =
|
||||
proc openHandle(path: string, followSymlink=true): Handle =
|
||||
var flags = FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL
|
||||
if not followSymlink:
|
||||
flags = flags or FILE_FLAG_OPEN_REPARSE_POINT
|
||||
|
||||
when useWinUnicode:
|
||||
result = createFileW(
|
||||
newWideCString(path), 0'i32,
|
||||
newWideCString(path), 0'i32,
|
||||
FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE,
|
||||
nil, OPEN_EXISTING, flags, 0
|
||||
)
|
||||
else:
|
||||
result = createFileA(
|
||||
path, 0'i32,
|
||||
path, 0'i32,
|
||||
FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE,
|
||||
nil, OPEN_EXISTING, flags, 0
|
||||
)
|
||||
@@ -827,7 +827,7 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
|
||||
|
||||
var lastErr: OSErrorCode
|
||||
if f1 != INVALID_HANDLE_VALUE and f2 != INVALID_HANDLE_VALUE:
|
||||
var fi1, fi2: TBY_HANDLE_FILE_INFORMATION
|
||||
var fi1, fi2: BY_HANDLE_FILE_INFORMATION
|
||||
|
||||
if getFileInformationByHandle(f1, addr(fi1)) != 0 and
|
||||
getFileInformationByHandle(f2, addr(fi2)) != 0:
|
||||
@@ -846,7 +846,7 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
|
||||
|
||||
if not success: raiseOSError(lastErr)
|
||||
else:
|
||||
var a, b: TStat
|
||||
var a, b: Stat
|
||||
if stat(path1, a) < 0'i32 or stat(path2, b) < 0'i32:
|
||||
raiseOSError(osLastError())
|
||||
else:
|
||||
@@ -903,7 +903,7 @@ proc getFilePermissions*(filename: string): set[FilePermission] {.
|
||||
## an error. On Windows, only the ``readonly`` flag is checked, every other
|
||||
## permission is available in any case.
|
||||
when defined(posix):
|
||||
var a: TStat
|
||||
var a: Stat
|
||||
if stat(filename, a) < 0'i32: raiseOSError(osLastError())
|
||||
result = {}
|
||||
if (a.st_mode and S_IRUSR) != 0'i32: result.incl(fpUserRead)
|
||||
@@ -924,11 +924,11 @@ proc getFilePermissions*(filename: string): set[FilePermission] {.
|
||||
var res = getFileAttributesA(filename)
|
||||
if res == -1'i32: raiseOSError(osLastError())
|
||||
if (res and FILE_ATTRIBUTE_READONLY) != 0'i32:
|
||||
result = {fpUserExec, fpUserRead, fpGroupExec, fpGroupRead,
|
||||
result = {fpUserExec, fpUserRead, fpGroupExec, fpGroupRead,
|
||||
fpOthersExec, fpOthersRead}
|
||||
else:
|
||||
result = {fpUserExec..fpOthersRead}
|
||||
|
||||
|
||||
proc setFilePermissions*(filename: string, permissions: set[FilePermission]) {.
|
||||
rtl, extern: "nos$1", tags: [WriteDirEffect].} =
|
||||
## sets the file permissions for `filename`. `OSError` is raised in case of
|
||||
@@ -939,15 +939,15 @@ proc setFilePermissions*(filename: string, permissions: set[FilePermission]) {.
|
||||
if fpUserRead in permissions: p = p or S_IRUSR
|
||||
if fpUserWrite in permissions: p = p or S_IWUSR
|
||||
if fpUserExec in permissions: p = p or S_IXUSR
|
||||
|
||||
|
||||
if fpGroupRead in permissions: p = p or S_IRGRP
|
||||
if fpGroupWrite in permissions: p = p or S_IWGRP
|
||||
if fpGroupExec in permissions: p = p or S_IXGRP
|
||||
|
||||
|
||||
if fpOthersRead in permissions: p = p or S_IROTH
|
||||
if fpOthersWrite in permissions: p = p or S_IWOTH
|
||||
if fpOthersExec in permissions: p = p or S_IXOTH
|
||||
|
||||
|
||||
if chmod(filename, p) != 0: raiseOSError(osLastError())
|
||||
else:
|
||||
when useWinUnicode:
|
||||
@@ -955,7 +955,7 @@ proc setFilePermissions*(filename: string, permissions: set[FilePermission]) {.
|
||||
else:
|
||||
var res = getFileAttributesA(filename)
|
||||
if res == -1'i32: raiseOSError(osLastError())
|
||||
if fpUserWrite in permissions:
|
||||
if fpUserWrite in permissions:
|
||||
res = res and not FILE_ATTRIBUTE_READONLY
|
||||
else:
|
||||
res = res or FILE_ATTRIBUTE_READONLY
|
||||
@@ -1030,11 +1030,11 @@ when not declared(ENOENT) and not defined(Windows):
|
||||
when defined(Windows):
|
||||
when useWinUnicode:
|
||||
template deleteFile(file: expr): expr {.immediate.} = deleteFileW(file)
|
||||
template setFileAttributes(file, attrs: expr): expr {.immediate.} =
|
||||
template setFileAttributes(file, attrs: expr): expr {.immediate.} =
|
||||
setFileAttributesW(file, attrs)
|
||||
else:
|
||||
template deleteFile(file: expr): expr {.immediate.} = deleteFileA(file)
|
||||
template setFileAttributes(file, attrs: expr): expr {.immediate.} =
|
||||
template setFileAttributes(file, attrs: expr): expr {.immediate.} =
|
||||
setFileAttributesA(file, attrs)
|
||||
|
||||
proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].} =
|
||||
@@ -1047,7 +1047,7 @@ proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].}
|
||||
else:
|
||||
let f = file
|
||||
if deleteFile(f) == 0:
|
||||
if getLastError() == ERROR_ACCESS_DENIED:
|
||||
if getLastError() == ERROR_ACCESS_DENIED:
|
||||
if setFileAttributes(f, FILE_ATTRIBUTE_NORMAL) == 0:
|
||||
raiseOSError(osLastError())
|
||||
if deleteFile(f) == 0:
|
||||
@@ -1220,7 +1220,7 @@ iterator walkFiles*(pattern: string): string {.tags: [ReadDirEffect].} =
|
||||
## notation is supported.
|
||||
when defined(windows):
|
||||
var
|
||||
f: TWIN32_FIND_DATA
|
||||
f: WIN32_FIND_DATA
|
||||
res: int
|
||||
res = findFirstFile(pattern, f)
|
||||
if res != -1:
|
||||
@@ -1232,7 +1232,7 @@ iterator walkFiles*(pattern: string): string {.tags: [ReadDirEffect].} =
|
||||
findClose(res)
|
||||
else: # here we use glob
|
||||
var
|
||||
f: TGlob
|
||||
f: Glob
|
||||
res: int
|
||||
f.gl_offs = 0
|
||||
f.gl_pathc = 0
|
||||
@@ -1276,7 +1276,7 @@ iterator walkDir*(dir: string): tuple[kind: PathComponent, path: string] {.
|
||||
## dirA/fileA1.txt
|
||||
## dirA/fileA2.txt
|
||||
when defined(windows):
|
||||
var f: TWIN32_FIND_DATA
|
||||
var f: WIN32_FIND_DATA
|
||||
var h = findFirstFile(dir / "*", f)
|
||||
if h != -1:
|
||||
while true:
|
||||
@@ -1297,7 +1297,7 @@ iterator walkDir*(dir: string): tuple[kind: PathComponent, path: string] {.
|
||||
if x == nil: break
|
||||
var y = $x.d_name
|
||||
if y != "." and y != "..":
|
||||
var s: TStat
|
||||
var s: Stat
|
||||
y = dir / y
|
||||
var k = pcFile
|
||||
|
||||
@@ -1319,9 +1319,9 @@ iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string {.
|
||||
## walks over the directory `dir` and yields for each file in `dir`. The
|
||||
## full path for each file is returned.
|
||||
## **Warning**:
|
||||
## Modifying the directory structure while the iterator
|
||||
## is traversing may result in undefined behavior!
|
||||
##
|
||||
## Modifying the directory structure while the iterator
|
||||
## is traversing may result in undefined behavior!
|
||||
##
|
||||
## Walking is recursive. `filter` controls the behaviour of the iterator:
|
||||
##
|
||||
## --------------------- ---------------------------------------------
|
||||
@@ -1424,7 +1424,7 @@ proc createSymlink*(src, dest: string) =
|
||||
## by `src`. On most operating systems, will fail if a lonk
|
||||
##
|
||||
## **Warning**:
|
||||
## Some OS's (such as Microsoft Windows) restrict the creation
|
||||
## Some OS's (such as Microsoft Windows) restrict the creation
|
||||
## of symlinks to root users (administrators).
|
||||
when defined(Windows):
|
||||
let flag = dirExists(src).int32
|
||||
@@ -1444,7 +1444,7 @@ proc createHardlink*(src, dest: string) =
|
||||
## Create a hard link at `dest` which points to the item specified
|
||||
## by `src`.
|
||||
##
|
||||
## **Warning**: Most OS's restrict the creation of hard links to
|
||||
## **Warning**: Most OS's restrict the creation of hard links to
|
||||
## root users (administrators) .
|
||||
when defined(Windows):
|
||||
when useWinUnicode:
|
||||
@@ -1548,7 +1548,7 @@ proc parseCmdLine*(c: string): seq[string] {.
|
||||
add(a, c[i])
|
||||
inc(i)
|
||||
add(result, a)
|
||||
|
||||
|
||||
proc copyFileWithPermissions*(source, dest: string,
|
||||
ignorePermissionErrors = true) =
|
||||
## Copies a file from `source` to `dest` preserving file permissions.
|
||||
@@ -1842,7 +1842,7 @@ proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect].} =
|
||||
when defined(windows):
|
||||
winlean.sleep(int32(milsecs))
|
||||
else:
|
||||
var a, b: Ttimespec
|
||||
var a, b: Timespec
|
||||
a.tv_sec = Time(milsecs div 1000)
|
||||
a.tv_nsec = (milsecs mod 1000) * 1000 * 1000
|
||||
discard posix.nanosleep(a, b)
|
||||
@@ -1851,7 +1851,7 @@ proc getFileSize*(file: string): BiggestInt {.rtl, extern: "nos$1",
|
||||
tags: [ReadIOEffect].} =
|
||||
## returns the file size of `file`. Can raise ``OSError``.
|
||||
when defined(windows):
|
||||
var a: TWIN32_FIND_DATA
|
||||
var a: WIN32_FIND_DATA
|
||||
var resA = findFirstFile(file, a)
|
||||
if resA == -1: raiseOSError(osLastError())
|
||||
result = rdFileSize(a)
|
||||
@@ -1907,8 +1907,8 @@ when defined(Windows):
|
||||
FileId* = int64
|
||||
else:
|
||||
type
|
||||
DeviceId* = TDev
|
||||
FileId* = Tino
|
||||
DeviceId* = Dev
|
||||
FileId* = Ino
|
||||
|
||||
type
|
||||
FileInfo* = object
|
||||
@@ -1925,7 +1925,7 @@ type
|
||||
template rawToFormalFileInfo(rawInfo, formalInfo): expr =
|
||||
## Transforms the native file info structure into the one nim uses.
|
||||
## 'rawInfo' is either a 'TBY_HANDLE_FILE_INFORMATION' structure on Windows,
|
||||
## or a 'TStat' structure on posix
|
||||
## or a 'Stat' structure on posix
|
||||
when defined(Windows):
|
||||
template toTime(e): expr = winTimeToUnixTime(rdFileTime(e))
|
||||
template merge(a, b): expr = a or (b shl 32)
|
||||
@@ -1936,10 +1936,10 @@ template rawToFormalFileInfo(rawInfo, formalInfo): expr =
|
||||
formalInfo.lastAccessTime = toTime(rawInfo.ftLastAccessTime)
|
||||
formalInfo.lastWriteTime = toTime(rawInfo.ftLastWriteTime)
|
||||
formalInfo.creationTime = toTime(rawInfo.ftCreationTime)
|
||||
|
||||
|
||||
# Retrieve basic permissions
|
||||
if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_READONLY) != 0'i32:
|
||||
formalInfo.permissions = {fpUserExec, fpUserRead, fpGroupExec,
|
||||
formalInfo.permissions = {fpUserExec, fpUserRead, fpGroupExec,
|
||||
fpGroupRead, fpOthersExec, fpOthersRead}
|
||||
else:
|
||||
result.permissions = {fpUserExec..fpOthersRead}
|
||||
@@ -1953,7 +1953,7 @@ template rawToFormalFileInfo(rawInfo, formalInfo): expr =
|
||||
|
||||
|
||||
else:
|
||||
template checkAndIncludeMode(rawMode, formalMode: expr) =
|
||||
template checkAndIncludeMode(rawMode, formalMode: expr) =
|
||||
if (rawInfo.st_mode and rawMode) != 0'i32:
|
||||
formalInfo.permissions.incl(formalMode)
|
||||
formalInfo.id = (rawInfo.st_dev, rawInfo.st_ino)
|
||||
@@ -1988,7 +1988,7 @@ proc getFileInfo*(handle: FileHandle): FileInfo =
|
||||
## is invalid, an error will be thrown.
|
||||
# Done: ID, Kind, Size, Permissions, Link Count
|
||||
when defined(Windows):
|
||||
var rawInfo: TBY_HANDLE_FILE_INFORMATION
|
||||
var rawInfo: BY_HANDLE_FILE_INFORMATION
|
||||
# We have to use the super special '_get_osfhandle' call (wrapped above)
|
||||
# To transform the C file descripter to a native file handle.
|
||||
var realHandle = get_osfhandle(handle)
|
||||
@@ -1996,7 +1996,7 @@ proc getFileInfo*(handle: FileHandle): FileInfo =
|
||||
raiseOSError(osLastError())
|
||||
rawToFormalFileInfo(rawInfo, result)
|
||||
else:
|
||||
var rawInfo: TStat
|
||||
var rawInfo: Stat
|
||||
if fstat(handle, rawInfo) < 0'i32:
|
||||
raiseOSError(osLastError())
|
||||
rawToFormalFileInfo(rawInfo, result)
|
||||
@@ -2008,22 +2008,22 @@ proc getFileInfo*(file: File): FileInfo =
|
||||
|
||||
proc getFileInfo*(path: string, followSymlink = true): FileInfo =
|
||||
## Retrieves file information for the file object pointed to by `path`.
|
||||
##
|
||||
##
|
||||
## Due to intrinsic differences between operating systems, the information
|
||||
## contained by the returned `FileInfo` structure will be slightly different
|
||||
## across platforms, and in some cases, incomplete or inaccurate.
|
||||
##
|
||||
##
|
||||
## When `followSymlink` is true, symlinks are followed and the information
|
||||
## retrieved is information related to the symlink's target. Otherwise,
|
||||
## information on the symlink itself is retrieved.
|
||||
##
|
||||
##
|
||||
## If the information cannot be retrieved, such as when the path doesn't
|
||||
## exist, or when permission restrictions prevent the program from retrieving
|
||||
## file information, an error will be thrown.
|
||||
when defined(Windows):
|
||||
var
|
||||
var
|
||||
handle = openHandle(path, followSymlink)
|
||||
rawInfo: TBY_HANDLE_FILE_INFORMATION
|
||||
rawInfo: BY_HANDLE_FILE_INFORMATION
|
||||
if handle == INVALID_HANDLE_VALUE:
|
||||
raiseOSError(osLastError())
|
||||
if getFileInformationByHandle(handle, addr rawInfo) == 0:
|
||||
@@ -2031,7 +2031,7 @@ proc getFileInfo*(path: string, followSymlink = true): FileInfo =
|
||||
rawToFormalFileInfo(rawInfo, result)
|
||||
discard closeHandle(handle)
|
||||
else:
|
||||
var rawInfo: TStat
|
||||
var rawInfo: Stat
|
||||
if followSymlink:
|
||||
if stat(path, rawInfo) < 0'i32:
|
||||
raiseOSError(osLastError())
|
||||
@@ -2044,7 +2044,7 @@ proc isHidden*(path: string): bool =
|
||||
## Determines whether a given path is hidden or not. Returns false if the
|
||||
## file doesn't exist. The given path must be accessible from the current
|
||||
## working directory of the program.
|
||||
##
|
||||
##
|
||||
## On Windows, a file is hidden if the file's 'hidden' attribute is set.
|
||||
## On Unix-like systems, a file is hidden if it starts with a '.' (period)
|
||||
## and is not *just* '.' or '..' ' ."
|
||||
|
||||
@@ -26,13 +26,13 @@ when defined(linux):
|
||||
type
|
||||
ProcessObj = object of RootObj
|
||||
when defined(windows):
|
||||
fProcessHandle: THandle
|
||||
fProcessHandle: Handle
|
||||
inHandle, outHandle, errHandle: FileHandle
|
||||
id: THandle
|
||||
id: Handle
|
||||
else:
|
||||
inHandle, outHandle, errHandle: FileHandle
|
||||
inStream, outStream, errStream: Stream
|
||||
id: TPid
|
||||
id: Pid
|
||||
exitCode: cint
|
||||
|
||||
Process* = ref ProcessObj ## represents an operating system process
|
||||
@@ -334,10 +334,11 @@ when not defined(useNimRtl):
|
||||
when defined(Windows) and not defined(useNimRtl):
|
||||
# We need to implement a handle stream for Windows:
|
||||
type
|
||||
PFileHandleStream = ref TFileHandleStream
|
||||
TFileHandleStream = object of StreamObj
|
||||
handle: THandle
|
||||
PFileHandleStream = ref FileHandleStream
|
||||
FileHandleStream = object of StreamObj
|
||||
handle: Handle
|
||||
atTheEnd: bool
|
||||
{.deprecated: [TFileHandleStream: FileHandleStream].}
|
||||
|
||||
proc hsClose(s: Stream) = discard # nothing to do here
|
||||
proc hsAtEnd(s: Stream): bool = return PFileHandleStream(s).atTheEnd
|
||||
@@ -361,7 +362,7 @@ when defined(Windows) and not defined(useNimRtl):
|
||||
addr bytesWritten, nil)
|
||||
if a == 0: raiseOSError(osLastError())
|
||||
|
||||
proc newFileHandleStream(handle: THandle): PFileHandleStream =
|
||||
proc newFileHandleStream(handle: Handle): PFileHandleStream =
|
||||
new(result)
|
||||
result.handle = handle
|
||||
result.closeImpl = hsClose
|
||||
@@ -387,22 +388,22 @@ when defined(Windows) and not defined(useNimRtl):
|
||||
copyMem(addr(result[L]), cstring(x), x.len+1) # copy \0
|
||||
inc(L, x.len+1)
|
||||
|
||||
#proc open_osfhandle(osh: THandle, mode: int): int {.
|
||||
#proc open_osfhandle(osh: Handle, mode: int): int {.
|
||||
# importc: "_open_osfhandle", header: "<fcntl.h>".}
|
||||
|
||||
#var
|
||||
# O_WRONLY {.importc: "_O_WRONLY", header: "<fcntl.h>".}: int
|
||||
# O_RDONLY {.importc: "_O_RDONLY", header: "<fcntl.h>".}: int
|
||||
|
||||
proc createPipeHandles(rdHandle, wrHandle: var THandle) =
|
||||
var piInheritablePipe: TSECURITY_ATTRIBUTES
|
||||
piInheritablePipe.nLength = sizeof(TSECURITY_ATTRIBUTES).cint
|
||||
proc createPipeHandles(rdHandle, wrHandle: var Handle) =
|
||||
var piInheritablePipe: SECURITY_ATTRIBUTES
|
||||
piInheritablePipe.nLength = sizeof(SECURITY_ATTRIBUTES).cint
|
||||
piInheritablePipe.lpSecurityDescriptor = nil
|
||||
piInheritablePipe.bInheritHandle = 1
|
||||
if createPipe(rdHandle, wrHandle, piInheritablePipe, 1024) == 0'i32:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc fileClose(h: THandle) {.inline.} =
|
||||
proc fileClose(h: Handle) {.inline.} =
|
||||
if h > 4: discard closeHandle(h)
|
||||
|
||||
proc startProcess(command: string,
|
||||
@@ -411,10 +412,10 @@ when defined(Windows) and not defined(useNimRtl):
|
||||
env: StringTableRef = nil,
|
||||
options: set[ProcessOption] = {poStdErrToStdOut}): Process =
|
||||
var
|
||||
si: TSTARTUPINFO
|
||||
procInfo: TPROCESS_INFORMATION
|
||||
si: STARTUPINFO
|
||||
procInfo: PROCESS_INFORMATION
|
||||
success: int
|
||||
hi, ho, he: THandle
|
||||
hi, ho, he: Handle
|
||||
new(result)
|
||||
si.cb = sizeof(si).cint
|
||||
if poParentStreams notin options:
|
||||
@@ -525,9 +526,9 @@ when defined(Windows) and not defined(useNimRtl):
|
||||
|
||||
proc execCmd(command: string): int =
|
||||
var
|
||||
si: TSTARTUPINFO
|
||||
procInfo: TPROCESS_INFORMATION
|
||||
process: THandle
|
||||
si: STARTUPINFO
|
||||
procInfo: PROCESS_INFORMATION
|
||||
process: Handle
|
||||
L: int32
|
||||
si.cb = sizeof(si).cint
|
||||
si.hStdError = getStdHandle(STD_ERROR_HANDLE)
|
||||
@@ -554,7 +555,7 @@ when defined(Windows) and not defined(useNimRtl):
|
||||
|
||||
proc select(readfds: var seq[Process], timeout = 500): int =
|
||||
assert readfds.len <= MAXIMUM_WAIT_OBJECTS
|
||||
var rfds: TWOHandleArray
|
||||
var rfds: WOHandleArray
|
||||
for i in 0..readfds.len()-1:
|
||||
rfds[i] = readfds[i].fProcessHandle
|
||||
|
||||
@@ -595,7 +596,7 @@ elif not defined(useNimRtl):
|
||||
copyMem(result[i], addr(x[0]), x.len+1)
|
||||
inc(i)
|
||||
|
||||
type TStartProcessData = object
|
||||
type StartProcessData = object
|
||||
sysCommand: cstring
|
||||
sysArgs: cstringArray
|
||||
sysEnv: cstringArray
|
||||
@@ -604,14 +605,15 @@ elif not defined(useNimRtl):
|
||||
optionPoUsePath: bool
|
||||
optionPoParentStreams: bool
|
||||
optionPoStdErrToStdOut: bool
|
||||
{.deprecated: [TStartProcessData: StartProcessData].}
|
||||
|
||||
when not defined(useFork):
|
||||
proc startProcessAuxSpawn(data: TStartProcessData): TPid {.
|
||||
proc startProcessAuxSpawn(data: StartProcessData): Pid {.
|
||||
tags: [ExecIOEffect, ReadEnvEffect], gcsafe.}
|
||||
proc startProcessAuxFork(data: TStartProcessData): TPid {.
|
||||
proc startProcessAuxFork(data: StartProcessData): Pid {.
|
||||
tags: [ExecIOEffect, ReadEnvEffect], gcsafe.}
|
||||
{.push stacktrace: off, profiler: off.}
|
||||
proc startProcessAfterFork(data: ptr TStartProcessData) {.
|
||||
proc startProcessAfterFork(data: ptr StartProcessData) {.
|
||||
tags: [ExecIOEffect, ReadEnvEffect], cdecl, gcsafe.}
|
||||
{.pop.}
|
||||
|
||||
@@ -641,7 +643,7 @@ elif not defined(useNimRtl):
|
||||
for arg in args.items:
|
||||
sysArgsRaw.add arg
|
||||
|
||||
var pid: TPid
|
||||
var pid: Pid
|
||||
|
||||
var sysArgs = allocCStringArray(sysArgsRaw)
|
||||
defer: deallocCStringArray(sysArgs)
|
||||
@@ -653,7 +655,7 @@ elif not defined(useNimRtl):
|
||||
|
||||
defer: deallocCStringArray(sysEnv)
|
||||
|
||||
var data: TStartProcessData
|
||||
var data: StartProcessData
|
||||
data.sysCommand = sysCommand
|
||||
data.sysArgs = sysArgs
|
||||
data.sysEnv = sysEnv
|
||||
@@ -698,7 +700,7 @@ elif not defined(useNimRtl):
|
||||
discard close(pStdout[writeIdx])
|
||||
|
||||
when not defined(useFork):
|
||||
proc startProcessAuxSpawn(data: TStartProcessData): TPid =
|
||||
proc startProcessAuxSpawn(data: StartProcessData): Pid =
|
||||
var attr: Tposix_spawnattr
|
||||
var fops: Tposix_spawn_file_actions
|
||||
|
||||
@@ -708,7 +710,7 @@ elif not defined(useNimRtl):
|
||||
chck posix_spawn_file_actions_init(fops)
|
||||
chck posix_spawnattr_init(attr)
|
||||
|
||||
var mask: Tsigset
|
||||
var mask: Sigset
|
||||
chck sigemptyset(mask)
|
||||
chck posix_spawnattr_setsigmask(attr, mask)
|
||||
chck posix_spawnattr_setpgroup(attr, 0'i32)
|
||||
@@ -732,7 +734,7 @@ elif not defined(useNimRtl):
|
||||
# FIXME: chdir is global to process
|
||||
if data.workingDir.len > 0:
|
||||
setCurrentDir($data.workingDir)
|
||||
var pid: TPid
|
||||
var pid: Pid
|
||||
|
||||
if data.optionPoUsePath:
|
||||
res = posix_spawnp(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv)
|
||||
@@ -744,14 +746,14 @@ elif not defined(useNimRtl):
|
||||
chck res
|
||||
return pid
|
||||
|
||||
proc startProcessAuxFork(data: TStartProcessData): TPid =
|
||||
proc startProcessAuxFork(data: StartProcessData): Pid =
|
||||
if pipe(data.pErrorPipe) != 0:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
defer:
|
||||
discard close(data.pErrorPipe[readIdx])
|
||||
|
||||
var pid: TPid
|
||||
var pid: Pid
|
||||
var dataCopy = data
|
||||
|
||||
when defined(useClone):
|
||||
@@ -781,7 +783,7 @@ elif not defined(useNimRtl):
|
||||
return pid
|
||||
|
||||
{.push stacktrace: off, profiler: off.}
|
||||
proc startProcessFail(data: ptr TStartProcessData) =
|
||||
proc startProcessFail(data: ptr StartProcessData) =
|
||||
var error: cint = errno
|
||||
discard write(data.pErrorPipe[writeIdx], addr error, sizeof(error))
|
||||
exitnow(1)
|
||||
@@ -789,7 +791,7 @@ elif not defined(useNimRtl):
|
||||
when defined(macosx) or defined(freebsd):
|
||||
var environ {.importc.}: cstringArray
|
||||
|
||||
proc startProcessAfterFork(data: ptr TStartProcessData) =
|
||||
proc startProcessAfterFork(data: ptr StartProcessData) =
|
||||
# Warning: no GC here!
|
||||
# Or anything that touches global structures - all called nim procs
|
||||
# must be marked with stackTrace:off. Inspect C code after making changes.
|
||||
|
||||
@@ -133,7 +133,7 @@ proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo$1".} =
|
||||
when declared(initOptParser):
|
||||
iterator getopt*(): tuple[kind: CmdLineKind, key, val: TaintedString] =
|
||||
## This is an convenience iterator for iterating over the command line.
|
||||
## This uses the TOptParser object. Example:
|
||||
## This uses the OptParser object. Example:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## var
|
||||
|
||||
@@ -74,8 +74,8 @@ type
|
||||
line: int ## line the symbol has been declared/used in
|
||||
col: int ## column the symbol has been declared/used in
|
||||
flags: set[NonTerminalFlag] ## the nonterminal's flags
|
||||
rule: TNode ## the rule that the symbol refers to
|
||||
TNode {.shallow.} = object
|
||||
rule: Node ## the rule that the symbol refers to
|
||||
Node {.shallow.} = object
|
||||
case kind: PegKind
|
||||
of pkEmpty..pkWhitespace: nil
|
||||
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string
|
||||
@@ -83,12 +83,12 @@ type
|
||||
of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char]
|
||||
of pkNonTerminal: nt: NonTerminal
|
||||
of pkBackRef..pkBackRefIgnoreStyle: index: range[0..MaxSubpatterns]
|
||||
else: sons: seq[TNode]
|
||||
else: sons: seq[Node]
|
||||
NonTerminal* = ref NonTerminalObj
|
||||
|
||||
Peg* = TNode ## type that represents a PEG
|
||||
Peg* = Node ## type that represents a PEG
|
||||
|
||||
{.deprecated: [TPeg: Peg].}
|
||||
{.deprecated: [TPeg: Peg, TNode: Node].}
|
||||
|
||||
proc term*(t: string): Peg {.nosideEffect, rtl, extern: "npegs$1Str".} =
|
||||
## constructs a PEG from a terminal string
|
||||
@@ -1014,12 +1014,12 @@ proc split*(s: string, sep: Peg): seq[string] {.
|
||||
# ------------------- scanner -------------------------------------------------
|
||||
|
||||
type
|
||||
TModifier = enum
|
||||
Modifier = enum
|
||||
modNone,
|
||||
modVerbatim,
|
||||
modIgnoreCase,
|
||||
modIgnoreStyle
|
||||
TTokKind = enum ## enumeration of all tokens
|
||||
TokKind = enum ## enumeration of all tokens
|
||||
tkInvalid, ## invalid token
|
||||
tkEof, ## end of file reached
|
||||
tkAny, ## .
|
||||
@@ -1046,9 +1046,9 @@ type
|
||||
tkDollar, ## '$'
|
||||
tkHat ## '^'
|
||||
|
||||
TToken {.final.} = object ## a token
|
||||
kind: TTokKind ## the type of the token
|
||||
modifier: TModifier
|
||||
Token {.final.} = object ## a token
|
||||
kind: TokKind ## the type of the token
|
||||
modifier: Modifier
|
||||
literal: string ## the parsed (string) literal
|
||||
charset: set[char] ## if kind == tkCharSet
|
||||
index: int ## if kind == tkBackref
|
||||
@@ -1060,9 +1060,10 @@ type
|
||||
lineStart: int ## index of last line start in buffer
|
||||
colOffset: int ## column to add
|
||||
filename: string
|
||||
{.deprecated: [TTokKind: TokKind, TToken: Token, TModifier: Modifier].}
|
||||
|
||||
const
|
||||
tokKindToStr: array[TTokKind, string] = [
|
||||
tokKindToStr: array[TokKind, string] = [
|
||||
"invalid", "[EOF]", ".", "_", "identifier", "string literal",
|
||||
"character set", "(", ")", "{", "}", "{@}",
|
||||
"<-", "/", "*", "+", "&", "!", "?",
|
||||
@@ -1114,7 +1115,7 @@ proc handleHexChar(c: var PegLexer, xi: var int) =
|
||||
inc(c.bufpos)
|
||||
else: discard
|
||||
|
||||
proc getEscapedChar(c: var PegLexer, tok: var TToken) =
|
||||
proc getEscapedChar(c: var PegLexer, tok: var Token) =
|
||||
inc(c.bufpos)
|
||||
case c.buf[c.bufpos]
|
||||
of 'r', 'R', 'c', 'C':
|
||||
@@ -1185,7 +1186,7 @@ proc skip(c: var PegLexer) =
|
||||
break # EndOfFile also leaves the loop
|
||||
c.bufpos = pos
|
||||
|
||||
proc getString(c: var PegLexer, tok: var TToken) =
|
||||
proc getString(c: var PegLexer, tok: var Token) =
|
||||
tok.kind = tkStringLit
|
||||
var pos = c.bufpos + 1
|
||||
var buf = c.buf
|
||||
@@ -1207,7 +1208,7 @@ proc getString(c: var PegLexer, tok: var TToken) =
|
||||
inc(pos)
|
||||
c.bufpos = pos
|
||||
|
||||
proc getDollar(c: var PegLexer, tok: var TToken) =
|
||||
proc getDollar(c: var PegLexer, tok: var Token) =
|
||||
var pos = c.bufpos + 1
|
||||
var buf = c.buf
|
||||
if buf[pos] in {'0'..'9'}:
|
||||
@@ -1220,7 +1221,7 @@ proc getDollar(c: var PegLexer, tok: var TToken) =
|
||||
tok.kind = tkDollar
|
||||
c.bufpos = pos
|
||||
|
||||
proc getCharSet(c: var PegLexer, tok: var TToken) =
|
||||
proc getCharSet(c: var PegLexer, tok: var Token) =
|
||||
tok.kind = tkCharSet
|
||||
tok.charset = {}
|
||||
var pos = c.bufpos + 1
|
||||
@@ -1271,7 +1272,7 @@ proc getCharSet(c: var PegLexer, tok: var TToken) =
|
||||
c.bufpos = pos
|
||||
if caret: tok.charset = {'\1'..'\xFF'} - tok.charset
|
||||
|
||||
proc getSymbol(c: var PegLexer, tok: var TToken) =
|
||||
proc getSymbol(c: var PegLexer, tok: var Token) =
|
||||
var pos = c.bufpos
|
||||
var buf = c.buf
|
||||
while true:
|
||||
@@ -1281,7 +1282,7 @@ proc getSymbol(c: var PegLexer, tok: var TToken) =
|
||||
c.bufpos = pos
|
||||
tok.kind = tkIdentifier
|
||||
|
||||
proc getBuiltin(c: var PegLexer, tok: var TToken) =
|
||||
proc getBuiltin(c: var PegLexer, tok: var Token) =
|
||||
if c.buf[c.bufpos+1] in strutils.Letters:
|
||||
inc(c.bufpos)
|
||||
getSymbol(c, tok)
|
||||
@@ -1290,7 +1291,7 @@ proc getBuiltin(c: var PegLexer, tok: var TToken) =
|
||||
tok.kind = tkEscaped
|
||||
getEscapedChar(c, tok) # may set tok.kind to tkInvalid
|
||||
|
||||
proc getTok(c: var PegLexer, tok: var TToken) =
|
||||
proc getTok(c: var PegLexer, tok: var Token) =
|
||||
tok.kind = tkInvalid
|
||||
tok.modifier = modNone
|
||||
setLen(tok.literal, 0)
|
||||
@@ -1408,9 +1409,9 @@ type
|
||||
EInvalidPeg* = object of ValueError ## raised if an invalid
|
||||
## PEG has been detected
|
||||
PegParser = object of PegLexer ## the PEG parser object
|
||||
tok: TToken
|
||||
tok: Token
|
||||
nonterms: seq[NonTerminal]
|
||||
modifier: TModifier
|
||||
modifier: Modifier
|
||||
captures: int
|
||||
identIsVerbatim: bool
|
||||
skip: Peg
|
||||
@@ -1425,7 +1426,7 @@ proc getTok(p: var PegParser) =
|
||||
getTok(p, p.tok)
|
||||
if p.tok.kind == tkInvalid: pegError(p, "invalid token")
|
||||
|
||||
proc eat(p: var PegParser, kind: TTokKind) =
|
||||
proc eat(p: var PegParser, kind: TokKind) =
|
||||
if p.tok.kind == kind: getTok(p)
|
||||
else: pegError(p, tokKindToStr[kind] & " expected")
|
||||
|
||||
@@ -1439,13 +1440,13 @@ proc getNonTerminal(p: var PegParser, name: string): NonTerminal =
|
||||
result = newNonTerminal(name, getLine(p), getColumn(p))
|
||||
add(p.nonterms, result)
|
||||
|
||||
proc modifiedTerm(s: string, m: TModifier): Peg =
|
||||
proc modifiedTerm(s: string, m: Modifier): Peg =
|
||||
case m
|
||||
of modNone, modVerbatim: result = term(s)
|
||||
of modIgnoreCase: result = termIgnoreCase(s)
|
||||
of modIgnoreStyle: result = termIgnoreStyle(s)
|
||||
|
||||
proc modifiedBackref(s: int, m: TModifier): Peg =
|
||||
proc modifiedBackref(s: int, m: Modifier): Peg =
|
||||
case m
|
||||
of modNone, modVerbatim: result = backref(s)
|
||||
of modIgnoreCase: result = backrefIgnoreCase(s)
|
||||
|
||||
@@ -206,13 +206,13 @@ proc abs*[T](x: Rational[T]): Rational[T] =
|
||||
result.num = abs x.num
|
||||
result.den = abs x.den
|
||||
|
||||
proc hash*[T](x: Rational[T]): THash =
|
||||
proc hash*[T](x: Rational[T]): Hash =
|
||||
## Computes hash for rational `x`
|
||||
# reduce first so that hash(x) == hash(y) for x == y
|
||||
var copy = x
|
||||
reduce(copy)
|
||||
|
||||
var h: THash = 0
|
||||
var h: Hash = 0
|
||||
h = h !& hash(copy.num)
|
||||
h = h !& hash(copy.den)
|
||||
result = !$h
|
||||
|
||||
@@ -106,13 +106,13 @@ proc `$`*(p: Port): string {.borrow.}
|
||||
## returns the port number as a string
|
||||
|
||||
proc toInt*(domain: Domain): cint
|
||||
## Converts the TDomain enum to a platform-dependent ``cint``.
|
||||
## Converts the Domain enum to a platform-dependent ``cint``.
|
||||
|
||||
proc toInt*(typ: SockType): cint
|
||||
## Converts the TType enum to a platform-dependent ``cint``.
|
||||
## Converts the SockType enum to a platform-dependent ``cint``.
|
||||
|
||||
proc toInt*(p: Protocol): cint
|
||||
## Converts the TProtocol enum to a platform-dependent ``cint``.
|
||||
## Converts the Protocol enum to a platform-dependent ``cint``.
|
||||
|
||||
when not useWinVersion:
|
||||
proc toInt(domain: Domain): cint =
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
## This module implements a redis client. It allows you to connect to a
|
||||
## redis-server instance, send commands and receive replies.
|
||||
##
|
||||
## **Beware**: Most (if not all) functions that return a ``TRedisString`` may
|
||||
## return ``redisNil``, and functions which return a ``TRedisList``
|
||||
## **Beware**: Most (if not all) functions that return a ``RedisString`` may
|
||||
## return ``redisNil``, and functions which return a ``RedisList``
|
||||
## may return ``nil``.
|
||||
|
||||
import sockets, os, strutils, parseutils
|
||||
@@ -843,27 +843,27 @@ proc pfmerge*(r: Redis, destination: string, sources: varargs[string]) =
|
||||
|
||||
# TODO: pub/sub -- I don't think this will work synchronously.
|
||||
discard """
|
||||
proc psubscribe*(r: TRedis, pattern: openarray[string]): ???? =
|
||||
proc psubscribe*(r: Redis, pattern: openarray[string]): ???? =
|
||||
## Listen for messages published to channels matching the given patterns
|
||||
r.socket.send("PSUBSCRIBE $#\c\L" % pattern)
|
||||
return ???
|
||||
|
||||
proc publish*(r: TRedis, channel: string, message: string): TRedisInteger =
|
||||
proc publish*(r: Redis, channel: string, message: string): RedisInteger =
|
||||
## Post a message to a channel
|
||||
r.socket.send("PUBLISH $# $#\c\L" % [channel, message])
|
||||
return r.readInteger()
|
||||
|
||||
proc punsubscribe*(r: TRedis, [pattern: openarray[string], : string): ???? =
|
||||
proc punsubscribe*(r: Redis, [pattern: openarray[string], : string): ???? =
|
||||
## Stop listening for messages posted to channels matching the given patterns
|
||||
r.socket.send("PUNSUBSCRIBE $# $#\c\L" % [[pattern.join(), ])
|
||||
return ???
|
||||
|
||||
proc subscribe*(r: TRedis, channel: openarray[string]): ???? =
|
||||
proc subscribe*(r: Redis, channel: openarray[string]): ???? =
|
||||
## Listen for messages published to the given channels
|
||||
r.socket.send("SUBSCRIBE $#\c\L" % channel.join)
|
||||
return ???
|
||||
|
||||
proc unsubscribe*(r: TRedis, [channel: openarray[string], : string): ???? =
|
||||
proc unsubscribe*(r: Redis, [channel: openarray[string], : string): ???? =
|
||||
## Stop listening for messages posted to the given channels
|
||||
r.socket.send("UNSUBSCRIBE $# $#\c\L" % [[channel.join(), ])
|
||||
return ???
|
||||
@@ -991,7 +991,7 @@ proc lastsave*(r: Redis): RedisInteger =
|
||||
return r.readInteger()
|
||||
|
||||
discard """
|
||||
proc monitor*(r: TRedis) =
|
||||
proc monitor*(r: Redis) =
|
||||
## Listen for all requests received by the server in real time
|
||||
r.socket.send("MONITOR\c\L")
|
||||
raiseNoOK(r.readStatus(), r.pipeline.enabled)
|
||||
|
||||
@@ -18,7 +18,7 @@ elif defined(windows):
|
||||
else:
|
||||
import posix
|
||||
|
||||
proc hash*(x: SocketHandle): THash {.borrow.}
|
||||
proc hash*(x: SocketHandle): Hash {.borrow.}
|
||||
proc `$`*(x: SocketHandle): string {.borrow.}
|
||||
|
||||
type
|
||||
@@ -41,7 +41,7 @@ when defined(nimdoc):
|
||||
|
||||
proc register*(s: Selector, fd: SocketHandle, events: set[Event],
|
||||
data: RootRef): SelectorKey {.discardable.} =
|
||||
## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent
|
||||
## Registers file descriptor ``fd`` to selector ``s`` with a set of Event
|
||||
## ``events``.
|
||||
|
||||
proc update*(s: Selector, fd: SocketHandle,
|
||||
|
||||
@@ -1,697 +0,0 @@
|
||||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2015 Andreas Rumpf, Dominik Picheta
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
import
|
||||
hashes, strutils, lexbase, streams, unicode, macros
|
||||
|
||||
type
|
||||
SexpEventKind* = enum ## enumeration of all events that may occur when parsing
|
||||
sexpError, ## an error occurred during parsing
|
||||
sexpEof, ## end of file reached
|
||||
sexpString, ## a string literal
|
||||
sexpSymbol, ## a symbol
|
||||
sexpInt, ## an integer literal
|
||||
sexpFloat, ## a float literal
|
||||
sexpNil, ## the value ``nil``
|
||||
sexpDot, ## the dot to separate car/cdr
|
||||
sexpListStart, ## start of a list: the ``(`` token
|
||||
sexpListEnd, ## end of a list: the ``)`` token
|
||||
|
||||
TTokKind = enum # must be synchronized with SexpEventKind!
|
||||
tkError,
|
||||
tkEof,
|
||||
tkString,
|
||||
tkSymbol,
|
||||
tkInt,
|
||||
tkFloat,
|
||||
tkNil,
|
||||
tkDot,
|
||||
tkParensLe,
|
||||
tkParensRi
|
||||
tkSpace
|
||||
|
||||
SexpError* = enum ## enumeration that lists all errors that can occur
|
||||
errNone, ## no error
|
||||
errInvalidToken, ## invalid token
|
||||
errParensRiExpected, ## ``)`` expected
|
||||
errQuoteExpected, ## ``"`` expected
|
||||
errEofExpected, ## EOF expected
|
||||
|
||||
SexpParser* = object of BaseLexer ## the parser object.
|
||||
a: string
|
||||
tok: TTokKind
|
||||
kind: SexpEventKind
|
||||
err: SexpError
|
||||
|
||||
const
|
||||
errorMessages: array [SexpError, string] = [
|
||||
"no error",
|
||||
"invalid token",
|
||||
"')' expected",
|
||||
"'\"' or \"'\" expected",
|
||||
"EOF expected",
|
||||
]
|
||||
tokToStr: array [TTokKind, string] = [
|
||||
"invalid token",
|
||||
"EOF",
|
||||
"string literal",
|
||||
"symbol",
|
||||
"int literal",
|
||||
"float literal",
|
||||
"nil",
|
||||
".",
|
||||
"(", ")", "space"
|
||||
]
|
||||
|
||||
proc close*(my: var SexpParser) {.inline.} =
|
||||
## closes the parser `my` and its associated input stream.
|
||||
lexbase.close(my)
|
||||
|
||||
proc str*(my: SexpParser): string {.inline.} =
|
||||
## returns the character data for the events: ``sexpInt``, ``sexpFloat``,
|
||||
## ``sexpString``
|
||||
assert(my.kind in {sexpInt, sexpFloat, sexpString})
|
||||
result = my.a
|
||||
|
||||
proc getInt*(my: SexpParser): BiggestInt {.inline.} =
|
||||
## returns the number for the event: ``sexpInt``
|
||||
assert(my.kind == sexpInt)
|
||||
result = parseBiggestInt(my.a)
|
||||
|
||||
proc getFloat*(my: SexpParser): float {.inline.} =
|
||||
## returns the number for the event: ``sexpFloat``
|
||||
assert(my.kind == sexpFloat)
|
||||
result = parseFloat(my.a)
|
||||
|
||||
proc kind*(my: SexpParser): SexpEventKind {.inline.} =
|
||||
## returns the current event type for the SEXP parser
|
||||
result = my.kind
|
||||
|
||||
proc getColumn*(my: SexpParser): int {.inline.} =
|
||||
## get the current column the parser has arrived at.
|
||||
result = getColNumber(my, my.bufpos)
|
||||
|
||||
proc getLine*(my: SexpParser): int {.inline.} =
|
||||
## get the current line the parser has arrived at.
|
||||
result = my.lineNumber
|
||||
|
||||
proc errorMsg*(my: SexpParser): string =
|
||||
## returns a helpful error message for the event ``sexpError``
|
||||
assert(my.kind == sexpError)
|
||||
result = "($1, $2) Error: $3" % [$getLine(my), $getColumn(my), errorMessages[my.err]]
|
||||
|
||||
proc errorMsgExpected*(my: SexpParser, e: string): string =
|
||||
## returns an error message "`e` expected" in the same format as the
|
||||
## other error messages
|
||||
result = "($1, $2) Error: $3" % [$getLine(my), $getColumn(my), e & " expected"]
|
||||
|
||||
proc handleHexChar(c: char, x: var int): bool =
|
||||
result = true # Success
|
||||
case c
|
||||
of '0'..'9': x = (x shl 4) or (ord(c) - ord('0'))
|
||||
of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10)
|
||||
of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
|
||||
else: result = false # error
|
||||
|
||||
proc parseString(my: var SexpParser): TTokKind =
|
||||
result = tkString
|
||||
var pos = my.bufpos + 1
|
||||
var buf = my.buf
|
||||
while true:
|
||||
case buf[pos]
|
||||
of '\0':
|
||||
my.err = errQuoteExpected
|
||||
result = tkError
|
||||
break
|
||||
of '"':
|
||||
inc(pos)
|
||||
break
|
||||
of '\\':
|
||||
case buf[pos+1]
|
||||
of '\\', '"', '\'', '/':
|
||||
add(my.a, buf[pos+1])
|
||||
inc(pos, 2)
|
||||
of 'b':
|
||||
add(my.a, '\b')
|
||||
inc(pos, 2)
|
||||
of 'f':
|
||||
add(my.a, '\f')
|
||||
inc(pos, 2)
|
||||
of 'n':
|
||||
add(my.a, '\L')
|
||||
inc(pos, 2)
|
||||
of 'r':
|
||||
add(my.a, '\C')
|
||||
inc(pos, 2)
|
||||
of 't':
|
||||
add(my.a, '\t')
|
||||
inc(pos, 2)
|
||||
of 'u':
|
||||
inc(pos, 2)
|
||||
var r: int
|
||||
if handleHexChar(buf[pos], r): inc(pos)
|
||||
if handleHexChar(buf[pos], r): inc(pos)
|
||||
if handleHexChar(buf[pos], r): inc(pos)
|
||||
if handleHexChar(buf[pos], r): inc(pos)
|
||||
add(my.a, toUTF8(Rune(r)))
|
||||
else:
|
||||
# don't bother with the error
|
||||
add(my.a, buf[pos])
|
||||
inc(pos)
|
||||
of '\c':
|
||||
pos = lexbase.handleCR(my, pos)
|
||||
buf = my.buf
|
||||
add(my.a, '\c')
|
||||
of '\L':
|
||||
pos = lexbase.handleLF(my, pos)
|
||||
buf = my.buf
|
||||
add(my.a, '\L')
|
||||
else:
|
||||
add(my.a, buf[pos])
|
||||
inc(pos)
|
||||
my.bufpos = pos # store back
|
||||
|
||||
proc parseNumber(my: var SexpParser) =
|
||||
var pos = my.bufpos
|
||||
var buf = my.buf
|
||||
if buf[pos] == '-':
|
||||
add(my.a, '-')
|
||||
inc(pos)
|
||||
if buf[pos] == '.':
|
||||
add(my.a, "0.")
|
||||
inc(pos)
|
||||
else:
|
||||
while buf[pos] in Digits:
|
||||
add(my.a, buf[pos])
|
||||
inc(pos)
|
||||
if buf[pos] == '.':
|
||||
add(my.a, '.')
|
||||
inc(pos)
|
||||
# digits after the dot:
|
||||
while buf[pos] in Digits:
|
||||
add(my.a, buf[pos])
|
||||
inc(pos)
|
||||
if buf[pos] in {'E', 'e'}:
|
||||
add(my.a, buf[pos])
|
||||
inc(pos)
|
||||
if buf[pos] in {'+', '-'}:
|
||||
add(my.a, buf[pos])
|
||||
inc(pos)
|
||||
while buf[pos] in Digits:
|
||||
add(my.a, buf[pos])
|
||||
inc(pos)
|
||||
my.bufpos = pos
|
||||
|
||||
proc parseSymbol(my: var SexpParser) =
|
||||
var pos = my.bufpos
|
||||
var buf = my.buf
|
||||
if buf[pos] in IdentStartChars:
|
||||
while buf[pos] in IdentChars:
|
||||
add(my.a, buf[pos])
|
||||
inc(pos)
|
||||
my.bufpos = pos
|
||||
|
||||
proc getTok(my: var SexpParser): TTokKind =
|
||||
setLen(my.a, 0)
|
||||
case my.buf[my.bufpos]
|
||||
of '-', '0'..'9': # numbers that start with a . are not parsed
|
||||
# correctly.
|
||||
parseNumber(my)
|
||||
if {'.', 'e', 'E'} in my.a:
|
||||
result = tkFloat
|
||||
else:
|
||||
result = tkInt
|
||||
of '"': #" # gotta fix nim-mode
|
||||
result = parseString(my)
|
||||
of '(':
|
||||
inc(my.bufpos)
|
||||
result = tkParensLe
|
||||
of ')':
|
||||
inc(my.bufpos)
|
||||
result = tkParensRi
|
||||
of '\0':
|
||||
result = tkEof
|
||||
of 'a'..'z', 'A'..'Z', '_':
|
||||
parseSymbol(my)
|
||||
if my.a == "nil":
|
||||
result = tkNil
|
||||
else:
|
||||
result = tkSymbol
|
||||
of ' ':
|
||||
result = tkSpace
|
||||
inc(my.bufpos)
|
||||
of '.':
|
||||
result = tkDot
|
||||
inc(my.bufpos)
|
||||
else:
|
||||
inc(my.bufpos)
|
||||
result = tkError
|
||||
my.tok = result
|
||||
|
||||
# ------------- higher level interface ---------------------------------------
|
||||
|
||||
type
|
||||
SexpNodeKind* = enum ## possible SEXP node types
|
||||
SNil,
|
||||
SInt,
|
||||
SFloat,
|
||||
SString,
|
||||
SSymbol,
|
||||
SList,
|
||||
SCons
|
||||
|
||||
SexpNode* = ref SexpNodeObj ## SEXP node
|
||||
SexpNodeObj* {.acyclic.} = object
|
||||
case kind*: SexpNodeKind
|
||||
of SString:
|
||||
str*: string
|
||||
of SSymbol:
|
||||
symbol*: string
|
||||
of SInt:
|
||||
num*: BiggestInt
|
||||
of SFloat:
|
||||
fnum*: float
|
||||
of SList:
|
||||
elems*: seq[SexpNode]
|
||||
of SCons:
|
||||
car: SexpNode
|
||||
cdr: SexpNode
|
||||
of SNil:
|
||||
discard
|
||||
|
||||
Cons = tuple[car: SexpNode, cdr: SexpNode]
|
||||
|
||||
SexpParsingError* = object of ValueError ## is raised for a SEXP error
|
||||
|
||||
proc raiseParseErr*(p: SexpParser, msg: string) {.noinline, noreturn.} =
|
||||
## raises an `ESexpParsingError` exception.
|
||||
raise newException(SexpParsingError, errorMsgExpected(p, msg))
|
||||
|
||||
proc newSString*(s: string): SexpNode {.procvar.}=
|
||||
## Creates a new `SString SexpNode`.
|
||||
new(result)
|
||||
result.kind = SString
|
||||
result.str = s
|
||||
|
||||
proc newSStringMove(s: string): SexpNode =
|
||||
new(result)
|
||||
result.kind = SString
|
||||
shallowCopy(result.str, s)
|
||||
|
||||
proc newSInt*(n: BiggestInt): SexpNode {.procvar.} =
|
||||
## Creates a new `SInt SexpNode`.
|
||||
new(result)
|
||||
result.kind = SInt
|
||||
result.num = n
|
||||
|
||||
proc newSFloat*(n: float): SexpNode {.procvar.} =
|
||||
## Creates a new `SFloat SexpNode`.
|
||||
new(result)
|
||||
result.kind = SFloat
|
||||
result.fnum = n
|
||||
|
||||
proc newSNil*(): SexpNode {.procvar.} =
|
||||
## Creates a new `SNil SexpNode`.
|
||||
new(result)
|
||||
|
||||
proc newSCons*(car, cdr: SexpNode): SexpNode {.procvar.} =
|
||||
## Creates a new `SCons SexpNode`
|
||||
new(result)
|
||||
result.kind = SCons
|
||||
result.car = car
|
||||
result.cdr = cdr
|
||||
|
||||
proc newSList*(): SexpNode {.procvar.} =
|
||||
## Creates a new `SList SexpNode`
|
||||
new(result)
|
||||
result.kind = SList
|
||||
result.elems = @[]
|
||||
|
||||
proc newSSymbol*(s: string): SexpNode {.procvar.} =
|
||||
new(result)
|
||||
result.kind = SSymbol
|
||||
result.symbol = s
|
||||
|
||||
proc newSSymbolMove(s: string): SexpNode =
|
||||
new(result)
|
||||
result.kind = SSymbol
|
||||
shallowCopy(result.symbol, s)
|
||||
|
||||
proc getStr*(n: SexpNode, default: string = ""): string =
|
||||
## Retrieves the string value of a `SString SexpNode`.
|
||||
##
|
||||
## Returns ``default`` if ``n`` is not a ``SString``.
|
||||
if n.kind != SString: return default
|
||||
else: return n.str
|
||||
|
||||
proc getNum*(n: SexpNode, default: BiggestInt = 0): BiggestInt =
|
||||
## Retrieves the int value of a `SInt SexpNode`.
|
||||
##
|
||||
## Returns ``default`` if ``n`` is not a ``SInt``.
|
||||
if n.kind != SInt: return default
|
||||
else: return n.num
|
||||
|
||||
proc getFNum*(n: SexpNode, default: float = 0.0): float =
|
||||
## Retrieves the float value of a `SFloat SexpNode`.
|
||||
##
|
||||
## Returns ``default`` if ``n`` is not a ``SFloat``.
|
||||
if n.kind != SFloat: return default
|
||||
else: return n.fnum
|
||||
|
||||
proc getSymbol*(n: SexpNode, default: string = ""): string =
|
||||
## Retrieves the int value of a `SList SexpNode`.
|
||||
##
|
||||
## Returns ``default`` if ``n`` is not a ``SList``.
|
||||
if n.kind != SSymbol: return default
|
||||
else: return n.symbol
|
||||
|
||||
proc getElems*(n: SexpNode, default: seq[SexpNode] = @[]): seq[SexpNode] =
|
||||
## Retrieves the int value of a `SList SexpNode`.
|
||||
##
|
||||
## Returns ``default`` if ``n`` is not a ``SList``.
|
||||
if n.kind == SNil: return @[]
|
||||
elif n.kind != SList: return default
|
||||
else: return n.elems
|
||||
|
||||
proc getCons*(n: SexpNode, defaults: Cons = (newSNil(), newSNil())): Cons =
|
||||
## Retrieves the cons value of a `SList SexpNode`.
|
||||
##
|
||||
## Returns ``default`` if ``n`` is not a ``SList``.
|
||||
if n.kind == SCons: return (n.car, n.cdr)
|
||||
elif n.kind == SList: return (n.elems[0], n.elems[1])
|
||||
else: return defaults
|
||||
|
||||
proc sexp*(s: string): SexpNode =
|
||||
## Generic constructor for SEXP data. Creates a new `SString SexpNode`.
|
||||
new(result)
|
||||
result.kind = SString
|
||||
result.str = s
|
||||
|
||||
proc sexp*(n: BiggestInt): SexpNode =
|
||||
## Generic constructor for SEXP data. Creates a new `SInt SexpNode`.
|
||||
new(result)
|
||||
result.kind = SInt
|
||||
result.num = n
|
||||
|
||||
proc sexp*(n: float): SexpNode =
|
||||
## Generic constructor for SEXP data. Creates a new `SFloat SexpNode`.
|
||||
new(result)
|
||||
result.kind = SFloat
|
||||
result.fnum = n
|
||||
|
||||
proc sexp*(b: bool): SexpNode =
|
||||
## Generic constructor for SEXP data. Creates a new `SSymbol
|
||||
## SexpNode` with value t or `SNil SexpNode`.
|
||||
new(result)
|
||||
if b:
|
||||
result.kind = SSymbol
|
||||
result.symbol = "t"
|
||||
else:
|
||||
result.kind = SNil
|
||||
|
||||
proc sexp*(elements: openArray[SexpNode]): SexpNode =
|
||||
## Generic constructor for SEXP data. Creates a new `SList SexpNode`
|
||||
new(result)
|
||||
result.kind = SList
|
||||
newSeq(result.elems, elements.len)
|
||||
for i, p in pairs(elements): result.elems[i] = p
|
||||
|
||||
proc sexp*(s: SexpNode): SexpNode =
|
||||
result = s
|
||||
|
||||
proc toSexp(x: NimNode): NimNode {.compiletime.} =
|
||||
case x.kind
|
||||
of nnkBracket:
|
||||
result = newNimNode(nnkBracket)
|
||||
for i in 0 .. <x.len:
|
||||
result.add(toSexp(x[i]))
|
||||
|
||||
else:
|
||||
result = x
|
||||
|
||||
result = prefix(result, "sexp")
|
||||
|
||||
macro convertSexp*(x: expr): expr =
|
||||
## Convert an expression to a SexpNode directly, without having to specify
|
||||
## `%` for every element.
|
||||
result = toSexp(x)
|
||||
|
||||
proc `==`* (a,b: SexpNode): bool =
|
||||
## Check two nodes for equality
|
||||
if a.isNil:
|
||||
if b.isNil: return true
|
||||
return false
|
||||
elif b.isNil or a.kind != b.kind:
|
||||
return false
|
||||
else:
|
||||
return case a.kind
|
||||
of SString:
|
||||
a.str == b.str
|
||||
of SInt:
|
||||
a.num == b.num
|
||||
of SFloat:
|
||||
a.fnum == b.fnum
|
||||
of SNil:
|
||||
true
|
||||
of SList:
|
||||
a.elems == b.elems
|
||||
of SSymbol:
|
||||
a.symbol == b.symbol
|
||||
of SCons:
|
||||
a.car == b.car and a.cdr == b.cdr
|
||||
|
||||
proc hash* (n:SexpNode): THash =
|
||||
## Compute the hash for a SEXP node
|
||||
case n.kind
|
||||
of SList:
|
||||
result = hash(n.elems)
|
||||
of SInt:
|
||||
result = hash(n.num)
|
||||
of SFloat:
|
||||
result = hash(n.fnum)
|
||||
of SString:
|
||||
result = hash(n.str)
|
||||
of SNil:
|
||||
result = hash(0)
|
||||
of SSymbol:
|
||||
result = hash(n.symbol)
|
||||
of SCons:
|
||||
result = hash(n.car) !& hash(n.cdr)
|
||||
|
||||
proc len*(n: SexpNode): int =
|
||||
## If `n` is a `SList`, it returns the number of elements.
|
||||
## If `n` is a `JObject`, it returns the number of pairs.
|
||||
## Else it returns 0.
|
||||
case n.kind
|
||||
of SList: result = n.elems.len
|
||||
else: discard
|
||||
|
||||
proc `[]`*(node: SexpNode, index: int): SexpNode =
|
||||
## Gets the node at `index` in a List. Result is undefined if `index`
|
||||
## is out of bounds
|
||||
assert(not isNil(node))
|
||||
assert(node.kind == SList)
|
||||
return node.elems[index]
|
||||
|
||||
proc add*(father, child: SexpNode) =
|
||||
## Adds `child` to a SList node `father`.
|
||||
assert father.kind == SList
|
||||
father.elems.add(child)
|
||||
|
||||
# ------------- pretty printing ----------------------------------------------
|
||||
|
||||
proc indent(s: var string, i: int) =
|
||||
s.add(spaces(i))
|
||||
|
||||
proc newIndent(curr, indent: int, ml: bool): int =
|
||||
if ml: return curr + indent
|
||||
else: return indent
|
||||
|
||||
proc nl(s: var string, ml: bool) =
|
||||
if ml: s.add("\n")
|
||||
|
||||
proc escapeJson*(s: string): string =
|
||||
## Converts a string `s` to its JSON representation.
|
||||
result = newStringOfCap(s.len + s.len shr 3)
|
||||
result.add("\"")
|
||||
for x in runes(s):
|
||||
var r = int(x)
|
||||
if r >= 32 and r <= 127:
|
||||
var c = chr(r)
|
||||
case c
|
||||
of '"': result.add("\\\"") #" # gotta fix nim-mode
|
||||
of '\\': result.add("\\\\")
|
||||
else: result.add(c)
|
||||
else:
|
||||
result.add("\\u")
|
||||
result.add(toHex(r, 4))
|
||||
result.add("\"")
|
||||
|
||||
proc copy*(p: SexpNode): SexpNode =
|
||||
## Performs a deep copy of `a`.
|
||||
case p.kind
|
||||
of SString:
|
||||
result = newSString(p.str)
|
||||
of SInt:
|
||||
result = newSInt(p.num)
|
||||
of SFloat:
|
||||
result = newSFloat(p.fnum)
|
||||
of SNil:
|
||||
result = newSNil()
|
||||
of SSymbol:
|
||||
result = newSSymbol(p.symbol)
|
||||
of SList:
|
||||
result = newSList()
|
||||
for i in items(p.elems):
|
||||
result.elems.add(copy(i))
|
||||
of SCons:
|
||||
result = newSCons(copy(p.car), copy(p.cdr))
|
||||
|
||||
proc toPretty(result: var string, node: SexpNode, indent = 2, ml = true,
|
||||
lstArr = false, currIndent = 0) =
|
||||
case node.kind
|
||||
of SString:
|
||||
if lstArr: result.indent(currIndent)
|
||||
result.add(escapeJson(node.str))
|
||||
of SInt:
|
||||
if lstArr: result.indent(currIndent)
|
||||
result.add($node.num)
|
||||
of SFloat:
|
||||
if lstArr: result.indent(currIndent)
|
||||
result.add($node.fnum)
|
||||
of SNil:
|
||||
if lstArr: result.indent(currIndent)
|
||||
result.add("nil")
|
||||
of SSymbol:
|
||||
if lstArr: result.indent(currIndent)
|
||||
result.add($node.symbol)
|
||||
of SList:
|
||||
if lstArr: result.indent(currIndent)
|
||||
if len(node.elems) != 0:
|
||||
result.add("(")
|
||||
result.nl(ml)
|
||||
for i in 0..len(node.elems)-1:
|
||||
if i > 0:
|
||||
result.add(" ")
|
||||
result.nl(ml) # New Line
|
||||
toPretty(result, node.elems[i], indent, ml,
|
||||
true, newIndent(currIndent, indent, ml))
|
||||
result.nl(ml)
|
||||
result.indent(currIndent)
|
||||
result.add(")")
|
||||
else: result.add("nil")
|
||||
of SCons:
|
||||
if lstArr: result.indent(currIndent)
|
||||
result.add("(")
|
||||
toPretty(result, node.car, indent, ml,
|
||||
true, newIndent(currIndent, indent, ml))
|
||||
result.add(" . ")
|
||||
toPretty(result, node.cdr, indent, ml,
|
||||
true, newIndent(currIndent, indent, ml))
|
||||
result.add(")")
|
||||
|
||||
proc pretty*(node: SexpNode, indent = 2): string =
|
||||
## Converts `node` to its Sexp Representation, with indentation and
|
||||
## on multiple lines.
|
||||
result = ""
|
||||
toPretty(result, node, indent)
|
||||
|
||||
proc `$`*(node: SexpNode): string =
|
||||
## Converts `node` to its SEXP Representation on one line.
|
||||
result = ""
|
||||
toPretty(result, node, 0, false)
|
||||
|
||||
iterator items*(node: SexpNode): SexpNode =
|
||||
## Iterator for the items of `node`. `node` has to be a SList.
|
||||
assert node.kind == SList
|
||||
for i in items(node.elems):
|
||||
yield i
|
||||
|
||||
iterator mitems*(node: var SexpNode): var SexpNode =
|
||||
## Iterator for the items of `node`. `node` has to be a SList. Items can be
|
||||
## modified.
|
||||
assert node.kind == SList
|
||||
for i in mitems(node.elems):
|
||||
yield i
|
||||
|
||||
proc eat(p: var SexpParser, tok: TTokKind) =
|
||||
if p.tok == tok: discard getTok(p)
|
||||
else: raiseParseErr(p, tokToStr[tok])
|
||||
|
||||
proc parseSexp(p: var SexpParser): SexpNode =
|
||||
## Parses SEXP from a SEXP Parser `p`.
|
||||
case p.tok
|
||||
of tkString:
|
||||
# we capture 'p.a' here, so we need to give it a fresh buffer afterwards:
|
||||
result = newSStringMove(p.a)
|
||||
p.a = ""
|
||||
discard getTok(p)
|
||||
of tkInt:
|
||||
result = newSInt(parseBiggestInt(p.a))
|
||||
discard getTok(p)
|
||||
of tkFloat:
|
||||
result = newSFloat(parseFloat(p.a))
|
||||
discard getTok(p)
|
||||
of tkNil:
|
||||
result = newSNil()
|
||||
discard getTok(p)
|
||||
of tkSymbol:
|
||||
result = newSSymbolMove(p.a)
|
||||
p.a = ""
|
||||
discard getTok(p)
|
||||
of tkParensLe:
|
||||
result = newSList()
|
||||
discard getTok(p)
|
||||
while p.tok notin {tkParensRi, tkDot}:
|
||||
result.add(parseSexp(p))
|
||||
if p.tok != tkSpace: break
|
||||
discard getTok(p)
|
||||
if p.tok == tkDot:
|
||||
eat(p, tkDot)
|
||||
eat(p, tkSpace)
|
||||
result.add(parseSexp(p))
|
||||
result = newSCons(result[0], result[1])
|
||||
eat(p, tkParensRi)
|
||||
of tkSpace, tkDot, tkError, tkParensRi, tkEof:
|
||||
raiseParseErr(p, "(")
|
||||
|
||||
proc open*(my: var SexpParser, input: Stream) =
|
||||
## initializes the parser with an input stream.
|
||||
lexbase.open(my, input)
|
||||
my.kind = sexpError
|
||||
my.a = ""
|
||||
|
||||
proc parseSexp*(s: Stream): SexpNode =
|
||||
## Parses from a buffer `s` into a `SexpNode`.
|
||||
var p: SexpParser
|
||||
p.open(s)
|
||||
discard getTok(p) # read first token
|
||||
result = p.parseSexp()
|
||||
p.close()
|
||||
|
||||
proc parseSexp*(buffer: string): SexpNode =
|
||||
## Parses Sexp from `buffer`.
|
||||
result = parseSexp(newStringStream(buffer))
|
||||
|
||||
when isMainModule:
|
||||
let testSexp = parseSexp("""(1 (98 2) nil (2) foobar "foo" 9.234)""")
|
||||
assert(testSexp[0].getNum == 1)
|
||||
assert(testSexp[1][0].getNum == 98)
|
||||
assert(testSexp[2].getElems == @[])
|
||||
assert(testSexp[4].getSymbol == "foobar")
|
||||
assert(testSexp[5].getStr == "foo")
|
||||
|
||||
let alist = parseSexp("""((1 . 2) (2 . "foo"))""")
|
||||
assert(alist[0].getCons.car.getNum == 1)
|
||||
assert(alist[0].getCons.cdr.getNum == 2)
|
||||
assert(alist[1].getCons.cdr.getStr == "foo")
|
||||
|
||||
# Generator:
|
||||
var j = convertSexp([true, false, "foobar", [1, 2, "baz"]])
|
||||
assert($j == """(t nil "foobar" (1 2 "baz"))""")
|
||||
@@ -75,7 +75,7 @@ const
|
||||
BufferSize*: int = 4000 ## size of a buffered socket's buffer
|
||||
|
||||
type
|
||||
TSocketImpl = object ## socket type
|
||||
SocketImpl = object ## socket type
|
||||
fd: SocketHandle
|
||||
case isBuffered: bool # determines whether this socket is buffered.
|
||||
of true:
|
||||
@@ -94,7 +94,7 @@ type
|
||||
of false: nil
|
||||
nonblocking: bool
|
||||
|
||||
Socket* = ref TSocketImpl
|
||||
Socket* = ref SocketImpl
|
||||
|
||||
Port* = distinct uint16 ## port type
|
||||
|
||||
@@ -146,8 +146,9 @@ type
|
||||
|
||||
{.deprecated: [TSocket: Socket, TType: SockType, TPort: Port, TDomain: Domain,
|
||||
TProtocol: Protocol, TServent: Servent, THostent: Hostent,
|
||||
TSOBool: SOBool, TRecvLineResult: RecvLineResult,
|
||||
TReadLineResult: ReadLineResult, ETimeout: TimeoutError].}
|
||||
TSOBool: SOBool, TRecvLineResult: RecvLineResult,
|
||||
TReadLineResult: ReadLineResult, ETimeout: TimeoutError,
|
||||
TSocketImpl: SocketImpl].}
|
||||
|
||||
when defined(booting):
|
||||
let invalidSocket*: Socket = nil ## invalid socket
|
||||
|
||||
@@ -327,7 +327,7 @@ proc newStringStream*(s: string = ""): StringStream =
|
||||
when not defined(js):
|
||||
|
||||
type
|
||||
FileStream* = ref FileStreamObj ## a stream that encapsulates a `TFile`
|
||||
FileStream* = ref FileStreamObj ## a stream that encapsulates a `File`
|
||||
FileStreamObj* = object of Stream
|
||||
f: File
|
||||
{.deprecated: [PFileStream: FileStream, TFileStream: FileStreamObj].}
|
||||
|
||||
@@ -74,7 +74,7 @@ const
|
||||
growthFactor = 2
|
||||
startSize = 64
|
||||
|
||||
proc myhash(t: StringTableRef, key: string): THash =
|
||||
proc myhash(t: StringTableRef, key: string): Hash =
|
||||
case t.mode
|
||||
of modeCaseSensitive: result = hashes.hash(key)
|
||||
of modeCaseInsensitive: result = hashes.hashIgnoreCase(key)
|
||||
@@ -90,11 +90,11 @@ proc mustRehash(length, counter: int): bool =
|
||||
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 rawGet(t: StringTableRef, key: string): int =
|
||||
var h: THash = myhash(t, key) and high(t.data) # start with real hash value
|
||||
var h: Hash = myhash(t, key) and high(t.data) # start with real hash value
|
||||
while not isNil(t.data[h].key):
|
||||
if myCmp(t, t.data[h].key, key):
|
||||
return h
|
||||
@@ -122,7 +122,7 @@ proc hasKey*(t: StringTableRef, key: string): bool {.rtl, extern: "nst$1".} =
|
||||
result = rawGet(t, key) >= 0
|
||||
|
||||
proc rawInsert(t: StringTableRef, data: var KeyValuePairSeq, key, val: string) =
|
||||
var h: THash = myhash(t, key) and high(data)
|
||||
var h: Hash = myhash(t, key) and high(data)
|
||||
while not isNil(data[h].key):
|
||||
h = nextTry(h, high(data))
|
||||
data[h].key = key
|
||||
|
||||
@@ -23,7 +23,8 @@ import parseutils
|
||||
include "system/inclrtl"
|
||||
|
||||
type
|
||||
TCharSet* {.deprecated.} = set[char] # for compatibility with Nim
|
||||
CharSet* {.deprecated.} = set[char] # for compatibility with Nim
|
||||
{.deprecated: [TCharSet: CharSet].}
|
||||
|
||||
const
|
||||
Whitespace* = {' ', '\t', '\v', '\r', '\l', '\f'}
|
||||
|
||||
@@ -37,13 +37,14 @@ proc raiseInvalidFormat(msg: string) {.noinline.} =
|
||||
raise newException(SubexError, "invalid format string: " & msg)
|
||||
|
||||
type
|
||||
TFormatParser = object {.pure, final.}
|
||||
FormatParser = object {.pure, final.}
|
||||
when defined(js):
|
||||
f: string # we rely on the '\0' terminator
|
||||
# which JS's native string doesn't have
|
||||
else:
|
||||
f: cstring
|
||||
num, i, lineLen: int
|
||||
{.deprecated: [TFormatParser: FormatParser].}
|
||||
|
||||
template call(x: stmt) {.immediate.} =
|
||||
p.i = i
|
||||
@@ -57,7 +58,7 @@ template callNoLineLenTracking(x: stmt) {.immediate.} =
|
||||
i = p.i
|
||||
p.lineLen = oldLineLen
|
||||
|
||||
proc getFormatArg(p: var TFormatParser, a: openArray[string]): int =
|
||||
proc getFormatArg(p: var FormatParser, a: openArray[string]): int =
|
||||
const PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\128'..'\255', '_'}
|
||||
var i = p.i
|
||||
var f = p.f
|
||||
@@ -90,22 +91,22 @@ proc getFormatArg(p: var TFormatParser, a: openArray[string]): int =
|
||||
if result >=% a.len: raiseInvalidFormat("index out of bounds: " & $result)
|
||||
p.i = i
|
||||
|
||||
proc scanDollar(p: var TFormatParser, a: openarray[string], s: var string) {.
|
||||
proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) {.
|
||||
noSideEffect.}
|
||||
|
||||
proc emitChar(p: var TFormatParser, x: var string, ch: char) {.inline.} =
|
||||
proc emitChar(p: var FormatParser, x: var string, ch: char) {.inline.} =
|
||||
x.add(ch)
|
||||
if ch == '\L': p.lineLen = 0
|
||||
else: inc p.lineLen
|
||||
|
||||
proc emitStrLinear(p: var TFormatParser, x: var string, y: string) {.inline.} =
|
||||
proc emitStrLinear(p: var FormatParser, x: var string, y: string) {.inline.} =
|
||||
for ch in items(y): emitChar(p, x, ch)
|
||||
|
||||
proc emitStr(p: var TFormatParser, x: var string, y: string) {.inline.} =
|
||||
proc emitStr(p: var FormatParser, x: var string, y: string) {.inline.} =
|
||||
x.add(y)
|
||||
inc p.lineLen, y.len
|
||||
|
||||
proc scanQuote(p: var TFormatParser, x: var string, toAdd: bool) =
|
||||
proc scanQuote(p: var FormatParser, x: var string, toAdd: bool) =
|
||||
var i = p.i+1
|
||||
var f = p.f
|
||||
while true:
|
||||
@@ -120,7 +121,7 @@ proc scanQuote(p: var TFormatParser, x: var string, toAdd: bool) =
|
||||
inc i
|
||||
p.i = i
|
||||
|
||||
proc scanBranch(p: var TFormatParser, a: openArray[string],
|
||||
proc scanBranch(p: var FormatParser, a: openArray[string],
|
||||
x: var string, choice: int) =
|
||||
var i = p.i
|
||||
var f = p.f
|
||||
@@ -167,7 +168,7 @@ proc scanBranch(p: var TFormatParser, a: openArray[string],
|
||||
i = last
|
||||
p.i = i+1
|
||||
|
||||
proc scanSlice(p: var TFormatParser, a: openarray[string]): tuple[x, y: int] =
|
||||
proc scanSlice(p: var FormatParser, a: openarray[string]): tuple[x, y: int] =
|
||||
var slice = false
|
||||
var i = p.i
|
||||
var f = p.f
|
||||
@@ -193,7 +194,7 @@ proc scanSlice(p: var TFormatParser, a: openarray[string]): tuple[x, y: int] =
|
||||
inc i
|
||||
p.i = i
|
||||
|
||||
proc scanDollar(p: var TFormatParser, a: openarray[string], s: var string) =
|
||||
proc scanDollar(p: var FormatParser, a: openarray[string], s: var string) =
|
||||
var i = p.i
|
||||
var f = p.f
|
||||
case f[i]
|
||||
@@ -312,7 +313,7 @@ proc subex*(s: string): Subex =
|
||||
proc addf*(s: var string, formatstr: Subex, a: varargs[string, `$`]) {.
|
||||
noSideEffect, rtl, extern: "nfrmtAddf".} =
|
||||
## The same as ``add(s, formatstr % a)``, but more efficient.
|
||||
var p: TFormatParser
|
||||
var p: FormatParser
|
||||
p.f = formatstr.string
|
||||
var i = 0
|
||||
while i < len(formatstr.string):
|
||||
@@ -386,10 +387,10 @@ when isMainModule:
|
||||
longishA,
|
||||
longish)"""
|
||||
|
||||
assert "type TMyEnum* = enum\n $', '2i'\n '{..}" % ["fieldA",
|
||||
assert "type MyEnum* = enum\n $', '2i'\n '{..}" % ["fieldA",
|
||||
"fieldB", "FiledClkad", "fieldD", "fieldE", "longishFieldName"] ==
|
||||
strutils.unindent """
|
||||
type TMyEnum* = enum
|
||||
type MyEnum* = enum
|
||||
fieldA, fieldB,
|
||||
FiledClkad, fieldD,
|
||||
fieldE, longishFieldName"""
|
||||
@@ -400,11 +401,11 @@ when isMainModule:
|
||||
|
||||
doAssert subex"$['''|'|''''|']']#" % "0" == "'|"
|
||||
|
||||
assert subex("type\n TEnum = enum\n $', '40c'\n '{..}") % [
|
||||
assert subex("type\n Enum = enum\n $', '40c'\n '{..}") % [
|
||||
"fieldNameA", "fieldNameB", "fieldNameC", "fieldNameD"] ==
|
||||
strutils.unindent """
|
||||
type
|
||||
TEnum = enum
|
||||
Enum = enum
|
||||
fieldNameA, fieldNameB, fieldNameC,
|
||||
fieldNameD"""
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ when defined(windows):
|
||||
import windows, os
|
||||
|
||||
var
|
||||
conHandle: THandle
|
||||
conHandle: Handle
|
||||
# = createFile("CONOUT$", GENERIC_WRITE, 0, nil, OPEN_ALWAYS, 0, 0)
|
||||
|
||||
block:
|
||||
@@ -30,13 +30,13 @@ when defined(windows):
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc getCursorPos(): tuple [x,y: int] =
|
||||
var c: TCONSOLESCREENBUFFERINFO
|
||||
var c: CONSOLESCREENBUFFERINFO
|
||||
if GetConsoleScreenBufferInfo(conHandle, addr(c)) == 0:
|
||||
raiseOSError(osLastError())
|
||||
return (int(c.dwCursorPosition.X), int(c.dwCursorPosition.Y))
|
||||
|
||||
proc getAttributes(): int16 =
|
||||
var c: TCONSOLESCREENBUFFERINFO
|
||||
var c: CONSOLESCREENBUFFERINFO
|
||||
# workaround Windows bugs: try several times
|
||||
if GetConsoleScreenBufferInfo(conHandle, addr(c)) != 0:
|
||||
return c.wAttributes
|
||||
@@ -51,11 +51,11 @@ else:
|
||||
proc setRaw(fd: FileHandle, time: cint = TCSAFLUSH) =
|
||||
var mode: Termios
|
||||
discard fd.tcgetattr(addr mode)
|
||||
mode.c_iflag = mode.c_iflag and not Tcflag(BRKINT or ICRNL or INPCK or
|
||||
mode.c_iflag = mode.c_iflag and not Cflag(BRKINT or ICRNL or INPCK or
|
||||
ISTRIP or IXON)
|
||||
mode.c_oflag = mode.c_oflag and not Tcflag(OPOST)
|
||||
mode.c_cflag = (mode.c_cflag and not Tcflag(CSIZE or PARENB)) or CS8
|
||||
mode.c_lflag = mode.c_lflag and not Tcflag(ECHO or ICANON or IEXTEN or ISIG)
|
||||
mode.c_oflag = mode.c_oflag and not Cflag(OPOST)
|
||||
mode.c_cflag = (mode.c_cflag and not Cflag(CSIZE or PARENB)) or CS8
|
||||
mode.c_lflag = mode.c_lflag and not Cflag(ECHO or ICANON or IEXTEN or ISIG)
|
||||
mode.c_cc[VMIN] = 1.cuchar
|
||||
mode.c_cc[VTIME] = 0.cuchar
|
||||
discard fd.tcsetattr(time, addr mode)
|
||||
@@ -64,7 +64,7 @@ proc setCursorPos*(x, y: int) =
|
||||
## sets the terminal's cursor to the (x,y) position. (0,0) is the
|
||||
## upper left of the screen.
|
||||
when defined(windows):
|
||||
var c: TCOORD
|
||||
var c: COORD
|
||||
c.X = int16(x)
|
||||
c.Y = int16(y)
|
||||
if SetConsoleCursorPosition(conHandle, c) == 0: raiseOSError(osLastError())
|
||||
@@ -75,7 +75,7 @@ proc setCursorXPos*(x: int) =
|
||||
## sets the terminal's cursor to the x position. The y position is
|
||||
## not changed.
|
||||
when defined(windows):
|
||||
var scrbuf: TCONSOLESCREENBUFFERINFO
|
||||
var scrbuf: CONSOLESCREENBUFFERINFO
|
||||
var hStdout = conHandle
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0:
|
||||
raiseOSError(osLastError())
|
||||
@@ -91,7 +91,7 @@ when defined(windows):
|
||||
## sets the terminal's cursor to the y position. The x position is
|
||||
## not changed. **Warning**: This is not supported on UNIX!
|
||||
when defined(windows):
|
||||
var scrbuf: TCONSOLESCREENBUFFERINFO
|
||||
var scrbuf: CONSOLESCREENBUFFERINFO
|
||||
var hStdout = conHandle
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0:
|
||||
raiseOSError(osLastError())
|
||||
@@ -172,7 +172,7 @@ else:
|
||||
proc eraseLine* =
|
||||
## Erases the entire current line.
|
||||
when defined(windows):
|
||||
var scrbuf: TCONSOLESCREENBUFFERINFO
|
||||
var scrbuf: CONSOLESCREENBUFFERINFO
|
||||
var numwrote: DWORD
|
||||
var hStdout = conHandle
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0:
|
||||
@@ -196,9 +196,9 @@ proc eraseLine* =
|
||||
proc eraseScreen* =
|
||||
## Erases the screen with the background colour and moves the cursor to home.
|
||||
when defined(windows):
|
||||
var scrbuf: TCONSOLESCREENBUFFERINFO
|
||||
var scrbuf: CONSOLESCREENBUFFERINFO
|
||||
var numwrote: DWORD
|
||||
var origin: TCOORD # is inititalized to 0, 0
|
||||
var origin: COORD # is inititalized to 0, 0
|
||||
var hStdout = conHandle
|
||||
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0:
|
||||
@@ -364,7 +364,13 @@ macro styledEcho*(m: varargs[expr]): stmt =
|
||||
result.add(newCall(bindSym"write", bindSym"stdout", newStrLitNode("\n")))
|
||||
result.add(newCall(bindSym"resetAttributes"))
|
||||
|
||||
when not defined(windows):
|
||||
when defined(nimdoc):
|
||||
proc getch*(): char =
|
||||
## Read a single character from the terminal, blocking until it is entered.
|
||||
## The character is not printed to the terminal. This is not available for
|
||||
## Windows.
|
||||
discard
|
||||
elif not defined(windows):
|
||||
proc getch*(): char =
|
||||
## Read a single character from the terminal, blocking until it is entered.
|
||||
## The character is not printed to the terminal. This is not available for
|
||||
|
||||
@@ -470,7 +470,7 @@ when not defined(JS):
|
||||
posix_gettimeofday(a)
|
||||
result = toFloat(a.tv_sec) + toFloat(a.tv_usec)*0.00_0001
|
||||
elif defined(windows):
|
||||
var f: winlean.TFILETIME
|
||||
var f: winlean.FILETIME
|
||||
getSystemTimeAsFileTime(f)
|
||||
var i64 = rdFileTime(f) - epochDiff
|
||||
var secs = i64 div rateDiff
|
||||
|
||||
@@ -99,8 +99,9 @@ template test*(name: expr, body: stmt): stmt {.immediate, dirty.} =
|
||||
body
|
||||
|
||||
except:
|
||||
checkpoint("Unhandled exception: " & getCurrentExceptionMsg())
|
||||
echo getCurrentException().getStackTrace()
|
||||
when not defined(js):
|
||||
checkpoint("Unhandled exception: " & getCurrentExceptionMsg())
|
||||
echo getCurrentException().getStackTrace()
|
||||
fail()
|
||||
|
||||
finally:
|
||||
@@ -114,9 +115,7 @@ proc checkpoint*(msg: string) =
|
||||
template fail* =
|
||||
bind checkpoints
|
||||
for msg in items(checkpoints):
|
||||
# this used to be 'echo' which now breaks due to a bug. XXX will revisit
|
||||
# this issue later.
|
||||
stdout.writeln msg
|
||||
echo msg
|
||||
|
||||
when not defined(ECMAScript):
|
||||
if abortOnError: quit(1)
|
||||
@@ -157,12 +156,13 @@ macro check*(conditions: stmt): stmt {.immediate.} =
|
||||
# Ident !"v"
|
||||
# IntLit 2
|
||||
paramAst = exp[i][1]
|
||||
argsAsgns.add getAst(asgn(arg, paramAst))
|
||||
argsPrintOuts.add getAst(print(argStr, arg))
|
||||
if exp[i].kind != nnkExprEqExpr:
|
||||
exp[i] = arg
|
||||
else:
|
||||
exp[i][1] = arg
|
||||
if exp[i].typekind notin {ntyTypeDesc}:
|
||||
argsAsgns.add getAst(asgn(arg, paramAst))
|
||||
argsPrintOuts.add getAst(print(argStr, arg))
|
||||
if exp[i].kind != nnkExprEqExpr:
|
||||
exp[i] = arg
|
||||
else:
|
||||
exp[i][1] = arg
|
||||
|
||||
case checked.kind
|
||||
of nnkCallKinds:
|
||||
|
||||
@@ -249,7 +249,7 @@ when defined(nimNewShared):
|
||||
guarded* {.magic: "Guarded".}
|
||||
|
||||
# comparison operators:
|
||||
proc `==` *[TEnum: enum](x, y: TEnum): bool {.magic: "EqEnum", noSideEffect.}
|
||||
proc `==` *[Enum: enum](x, y: Enum): bool {.magic: "EqEnum", noSideEffect.}
|
||||
proc `==` *(x, y: pointer): bool {.magic: "EqRef", noSideEffect.}
|
||||
proc `==` *(x, y: string): bool {.magic: "EqStr", noSideEffect.}
|
||||
proc `==` *(x, y: cstring): bool {.magic: "EqCString", noSideEffect.}
|
||||
@@ -260,7 +260,7 @@ proc `==` *[T](x, y: ref T): bool {.magic: "EqRef", noSideEffect.}
|
||||
proc `==` *[T](x, y: ptr T): bool {.magic: "EqRef", noSideEffect.}
|
||||
proc `==` *[T: proc](x, y: T): bool {.magic: "EqProc", noSideEffect.}
|
||||
|
||||
proc `<=` *[TEnum: enum](x, y: TEnum): bool {.magic: "LeEnum", noSideEffect.}
|
||||
proc `<=` *[Enum: enum](x, y: Enum): bool {.magic: "LeEnum", noSideEffect.}
|
||||
proc `<=` *(x, y: string): bool {.magic: "LeStr", noSideEffect.}
|
||||
proc `<=` *(x, y: char): bool {.magic: "LeCh", noSideEffect.}
|
||||
proc `<=` *[T](x, y: set[T]): bool {.magic: "LeSet", noSideEffect.}
|
||||
@@ -268,7 +268,7 @@ proc `<=` *(x, y: bool): bool {.magic: "LeB", noSideEffect.}
|
||||
proc `<=` *[T](x, y: ref T): bool {.magic: "LePtr", noSideEffect.}
|
||||
proc `<=` *(x, y: pointer): bool {.magic: "LePtr", noSideEffect.}
|
||||
|
||||
proc `<` *[TEnum: enum](x, y: TEnum): bool {.magic: "LtEnum", noSideEffect.}
|
||||
proc `<` *[Enum: enum](x, y: Enum): bool {.magic: "LtEnum", noSideEffect.}
|
||||
proc `<` *(x, y: string): bool {.magic: "LtStr", noSideEffect.}
|
||||
proc `<` *(x, y: char): bool {.magic: "LtCh", noSideEffect.}
|
||||
proc `<` *[T](x, y: set[T]): bool {.magic: "LtSet", noSideEffect.}
|
||||
@@ -332,7 +332,7 @@ type
|
||||
|
||||
RootObj* {.exportc: "TNimObject", inheritable.} =
|
||||
object ## the root of Nim's object hierarchy. Objects should
|
||||
## inherit from TObject or one of its descendants. However,
|
||||
## inherit from RootObj or one of its descendants. However,
|
||||
## objects that have no ancestor are allowed.
|
||||
RootRef* = ref RootObj ## reference to RootObj
|
||||
|
||||
@@ -1505,7 +1505,7 @@ proc `$` *(x: string): string {.magic: "StrToStr", noSideEffect.}
|
||||
## as it is. This operator is useful for generic code, so
|
||||
## that ``$expr`` also works if ``expr`` is already a string.
|
||||
|
||||
proc `$` *[TEnum: enum](x: TEnum): string {.magic: "EnumToStr", noSideEffect.}
|
||||
proc `$` *[Enum: enum](x: Enum): string {.magic: "EnumToStr", noSideEffect.}
|
||||
## The stringify operator for an enumeration argument. This works for
|
||||
## any enumeration type thanks to compiler magic. If
|
||||
## a ``$`` operator for a concrete enumeration is provided, this is
|
||||
@@ -1535,7 +1535,7 @@ const
|
||||
NimMinor*: int = 11
|
||||
## is the minor number of Nim's version.
|
||||
|
||||
NimPatch*: int = 2
|
||||
NimPatch*: int = 3
|
||||
## is the patch number of Nim's version.
|
||||
|
||||
NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch
|
||||
@@ -1578,7 +1578,7 @@ else:
|
||||
type IntLikeForCount = int|int8|int16|int32|char|bool|uint8|uint16|enum
|
||||
|
||||
iterator countdown*[T](a, b: T, step = 1): T {.inline.} =
|
||||
## Counts from ordinal value `a` down to `b` with the given
|
||||
## Counts from ordinal value `a` down to `b` (inclusive) with the given
|
||||
## step count. `T` may be any ordinal type, `step` may only
|
||||
## be positive. **Note**: This fails to count to ``low(int)`` if T = int for
|
||||
## efficiency reasons.
|
||||
@@ -1606,7 +1606,7 @@ template countupImpl(incr: stmt) {.immediate, dirty.} =
|
||||
incr
|
||||
|
||||
iterator countup*[S, T](a: S, b: T, step = 1): T {.inline.} =
|
||||
## Counts from ordinal value `a` up to `b` with the given
|
||||
## Counts from ordinal value `a` up to `b` (inclusive) with the given
|
||||
## step count. `S`, `T` may be any ordinal type, `step` may only
|
||||
## be positive. **Note**: This fails to count to ``high(int)`` if T = int for
|
||||
## efficiency reasons.
|
||||
@@ -2213,6 +2213,7 @@ type
|
||||
filename*: cstring ## filename of the proc that is currently executing
|
||||
len*: int16 ## length of the inspectable slots
|
||||
calldepth*: int16 ## used for max call depth checking
|
||||
#{.deprecated: [TFrame: Frame].}
|
||||
|
||||
when defined(JS):
|
||||
proc add*(x: var string, y: cstring) {.asmNoStackFrame.} =
|
||||
@@ -2414,7 +2415,7 @@ when not defined(JS): #and not defined(NimrodVM):
|
||||
|
||||
proc open*(f: var File, filehandle: FileHandle,
|
||||
mode: FileMode = fmRead): bool {.tags: [], benign.}
|
||||
## Creates a ``TFile`` from a `filehandle` with given `mode`.
|
||||
## Creates a ``File`` from a `filehandle` with given `mode`.
|
||||
##
|
||||
## Default mode is readonly. Returns true iff the file could be opened.
|
||||
|
||||
@@ -2604,6 +2605,8 @@ when not defined(JS): #and not defined(NimrodVM):
|
||||
context: C_JmpBuf
|
||||
hasRaiseAction: bool
|
||||
raiseAction: proc (e: ref Exception): bool {.closure.}
|
||||
SafePoint = TSafePoint
|
||||
# {.deprecated: [TSafePoint: SafePoint].}
|
||||
|
||||
when declared(initAllocator):
|
||||
initAllocator()
|
||||
@@ -3054,9 +3057,10 @@ proc raiseAssert*(msg: string) {.noinline.} =
|
||||
proc failedAssertImpl*(msg: string) {.raises: [], tags: [].} =
|
||||
# trick the compiler to not list ``AssertionError`` when called
|
||||
# by ``assert``.
|
||||
type THide = proc (msg: string) {.noinline, raises: [], noSideEffect,
|
||||
type Hide = proc (msg: string) {.noinline, raises: [], noSideEffect,
|
||||
tags: [].}
|
||||
THide(raiseAssert)(msg)
|
||||
{.deprecated: [THide: Hide].}
|
||||
Hide(raiseAssert)(msg)
|
||||
|
||||
template assert*(cond: bool, msg = "") =
|
||||
## Raises ``AssertionError`` with `msg` if `cond` is false. Note
|
||||
|
||||
@@ -98,46 +98,49 @@ const
|
||||
SmallChunkSize = PageSize
|
||||
|
||||
type
|
||||
PTrunk = ptr TTrunk
|
||||
TTrunk {.final.} = object
|
||||
PTrunk = ptr 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, int] # a bit vector
|
||||
|
||||
TTrunkBuckets = array[0..255, PTrunk]
|
||||
TIntSet {.final.} = object
|
||||
data: TTrunkBuckets
|
||||
TrunkBuckets = array[0..255, PTrunk]
|
||||
IntSet {.final.} = object
|
||||
data: TrunkBuckets
|
||||
{.deprecated: [TIntSet: IntSet, TTrunk: Trunk, TTrunkBuckets: TrunkBuckets].}
|
||||
|
||||
type
|
||||
TAlignType = BiggestFloat
|
||||
TFreeCell {.final, pure.} = object
|
||||
next: ptr TFreeCell # next free cell in chunk (overlaid with refcount)
|
||||
AlignType = BiggestFloat
|
||||
FreeCell {.final, pure.} = object
|
||||
next: ptr FreeCell # next free cell in chunk (overlaid with refcount)
|
||||
zeroField: int # 0 means cell is not used (overlaid with typ field)
|
||||
# 1 means cell is manually managed pointer
|
||||
# otherwise a PNimType is stored in there
|
||||
|
||||
PChunk = ptr TBaseChunk
|
||||
PBigChunk = ptr TBigChunk
|
||||
PSmallChunk = ptr TSmallChunk
|
||||
TBaseChunk {.pure, inheritable.} = object
|
||||
PChunk = ptr BaseChunk
|
||||
PBigChunk = ptr BigChunk
|
||||
PSmallChunk = ptr SmallChunk
|
||||
BaseChunk {.pure, inheritable.} = object
|
||||
prevSize: int # size of previous chunk; for coalescing
|
||||
size: int # if < PageSize it is a small chunk
|
||||
used: bool # later will be optimized into prevSize...
|
||||
|
||||
TSmallChunk = object of TBaseChunk
|
||||
SmallChunk = object of BaseChunk
|
||||
next, prev: PSmallChunk # chunks of the same size
|
||||
freeList: ptr TFreeCell
|
||||
freeList: ptr FreeCell
|
||||
free: int # how many bytes remain
|
||||
acc: int # accumulator for small object allocation
|
||||
data: TAlignType # start of usable memory
|
||||
data: AlignType # start of usable memory
|
||||
|
||||
TBigChunk = object of TBaseChunk # not necessarily > PageSize!
|
||||
BigChunk = object of BaseChunk # not necessarily > PageSize!
|
||||
next, prev: PBigChunk # chunks of the same (or bigger) size
|
||||
align: int
|
||||
data: TAlignType # start of usable memory
|
||||
data: AlignType # start of usable memory
|
||||
{.deprecated: [TAlignType: AlignType, TFreeCell: FreeCell, TBaseChunk: BaseChunk,
|
||||
TBigChunk: BigChunk, TSmallChunk: SmallChunk].}
|
||||
|
||||
template smallChunkOverhead(): expr = sizeof(TSmallChunk)-sizeof(TAlignType)
|
||||
template bigChunkOverhead(): expr = sizeof(TBigChunk)-sizeof(TAlignType)
|
||||
template smallChunkOverhead(): expr = sizeof(SmallChunk)-sizeof(AlignType)
|
||||
template bigChunkOverhead(): expr = sizeof(BigChunk)-sizeof(AlignType)
|
||||
|
||||
proc roundup(x, v: int): int {.inline.} =
|
||||
result = (x + (v-1)) and not (v-1)
|
||||
@@ -156,31 +159,32 @@ sysAssert(roundup(65, 8) == 72, "roundup broken 2")
|
||||
# to the OS), a fixed size array can be used.
|
||||
|
||||
type
|
||||
PLLChunk = ptr TLLChunk
|
||||
TLLChunk {.pure.} = object ## *low-level* chunk
|
||||
PLLChunk = ptr LLChunk
|
||||
LLChunk {.pure.} = object ## *low-level* chunk
|
||||
size: int # remaining size
|
||||
acc: int # accumulator
|
||||
next: PLLChunk # next low-level chunk; only needed for dealloc
|
||||
|
||||
PAvlNode = ptr TAvlNode
|
||||
TAvlNode {.pure, final.} = object
|
||||
PAvlNode = ptr AvlNode
|
||||
AvlNode {.pure, final.} = object
|
||||
link: array[0..1, PAvlNode] # Left (0) and right (1) links
|
||||
key, upperBound: int
|
||||
level: int
|
||||
|
||||
TMemRegion {.final, pure.} = object
|
||||
MemRegion {.final, pure.} = object
|
||||
minLargeObj, maxLargeObj: int
|
||||
freeSmallChunks: array[0..SmallChunkSize div MemAlign-1, PSmallChunk]
|
||||
llmem: PLLChunk
|
||||
currMem, maxMem, freeMem: int # memory sizes (allocated from OS)
|
||||
lastSize: int # needed for the case that OS gives us pages linearly
|
||||
freeChunksList: PBigChunk # XXX make this a datastructure with O(1) access
|
||||
chunkStarts: TIntSet
|
||||
chunkStarts: IntSet
|
||||
root, deleted, last, freeAvlNodes: PAvlNode
|
||||
{.deprecated: [TLLChunk: LLChunk, TAvlNode: AvlNode, TMemRegion: MemRegion].}
|
||||
|
||||
# shared:
|
||||
var
|
||||
bottomData: TAvlNode
|
||||
bottomData: AvlNode
|
||||
bottom: PAvlNode
|
||||
|
||||
{.push stack_trace: off.}
|
||||
@@ -191,44 +195,44 @@ proc initAllocator() =
|
||||
bottom.link[1] = bottom
|
||||
{.pop.}
|
||||
|
||||
proc incCurrMem(a: var TMemRegion, bytes: int) {.inline.} =
|
||||
proc incCurrMem(a: var MemRegion, bytes: int) {.inline.} =
|
||||
inc(a.currMem, bytes)
|
||||
|
||||
proc decCurrMem(a: var TMemRegion, bytes: int) {.inline.} =
|
||||
proc decCurrMem(a: var MemRegion, bytes: int) {.inline.} =
|
||||
a.maxMem = max(a.maxMem, a.currMem)
|
||||
dec(a.currMem, bytes)
|
||||
|
||||
proc getMaxMem(a: var TMemRegion): int =
|
||||
proc getMaxMem(a: var MemRegion): int =
|
||||
# Since we update maxPagesCount only when freeing pages,
|
||||
# maxPagesCount may not be up to date. Thus we use the
|
||||
# maximum of these both values here:
|
||||
result = max(a.currMem, a.maxMem)
|
||||
|
||||
proc llAlloc(a: var TMemRegion, size: int): pointer =
|
||||
proc llAlloc(a: var MemRegion, size: int): pointer =
|
||||
# *low-level* alloc for the memory managers data structures. Deallocation
|
||||
# is done at he end of the allocator's life time.
|
||||
if a.llmem == nil or size > a.llmem.size:
|
||||
# the requested size is ``roundup(size+sizeof(TLLChunk), PageSize)``, but
|
||||
# the requested size is ``roundup(size+sizeof(LLChunk), PageSize)``, but
|
||||
# since we know ``size`` is a (small) constant, we know the requested size
|
||||
# is one page:
|
||||
sysAssert roundup(size+sizeof(TLLChunk), PageSize) == PageSize, "roundup 6"
|
||||
sysAssert roundup(size+sizeof(LLChunk), PageSize) == PageSize, "roundup 6"
|
||||
var old = a.llmem # can be nil and is correct with nil
|
||||
a.llmem = cast[PLLChunk](osAllocPages(PageSize))
|
||||
incCurrMem(a, PageSize)
|
||||
a.llmem.size = PageSize - sizeof(TLLChunk)
|
||||
a.llmem.acc = sizeof(TLLChunk)
|
||||
a.llmem.size = PageSize - sizeof(LLChunk)
|
||||
a.llmem.acc = sizeof(LLChunk)
|
||||
a.llmem.next = old
|
||||
result = cast[pointer](cast[ByteAddress](a.llmem) + a.llmem.acc)
|
||||
dec(a.llmem.size, size)
|
||||
inc(a.llmem.acc, size)
|
||||
zeroMem(result, size)
|
||||
|
||||
proc allocAvlNode(a: var TMemRegion, key, upperBound: int): PAvlNode =
|
||||
proc allocAvlNode(a: var MemRegion, key, upperBound: int): PAvlNode =
|
||||
if a.freeAvlNodes != nil:
|
||||
result = a.freeAvlNodes
|
||||
a.freeAvlNodes = a.freeAvlNodes.link[0]
|
||||
else:
|
||||
result = cast[PAvlNode](llAlloc(a, sizeof(TAvlNode)))
|
||||
result = cast[PAvlNode](llAlloc(a, sizeof(AvlNode)))
|
||||
result.key = key
|
||||
result.upperBound = upperBound
|
||||
result.link[0] = bottom
|
||||
@@ -238,13 +242,13 @@ proc allocAvlNode(a: var TMemRegion, key, upperBound: int): PAvlNode =
|
||||
sysAssert(bottom.link[0] == bottom, "bottom link[0]")
|
||||
sysAssert(bottom.link[1] == bottom, "bottom link[1]")
|
||||
|
||||
proc deallocAvlNode(a: var TMemRegion, n: PAvlNode) {.inline.} =
|
||||
proc deallocAvlNode(a: var MemRegion, n: PAvlNode) {.inline.} =
|
||||
n.link[0] = a.freeAvlNodes
|
||||
a.freeAvlNodes = n
|
||||
|
||||
include "system/avltree"
|
||||
|
||||
proc llDeallocAll(a: var TMemRegion) =
|
||||
proc llDeallocAll(a: var MemRegion) =
|
||||
var it = a.llmem
|
||||
while it != nil:
|
||||
# we know each block in the list has the size of 1 page:
|
||||
@@ -252,14 +256,14 @@ proc llDeallocAll(a: var TMemRegion) =
|
||||
osDeallocPages(it, PageSize)
|
||||
it = next
|
||||
|
||||
proc intSetGet(t: TIntSet, key: int): PTrunk =
|
||||
proc intSetGet(t: IntSet, key: int): PTrunk =
|
||||
var it = t.data[key and high(t.data)]
|
||||
while it != nil:
|
||||
if it.key == key: return it
|
||||
it = it.next
|
||||
result = nil
|
||||
|
||||
proc intSetPut(a: var TMemRegion, t: var TIntSet, key: int): PTrunk =
|
||||
proc intSetPut(a: var MemRegion, t: var IntSet, key: int): PTrunk =
|
||||
result = intSetGet(t, key)
|
||||
if result == nil:
|
||||
result = cast[PTrunk](llAlloc(a, sizeof(result[])))
|
||||
@@ -267,7 +271,7 @@ proc intSetPut(a: var TMemRegion, t: var TIntSet, key: int): PTrunk =
|
||||
t.data[key and high(t.data)] = result
|
||||
result.key = key
|
||||
|
||||
proc contains(s: TIntSet, key: int): bool =
|
||||
proc contains(s: IntSet, key: int): bool =
|
||||
var t = intSetGet(s, key shr TrunkShift)
|
||||
if t != nil:
|
||||
var u = key and TrunkMask
|
||||
@@ -275,19 +279,19 @@ proc contains(s: TIntSet, key: int): bool =
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc incl(a: var TMemRegion, s: var TIntSet, key: int) =
|
||||
proc incl(a: var MemRegion, s: var IntSet, key: int) =
|
||||
var t = intSetPut(a, s, key shr TrunkShift)
|
||||
var u = key and TrunkMask
|
||||
t.bits[u shr IntShift] = t.bits[u shr IntShift] or (1 shl (u and IntMask))
|
||||
|
||||
proc excl(s: var TIntSet, key: int) =
|
||||
proc excl(s: var IntSet, key: int) =
|
||||
var t = intSetGet(s, key shr TrunkShift)
|
||||
if t != nil:
|
||||
var u = key and TrunkMask
|
||||
t.bits[u shr IntShift] = t.bits[u shr IntShift] and not
|
||||
(1 shl (u and IntMask))
|
||||
|
||||
iterator elements(t: TIntSet): int {.inline.} =
|
||||
iterator elements(t: IntSet): int {.inline.} =
|
||||
# while traversing it is forbidden to change the set!
|
||||
for h in 0..high(t.data):
|
||||
var r = t.data[h]
|
||||
@@ -311,7 +315,7 @@ proc isSmallChunk(c: PChunk): bool {.inline.} =
|
||||
proc chunkUnused(c: PChunk): bool {.inline.} =
|
||||
result = not c.used
|
||||
|
||||
iterator allObjects(m: TMemRegion): pointer {.inline.} =
|
||||
iterator allObjects(m: MemRegion): pointer {.inline.} =
|
||||
for s in elements(m.chunkStarts):
|
||||
# we need to check here again as it could have been modified:
|
||||
if s in m.chunkStarts:
|
||||
@@ -331,7 +335,7 @@ iterator allObjects(m: TMemRegion): pointer {.inline.} =
|
||||
yield addr(c.data)
|
||||
|
||||
proc isCell(p: pointer): bool {.inline.} =
|
||||
result = cast[ptr TFreeCell](p).zeroField >% 1
|
||||
result = cast[ptr FreeCell](p).zeroField >% 1
|
||||
|
||||
# ------------- chunk management ----------------------------------------------
|
||||
proc pageIndex(c: PChunk): int {.inline.} =
|
||||
@@ -344,7 +348,7 @@ proc pageAddr(p: pointer): PChunk {.inline.} =
|
||||
result = cast[PChunk](cast[ByteAddress](p) and not PageMask)
|
||||
#sysAssert(Contains(allocator.chunkStarts, pageIndex(result)))
|
||||
|
||||
proc requestOsChunks(a: var TMemRegion, size: int): PBigChunk =
|
||||
proc requestOsChunks(a: var MemRegion, size: int): PBigChunk =
|
||||
incCurrMem(a, size)
|
||||
inc(a.freeMem, size)
|
||||
result = cast[PBigChunk](osAllocPages(size))
|
||||
@@ -373,7 +377,7 @@ proc requestOsChunks(a: var TMemRegion, size: int): PBigChunk =
|
||||
result.prevSize = 0 # unknown
|
||||
a.lastSize = size # for next request
|
||||
|
||||
proc freeOsChunks(a: var TMemRegion, p: pointer, size: int) =
|
||||
proc freeOsChunks(a: var MemRegion, p: pointer, size: int) =
|
||||
# update next.prevSize:
|
||||
var c = cast[PChunk](p)
|
||||
var nxt = cast[ByteAddress](p) +% c.size
|
||||
@@ -387,7 +391,7 @@ proc freeOsChunks(a: var TMemRegion, p: pointer, size: int) =
|
||||
dec(a.freeMem, size)
|
||||
#c_fprintf(c_stdout, "[Alloc] back to OS: %ld\n", size)
|
||||
|
||||
proc isAccessible(a: TMemRegion, p: pointer): bool {.inline.} =
|
||||
proc isAccessible(a: MemRegion, p: pointer): bool {.inline.} =
|
||||
result = contains(a.chunkStarts, pageIndex(p))
|
||||
|
||||
proc contains[T](list, x: T): bool =
|
||||
@@ -396,7 +400,7 @@ proc contains[T](list, x: T): bool =
|
||||
if it == x: return true
|
||||
it = it.next
|
||||
|
||||
proc writeFreeList(a: TMemRegion) =
|
||||
proc writeFreeList(a: MemRegion) =
|
||||
var it = a.freeChunksList
|
||||
c_fprintf(c_stdout, "freeChunksList: %p\n", it)
|
||||
while it != nil:
|
||||
@@ -427,14 +431,14 @@ proc listRemove[T](head: var T, c: T) {.inline.} =
|
||||
c.next = nil
|
||||
c.prev = nil
|
||||
|
||||
proc updatePrevSize(a: var TMemRegion, c: PBigChunk,
|
||||
proc updatePrevSize(a: var MemRegion, c: PBigChunk,
|
||||
prevSize: int) {.inline.} =
|
||||
var ri = cast[PChunk](cast[ByteAddress](c) +% c.size)
|
||||
sysAssert((cast[ByteAddress](ri) and PageMask) == 0, "updatePrevSize")
|
||||
if isAccessible(a, ri):
|
||||
ri.prevSize = prevSize
|
||||
|
||||
proc freeBigChunk(a: var TMemRegion, c: PBigChunk) =
|
||||
proc freeBigChunk(a: var MemRegion, c: PBigChunk) =
|
||||
var c = c
|
||||
sysAssert(c.size >= PageSize, "freeBigChunk")
|
||||
inc(a.freeMem, c.size)
|
||||
@@ -467,7 +471,7 @@ proc freeBigChunk(a: var TMemRegion, c: PBigChunk) =
|
||||
else:
|
||||
freeOsChunks(a, c, c.size)
|
||||
|
||||
proc splitChunk(a: var TMemRegion, c: PBigChunk, size: int) =
|
||||
proc splitChunk(a: var MemRegion, c: PBigChunk, size: int) =
|
||||
var rest = cast[PBigChunk](cast[ByteAddress](c) +% size)
|
||||
sysAssert(rest notin a.freeChunksList, "splitChunk")
|
||||
rest.size = c.size - size
|
||||
@@ -480,7 +484,7 @@ proc splitChunk(a: var TMemRegion, c: PBigChunk, size: int) =
|
||||
incl(a, a.chunkStarts, pageIndex(rest))
|
||||
listAdd(a.freeChunksList, rest)
|
||||
|
||||
proc getBigChunk(a: var TMemRegion, size: int): PBigChunk =
|
||||
proc getBigChunk(a: var MemRegion, size: int): PBigChunk =
|
||||
# use first fit for now:
|
||||
sysAssert((size and PageMask) == 0, "getBigChunk 1")
|
||||
sysAssert(size > 0, "getBigChunk 2")
|
||||
@@ -507,16 +511,16 @@ proc getBigChunk(a: var TMemRegion, size: int): PBigChunk =
|
||||
incl(a, a.chunkStarts, pageIndex(result))
|
||||
dec(a.freeMem, size)
|
||||
|
||||
proc getSmallChunk(a: var TMemRegion): PSmallChunk =
|
||||
proc getSmallChunk(a: var MemRegion): PSmallChunk =
|
||||
var res = getBigChunk(a, PageSize)
|
||||
sysAssert res.prev == nil, "getSmallChunk 1"
|
||||
sysAssert res.next == nil, "getSmallChunk 2"
|
||||
result = cast[PSmallChunk](res)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
proc isAllocatedPtr(a: TMemRegion, p: pointer): bool {.benign.}
|
||||
proc isAllocatedPtr(a: MemRegion, p: pointer): bool {.benign.}
|
||||
|
||||
proc allocInv(a: TMemRegion): bool =
|
||||
proc allocInv(a: MemRegion): bool =
|
||||
## checks some (not all yet) invariants of the allocator's data structures.
|
||||
for s in low(a.freeSmallChunks)..high(a.freeSmallChunks):
|
||||
var c = a.freeSmallChunks[s]
|
||||
@@ -537,10 +541,10 @@ proc allocInv(a: TMemRegion): bool =
|
||||
c = c.next
|
||||
result = true
|
||||
|
||||
proc rawAlloc(a: var TMemRegion, requestedSize: int): pointer =
|
||||
proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
|
||||
sysAssert(allocInv(a), "rawAlloc: begin")
|
||||
sysAssert(roundup(65, 8) == 72, "rawAlloc: roundup broken")
|
||||
sysAssert(requestedSize >= sizeof(TFreeCell), "rawAlloc: requested size too small")
|
||||
sysAssert(requestedSize >= sizeof(FreeCell), "rawAlloc: requested size too small")
|
||||
var size = roundup(requestedSize, MemAlign)
|
||||
sysAssert(size >= requestedSize, "insufficient allocated size!")
|
||||
#c_fprintf(c_stdout, "alloc; size: %ld; %ld\n", requestedSize, size)
|
||||
@@ -601,11 +605,11 @@ proc rawAlloc(a: var TMemRegion, requestedSize: int): pointer =
|
||||
sysAssert(allocInv(a), "rawAlloc: end")
|
||||
when logAlloc: cprintf("rawAlloc: %ld %p\n", requestedSize, result)
|
||||
|
||||
proc rawAlloc0(a: var TMemRegion, requestedSize: int): pointer =
|
||||
proc rawAlloc0(a: var MemRegion, requestedSize: int): pointer =
|
||||
result = rawAlloc(a, requestedSize)
|
||||
zeroMem(result, requestedSize)
|
||||
|
||||
proc rawDealloc(a: var TMemRegion, p: pointer) =
|
||||
proc rawDealloc(a: var MemRegion, p: pointer) =
|
||||
#sysAssert(isAllocatedPtr(a, p), "rawDealloc: no allocated pointer")
|
||||
sysAssert(allocInv(a), "rawDealloc: begin")
|
||||
var c = pageAddr(p)
|
||||
@@ -615,7 +619,7 @@ proc rawDealloc(a: var TMemRegion, p: pointer) =
|
||||
var s = c.size
|
||||
sysAssert(((cast[ByteAddress](p) and PageMask) - smallChunkOverhead()) %%
|
||||
s == 0, "rawDealloc 3")
|
||||
var f = cast[ptr TFreeCell](p)
|
||||
var f = cast[ptr FreeCell](p)
|
||||
#echo("setting to nil: ", $cast[TAddress](addr(f.zeroField)))
|
||||
sysAssert(f.zeroField != 0, "rawDealloc 1")
|
||||
f.zeroField = 0
|
||||
@@ -623,8 +627,8 @@ proc rawDealloc(a: var TMemRegion, p: pointer) =
|
||||
c.freeList = f
|
||||
when overwriteFree:
|
||||
# set to 0xff to check for usage after free bugs:
|
||||
c_memset(cast[pointer](cast[int](p) +% sizeof(TFreeCell)), -1'i32,
|
||||
s -% sizeof(TFreeCell))
|
||||
c_memset(cast[pointer](cast[int](p) +% sizeof(FreeCell)), -1'i32,
|
||||
s -% sizeof(FreeCell))
|
||||
# check if it is not in the freeSmallChunks[s] list:
|
||||
if c.free < s:
|
||||
# add it to the freeSmallChunks[s] array:
|
||||
@@ -649,7 +653,7 @@ proc rawDealloc(a: var TMemRegion, p: pointer) =
|
||||
sysAssert(allocInv(a), "rawDealloc: end")
|
||||
when logAlloc: cprintf("rawDealloc: %p\n", p)
|
||||
|
||||
proc isAllocatedPtr(a: TMemRegion, p: pointer): bool =
|
||||
proc isAllocatedPtr(a: MemRegion, p: pointer): bool =
|
||||
if isAccessible(a, p):
|
||||
var c = pageAddr(p)
|
||||
if not chunkUnused(c):
|
||||
@@ -658,16 +662,16 @@ proc isAllocatedPtr(a: TMemRegion, p: pointer): bool =
|
||||
var offset = (cast[ByteAddress](p) and (PageSize-1)) -%
|
||||
smallChunkOverhead()
|
||||
result = (c.acc >% offset) and (offset %% c.size == 0) and
|
||||
(cast[ptr TFreeCell](p).zeroField >% 1)
|
||||
(cast[ptr FreeCell](p).zeroField >% 1)
|
||||
else:
|
||||
var c = cast[PBigChunk](c)
|
||||
result = p == addr(c.data) and cast[ptr TFreeCell](p).zeroField >% 1
|
||||
result = p == addr(c.data) and cast[ptr FreeCell](p).zeroField >% 1
|
||||
|
||||
proc prepareForInteriorPointerChecking(a: var TMemRegion) {.inline.} =
|
||||
proc prepareForInteriorPointerChecking(a: var MemRegion) {.inline.} =
|
||||
a.minLargeObj = lowGauge(a.root)
|
||||
a.maxLargeObj = highGauge(a.root)
|
||||
|
||||
proc interiorAllocatedPtr(a: TMemRegion, p: pointer): pointer =
|
||||
proc interiorAllocatedPtr(a: MemRegion, p: pointer): pointer =
|
||||
if isAccessible(a, p):
|
||||
var c = pageAddr(p)
|
||||
if not chunkUnused(c):
|
||||
@@ -678,7 +682,7 @@ proc interiorAllocatedPtr(a: TMemRegion, p: pointer): pointer =
|
||||
if c.acc >% offset:
|
||||
sysAssert(cast[ByteAddress](addr(c.data)) +% offset ==
|
||||
cast[ByteAddress](p), "offset is not what you think it is")
|
||||
var d = cast[ptr TFreeCell](cast[ByteAddress](addr(c.data)) +%
|
||||
var d = cast[ptr FreeCell](cast[ByteAddress](addr(c.data)) +%
|
||||
offset -% (offset %% c.size))
|
||||
if d.zeroField >% 1:
|
||||
result = d
|
||||
@@ -686,7 +690,7 @@ proc interiorAllocatedPtr(a: TMemRegion, p: pointer): pointer =
|
||||
else:
|
||||
var c = cast[PBigChunk](c)
|
||||
var d = addr(c.data)
|
||||
if p >= d and cast[ptr TFreeCell](d).zeroField >% 1:
|
||||
if p >= d and cast[ptr FreeCell](d).zeroField >% 1:
|
||||
result = d
|
||||
sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
|
||||
else:
|
||||
@@ -699,38 +703,38 @@ proc interiorAllocatedPtr(a: TMemRegion, p: pointer): pointer =
|
||||
var k = cast[pointer](avlNode.key)
|
||||
var c = cast[PBigChunk](pageAddr(k))
|
||||
sysAssert(addr(c.data) == k, " k is not the same as addr(c.data)!")
|
||||
if cast[ptr TFreeCell](k).zeroField >% 1:
|
||||
if cast[ptr FreeCell](k).zeroField >% 1:
|
||||
result = k
|
||||
sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
|
||||
|
||||
proc ptrSize(p: pointer): int =
|
||||
var x = cast[pointer](cast[ByteAddress](p) -% sizeof(TFreeCell))
|
||||
var x = cast[pointer](cast[ByteAddress](p) -% sizeof(FreeCell))
|
||||
var c = pageAddr(p)
|
||||
sysAssert(not chunkUnused(c), "ptrSize")
|
||||
result = c.size -% sizeof(TFreeCell)
|
||||
result = c.size -% sizeof(FreeCell)
|
||||
if not isSmallChunk(c):
|
||||
dec result, bigChunkOverhead()
|
||||
|
||||
proc alloc(allocator: var TMemRegion, size: Natural): pointer =
|
||||
result = rawAlloc(allocator, size+sizeof(TFreeCell))
|
||||
cast[ptr TFreeCell](result).zeroField = 1 # mark it as used
|
||||
proc alloc(allocator: var MemRegion, size: Natural): pointer =
|
||||
result = rawAlloc(allocator, size+sizeof(FreeCell))
|
||||
cast[ptr FreeCell](result).zeroField = 1 # mark it as used
|
||||
sysAssert(not isAllocatedPtr(allocator, result), "alloc")
|
||||
result = cast[pointer](cast[ByteAddress](result) +% sizeof(TFreeCell))
|
||||
result = cast[pointer](cast[ByteAddress](result) +% sizeof(FreeCell))
|
||||
|
||||
proc alloc0(allocator: var TMemRegion, size: Natural): pointer =
|
||||
proc alloc0(allocator: var MemRegion, size: Natural): pointer =
|
||||
result = alloc(allocator, size)
|
||||
zeroMem(result, size)
|
||||
|
||||
proc dealloc(allocator: var TMemRegion, p: pointer) =
|
||||
proc dealloc(allocator: var MemRegion, p: pointer) =
|
||||
sysAssert(p != nil, "dealloc 0")
|
||||
var x = cast[pointer](cast[ByteAddress](p) -% sizeof(TFreeCell))
|
||||
var x = cast[pointer](cast[ByteAddress](p) -% sizeof(FreeCell))
|
||||
sysAssert(x != nil, "dealloc 1")
|
||||
sysAssert(isAccessible(allocator, x), "is not accessible")
|
||||
sysAssert(cast[ptr TFreeCell](x).zeroField == 1, "dealloc 2")
|
||||
sysAssert(cast[ptr FreeCell](x).zeroField == 1, "dealloc 2")
|
||||
rawDealloc(allocator, x)
|
||||
sysAssert(not isAllocatedPtr(allocator, x), "dealloc 3")
|
||||
|
||||
proc realloc(allocator: var TMemRegion, p: pointer, newsize: Natural): pointer =
|
||||
proc realloc(allocator: var MemRegion, p: pointer, newsize: Natural): pointer =
|
||||
if newsize > 0:
|
||||
result = alloc0(allocator, newsize)
|
||||
if p != nil:
|
||||
@@ -739,7 +743,7 @@ proc realloc(allocator: var TMemRegion, p: pointer, newsize: Natural): pointer =
|
||||
elif p != nil:
|
||||
dealloc(allocator, p)
|
||||
|
||||
proc deallocOsPages(a: var TMemRegion) =
|
||||
proc deallocOsPages(a: var MemRegion) =
|
||||
# we free every 'ordinarily' allocated page by iterating over the page bits:
|
||||
for p in elements(a.chunkStarts):
|
||||
var page = cast[PChunk](p shl PageShift)
|
||||
@@ -756,9 +760,9 @@ proc deallocOsPages(a: var TMemRegion) =
|
||||
# And then we free the pages that are in use for the page bits:
|
||||
llDeallocAll(a)
|
||||
|
||||
proc getFreeMem(a: TMemRegion): int {.inline.} = result = a.freeMem
|
||||
proc getTotalMem(a: TMemRegion): int {.inline.} = result = a.currMem
|
||||
proc getOccupiedMem(a: TMemRegion): int {.inline.} =
|
||||
proc getFreeMem(a: MemRegion): int {.inline.} = result = a.freeMem
|
||||
proc getTotalMem(a: MemRegion): int {.inline.} = result = a.currMem
|
||||
proc getOccupiedMem(a: MemRegion): int {.inline.} =
|
||||
result = a.currMem - a.freeMem
|
||||
|
||||
# ---------------------- thread memory region -------------------------------
|
||||
@@ -769,7 +773,7 @@ template instantiateForRegion(allocator: expr) =
|
||||
result = interiorAllocatedPtr(allocator, p)
|
||||
|
||||
proc isAllocatedPtr*(p: pointer): bool =
|
||||
let p = cast[pointer](cast[ByteAddress](p)-%ByteAddress(sizeof(TCell)))
|
||||
let p = cast[pointer](cast[ByteAddress](p)-%ByteAddress(sizeof(Cell)))
|
||||
result = isAllocatedPtr(allocator, p)
|
||||
|
||||
proc deallocOsPages = deallocOsPages(allocator)
|
||||
@@ -803,8 +807,8 @@ template instantiateForRegion(allocator: expr) =
|
||||
|
||||
# -------------------- shared heap region ----------------------------------
|
||||
when hasThreadSupport:
|
||||
var sharedHeap: TMemRegion
|
||||
var heapLock: TSysLock
|
||||
var sharedHeap: MemRegion
|
||||
var heapLock: SysLock
|
||||
initSysLock(heapLock)
|
||||
|
||||
proc allocShared(size: Natural): pointer =
|
||||
|
||||
@@ -17,17 +17,114 @@ proc raiseOverflow {.compilerproc, noinline.} =
|
||||
proc raiseDivByZero {.compilerproc, noinline.} =
|
||||
sysFatal(DivByZeroError, "division by zero")
|
||||
|
||||
proc addInt64(a, b: int64): int64 {.compilerProc, inline.} =
|
||||
result = a +% b
|
||||
if (result xor a) >= int64(0) or (result xor b) >= int64(0):
|
||||
return result
|
||||
raiseOverflow()
|
||||
when defined(builtinOverflow):
|
||||
# Builtin compiler functions for improved performance
|
||||
when sizeof(clong) == 8:
|
||||
proc addInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
|
||||
importc: "__builtin_saddl_overflow", nodecl, nosideeffect.}
|
||||
|
||||
proc subInt64(a, b: int64): int64 {.compilerProc, inline.} =
|
||||
result = a -% b
|
||||
if (result xor a) >= int64(0) or (result xor not b) >= int64(0):
|
||||
return result
|
||||
raiseOverflow()
|
||||
proc subInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
|
||||
importc: "__builtin_ssubl_overflow", nodecl, nosideeffect.}
|
||||
|
||||
proc mulInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
|
||||
importc: "__builtin_smull_overflow", nodecl, nosideeffect.}
|
||||
|
||||
elif sizeof(clonglong) == 8:
|
||||
proc addInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
|
||||
importc: "__builtin_saddll_overflow", nodecl, nosideeffect.}
|
||||
|
||||
proc subInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
|
||||
importc: "__builtin_ssubll_overflow", nodecl, nosideeffect.}
|
||||
|
||||
proc mulInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
|
||||
importc: "__builtin_smulll_overflow", nodecl, nosideeffect.}
|
||||
|
||||
when sizeof(int) == 8:
|
||||
proc addIntOverflow(a, b: int, c: var int): bool {.inline.} =
|
||||
addInt64Overflow(a, b, c)
|
||||
|
||||
proc subIntOverflow(a, b: int, c: var int): bool {.inline.} =
|
||||
subInt64Overflow(a, b, c)
|
||||
|
||||
proc mulIntOverflow(a, b: int, c: var int): bool {.inline.} =
|
||||
mulInt64Overflow(a, b, c)
|
||||
|
||||
elif sizeof(int) == 4 and sizeof(cint) == 4:
|
||||
proc addIntOverflow(a, b: int, c: var int): bool {.
|
||||
importc: "__builtin_sadd_overflow", nodecl, nosideeffect.}
|
||||
|
||||
proc subIntOverflow(a, b: int, c: var int): bool {.
|
||||
importc: "__builtin_ssub_overflow", nodecl, nosideeffect.}
|
||||
|
||||
proc mulIntOverflow(a, b: int, c: var int): bool {.
|
||||
importc: "__builtin_smul_overflow", nodecl, nosideeffect.}
|
||||
|
||||
proc addInt64(a, b: int64): int64 {.compilerProc, inline.} =
|
||||
if addInt64Overflow(a, b, result):
|
||||
raiseOverflow()
|
||||
|
||||
proc subInt64(a, b: int64): int64 {.compilerProc, inline.} =
|
||||
if subInt64Overflow(a, b, result):
|
||||
raiseOverflow()
|
||||
|
||||
proc mulInt64(a, b: int64): int64 {.compilerproc, inline.} =
|
||||
if mulInt64Overflow(a, b, result):
|
||||
raiseOverflow()
|
||||
else:
|
||||
proc addInt64(a, b: int64): int64 {.compilerProc, inline.} =
|
||||
result = a +% b
|
||||
if (result xor a) >= int64(0) or (result xor b) >= int64(0):
|
||||
return result
|
||||
raiseOverflow()
|
||||
|
||||
proc subInt64(a, b: int64): int64 {.compilerProc, inline.} =
|
||||
result = a -% b
|
||||
if (result xor a) >= int64(0) or (result xor not b) >= int64(0):
|
||||
return result
|
||||
raiseOverflow()
|
||||
|
||||
#
|
||||
# This code has been inspired by Python's source code.
|
||||
# The native int product x*y is either exactly right or *way* off, being
|
||||
# just the last n bits of the true product, where n is the number of bits
|
||||
# in an int (the delivered product is the true product plus i*2**n for
|
||||
# some integer i).
|
||||
#
|
||||
# The native float64 product x*y is subject to three
|
||||
# rounding errors: on a sizeof(int)==8 box, each cast to double can lose
|
||||
# info, and even on a sizeof(int)==4 box, the multiplication can lose info.
|
||||
# But, unlike the native int product, it's not in *range* trouble: even
|
||||
# if sizeof(int)==32 (256-bit ints), the product easily fits in the
|
||||
# dynamic range of a float64. So the leading 50 (or so) bits of the float64
|
||||
# product are correct.
|
||||
#
|
||||
# We check these two ways against each other, and declare victory if they're
|
||||
# approximately the same. Else, because the native int product is the only
|
||||
# one that can lose catastrophic amounts of information, it's the native int
|
||||
# product that must have overflowed.
|
||||
#
|
||||
proc mulInt64(a, b: int64): int64 {.compilerproc.} =
|
||||
var
|
||||
resAsFloat, floatProd: float64
|
||||
result = a *% b
|
||||
floatProd = toBiggestFloat(a) # conversion
|
||||
floatProd = floatProd * toBiggestFloat(b)
|
||||
resAsFloat = toBiggestFloat(result)
|
||||
|
||||
# Fast path for normal case: small multiplicands, and no info
|
||||
# is lost in either method.
|
||||
if resAsFloat == floatProd: return result
|
||||
|
||||
# Somebody somewhere lost info. Close enough, or way off? Note
|
||||
# that a != 0 and b != 0 (else resAsFloat == floatProd == 0).
|
||||
# The difference either is or isn't significant compared to the
|
||||
# true value (of which floatProd is a good approximation).
|
||||
|
||||
# abs(diff)/abs(prod) <= 1/32 iff
|
||||
# 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
|
||||
if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd):
|
||||
return result
|
||||
raiseOverflow()
|
||||
|
||||
proc negInt64(a: int64): int64 {.compilerProc, inline.} =
|
||||
if a != low(int64): return -a
|
||||
@@ -51,50 +148,6 @@ proc modInt64(a, b: int64): int64 {.compilerProc, inline.} =
|
||||
raiseDivByZero()
|
||||
return a mod b
|
||||
|
||||
#
|
||||
# This code has been inspired by Python's source code.
|
||||
# The native int product x*y is either exactly right or *way* off, being
|
||||
# just the last n bits of the true product, where n is the number of bits
|
||||
# in an int (the delivered product is the true product plus i*2**n for
|
||||
# some integer i).
|
||||
#
|
||||
# The native float64 product x*y is subject to three
|
||||
# rounding errors: on a sizeof(int)==8 box, each cast to double can lose
|
||||
# info, and even on a sizeof(int)==4 box, the multiplication can lose info.
|
||||
# But, unlike the native int product, it's not in *range* trouble: even
|
||||
# if sizeof(int)==32 (256-bit ints), the product easily fits in the
|
||||
# dynamic range of a float64. So the leading 50 (or so) bits of the float64
|
||||
# product are correct.
|
||||
#
|
||||
# We check these two ways against each other, and declare victory if they're
|
||||
# approximately the same. Else, because the native int product is the only
|
||||
# one that can lose catastrophic amounts of information, it's the native int
|
||||
# product that must have overflowed.
|
||||
#
|
||||
proc mulInt64(a, b: int64): int64 {.compilerproc.} =
|
||||
var
|
||||
resAsFloat, floatProd: float64
|
||||
result = a *% b
|
||||
floatProd = toBiggestFloat(a) # conversion
|
||||
floatProd = floatProd * toBiggestFloat(b)
|
||||
resAsFloat = toBiggestFloat(result)
|
||||
|
||||
# Fast path for normal case: small multiplicands, and no info
|
||||
# is lost in either method.
|
||||
if resAsFloat == floatProd: return result
|
||||
|
||||
# Somebody somewhere lost info. Close enough, or way off? Note
|
||||
# that a != 0 and b != 0 (else resAsFloat == floatProd == 0).
|
||||
# The difference either is or isn't significant compared to the
|
||||
# true value (of which floatProd is a good approximation).
|
||||
|
||||
# abs(diff)/abs(prod) <= 1/32 iff
|
||||
# 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
|
||||
if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd):
|
||||
return result
|
||||
raiseOverflow()
|
||||
|
||||
|
||||
proc absInt(a: int): int {.compilerProc, inline.} =
|
||||
if a != low(int):
|
||||
if a >= 0: return a
|
||||
@@ -246,6 +299,21 @@ elif false: # asmVersion and (defined(gcc) or defined(llvm_gcc)):
|
||||
:"%edx"
|
||||
"""
|
||||
|
||||
when not declared(addInt) and defined(builtinOverflow):
|
||||
proc addInt(a, b: int): int {.compilerProc, inline.} =
|
||||
if addIntOverflow(a, b, result):
|
||||
raiseOverflow()
|
||||
|
||||
when not declared(subInt) and defined(builtinOverflow):
|
||||
proc subInt(a, b: int): int {.compilerProc, inline.} =
|
||||
if subIntOverflow(a, b, result):
|
||||
raiseOverflow()
|
||||
|
||||
when not declared(mulInt) and defined(builtinOverflow):
|
||||
proc mulInt(a, b: int): int {.compilerProc, inline.} =
|
||||
if mulIntOverflow(a, b, result):
|
||||
raiseOverflow()
|
||||
|
||||
# Platform independent versions of the above (slower!)
|
||||
when not declared(addInt):
|
||||
proc addInt(a, b: int): int {.compilerProc, inline.} =
|
||||
|
||||
@@ -37,39 +37,40 @@ when someGcc and hasThreadSupport:
|
||||
## and release stores in all threads.
|
||||
|
||||
type
|
||||
TAtomType* = SomeNumber|pointer|ptr|char|bool
|
||||
AtomType* = SomeNumber|pointer|ptr|char|bool
|
||||
## Type Class representing valid types for use with atomic procs
|
||||
{.deprecated: [TAtomType: AtomType].}
|
||||
|
||||
proc atomicLoadN*[T: TAtomType](p: ptr T, mem: AtomMemModel): T {.
|
||||
proc atomicLoadN*[T: AtomType](p: ptr T, mem: AtomMemModel): T {.
|
||||
importc: "__atomic_load_n", nodecl.}
|
||||
## This proc implements an atomic load operation. It returns the contents at p.
|
||||
## ATOMIC_RELAXED, ATOMIC_SEQ_CST, ATOMIC_ACQUIRE, ATOMIC_CONSUME.
|
||||
|
||||
proc atomicLoad*[T: TAtomType](p, ret: ptr T, mem: AtomMemModel) {.
|
||||
proc atomicLoad*[T: AtomType](p, ret: ptr T, mem: AtomMemModel) {.
|
||||
importc: "__atomic_load", nodecl.}
|
||||
## This is the generic version of an atomic load. It returns the contents at p in ret.
|
||||
|
||||
proc atomicStoreN*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel) {.
|
||||
proc atomicStoreN*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel) {.
|
||||
importc: "__atomic_store_n", nodecl.}
|
||||
## This proc implements an atomic store operation. It writes val at p.
|
||||
## ATOMIC_RELAXED, ATOMIC_SEQ_CST, and ATOMIC_RELEASE.
|
||||
|
||||
proc atomicStore*[T: TAtomType](p, val: ptr T, mem: AtomMemModel) {.
|
||||
proc atomicStore*[T: AtomType](p, val: ptr T, mem: AtomMemModel) {.
|
||||
importc: "__atomic_store", nodecl.}
|
||||
## This is the generic version of an atomic store. It stores the value of val at p
|
||||
|
||||
proc atomicExchangeN*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
|
||||
proc atomicExchangeN*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
|
||||
importc: "__atomic_exchange_n", nodecl.}
|
||||
## This proc implements an atomic exchange operation. It writes val at p,
|
||||
## and returns the previous contents at p.
|
||||
## ATOMIC_RELAXED, ATOMIC_SEQ_CST, ATOMIC_ACQUIRE, ATOMIC_RELEASE, ATOMIC_ACQ_REL
|
||||
|
||||
proc atomicExchange*[T: TAtomType](p, val, ret: ptr T, mem: AtomMemModel) {.
|
||||
proc atomicExchange*[T: AtomType](p, val, ret: ptr T, mem: AtomMemModel) {.
|
||||
importc: "__atomic_exchange", nodecl.}
|
||||
## This is the generic version of an atomic exchange. It stores the contents at val at p.
|
||||
## The original value at p is copied into ret.
|
||||
|
||||
proc atomicCompareExchangeN*[T: TAtomType](p, expected: ptr T, desired: T,
|
||||
proc atomicCompareExchangeN*[T: AtomType](p, expected: ptr T, desired: T,
|
||||
weak: bool, success_memmodel: AtomMemModel, failure_memmodel: AtomMemModel): bool {.
|
||||
importc: "__atomic_compare_exchange_n ", nodecl.}
|
||||
## This proc implements an atomic compare and exchange operation. This compares the
|
||||
@@ -85,7 +86,7 @@ when someGcc and hasThreadSupport:
|
||||
## cannot be __ATOMIC_RELEASE nor __ATOMIC_ACQ_REL. It also cannot be a stronger model
|
||||
## than that specified by success_memmodel.
|
||||
|
||||
proc atomicCompareExchange*[T: TAtomType](p, expected, desired: ptr T,
|
||||
proc atomicCompareExchange*[T: AtomType](p, expected, desired: ptr T,
|
||||
weak: bool, success_memmodel: AtomMemModel, failure_memmodel: AtomMemModel): bool {.
|
||||
importc: "__atomic_compare_exchange", nodecl.}
|
||||
## This proc implements the generic version of atomic_compare_exchange.
|
||||
@@ -93,31 +94,31 @@ when someGcc and hasThreadSupport:
|
||||
## value is also a pointer.
|
||||
|
||||
## Perform the operation return the new value, all memory models are valid
|
||||
proc atomicAddFetch*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
|
||||
proc atomicAddFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
|
||||
importc: "__atomic_add_fetch", nodecl.}
|
||||
proc atomicSubFetch*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
|
||||
proc atomicSubFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
|
||||
importc: "__atomic_sub_fetch", nodecl.}
|
||||
proc atomicOrFetch*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
|
||||
proc atomicOrFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
|
||||
importc: "__atomic_or_fetch ", nodecl.}
|
||||
proc atomicAndFetch*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
|
||||
proc atomicAndFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
|
||||
importc: "__atomic_and_fetch", nodecl.}
|
||||
proc atomicXorFetch*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
|
||||
proc atomicXorFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
|
||||
importc: "__atomic_xor_fetch", nodecl.}
|
||||
proc atomicNandFetch*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
|
||||
proc atomicNandFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
|
||||
importc: "__atomic_nand_fetch ", nodecl.}
|
||||
|
||||
## Perform the operation return the old value, all memory models are valid
|
||||
proc atomicFetchAdd*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
|
||||
proc atomicFetchAdd*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
|
||||
importc: "__atomic_fetch_add", nodecl.}
|
||||
proc atomicFetchSub*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
|
||||
proc atomicFetchSub*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
|
||||
importc: "__atomic_fetch_sub", nodecl.}
|
||||
proc atomicFetchOr*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
|
||||
proc atomicFetchOr*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
|
||||
importc: "__atomic_fetch_or", nodecl.}
|
||||
proc atomicFetchAnd*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
|
||||
proc atomicFetchAnd*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
|
||||
importc: "__atomic_fetch_and", nodecl.}
|
||||
proc atomicFetchXor*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
|
||||
proc atomicFetchXor*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
|
||||
importc: "__atomic_fetch_xor", nodecl.}
|
||||
proc atomicFetchNand*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
|
||||
proc atomicFetchNand*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
|
||||
importc: "__atomic_fetch_nand", nodecl.}
|
||||
|
||||
proc atomicTestAndSet*(p: pointer, mem: AtomMemModel): bool {.
|
||||
|
||||
@@ -51,7 +51,7 @@ proc split(t: var PAvlNode) =
|
||||
t.link[0] = temp
|
||||
inc t.level
|
||||
|
||||
proc add(a: var TMemRegion, t: var PAvlNode, key, upperBound: int) {.benign.} =
|
||||
proc add(a: var MemRegion, t: var PAvlNode, key, upperBound: int) {.benign.} =
|
||||
if t == bottom:
|
||||
t = allocAvlNode(a, key, upperBound)
|
||||
else:
|
||||
@@ -64,7 +64,7 @@ proc add(a: var TMemRegion, t: var PAvlNode, key, upperBound: int) {.benign.} =
|
||||
skew(t)
|
||||
split(t)
|
||||
|
||||
proc del(a: var TMemRegion, t: var PAvlNode, x: int) {.benign.} =
|
||||
proc del(a: var MemRegion, t: var PAvlNode, x: int) {.benign.} =
|
||||
if t == bottom: return
|
||||
a.last = t
|
||||
if x <% t.key:
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
# Efficient set of pointers for the GC (and repr)
|
||||
|
||||
type
|
||||
TRefCount = int
|
||||
RefCount = int
|
||||
|
||||
TCell {.pure.} = object
|
||||
refcount: TRefCount # the refcount and some flags
|
||||
Cell {.pure.} = object
|
||||
refcount: RefCount # the refcount and some flags
|
||||
typ: PNimType
|
||||
when trackAllocationSource:
|
||||
filename: cstring
|
||||
@@ -21,34 +21,35 @@ type
|
||||
when useCellIds:
|
||||
id: int
|
||||
|
||||
PCell = ptr TCell
|
||||
PCell = ptr Cell
|
||||
|
||||
PPageDesc = ptr TPageDesc
|
||||
TBitIndex = range[0..UnitsPerPage-1]
|
||||
TPageDesc {.final, pure.} = object
|
||||
PPageDesc = ptr PageDesc
|
||||
BitIndex = range[0..UnitsPerPage-1]
|
||||
PageDesc {.final, pure.} = object
|
||||
next: PPageDesc # all nodes are connected with this pointer
|
||||
key: ByteAddress # start address at bit 0
|
||||
bits: array[TBitIndex, int] # a bit vector
|
||||
bits: array[BitIndex, int] # a bit vector
|
||||
|
||||
PPageDescArray = ptr array[0..1000_000, PPageDesc]
|
||||
TCellSet {.final, pure.} = object
|
||||
CellSet {.final, pure.} = object
|
||||
counter, max: int
|
||||
head: PPageDesc
|
||||
data: PPageDescArray
|
||||
|
||||
PCellArray = ptr array[0..100_000_000, PCell]
|
||||
TCellSeq {.final, pure.} = object
|
||||
CellSeq {.final, pure.} = object
|
||||
len, cap: int
|
||||
d: PCellArray
|
||||
|
||||
{.deprecated: [TCell: Cell, TBitIndex: BitIndex, TPageDesc: PageDesc,
|
||||
TRefCount: RefCount, TCellSet: CellSet, TCellSeq: CellSeq].}
|
||||
# ------------------- cell seq handling ---------------------------------------
|
||||
|
||||
proc contains(s: TCellSeq, c: PCell): bool {.inline.} =
|
||||
proc contains(s: CellSeq, c: PCell): bool {.inline.} =
|
||||
for i in 0 .. s.len-1:
|
||||
if s.d[i] == c: return true
|
||||
return false
|
||||
|
||||
proc add(s: var TCellSeq, c: PCell) {.inline.} =
|
||||
proc add(s: var CellSeq, c: PCell) {.inline.} =
|
||||
if s.len >= s.cap:
|
||||
s.cap = s.cap * 3 div 2
|
||||
var d = cast[PCellArray](alloc(s.cap * sizeof(PCell)))
|
||||
@@ -59,12 +60,12 @@ proc add(s: var TCellSeq, c: PCell) {.inline.} =
|
||||
s.d[s.len] = c
|
||||
inc(s.len)
|
||||
|
||||
proc init(s: var TCellSeq, cap: int = 1024) =
|
||||
proc init(s: var CellSeq, cap: int = 1024) =
|
||||
s.len = 0
|
||||
s.cap = cap
|
||||
s.d = cast[PCellArray](alloc0(cap * sizeof(PCell)))
|
||||
|
||||
proc deinit(s: var TCellSeq) =
|
||||
proc deinit(s: var CellSeq) =
|
||||
dealloc(s.d)
|
||||
s.d = nil
|
||||
s.len = 0
|
||||
@@ -75,13 +76,13 @@ proc deinit(s: var TCellSeq) =
|
||||
const
|
||||
InitCellSetSize = 1024 # must be a power of two!
|
||||
|
||||
proc init(s: var TCellSet) =
|
||||
proc init(s: var CellSet) =
|
||||
s.data = cast[PPageDescArray](alloc0(InitCellSetSize * sizeof(PPageDesc)))
|
||||
s.max = InitCellSetSize-1
|
||||
s.counter = 0
|
||||
s.head = nil
|
||||
|
||||
proc deinit(s: var TCellSet) =
|
||||
proc deinit(s: var CellSet) =
|
||||
var it = s.head
|
||||
while it != nil:
|
||||
var n = it.next
|
||||
@@ -98,14 +99,14 @@ proc nextTry(h, maxHash: int): int {.inline.} =
|
||||
# generates each int in range(maxHash) exactly once (see any text on
|
||||
# random-number generation for proof).
|
||||
|
||||
proc cellSetGet(t: TCellSet, key: ByteAddress): PPageDesc =
|
||||
proc cellSetGet(t: CellSet, key: ByteAddress): PPageDesc =
|
||||
var h = cast[int](key) and t.max
|
||||
while t.data[h] != nil:
|
||||
if t.data[h].key == key: return t.data[h]
|
||||
h = nextTry(h, t.max)
|
||||
return nil
|
||||
|
||||
proc cellSetRawInsert(t: TCellSet, data: PPageDescArray, desc: PPageDesc) =
|
||||
proc cellSetRawInsert(t: CellSet, data: PPageDescArray, desc: PPageDesc) =
|
||||
var h = cast[int](desc.key) and t.max
|
||||
while data[h] != nil:
|
||||
sysAssert(data[h] != desc, "CellSetRawInsert 1")
|
||||
@@ -113,7 +114,7 @@ proc cellSetRawInsert(t: TCellSet, data: PPageDescArray, desc: PPageDesc) =
|
||||
sysAssert(data[h] == nil, "CellSetRawInsert 2")
|
||||
data[h] = desc
|
||||
|
||||
proc cellSetEnlarge(t: var TCellSet) =
|
||||
proc cellSetEnlarge(t: var CellSet) =
|
||||
var oldMax = t.max
|
||||
t.max = ((t.max+1)*2)-1
|
||||
var n = cast[PPageDescArray](alloc0((t.max + 1) * sizeof(PPageDesc)))
|
||||
@@ -123,7 +124,7 @@ proc cellSetEnlarge(t: var TCellSet) =
|
||||
dealloc(t.data)
|
||||
t.data = n
|
||||
|
||||
proc cellSetPut(t: var TCellSet, key: ByteAddress): PPageDesc =
|
||||
proc cellSetPut(t: var CellSet, key: ByteAddress): PPageDesc =
|
||||
var h = cast[int](key) and t.max
|
||||
while true:
|
||||
var x = t.data[h]
|
||||
@@ -138,7 +139,7 @@ proc cellSetPut(t: var TCellSet, key: ByteAddress): PPageDesc =
|
||||
while t.data[h] != nil: h = nextTry(h, t.max)
|
||||
sysAssert(t.data[h] == nil, "CellSetPut")
|
||||
# the new page descriptor goes into result
|
||||
result = cast[PPageDesc](alloc0(sizeof(TPageDesc)))
|
||||
result = cast[PPageDesc](alloc0(sizeof(PageDesc)))
|
||||
result.next = t.head
|
||||
result.key = key
|
||||
t.head = result
|
||||
@@ -146,7 +147,7 @@ proc cellSetPut(t: var TCellSet, key: ByteAddress): PPageDesc =
|
||||
|
||||
# ---------- slightly higher level procs --------------------------------------
|
||||
|
||||
proc contains(s: TCellSet, cell: PCell): bool =
|
||||
proc contains(s: CellSet, cell: PCell): bool =
|
||||
var u = cast[ByteAddress](cell)
|
||||
var t = cellSetGet(s, u shr PageShift)
|
||||
if t != nil:
|
||||
@@ -155,13 +156,13 @@ proc contains(s: TCellSet, cell: PCell): bool =
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc incl(s: var TCellSet, cell: PCell) {.noinline.} =
|
||||
proc incl(s: var CellSet, cell: PCell) {.noinline.} =
|
||||
var u = cast[ByteAddress](cell)
|
||||
var t = cellSetPut(s, u shr PageShift)
|
||||
u = (u %% PageSize) /% MemAlign
|
||||
t.bits[u shr IntShift] = t.bits[u shr IntShift] or (1 shl (u and IntMask))
|
||||
|
||||
proc excl(s: var TCellSet, cell: PCell) =
|
||||
proc excl(s: var CellSet, cell: PCell) =
|
||||
var u = cast[ByteAddress](cell)
|
||||
var t = cellSetGet(s, u shr PageShift)
|
||||
if t != nil:
|
||||
@@ -169,7 +170,7 @@ proc excl(s: var TCellSet, cell: PCell) =
|
||||
t.bits[u shr IntShift] = (t.bits[u shr IntShift] and
|
||||
not (1 shl (u and IntMask)))
|
||||
|
||||
proc containsOrIncl(s: var TCellSet, cell: PCell): bool =
|
||||
proc containsOrIncl(s: var CellSet, cell: PCell): bool =
|
||||
var u = cast[ByteAddress](cell)
|
||||
var t = cellSetGet(s, u shr PageShift)
|
||||
if t != nil:
|
||||
@@ -182,7 +183,7 @@ proc containsOrIncl(s: var TCellSet, cell: PCell): bool =
|
||||
incl(s, cell)
|
||||
result = false
|
||||
|
||||
iterator elements(t: TCellSet): PCell {.inline.} =
|
||||
iterator elements(t: CellSet): PCell {.inline.} =
|
||||
# while traversing it is forbidden to add pointers to the tree!
|
||||
var r = t.head
|
||||
while r != nil:
|
||||
@@ -200,7 +201,7 @@ iterator elements(t: TCellSet): PCell {.inline.} =
|
||||
inc(i)
|
||||
r = r.next
|
||||
|
||||
iterator elementsExcept(t, s: TCellSet): PCell {.inline.} =
|
||||
iterator elementsExcept(t, s: CellSet): PCell {.inline.} =
|
||||
var r = t.head
|
||||
while r != nil:
|
||||
let ss = cellSetGet(s, r.key)
|
||||
|
||||
@@ -12,12 +12,13 @@
|
||||
proc addChar(s: NimString, c: char): NimString {.compilerProc, benign.}
|
||||
|
||||
type
|
||||
TLibHandle = pointer # private type
|
||||
TProcAddr = pointer # library loading and loading of procs:
|
||||
LibHandle = pointer # private type
|
||||
ProcAddr = pointer # library loading and loading of procs:
|
||||
{.deprecated: [TLibHandle: LibHandle, TProcAddr: ProcAddr].}
|
||||
|
||||
proc nimLoadLibrary(path: string): TLibHandle {.compilerproc.}
|
||||
proc nimUnloadLibrary(lib: TLibHandle) {.compilerproc.}
|
||||
proc nimGetProcAddr(lib: TLibHandle, name: cstring): TProcAddr {.compilerproc.}
|
||||
proc nimLoadLibrary(path: string): LibHandle {.compilerproc.}
|
||||
proc nimUnloadLibrary(lib: LibHandle) {.compilerproc.}
|
||||
proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr {.compilerproc.}
|
||||
|
||||
proc nimLoadLibraryError(path: string) {.compilerproc, noinline.}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user