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:
Flaviu Tamas
2015-05-26 19:32:50 -04:00
168 changed files with 6305 additions and 6961 deletions

View File

@@ -1,6 +1,6 @@
[Package]
name = "compiler"
version = "0.10.3"
version = "0.11.3"
author = "Andreas Rumpf"
description = "Compiler package providing the compiler sources as a library."
license = "MIT"
@@ -8,4 +8,4 @@ license = "MIT"
InstallDirs = "doc, compiler"
[Deps]
Requires: "nim >= 0.10.3"
Requires: "nim >= 0.11.3"

View File

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

View File

@@ -14,7 +14,7 @@
import
ast, hashes, intsets, strutils, options, msgs, ropes, idents, rodutils
proc hashNode*(p: RootRef): THash
proc hashNode*(p: RootRef): Hash
proc treeToYaml*(n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope
# Convert a tree into its YAML representation; this is used by the
# YAML code generator and it is invaluable for debugging purposes.
@@ -49,7 +49,7 @@ proc strTableGet*(t: TStrTable, name: PIdent): PSym
type
TTabIter*{.final.} = object # consider all fields here private
h*: THash # current hash
h*: Hash # current hash
proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym
proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym
@@ -65,7 +65,7 @@ proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym
type
TIdentIter*{.final.} = object # iterator over all syms with same identifier
h*: THash # current hash
h*: Hash # current hash
name*: PIdent
@@ -94,7 +94,7 @@ proc getSymFromList*(list: PNode, ident: PIdent, start: int = 0): PSym
proc lookupInRecord*(n: PNode, field: PIdent): PSym
proc getModule*(s: PSym): PSym
proc mustRehash*(length, counter: int): bool
proc nextTry*(h, maxHash: THash): THash {.inline.}
proc nextTry*(h, maxHash: Hash): Hash {.inline.}
# ------------- table[int, int] ---------------------------------------------
const
@@ -196,7 +196,7 @@ proc getSymFromList(list: PNode, ident: PIdent, start: int = 0): PSym =
else: internalError(list.info, "getSymFromList")
result = nil
proc hashNode(p: RootRef): THash =
proc hashNode(p: RootRef): Hash =
result = hash(cast[pointer](p))
proc mustRehash(length, counter: int): bool =
@@ -466,7 +466,7 @@ proc debug(n: PNode) =
const
EmptySeq = @[]
proc nextTry(h, maxHash: THash): THash =
proc nextTry(h, maxHash: Hash): Hash =
result = ((5 * h) + 1) and maxHash
# For any initial h in range(maxHash), repeating that maxHash times
# generates each int in range(maxHash) exactly once (see any text on
@@ -474,7 +474,7 @@ proc nextTry(h, maxHash: THash): THash =
proc objectSetContains(t: TObjectSet, obj: RootRef): bool =
# returns true whether n is in t
var h: THash = hashNode(obj) and high(t.data) # start with real hash value
var h: Hash = hashNode(obj) and high(t.data) # start with real hash value
while t.data[h] != nil:
if t.data[h] == obj:
return true
@@ -482,7 +482,7 @@ proc objectSetContains(t: TObjectSet, obj: RootRef): bool =
result = false
proc objectSetRawInsert(data: var TObjectSeq, obj: RootRef) =
var h: THash = hashNode(obj) and high(data)
var h: Hash = hashNode(obj) and high(data)
while data[h] != nil:
assert(data[h] != obj)
h = nextTry(h, high(data))
@@ -503,7 +503,7 @@ proc objectSetIncl(t: var TObjectSet, obj: RootRef) =
proc objectSetContainsOrIncl(t: var TObjectSet, obj: RootRef): bool =
# returns true if obj is already in the string table:
var h: THash = hashNode(obj) and high(t.data)
var h: Hash = hashNode(obj) and high(t.data)
while true:
var it = t.data[h]
if it == nil: break
@@ -520,7 +520,7 @@ proc objectSetContainsOrIncl(t: var TObjectSet, obj: RootRef): bool =
result = false
proc tableRawGet(t: TTable, key: RootRef): int =
var h: THash = hashNode(key) and high(t.data) # start with real hash value
var h: Hash = hashNode(key) and high(t.data) # start with real hash value
while t.data[h].key != nil:
if t.data[h].key == key:
return h
@@ -529,7 +529,7 @@ proc tableRawGet(t: TTable, key: RootRef): int =
proc tableSearch(t: TTable, key, closure: RootRef,
comparator: TCmpProc): RootRef =
var h: THash = hashNode(key) and high(t.data) # start with real hash value
var h: Hash = hashNode(key) and high(t.data) # start with real hash value
while t.data[h].key != nil:
if t.data[h].key == key:
if comparator(t.data[h].val, closure):
@@ -544,7 +544,7 @@ proc tableGet(t: TTable, key: RootRef): RootRef =
else: result = nil
proc tableRawInsert(data: var TPairSeq, key, val: RootRef) =
var h: THash = hashNode(key) and high(data)
var h: Hash = hashNode(key) and high(data)
while data[h].key != nil:
assert(data[h].key != key)
h = nextTry(h, high(data))
@@ -569,7 +569,7 @@ proc tablePut(t: var TTable, key, val: RootRef) =
inc(t.counter)
proc strTableContains(t: TStrTable, n: PSym): bool =
var h: THash = n.name.h and high(t.data) # start with real hash value
var h: Hash = n.name.h and high(t.data) # start with real hash value
while t.data[h] != nil:
if (t.data[h] == n):
return true
@@ -577,7 +577,7 @@ proc strTableContains(t: TStrTable, n: PSym): bool =
result = false
proc strTableRawInsert(data: var TSymSeq, n: PSym) =
var h: THash = n.name.h and high(data)
var h: Hash = n.name.h and high(data)
if sfImmediate notin n.flags:
# fast path:
while data[h] != nil:
@@ -606,7 +606,7 @@ proc strTableRawInsert(data: var TSymSeq, n: PSym) =
proc symTabReplaceRaw(data: var TSymSeq, prevSym: PSym, newSym: PSym) =
assert prevSym.name.h == newSym.name.h
var h: THash = prevSym.name.h and high(data)
var h: Hash = prevSym.name.h and high(data)
while data[h] != nil:
if data[h] == prevSym:
data[h] = newSym
@@ -640,7 +640,7 @@ proc strTableIncl*(t: var TStrTable, n: PSym): bool {.discardable.} =
# It is essential that `n` is written nevertheless!
# This way the newest redefinition is picked by the semantic analyses!
assert n.name != nil
var h: THash = n.name.h and high(t.data)
var h: Hash = n.name.h and high(t.data)
var replaceSlot = -1
while true:
var it = t.data[h]
@@ -666,7 +666,7 @@ proc strTableIncl*(t: var TStrTable, n: PSym): bool {.discardable.} =
result = false
proc strTableGet(t: TStrTable, name: PIdent): PSym =
var h: THash = name.h and high(t.data)
var h: Hash = name.h and high(t.data)
while true:
result = t.data[h]
if result == nil: break
@@ -694,7 +694,7 @@ proc nextIdentIter(ti: var TIdentIter, tab: TStrTable): PSym =
proc nextIdentExcluding*(ti: var TIdentIter, tab: TStrTable,
excluding: IntSet): PSym =
var h: THash = ti.h and high(tab.data)
var h: Hash = ti.h and high(tab.data)
var start = h
result = tab.data[h]
while result != nil:
@@ -743,7 +743,7 @@ proc hasEmptySlot(data: TIdPairSeq): bool =
result = false
proc idTableRawGet(t: TIdTable, key: int): int =
var h: THash
var h: Hash
h = key and high(t.data) # start with real hash value
while t.data[h].key != nil:
if t.data[h].key.id == key:
@@ -772,7 +772,7 @@ iterator pairs*(t: TIdTable): tuple[key: int, value: RootRef] =
yield (t.data[i].key.id, t.data[i].val)
proc idTableRawInsert(data: var TIdPairSeq, key: PIdObj, val: RootRef) =
var h: THash
var h: Hash
h = key.id and high(data)
while data[h].key != nil:
assert(data[h].key.id != key.id)
@@ -805,7 +805,7 @@ iterator idTablePairs*(t: TIdTable): tuple[key: PIdObj, val: RootRef] =
if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val)
proc idNodeTableRawGet(t: TIdNodeTable, key: PIdObj): int =
var h: THash
var h: Hash
h = key.id and high(t.data) # start with real hash value
while t.data[h].key != nil:
if t.data[h].key.id == key.id:
@@ -824,7 +824,7 @@ proc idNodeTableGetLazy*(t: TIdNodeTable, key: PIdObj): PNode =
result = idNodeTableGet(t, key)
proc idNodeTableRawInsert(data: var TIdNodePairSeq, key: PIdObj, val: PNode) =
var h: THash
var h: Hash
h = key.id and high(data)
while data[h].key != nil:
assert(data[h].key.id != key.id)
@@ -863,7 +863,7 @@ proc initIITable(x: var TIITable) =
for i in countup(0, StartSize - 1): x.data[i].key = InvalidKey
proc iiTableRawGet(t: TIITable, key: int): int =
var h: THash
var h: Hash
h = key and high(t.data) # start with real hash value
while t.data[h].key != InvalidKey:
if t.data[h].key == key: return h
@@ -876,7 +876,7 @@ proc iiTableGet(t: TIITable, key: int): int =
else: result = InvalidKey
proc iiTableRawInsert(data: var TIIPairSeq, key, val: int) =
var h: THash
var h: Hash
h = key and high(data)
while data[h].key != InvalidKey:
assert(data[h].key != key)

View File

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

View File

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

View File

@@ -18,7 +18,7 @@ import
type
TSections = array[TSymKind, Rope]
TDocumentor = object of rstgen.TRstGenerator
TDocumentor = object of rstgen.RstGenerator
modDesc: Rope # module description
id: int # for generating IDs
toc, section: TSections
@@ -29,7 +29,7 @@ type
PDoc* = ref TDocumentor ## Alias to type less.
proc compilerMsgHandler(filename: string, line, col: int,
msgKind: rst.TMsgKind, arg: string) {.procvar.} =
msgKind: rst.MsgKind, arg: string) {.procvar.} =
# translate msg kind:
var k: msgs.TMsgKind
case msgKind
@@ -53,7 +53,7 @@ proc docgenFindFile(s: string): string {.procvar.} =
proc parseRst(text, filename: string,
line, column: int, hasToc: var bool,
rstOptions: TRstParseOptions): PRstNode =
rstOptions: RstParseOptions): PRstNode =
result = rstParse(text, filename, line, column, hasToc, rstOptions,
docgenFindFile, compilerMsgHandler)

View File

@@ -12,7 +12,7 @@
# id. This module is essential for the compiler's performance.
import
hashes, strutils
hashes, strutils, etcpriv
type
TIdObj* = object of RootObj
@@ -23,7 +23,7 @@ type
TIdent*{.acyclic.} = object of TIdObj
s*: string
next*: PIdent # for hash-table chaining
h*: THash # hash value of s
h*: Hash # hash value of s
var firstCharIsCS*: bool = true
var buckets*: array[0..4096 * 2 - 1, PIdent]
@@ -37,6 +37,8 @@ proc cmpIgnoreStyle(a, b: cstring, blen: int): int =
while j < blen:
while a[i] == '_': inc(i)
while b[j] == '_': inc(j)
while isMagicIdentSeparatorRune(a, i): inc(i, magicIdentSeparatorRuneByteWidth)
while isMagicIdentSeparatorRune(b, j): inc(j, magicIdentSeparatorRuneByteWidth)
# tolower inlined:
var aa = a[i]
var bb = b[j]
@@ -65,7 +67,7 @@ proc cmpExact(a, b: cstring, blen: int): int =
var wordCounter = 1
proc getIdent*(identifier: cstring, length: int, h: THash): PIdent =
proc getIdent*(identifier: cstring, length: int, h: Hash): PIdent =
var idx = h and high(buckets)
result = buckets[idx]
var last: PIdent = nil
@@ -99,7 +101,7 @@ proc getIdent*(identifier: string): PIdent =
result = getIdent(cstring(identifier), len(identifier),
hashIgnoreStyle(identifier))
proc getIdent*(identifier: string, h: THash): PIdent =
proc getIdent*(identifier: string, h: Hash): PIdent =
result = getIdent(cstring(identifier), len(identifier), h)
proc identEq*(id: PIdent, name: string): bool =

View File

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

View File

@@ -946,7 +946,11 @@ proc transformOuterProc(o: POuterContext, n: PNode; it: TIter): PNode =
proc liftLambdas*(fn: PSym, body: PNode): PNode =
# XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs
# the transformation even when compiling to JS ...
if body.kind == nkEmpty or gCmd == cmdCompileToJS or
# However we can do lifting for the stuff which is *only* compiletime.
let isCompileTime = sfCompileTime in fn.flags or fn.kind == skMacro
if body.kind == nkEmpty or (gCmd == cmdCompileToJS and not isCompileTime) or
fn.skipGenericOwner.kind != skModule:
# ignore forward declaration:
result = body

View File

@@ -17,7 +17,7 @@
import
hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream,
wordrecg
wordrecg, etcpriv
const
MaxLineLength* = 80 # lines longer than this lead to a warning
@@ -140,10 +140,12 @@ proc isKeyword*(kind: TTokType): bool =
proc isNimIdentifier*(s: string): bool =
if s[0] in SymStartChars:
var i = 1
while i < s.len:
var sLen = s.len
while i < sLen:
if s[i] == '_':
inc(i)
if s[i] notin SymChars: return
elif isMagicIdentSeparatorRune(cstring s, i):
inc(i, magicIdentSeparatorRuneByteWidth)
if s[i] notin SymChars: return
inc(i)
result = true
@@ -229,23 +231,6 @@ proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") =
var info = newLineInfo(L.fileIdx, L.lineNumber, pos - L.lineStart)
L.dispMessage(info, msg, arg)
proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: set[char]) =
var pos = L.bufpos # use registers for pos, buf
var buf = L.buf
while true:
if buf[pos] in chars:
add(tok.literal, buf[pos])
inc(pos)
else:
break
if buf[pos] == '_':
if buf[pos+1] notin chars:
lexMessage(L, errInvalidToken, "_")
break
add(tok.literal, '_')
inc(pos)
L.bufpos = pos
proc matchTwoChars(L: TLexer, first: char, second: set[char]): bool =
result = (L.buf[L.bufpos] == first) and (L.buf[L.bufpos + 1] in second)
@@ -268,136 +253,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

View File

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

View File

@@ -10,8 +10,10 @@
## Nimfix is a tool that helps to convert old-style Nimrod code to Nim code.
import strutils, os, parseopt
import options, commands, modules, sem, passes, passaux, pretty, msgs, nimconf,
extccomp, condsyms, lists
import compiler/options, compiler/commands, compiler/modules, compiler/sem,
compiler/passes, compiler/passaux, compiler/nimfix/pretty,
compiler/msgs, compiler/nimconf,
compiler/extccomp, compiler/condsyms, compiler/lists
const Usage = """
Nimfix - Tool to patch Nim code
@@ -24,7 +26,7 @@ Options:
--wholeProject overwrite every processed file.
--checkExtern:on|off style check also extern names
--styleCheck:on|off|auto performs style checking for identifiers
and suggests an alternative spelling;
and suggests an alternative spelling;
'auto' corrects the spelling.
--bestEffort try to fix the code even when there
are errors.
@@ -48,11 +50,11 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
var p = parseopt.initOptParser(cmd)
var argsCount = 0
gOnlyMainfile = true
while true:
while true:
parseopt.next(p)
case p.kind
of cmdEnd: break
of cmdLongoption, cmdShortOption:
of cmdEnd: break
of cmdLongoption, cmdShortOption:
case p.key.normalize
of "overwritefiles":
case p.val.normalize

View File

@@ -10,9 +10,11 @@
## This module implements the code "prettifier". This is part of the toolchain
## to convert Nim code into a consistent style.
import
strutils, os, options, ast, astalgo, msgs, ropes, idents,
intsets, strtabs, semdata, prettybase
import
strutils, os, intsets, strtabs
import compiler/options, compiler/ast, compiler/astalgo, compiler/msgs,
compiler/semdata, compiler/nimfix/prettybase, compiler/ropes, compiler/idents
type
StyleCheck* {.pure.} = enum None, Warn, Auto
@@ -92,7 +94,7 @@ proc beautifyName(s: string, k: TSymKind): string =
proc replaceInFile(info: TLineInfo; newName: string) =
loadFile(info)
let line = gSourceFiles[info.fileIndex].lines[info.line-1]
var first = min(info.col.int, line.len)
if first < 0: return
@@ -100,18 +102,18 @@ proc replaceInFile(info: TLineInfo; newName: string) =
while first > 0 and line[first-1] in prettybase.Letters: dec first
if first < 0: return
if line[first] == '`': inc first
let last = first+identLen(line, first)-1
if differ(line, first, last, newName):
# last-first+1 != newName.len or
var x = line.substr(0, first-1) & newName & line.substr(last+1)
# last-first+1 != newName.len or
var x = line.substr(0, first-1) & newName & line.substr(last+1)
system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x)
gSourceFiles[info.fileIndex].dirty = true
proc checkStyle(info: TLineInfo, s: string, k: TSymKind; sym: PSym) =
let beau = beautifyName(s, k)
if s != beau:
if gStyleCheck == StyleCheck.Auto:
if gStyleCheck == StyleCheck.Auto:
sym.name = getIdent(beau)
replaceInFile(info, beau)
else:
@@ -137,7 +139,7 @@ proc styleCheckUseImpl(info: TLineInfo; s: PSym) =
if info.fileIndex < 0: return
# we simply convert it to what it looks like in the definition
# for consistency
# operators stay as they are:
if s.kind in {skResult, skTemp} or s.name.s[0] notin prettybase.Letters:
return

View File

@@ -7,7 +7,8 @@
# distribution, for details about the copyright.
#
import ast, msgs, strutils, idents, lexbase, streams
import strutils, lexbase, streams
import compiler/ast, compiler/msgs, compiler/idents
from os import splitFile
type
@@ -39,7 +40,7 @@ proc loadFile*(info: TLineInfo) =
var pos = lex.bufpos
while true:
case lex.buf[pos]
of '\c':
of '\c':
gSourceFiles[i].newline = "\c\L"
break
of '\L', '\0':
@@ -70,7 +71,7 @@ proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) =
while first > 0 and line[first-1] in Letters: dec first
if first < 0: return
if line[first] == '`': inc first
let last = first+identLen(line, first)-1
if cmpIgnoreStyle(line[first..last], oldSym.s) == 0:
var x = line.substr(0, first-1) & newSym.s & line.substr(last+1)

View File

@@ -7,330 +7,6 @@
# distribution, for details about the copyright.
#
## Nimsuggest is a tool that helps to give editors IDE like capabilities.
## Nimsuggest has been moved to https://github.com/nim-lang/nimsuggest
import strutils, os, parseopt, parseutils, sequtils, net
# Do NOT import suggest. It will lead to wierd bugs with
# suggestionResultHook, because suggest.nim is included by sigmatch.
# So we import that one instead.
import options, commands, modules, sem, passes, passaux, msgs, nimconf,
extccomp, condsyms, lists, net, rdstdin, sexp, sigmatch, ast
when defined(windows):
import winlean
else:
import posix
const Usage = """
Nimsuggest - Tool to give every editor IDE like capabilities for Nim
Usage:
nimsuggest [options] projectfile.nim
Options:
--port:PORT port, by default 6000
--address:HOST binds to that address, by default ""
--stdin read commands from stdin and write results to
stdout instead of using sockets
--epc use emacs epc mode
The server then listens to the connection and takes line-based commands.
In addition, all command line options of Nim that do not affect code generation
are supported.
"""
type
Mode = enum mstdin, mtcp, mepc
var
gPort = 6000.Port
gAddress = ""
gMode: Mode
const
seps = {':', ';', ' ', '\t'}
Help = "usage: sug|con|def|use file.nim[;dirtyfile.nim]:line:col\n"&
"type 'quit' to quit\n" &
"type 'debug' to toggle debug mode on/off\n" &
"type 'terse' to toggle terse mode on/off"
type
EUnexpectedCommand = object of Exception
proc parseQuoted(cmd: string; outp: var string; start: int): int =
var i = start
i += skipWhitespace(cmd, i)
if cmd[i] == '"':
i += parseUntil(cmd, outp, '"', i+1)+2
else:
i += parseUntil(cmd, outp, seps, i)
result = i
proc sexp(s: IdeCmd): SexpNode = sexp($s)
proc sexp(s: TSymKind): SexpNode = sexp($s)
proc sexp(s: Suggest): SexpNode =
# If you change the oder here, make sure to change it over in
# nim-mode.el too.
result = convertSexp([
s.section,
s.symkind,
s.qualifiedPath.map(newSString),
s.filePath,
s.forth,
s.line,
s.column,
s.doc
])
proc sexp(s: seq[Suggest]): SexpNode =
result = newSList()
for sug in s:
result.add(sexp(sug))
proc listEPC(): SexpNode =
let
argspecs = sexp("file line column dirtyfile".split(" ").map(newSSymbol))
docstring = sexp("line starts at 1, column at 0, dirtyfile is optional")
result = newSList()
for command in ["sug", "con", "def", "use"]:
let
cmd = sexp(command)
methodDesc = newSList()
methodDesc.add(cmd)
methodDesc.add(argspecs)
methodDesc.add(docstring)
result.add(methodDesc)
proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int) =
gIdeCmd = cmd
if cmd == ideUse:
modules.resetAllModules()
var isKnownFile = true
let dirtyIdx = file.fileInfoIdx(isKnownFile)
if dirtyfile.len != 0: msgs.setDirtyFile(dirtyIdx, dirtyfile)
else: msgs.setDirtyFile(dirtyIdx, nil)
resetModule dirtyIdx
if dirtyIdx != gProjectMainIdx:
resetModule gProjectMainIdx
gTrackPos = newLineInfo(dirtyIdx, line, col)
gErrorCounter = 0
if not isKnownFile:
compileProject()
compileProject(dirtyIdx)
proc executeEPC(cmd: IdeCmd, args: SexpNode) =
let
file = args[0].getStr
line = args[1].getNum
column = args[2].getNum
var dirtyfile = ""
if len(args) > 3:
dirtyfile = args[3].getStr(nil)
execute(cmd, file, dirtyfile, int(line), int(column))
proc returnEPC(socket: var Socket, uid: BiggestInt, s: SexpNode, return_symbol = "return") =
let response = $convertSexp([newSSymbol(return_symbol), uid, s])
socket.send(toHex(len(response), 6))
socket.send(response)
proc connectToNextFreePort(server: Socket, host: string, start = 30000): int =
result = start
while true:
try:
server.bindaddr(Port(result), host)
return
except OsError:
when defined(windows):
let checkFor = WSAEADDRINUSE.OSErrorCode
else:
let checkFor = EADDRINUSE.OSErrorCode
if osLastError() != checkFor:
raise getCurrentException()
else:
result += 1
proc parseCmdLine(cmd: string) =
template toggle(sw) =
if sw in gGlobalOptions:
excl(gGlobalOptions, sw)
else:
incl(gGlobalOptions, sw)
return
template err() =
echo Help
return
var opc = ""
var i = parseIdent(cmd, opc, 0)
case opc.normalize
of "sug": gIdeCmd = ideSug
of "con": gIdeCmd = ideCon
of "def": gIdeCmd = ideDef
of "use": gIdeCmd = ideUse
of "quit": quit()
of "debug": toggle optIdeDebug
of "terse": toggle optIdeTerse
else: err()
var dirtyfile = ""
var orig = ""
i = parseQuoted(cmd, orig, i)
if cmd[i] == ';':
i = parseQuoted(cmd, dirtyfile, i+1)
i += skipWhile(cmd, seps, i)
var line = -1
var col = 0
i += parseInt(cmd, line, i)
i += skipWhile(cmd, seps, i)
i += parseInt(cmd, col, i)
execute(gIdeCmd, orig, dirtyfile, line, col-1)
proc serve() =
case gMode:
of mstdin:
echo Help
var line = ""
while readLineFromStdin("> ", line):
parseCmdLine line
echo ""
flushFile(stdout)
of mtcp:
var server = newSocket()
server.bindAddr(gPort, gAddress)
var inp = "".TaintedString
server.listen()
while true:
var stdoutSocket = newSocket()
msgs.writelnHook = proc (line: string) =
stdoutSocket.send(line & "\c\L")
accept(server, stdoutSocket)
stdoutSocket.readLine(inp)
parseCmdLine inp.string
stdoutSocket.send("\c\L")
stdoutSocket.close()
of mepc:
var server = newSocket()
let port = connectToNextFreePort(server, "localhost")
var inp = "".TaintedString
server.listen()
echo(port)
var client = newSocket()
# Wait for connection
accept(server, client)
while true:
var sizeHex = ""
if client.recv(sizeHex, 6) != 6:
raise newException(ValueError, "didn't get all the hexbytes")
var size = 0
if parseHex(sizeHex, size) == 0:
raise newException(ValueError, "invalid size hex: " & $sizeHex)
var messageBuffer = ""
if client.recv(messageBuffer, size) != size:
raise newException(ValueError, "didn't get all the bytes")
let
message = parseSexp($messageBuffer)
messageType = message[0].getSymbol
case messageType:
of "call":
var results: seq[Suggest] = @[]
suggestionResultHook = proc (s: Suggest) =
results.add(s)
let
uid = message[1].getNum
cmd = parseIdeCmd(message[2].getSymbol)
args = message[3]
executeEPC(cmd, args)
returnEPC(client, uid, sexp(results))
of "return":
raise newException(EUnexpectedCommand, "no return expected")
of "return-error":
raise newException(EUnexpectedCommand, "no return expected")
of "epc-error":
stderr.writeln("recieved epc error: " & $messageBuffer)
raise newException(IOError, "epc error")
of "methods":
returnEPC(client, message[1].getNum, listEPC())
else:
raise newException(EUnexpectedCommand, "unexpected call: " & messageType)
proc mainCommand =
registerPass verbosePass
registerPass semPass
gCmd = cmdIdeTools
incl gGlobalOptions, optCaasEnabled
isServing = true
wantMainModule()
appendStr(searchPaths, options.libpath)
if gProjectFull.len != 0:
# current path is always looked first for modules
prependStr(searchPaths, gProjectPath)
# do not stop after the first error:
msgs.gErrorMax = high(int)
compileProject()
serve()
proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
var p = parseopt.initOptParser(cmd)
while true:
parseopt.next(p)
case p.kind
of cmdEnd: break
of cmdLongoption, cmdShortOption:
case p.key.normalize
of "port":
gPort = parseInt(p.val).Port
gMode = mtcp
of "address":
gAddress = p.val
gMode = mtcp
of "stdin": gMode = mstdin
of "epc":
gMode = mepc
gVerbosity = 0 # Port number gotta be first.
else: processSwitch(pass, p)
of cmdArgument:
options.gProjectName = unixToNativePath(p.key)
# if processArgument(pass, p, argsCount): break
proc handleCmdLine() =
if paramCount() == 0:
stdout.writeln(Usage)
else:
processCmdLine(passCmd1, "")
if gProjectName != "":
try:
gProjectFull = canonicalizePath(gProjectName)
except OSError:
gProjectFull = gProjectName
var p = splitFile(gProjectFull)
gProjectPath = p.dir
gProjectName = p.name
else:
gProjectPath = getCurrentDir()
loadConfigs(DefaultConfig) # load all config files
# now process command line arguments again, because some options in the
# command line can overwite the config file's settings
extccomp.initVars()
processCmdLine(passCmd2, "")
mainCommand()
when false:
proc quitCalled() {.noconv.} =
writeStackTrace()
addQuitProc(quitCalled)
condsyms.initDefines()
defineSymbol "nimsuggest"
handleCmdline()
{.error: "This project has moved to the following repo: https://github.com/nim-lang/nimsuggest".}

View File

@@ -1,17 +0,0 @@
# Special configuration file for the Nim project
gc:markAndSweep
hint[XDeclaredButNotUsed]:off
path:"$projectPath/../.."
path:"$lib/packages/docutils"
path:"../../compiler"
define:useStdoutAsStdmsg
define:nimsuggest
cs:partial
#define:useNodeIds
define:booting
#define:noDocgen

View File

@@ -64,6 +64,7 @@ proc setBaseFlags*(n: PNode, base: TNumericalBase)
proc parseSymbol*(p: var TParser, allowNil = false): PNode
proc parseTry(p: var TParser; isExpr: bool): PNode
proc parseCase(p: var TParser): PNode
proc parseStmtPragma(p: var TParser): PNode
# implementation
proc getTok(p: var TParser) =
@@ -499,10 +500,13 @@ proc parsePar(p: var TParser): PNode =
#| parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try'
#| | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let'
#| | 'when' | 'var' | 'mixin'
#| par = '(' optInd (&parKeyw complexOrSimpleStmt ^+ ';'
#| | simpleExpr ('=' expr (';' complexOrSimpleStmt ^+ ';' )? )?
#| | (':' expr)? (',' (exprColonEqExpr comma?)*)? )?
#| optPar ')'
#| par = '(' optInd
#| ( &parKeyw complexOrSimpleStmt ^+ ';'
#| | ';' complexOrSimpleStmt ^+ ';'
#| | pragmaStmt
#| | simpleExpr ( ('=' expr (';' complexOrSimpleStmt ^+ ';' )? )
#| | (':' expr (',' exprColonEqExpr ^+ ',' )? ) ) )
#| optPar ')'
#
# unfortunately it's ambiguous: (expr: expr) vs (exprStmt); however a
# leading ';' could be used to enforce a 'stmt' context ...
@@ -521,6 +525,8 @@ proc parsePar(p: var TParser): PNode =
getTok(p)
optInd(p, result)
semiStmtList(p, result)
elif p.tok.tokType == tkCurlyDotLe:
result.add(parseStmtPragma(p))
elif p.tok.tokType != tkParRi:
var a = simpleExpr(p)
if p.tok.tokType == tkEquals:

View File

@@ -130,7 +130,9 @@ proc matchNested(c: PPatternContext, p, n: PNode, rpn: bool): bool =
proc matches(c: PPatternContext, p, n: PNode): bool =
# hidden conversions (?)
if isPatternParam(c, p):
if nfNoRewrite in n.flags:
result = false
elif isPatternParam(c, p):
result = bindOrCheck(c, p.sym, n)
elif n.kind == nkSym and p.kind == nkIdent:
result = p.ident.id == n.sym.name.id

View File

@@ -9,7 +9,8 @@
## The builtin 'system.locals' implemented as a plugin.
import plugins, ast, astalgo, magicsys, lookups, semdata, lowerings
import compiler/plugins, compiler/ast, compiler/astalgo, compiler/magicsys,
compiler/lookups, compiler/semdata, compiler/lowerings
proc semLocals(c: PContext, n: PNode): PNode =
var counter = 0

View File

@@ -37,7 +37,7 @@ const
wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises,
wTags, wLocks, wGcSafe}
exprPragmas* = {wLine, wLocks}
exprPragmas* = {wLine, wLocks, wNoRewrite}
stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks,
wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
wLinedir, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError,
@@ -859,6 +859,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
c.module.flags.incl sfExperimental
else:
localError(it.info, "'experimental' pragma only valid as toplevel statement")
of wNoRewrite:
noVal(it)
else: invalidPragma(it)
else: invalidPragma(it)
else: processNote(c, it)

View File

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

View File

@@ -209,7 +209,10 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
pickBest(callOp)
if overloadsState == csEmpty and result.state == csEmpty:
localError(n.info, errUndeclaredIdentifier, considerQuotedIdent(f).s)
if nfDotField in n.flags and nfExplicitCall notin n.flags:
localError(n.info, errUndeclaredField, considerQuotedIdent(f).s)
else:
localError(n.info, errUndeclaredRoutine, considerQuotedIdent(f).s)
return
elif result.state != csMatch:
if nfExprCall in n.flags:

View File

@@ -207,9 +207,9 @@ proc markGcUnsafe(a: PEffects; reason: PNode) =
a.owner.gcUnsafetyReason = newSym(skUnknown, getIdent("<unknown>"),
a.owner, reason.info)
proc listGcUnsafety(s: PSym; onlyWarning: bool) =
proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet) =
let u = s.gcUnsafetyReason
if u != nil:
if u != nil and not cycleCheck.containsOrIncl(u.id):
let msgKind = if onlyWarning: warnGcUnsafe2 else: errGenerated
if u.kind in {skLet, skVar}:
message(s.info, msgKind,
@@ -218,7 +218,7 @@ proc listGcUnsafety(s: PSym; onlyWarning: bool) =
elif u.kind in routineKinds:
# recursive call *always* produces only a warning so the full error
# message is printed:
listGcUnsafety(u, true)
listGcUnsafety(u, true, cycleCheck)
message(s.info, msgKind,
"'$#' is not GC-safe as it calls '$#'" %
[s.name.s, u.name.s])
@@ -227,6 +227,10 @@ proc listGcUnsafety(s: PSym; onlyWarning: bool) =
message(u.info, msgKind,
"'$#' is not GC-safe as it performs an indirect call here" % s.name.s)
proc listGcUnsafety(s: PSym; onlyWarning: bool) =
var cycleCheck = initIntSet()
listGcUnsafety(s, onlyWarning, cycleCheck)
proc useVar(a: PEffects, n: PNode) =
let s = n.sym
if isLocalVar(a, s):

View File

@@ -1268,6 +1268,8 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode =
of wLocks:
result = n
result.typ = n.sons[1].typ
of wNoRewrite:
incl(result.flags, nfNoRewrite)
else: discard
proc semStaticStmt(c: PContext, n: PNode): PNode =

View File

@@ -12,7 +12,7 @@
import
hashes, ast, astalgo, types
proc hashTree(n: PNode): THash =
proc hashTree(n: PNode): Hash =
if n == nil: return
result = ord(n.kind)
case n.kind
@@ -53,8 +53,8 @@ proc treesEquivalent(a, b: PNode): bool =
result = true
if result: result = sameTypeOrNil(a.typ, b.typ)
proc nodeTableRawGet(t: TNodeTable, k: THash, key: PNode): int =
var h: THash = k and high(t.data)
proc nodeTableRawGet(t: TNodeTable, k: Hash, key: PNode): int =
var h: Hash = k and high(t.data)
while t.data[h].key != nil:
if (t.data[h].h == k) and treesEquivalent(t.data[h].key, key):
return h
@@ -66,9 +66,9 @@ proc nodeTableGet*(t: TNodeTable, key: PNode): int =
if index >= 0: result = t.data[index].val
else: result = low(int)
proc nodeTableRawInsert(data: var TNodePairSeq, k: THash, key: PNode,
proc nodeTableRawInsert(data: var TNodePairSeq, k: Hash, key: PNode,
val: int) =
var h: THash = k and high(data)
var h: Hash = k and high(data)
while data[h].key != nil: h = nextTry(h, high(data))
assert(data[h].key == nil)
data[h].h = k
@@ -77,7 +77,7 @@ proc nodeTableRawInsert(data: var TNodePairSeq, k: THash, key: PNode,
proc nodeTablePut*(t: var TNodeTable, key: PNode, val: int) =
var n: TNodePairSeq
var k: THash = hashTree(key)
var k: Hash = hashTree(key)
var index = nodeTableRawGet(t, k, key)
if index >= 0:
assert(t.data[index].key != nil)
@@ -94,7 +94,7 @@ proc nodeTablePut*(t: var TNodeTable, key: PNode, val: int) =
proc nodeTableTestOrSet*(t: var TNodeTable, key: PNode, val: int): int =
var n: TNodePairSeq
var k: THash = hashTree(key)
var k: Hash = hashTree(key)
var index = nodeTableRawGet(t, k, key)
if index >= 0:
assert(t.data[index].key != nil)

View File

@@ -55,7 +55,7 @@ type
wFloatchecks, wNanChecks, wInfChecks,
wAssertions, wPatterns, wWarnings,
wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects, wTags,
wDeadCodeElim, wSafecode, wNoForward,
wDeadCodeElim, wSafecode, wNoForward, wNoRewrite,
wPragma,
wCompileTime, wNoInit,
wPassc, wPassl, wBorrow, wDiscardable,
@@ -139,7 +139,7 @@ const
"assertions", "patterns", "warnings", "hints",
"optimization", "raises", "writes", "reads", "size", "effects", "tags",
"deadcodeelim", "safecode", "noforward",
"deadcodeelim", "safecode", "noforward", "norewrite",
"pragma",
"compiletime", "noinit",
"passc", "passl", "borrow", "discardable", "fieldchecks",

View File

@@ -11,8 +11,8 @@
* `FloatInvalidOpError <system.html#FloatInvalidOpError>`_
* `FloatOverflowError <system.html#FloatOverflowError>`_
* `FloatUnderflowError <system.html#FloatUnderflowError>`_
* `FieldError <system.html#InvalidFieldError>`_
* `IndexError <system.html#InvalidIndexError>`_
* `FieldError <system.html#FieldError>`_
* `IndexError <system.html#IndexError>`_
* `ObjectAssignmentError <system.html#ObjectAssignmentError>`_
* `ObjectConversionError <system.html#ObjectConversionError>`_
* `ValueError <system.html#ValueError>`_

View File

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

View File

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

View File

@@ -404,7 +404,7 @@ dispatch.
result.a = a
result.b = b
echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
In the example the constructors ``newLit`` and ``newPlus`` are procs
because they should use static binding, but ``eval`` is a method because it

View File

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

View File

@@ -9,7 +9,7 @@
## This module implements an interface to Nim's `runtime type information`:idx:
## (`RTTI`:idx:).
## Note that even though ``TAny`` and its operations hide the nasty low level
## Note that even though ``Any`` and its operations hide the nasty low level
## details from its clients, it remains inherently unsafe!
##
## See the `marshal <marshal.html>`_ module for what this module allows you
@@ -23,7 +23,7 @@ include "system/hti.nim"
{.pop.}
type
TAnyKind* = enum ## what kind of ``any`` it is
AnyKind* = enum ## what kind of ``any`` it is
akNone = 0, ## invalid any
akBool = 1, ## any represents a ``bool``
akChar = 2, ## any represents a ``char``
@@ -55,9 +55,9 @@ type
akUInt32 = 43, ## any represents an unsigned int32
akUInt64 = 44, ## any represents an unsigned int64
TAny* = object ## can represent any nim value; NOTE: the wrapped
Any* = object ## can represent any nim value; NOTE: the wrapped
## value can be modified with its wrapper! This means
## that ``TAny`` keeps a non-traced pointer to its
## that ``Any`` keeps a non-traced pointer to its
## wrapped value and **must not** live longer than
## its wrapped value.
value: pointer
@@ -69,6 +69,7 @@ type
TGenericSeq {.importc.} = object
len, space: 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

View File

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

View File

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

View File

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

View File

@@ -17,23 +17,24 @@ from sdl import PSurface # Bug
from sdl_ttf import openFont, closeFont
type
TRect* = tuple[x, y, width, height: int]
TPoint* = tuple[x, y: int]
Rect* = tuple[x, y, width, height: int]
Point* = tuple[x, y: int]
PSurface* = ref TSurface ## a surface to draw onto
TSurface* {.pure, final.} = object
PSurface* = ref Surface ## a surface to draw onto
Surface* {.pure, final.} = object
w*, h*: Natural
s*: sdl.PSurface
EGraphics* = object of IOError
TFont {.pure, final.} = object
Font {.pure, final.} = object
f: sdl_ttf.PFont
color: sdl.TColor
PFont* = ref TFont ## represents a font
color: sdl.Color
PFont* = ref Font ## represents a font
{.deprecated: [TSurface: Surface, TFont: Font, TRect: Rect, TPoint: Point].}
proc toSdlColor*(c: Color): sdl.TColor =
## Convert colors.TColor to sdl.TColor
proc toSdlColor*(c: Color): sdl.Color =
## Convert colors.Color to sdl.Color
var x = c.extractRGB
result.r = x.r and 0xff
result.g = x.g and 0xff
@@ -45,8 +46,8 @@ proc createSdlColor*(sur: PSurface, c: Color, alpha: int = 0): int32 =
return sdl.mapRGBA(sur.s.format, x.r and 0xff, x.g and 0xff,
x.b and 0xff, alpha and 0xff)
proc toSdlRect*(r: TRect): sdl.TRect =
## Convert ``graphics.TRect`` to ``sdl.TRect``.
proc toSdlRect*(r: Rect): sdl.Rect =
## Convert ``graphics.Rect`` to ``sdl.Rect``.
result.x = int16(r.x)
result.y = int16(r.y)
result.w = uint16(r.width)
@@ -103,8 +104,9 @@ proc writeToBMP*(sur: PSurface, filename: string) =
raise newException(IOError, "cannot write: " & filename)
type
TPixels = array[0..1000_000-1, int32]
PPixels = ptr TPixels
Pixels = array[0..1000_000-1, int32]
PPixels = ptr Pixels
{.deprecated: [TPixels: Pixels].}
template setPix(video, pitch, x, y, col: expr): stmt =
video[y * pitch + x] = int32(col)
@@ -128,7 +130,7 @@ proc setPixel(sur: PSurface, x, y: Natural, col: colors.Color) {.inline.} =
#pixs[y * (sur.s.pitch div colSize) + x] = int(col)
setPix(pixs, sur.s.pitch.int div ColSize, x, y, col)
proc `[]`*(sur: PSurface, p: TPoint): Color =
proc `[]`*(sur: PSurface, p: Point): Color =
## get pixel at position `p`. No range checking is done!
result = getPixel(sur, p.x, p.y)
@@ -136,7 +138,7 @@ proc `[]`*(sur: PSurface, x, y: int): Color =
## get pixel at position ``(x, y)``. No range checking is done!
result = getPixel(sur, x, y)
proc `[]=`*(sur: PSurface, p: TPoint, col: Color) =
proc `[]=`*(sur: PSurface, p: Point, col: Color) =
## set the pixel at position `p`. No range checking is done!
setPixel(sur, p.x, p.y, col)
@@ -144,10 +146,10 @@ proc `[]=`*(sur: PSurface, x, y: int, col: Color) =
## set the pixel at position ``(x, y)``. No range checking is done!
setPixel(sur, x, y, col)
proc blit*(destSurf: PSurface, destRect: TRect, srcSurf: PSurface,
srcRect: TRect) =
proc blit*(destSurf: PSurface, destRect: Rect, srcSurf: PSurface,
srcRect: Rect) =
## Copies ``srcSurf`` into ``destSurf``
var destTRect, srcTRect: sdl.TRect
var destTRect, srcTRect: sdl.Rect
destTRect.x = int16(destRect.x)
destTRect.y = int16(destRect.y)
@@ -168,7 +170,7 @@ proc textBounds*(text: string, font = defaultFont): tuple[width, height: int] =
result.width = int(w)
result.height = int(h)
proc drawText*(sur: PSurface, p: TPoint, text: string, font = defaultFont) =
proc drawText*(sur: PSurface, p: Point, text: string, font = defaultFont) =
## Draws text with a transparent background, at location ``p`` with the given
## font.
var textSur: PSurface # This surface will have the text drawn on it
@@ -179,7 +181,7 @@ proc drawText*(sur: PSurface, p: TPoint, text: string, font = defaultFont) =
# Merge the text surface with sur
sur.blit((p.x, p.y, sur.w, sur.h), textSur, (0, 0, sur.w, sur.h))
proc drawText*(sur: PSurface, p: TPoint, text: string,
proc drawText*(sur: PSurface, p: Point, text: string,
bg: Color, font = defaultFont) =
## Draws text, at location ``p`` with font ``font``. ``bg``
## is the background color.
@@ -189,7 +191,7 @@ proc drawText*(sur: PSurface, p: TPoint, text: string,
# Merge the text surface with sur
sur.blit((p.x, p.y, sur.w, sur.h), textSur, (0, 0, sur.w, sur.h))
proc drawCircle*(sur: PSurface, p: TPoint, r: Natural, color: Color) =
proc drawCircle*(sur: PSurface, p: Point, r: Natural, color: Color) =
## draws a circle with center `p` and radius `r` with the given color
## onto the surface `sur`.
var video = cast[PPixels](sur.s.pixels)
@@ -229,7 +231,7 @@ proc `>-<`(val: int, s: PSurface): int {.inline.} =
proc `>|<`(val: int, s: PSurface): int {.inline.} =
return if val < 0: 0 elif val >= s.h: s.h-1 else: val
proc drawLine*(sur: PSurface, p1, p2: TPoint, color: Color) =
proc drawLine*(sur: PSurface, p1, p2: Point, color: Color) =
## draws a line between the two points `p1` and `p2` with the given color
## onto the surface `sur`.
var stepx, stepy: int = 0
@@ -291,7 +293,7 @@ proc drawVerLine*(sur: PSurface, x, y, h: Natural, color: Color) =
for i in 0 .. min(sur.s.h-y, h)-1:
setPix(video, pitch, x, y + i, color)
proc fillCircle*(s: PSurface, p: TPoint, r: Natural, color: Color) =
proc fillCircle*(s: PSurface, p: Point, r: Natural, color: Color) =
## draws a circle with center `p` and radius `r` with the given color
## onto the surface `sur` and fills it.
var a = 1 - r
@@ -319,7 +321,7 @@ proc fillCircle*(s: PSurface, p: TPoint, r: Natural, color: Color) =
drawVerLine(s, x - py - 1, y - px, px, color)
px = px + 1
proc drawRect*(sur: PSurface, r: TRect, color: Color) =
proc drawRect*(sur: PSurface, r: Rect, color: Color) =
## draws a rectangle.
var video = cast[PPixels](sur.s.pixels)
var pitch = sur.s.pitch.int div ColSize
@@ -337,7 +339,7 @@ proc drawRect*(sur: PSurface, r: TRect, color: Color) =
setPix(video, pitch, r.x, r.y + i, color)
setPix(video, pitch, r.x + minW - 1, r.y + i, color) # Draw right side
proc fillRect*(sur: PSurface, r: TRect, col: Color) =
proc fillRect*(sur: PSurface, r: Rect, col: Color) =
## Fills a rectangle using sdl's ``FillRect`` function.
var rect = toSdlRect(r)
if sdl.fillRect(sur.s, addr(rect), sur.createSdlColor(col)) == -1:
@@ -424,7 +426,7 @@ template cround(x: expr): expr = ipart(x + 0.5)
template fpart(x: expr): expr = x - ipart(x)
template rfpart(x: expr): expr = 1.0 - fpart(x)
proc drawLineAA*(sur: PSurface, p1, p2: TPoint, color: Color) =
proc drawLineAA*(sur: PSurface, p1, p2: Point, color: Color) =
## Draws a anti-aliased line from ``p1`` to ``p2``, using Xiaolin Wu's
## line algorithm
var (x1, x2, y1, y2) = (p1.x.toFloat(), p2.x.toFloat(),
@@ -490,9 +492,9 @@ proc fillSurface*(sur: PSurface, color: Color) =
template withEvents*(surf: PSurface, event: expr, actions: stmt): stmt {.
immediate.} =
## Simple template which creates an event loop. ``Event`` is the name of the
## variable containing the TEvent object.
## variable containing the Event object.
while true:
var event: sdl.TEvent
var event: sdl.Event
if sdl.waitEvent(addr(event)) == 1:
actions

View File

@@ -135,7 +135,7 @@ else:
var cur, old: Termios
discard fd.tcgetattr(cur.addr)
old = cur
cur.c_lflag = cur.c_lflag and not Tcflag(ECHO)
cur.c_lflag = cur.c_lflag and not Cflag(ECHO)
discard fd.tcsetattr(TCSADRAIN, cur.addr)
stdout.write prompt
result = stdin.readLine(password)

View File

@@ -15,11 +15,12 @@
import openssl, strutils, os
type
TSecureSocket* = object
SecureSocket* = object
ssl: SslPtr
bio: BIO
{.deprecated: [TSecureSocket: SecureSocket].}
proc connect*(sock: var TSecureSocket, address: string,
proc connect*(sock: var SecureSocket, address: string,
port: int): int =
## Connects to the specified `address` on the specified `port`.
## Returns the result of the certificate validation.
@@ -52,7 +53,7 @@ proc connect*(sock: var TSecureSocket, address: string,
result = SSL_get_verify_result(sock.ssl)
proc recvLine*(sock: TSecureSocket, line: var TaintedString): bool =
proc recvLine*(sock: SecureSocket, line: var TaintedString): bool =
## Acts in a similar fashion to the `recvLine` in the sockets module.
## Returns false when no data is available to be read.
## `Line` must be initialized and not nil!
@@ -71,19 +72,19 @@ proc recvLine*(sock: TSecureSocket, line: var TaintedString): bool =
add(line.string, c)
proc send*(sock: TSecureSocket, data: string) =
proc send*(sock: SecureSocket, data: string) =
## Writes `data` to the socket.
if BIO_write(sock.bio, data, data.len.cint) <= 0:
raiseOSError(osLastError())
proc close*(sock: TSecureSocket) =
proc close*(sock: SecureSocket) =
## Closes the socket
if BIO_free(sock.bio) <= 0:
ERR_print_errors_fp(stderr)
raiseOSError(osLastError())
when not defined(testing) and isMainModule:
var s: TSecureSocket
var s: SecureSocket
echo connect(s, "smtp.gmail.com", 465)
#var buffer: array[0..255, char]

View File

@@ -13,18 +13,18 @@ import
streams, libzip, times, os, strutils
type
TZipArchive* = object of RootObj ## represents a zip archive
ZipArchive* = object of RootObj ## represents a zip archive
mode: FileMode
w: PZip
{.deprecated: [TZipArchive: ZipArchive].}
proc zipError(z: var TZipArchive) =
proc zipError(z: var ZipArchive) =
var e: ref IOError
new(e)
e.msg = $zip_strerror(z.w)
raise e
proc open*(z: var TZipArchive, filename: string, mode: FileMode = fmRead): bool =
proc open*(z: var ZipArchive, filename: string, mode: FileMode = fmRead): bool =
## Opens a zip file for reading, writing or appending. All file modes are
## supported. Returns true iff successful, false otherwise.
var err, flags: int32
@@ -38,11 +38,11 @@ proc open*(z: var TZipArchive, filename: string, mode: FileMode = fmRead): bool
z.mode = mode
result = z.w != nil
proc close*(z: var TZipArchive) =
proc close*(z: var ZipArchive) =
## Closes a zip file.
zip_close(z.w)
proc createDir*(z: var TZipArchive, dir: string) =
proc createDir*(z: var ZipArchive, dir: string) =
## Creates a directory within the `z` archive. This does not fail if the
## directory already exists. Note that for adding a file like
## ``"path1/path2/filename"`` it is not necessary
@@ -52,7 +52,7 @@ proc createDir*(z: var TZipArchive, dir: string) =
discard zip_add_dir(z.w, dir)
zip_error_clear(z.w)
proc addFile*(z: var TZipArchive, dest, src: string) =
proc addFile*(z: var ZipArchive, dest, src: string) =
## Adds the file `src` to the archive `z` with the name `dest`. `dest`
## may contain a path that will be created.
assert(z.mode != fmRead)
@@ -67,13 +67,13 @@ proc addFile*(z: var TZipArchive, dest, src: string) =
zip_source_free(zipsrc)
zipError(z)
proc addFile*(z: var TZipArchive, file: string) =
proc addFile*(z: var ZipArchive, file: string) =
## A shortcut for ``addFile(z, file, file)``, i.e. the name of the source is
## the name of the destination.
addFile(z, file, file)
proc mySourceCallback(state, data: pointer, len: int,
cmd: TZipSourceCmd): int {.cdecl.} =
cmd: ZipSourceCmd): int {.cdecl.} =
var src = cast[Stream](state)
case cmd
of ZIP_SOURCE_OPEN:
@@ -86,7 +86,7 @@ proc mySourceCallback(state, data: pointer, len: int,
zip_stat_init(stat)
stat.size = high(int32)-1 # we don't know the size
stat.mtime = getTime()
result = sizeof(TZipStat)
result = sizeof(ZipStat)
of ZIP_SOURCE_ERROR:
var err = cast[ptr array[0..1, cint]](data)
err[0] = ZIP_ER_INTERNAL
@@ -95,7 +95,7 @@ proc mySourceCallback(state, data: pointer, len: int,
of constZIP_SOURCE_FREE: GC_unref(src)
else: assert(false)
proc addFile*(z: var TZipArchive, dest: string, src: Stream) =
proc addFile*(z: var ZipArchive, dest: string, src: Stream) =
## Adds a file named with `dest` to the archive `z`. `dest`
## may contain a path. The file's content is read from the `src` stream.
assert(z.mode != fmRead)
@@ -134,7 +134,7 @@ proc newZipFileStream(f: PZipFile): PZipFileStream =
# ----------------------------------------------------------------------------
proc getStream*(z: var TZipArchive, filename: string): PZipFileStream =
proc getStream*(z: var ZipArchive, filename: string): PZipFileStream =
## returns a stream that can be used to read the file named `filename`
## from the archive `z`. Returns nil in case of an error.
## The returned stream does not support the `setPosition`, `getPosition`,
@@ -142,7 +142,7 @@ proc getStream*(z: var TZipArchive, filename: string): PZipFileStream =
var x = zip_fopen(z.w, filename, 0'i32)
if x != nil: result = newZipFileStream(x)
iterator walkFiles*(z: var TZipArchive): string =
iterator walkFiles*(z: var ZipArchive): string =
## walks over all files in the archive `z` and returns the filename
## (including the path).
var i = 0'i32
@@ -152,7 +152,7 @@ iterator walkFiles*(z: var TZipArchive): string =
inc(i)
proc extractFile*(z: var TZipArchive, srcFile: string, dest: Stream) =
proc extractFile*(z: var ZipArchive, srcFile: string, dest: Stream) =
## extracts a file from the zip archive `z` to the destination stream.
var strm = getStream(z, srcFile)
while true:
@@ -162,13 +162,13 @@ proc extractFile*(z: var TZipArchive, srcFile: string, dest: Stream) =
dest.flush()
strm.close()
proc extractFile*(z: var TZipArchive, srcFile: string, dest: string) =
proc extractFile*(z: var ZipArchive, srcFile: string, dest: string) =
## extracts a file from the zip archive `z` to the destination filename.
var file = newFileStream(dest, fmWrite)
extractFile(z, srcFile, file)
file.close()
proc extractAll*(z: var TZipArchive, dest: string) =
proc extractAll*(z: var ZipArchive, dest: string) =
## extracts all files from archive `z` to the destination directory.
for file in walkFiles(z):
if file.endsWith("/"):
@@ -177,7 +177,7 @@ proc extractAll*(z: var TZipArchive, dest: string) =
extractFile(z, file, dest / file)
when not defined(testing) and isMainModule:
var zip: TZipArchive
var zip: ZipArchive
if not zip.open("nim-0.11.0.zip"):
raise newException(IOError, "opening zip failed")
zip.extractAll("test")

View File

@@ -15,7 +15,7 @@ import
strutils
type
TTokenClass* = enum
TokenClass* = enum
gtEof, gtNone, gtWhitespace, gtDecNumber, gtBinNumber, gtHexNumber,
gtOctNumber, gtFloatNumber, gtIdentifier, gtKeyword, gtStringLit,
gtLongStringLit, gtCharLit, gtEscapeSequence, # escape sequence like \xff
@@ -23,20 +23,22 @@ type
gtTagStart, gtTagEnd, gtKey, gtValue, gtRawData, gtAssembler,
gtPreprocessor, gtDirective, gtCommand, gtRule, gtHyperlink, gtLabel,
gtReference, gtOther
TGeneralTokenizer* = object of RootObj
kind*: TTokenClass
GeneralTokenizer* = object of RootObj
kind*: TokenClass
start*, length*: int
buf: cstring
pos: int
state: TTokenClass
state: TokenClass
TSourceLanguage* = enum
SourceLanguage* = enum
langNone, langNim, langNimrod, langCpp, langCsharp, langC, langJava
{.deprecated: [TSourceLanguage: SourceLanguage, TTokenClass: TokenClass,
TGeneralTokenizer: GeneralTokenizer].}
const
sourceLanguageToStr*: array[TSourceLanguage, string] = ["none",
sourceLanguageToStr*: array[SourceLanguage, string] = ["none",
"Nim", "Nimrod", "C++", "C#", "C", "Java"]
tokenClassToStr*: array[TTokenClass, string] = ["Eof", "None", "Whitespace",
tokenClassToStr*: array[TokenClass, string] = ["Eof", "None", "Whitespace",
"DecNumber", "BinNumber", "HexNumber", "OctNumber", "FloatNumber",
"Identifier", "Keyword", "StringLit", "LongStringLit", "CharLit",
"EscapeSequence", "Operator", "Punctuation", "Comment", "LongComment",
@@ -58,29 +60,29 @@ const
"template", "try", "tuple", "type", "using", "var", "when", "while", "with",
"without", "xor", "yield"]
proc getSourceLanguage*(name: string): TSourceLanguage =
for i in countup(succ(low(TSourceLanguage)), high(TSourceLanguage)):
proc getSourceLanguage*(name: string): SourceLanguage =
for i in countup(succ(low(SourceLanguage)), high(SourceLanguage)):
if cmpIgnoreStyle(name, sourceLanguageToStr[i]) == 0:
return i
result = langNone
proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: cstring) =
proc initGeneralTokenizer*(g: var GeneralTokenizer, buf: cstring) =
g.buf = buf
g.kind = low(TTokenClass)
g.kind = low(TokenClass)
g.start = 0
g.length = 0
g.state = low(TTokenClass)
g.state = low(TokenClass)
var pos = 0 # skip initial whitespace:
while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos)
g.pos = pos
proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: string) =
proc initGeneralTokenizer*(g: var GeneralTokenizer, buf: string) =
initGeneralTokenizer(g, cstring(buf))
proc deinitGeneralTokenizer*(g: var TGeneralTokenizer) =
proc deinitGeneralTokenizer*(g: var GeneralTokenizer) =
discard
proc nimGetKeyword(id: string): TTokenClass =
proc nimGetKeyword(id: string): TokenClass =
for k in nimKeywords:
if cmpIgnoreStyle(id, k) == 0: return gtKeyword
result = gtIdentifier
@@ -92,7 +94,7 @@ proc nimGetKeyword(id: string): TTokenClass =
else:
result = gtIdentifier
proc nimNumberPostfix(g: var TGeneralTokenizer, position: int): int =
proc nimNumberPostfix(g: var GeneralTokenizer, position: int): int =
var pos = position
if g.buf[pos] == '\'':
inc(pos)
@@ -110,7 +112,7 @@ proc nimNumberPostfix(g: var TGeneralTokenizer, position: int): int =
discard
result = pos
proc nimNumber(g: var TGeneralTokenizer, position: int): int =
proc nimNumber(g: var GeneralTokenizer, position: int): int =
const decChars = {'0'..'9', '_'}
var pos = position
g.kind = gtDecNumber
@@ -130,7 +132,7 @@ const
OpChars = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', '.',
'|', '=', '%', '&', '$', '@', '~', ':', '\x80'..'\xFF'}
proc nimNextToken(g: var TGeneralTokenizer) =
proc nimNextToken(g: var GeneralTokenizer) =
const
hexChars = {'0'..'9', 'A'..'F', 'a'..'f', '_'}
octChars = {'0'..'7', '_'}
@@ -278,7 +280,7 @@ proc nimNextToken(g: var TGeneralTokenizer) =
assert false, "nimNextToken: produced an empty token"
g.pos = pos
proc generalNumber(g: var TGeneralTokenizer, position: int): int =
proc generalNumber(g: var GeneralTokenizer, position: int): int =
const decChars = {'0'..'9'}
var pos = position
g.kind = gtDecNumber
@@ -294,7 +296,7 @@ proc generalNumber(g: var TGeneralTokenizer, position: int): int =
while g.buf[pos] in decChars: inc(pos)
result = pos
proc generalStrLit(g: var TGeneralTokenizer, position: int): int =
proc generalStrLit(g: var GeneralTokenizer, position: int): int =
const
decChars = {'0'..'9'}
hexChars = {'0'..'9', 'A'..'F', 'a'..'f'}
@@ -355,12 +357,13 @@ proc isKeywordIgnoreCase(x: openArray[string], y: string): int =
result = - 1
type
TTokenizerFlag = enum
TokenizerFlag = enum
hasPreprocessor, hasNestedComments
TTokenizerFlags = set[TTokenizerFlag]
TokenizerFlags = set[TokenizerFlag]
{.deprecated: [TTokenizerFlag: TokenizerFlag, TTokenizerFlags: TokenizerFlags].}
proc clikeNextToken(g: var TGeneralTokenizer, keywords: openArray[string],
flags: TTokenizerFlags) =
proc clikeNextToken(g: var GeneralTokenizer, keywords: openArray[string],
flags: TokenizerFlags) =
const
hexChars = {'0'..'9', 'A'..'F', 'a'..'f'}
octChars = {'0'..'7'}
@@ -493,7 +496,7 @@ proc clikeNextToken(g: var TGeneralTokenizer, keywords: openArray[string],
assert false, "clikeNextToken: produced an empty token"
g.pos = pos
proc cNextToken(g: var TGeneralTokenizer) =
proc cNextToken(g: var GeneralTokenizer) =
const
keywords: array[0..36, string] = ["_Bool", "_Complex", "_Imaginary", "auto",
"break", "case", "char", "const", "continue", "default", "do", "double",
@@ -503,7 +506,7 @@ proc cNextToken(g: var TGeneralTokenizer) =
"volatile", "while"]
clikeNextToken(g, keywords, {hasPreprocessor})
proc cppNextToken(g: var TGeneralTokenizer) =
proc cppNextToken(g: var GeneralTokenizer) =
const
keywords: array[0..47, string] = ["asm", "auto", "break", "case", "catch",
"char", "class", "const", "continue", "default", "delete", "do", "double",
@@ -514,7 +517,7 @@ proc cppNextToken(g: var TGeneralTokenizer) =
"union", "unsigned", "virtual", "void", "volatile", "while"]
clikeNextToken(g, keywords, {hasPreprocessor})
proc csharpNextToken(g: var TGeneralTokenizer) =
proc csharpNextToken(g: var GeneralTokenizer) =
const
keywords: array[0..76, string] = ["abstract", "as", "base", "bool", "break",
"byte", "case", "catch", "char", "checked", "class", "const", "continue",
@@ -529,7 +532,7 @@ proc csharpNextToken(g: var TGeneralTokenizer) =
"virtual", "void", "volatile", "while"]
clikeNextToken(g, keywords, {hasPreprocessor})
proc javaNextToken(g: var TGeneralTokenizer) =
proc javaNextToken(g: var GeneralTokenizer) =
const
keywords: array[0..52, string] = ["abstract", "assert", "boolean", "break",
"byte", "case", "catch", "char", "class", "const", "continue", "default",
@@ -541,7 +544,7 @@ proc javaNextToken(g: var TGeneralTokenizer) =
"try", "void", "volatile", "while"]
clikeNextToken(g, keywords, {})
proc getNextToken*(g: var TGeneralTokenizer, lang: TSourceLanguage) =
proc getNextToken*(g: var GeneralTokenizer, lang: SourceLanguage) =
case lang
of langNone: assert false
of langNim, langNimrod: nimNextToken(g)

View File

@@ -15,7 +15,7 @@ import
os, strutils, rstast
type
TRstParseOption* = enum ## options for the RST parser
RstParseOption* = enum ## options for the RST parser
roSkipPounds, ## skip ``#`` at line beginning (documentation
## embedded in Nim comments)
roSupportSmilies, ## make the RST parser support smilies like ``:)``
@@ -23,14 +23,14 @@ type
## it for sandboxing)
roSupportMarkdown ## support additional features of markdown
TRstParseOptions* = set[TRstParseOption]
RstParseOptions* = set[RstParseOption]
TMsgClass* = enum
MsgClass* = enum
mcHint = "Hint",
mcWarning = "Warning",
mcError = "Error"
TMsgKind* = enum ## the possible messages
MsgKind* = enum ## the possible messages
meCannotOpenFile,
meExpected,
meGridTableNotImplemented,
@@ -42,12 +42,14 @@ type
mwUnsupportedLanguage,
mwUnsupportedField
TMsgHandler* = proc (filename: string, line, col: int, msgKind: TMsgKind,
MsgHandler* = proc (filename: string, line, col: int, msgKind: MsgKind,
arg: string) {.nimcall.} ## what to do in case of an error
TFindFileHandler* = proc (filename: string): string {.nimcall.}
FindFileHandler* = proc (filename: string): string {.nimcall.}
{.deprecated: [TRstParseOptions: RstParseOptions, TRstParseOption: RstParseOption,
TMsgKind: MsgKind].}
const
messages: array [TMsgKind, string] = [
messages: array [MsgKind, string] = [
meCannotOpenFile: "cannot open '$1'",
meExpected: "'$1' expected",
meGridTableNotImplemented: "grid table is not implemented",
@@ -111,23 +113,24 @@ const
}
type
TTokType = enum
TokType = enum
tkEof, tkIndent, tkWhite, tkWord, tkAdornment, tkPunct, tkOther
TToken = object # a RST token
kind*: TTokType # the type of the token
Token = object # a RST token
kind*: TokType # the type of the token
ival*: int # the indentation or parsed integer value
symbol*: string # the parsed symbol as string
line*, col*: int # line and column of the token
TTokenSeq = seq[TToken]
TLexer = object of RootObj
TokenSeq = seq[Token]
Lexer = object of RootObj
buf*: cstring
bufpos*: int
line*, col*, baseIndent*: int
skipPounds*: bool
{.deprecated: [TTokType: TokType, TToken: Token, TTokenSeq: TokenSeq,
TLexer: Lexer].}
proc getThing(L: var TLexer, tok: var TToken, s: set[char]) =
proc getThing(L: var Lexer, tok: var Token, s: set[char]) =
tok.kind = tkWord
tok.line = L.line
tok.col = L.col
@@ -139,7 +142,7 @@ proc getThing(L: var TLexer, tok: var TToken, s: set[char]) =
inc(L.col, pos - L.bufpos)
L.bufpos = pos
proc getAdornment(L: var TLexer, tok: var TToken) =
proc getAdornment(L: var Lexer, tok: var Token) =
tok.kind = tkAdornment
tok.line = L.line
tok.col = L.col
@@ -152,7 +155,7 @@ proc getAdornment(L: var TLexer, tok: var TToken) =
inc(L.col, pos - L.bufpos)
L.bufpos = pos
proc getIndentAux(L: var TLexer, start: int): int =
proc getIndentAux(L: var Lexer, start: int): int =
var pos = start
var buf = L.buf
# skip the newline (but include it in the token!)
@@ -181,7 +184,7 @@ proc getIndentAux(L: var TLexer, start: int): int =
result = getIndentAux(L, pos)
L.bufpos = pos # no need to set back buf
proc getIndent(L: var TLexer, tok: var TToken) =
proc getIndent(L: var Lexer, tok: var Token) =
tok.col = 0
tok.kind = tkIndent # skip the newline (but include it in the token!)
tok.ival = getIndentAux(L, L.bufpos)
@@ -191,7 +194,7 @@ proc getIndent(L: var TLexer, tok: var TToken) =
tok.ival = max(tok.ival - L.baseIndent, 0)
tok.symbol = "\n" & spaces(tok.ival)
proc rawGetTok(L: var TLexer, tok: var TToken) =
proc rawGetTok(L: var Lexer, tok: var Token) =
tok.symbol = ""
tok.ival = 0
var c = L.buf[L.bufpos]
@@ -222,8 +225,8 @@ proc rawGetTok(L: var TLexer, tok: var TToken) =
inc(L.col)
tok.col = max(tok.col - L.baseIndent, 0)
proc getTokens(buffer: string, skipPounds: bool, tokens: var TTokenSeq): int =
var L: TLexer
proc getTokens(buffer: string, skipPounds: bool, tokens: var TokenSeq): int =
var L: Lexer
var length = len(tokens)
L.buf = cstring(buffer)
L.line = 0 # skip UTF-8 BOM
@@ -253,31 +256,31 @@ proc getTokens(buffer: string, skipPounds: bool, tokens: var TTokenSeq): int =
tokens[0].kind = tkIndent
type
TLevelMap = array[char, int]
TSubstitution = object
LevelMap = array[char, int]
Substitution = object
key*: string
value*: PRstNode
TSharedState = object
options: TRstParseOptions # parsing options
SharedState = object
options: RstParseOptions # parsing options
uLevel, oLevel: int # counters for the section levels
subs: seq[TSubstitution] # substitutions
refs: seq[TSubstitution] # references
underlineToLevel: TLevelMap # Saves for each possible title adornment
subs: seq[Substitution] # substitutions
refs: seq[Substitution] # references
underlineToLevel: LevelMap # Saves for each possible title adornment
# character its level in the
# current document.
# This is for single underline adornments.
overlineToLevel: TLevelMap # Saves for each possible title adornment
overlineToLevel: LevelMap # Saves for each possible title adornment
# character its level in the current
# document.
# This is for over-underline adornments.
msgHandler: TMsgHandler # How to handle errors.
findFile: TFindFileHandler # How to find files.
msgHandler: MsgHandler # How to handle errors.
findFile: FindFileHandler # How to find files.
PSharedState = ref TSharedState
TRstParser = object of RootObj
PSharedState = ref SharedState
RstParser = object of RootObj
idx*: int
tok*: TTokenSeq
tok*: TokenSeq
s*: PSharedState
indentStack*: seq[int]
filename*: string
@@ -285,8 +288,12 @@ type
hasToc*: bool
EParseError* = object of ValueError
{.deprecated: [TLevelMap: LevelMap, TSubstitution: Substitution,
TSharedState: SharedState, TRstParser: RstParser,
TMsgHandler: MsgHandler, TFindFileHandler: FindFileHandler,
TMsgClass: MsgClass].}
proc whichMsgClass*(k: TMsgKind): TMsgClass =
proc whichMsgClass*(k: MsgKind): MsgClass =
## returns which message class `k` belongs to.
case ($k)[1]
of 'e', 'E': result = mcError
@@ -294,7 +301,7 @@ proc whichMsgClass*(k: TMsgKind): TMsgClass =
of 'h', 'H': result = mcHint
else: assert false, "msgkind does not fit naming scheme"
proc defaultMsgHandler*(filename: string, line, col: int, msgkind: TMsgKind,
proc defaultMsgHandler*(filename: string, line, col: int, msgkind: MsgKind,
arg: string) {.procvar.} =
let mc = msgkind.whichMsgClass
let a = messages[msgkind] % arg
@@ -306,9 +313,9 @@ proc defaultFindFile*(filename: string): string {.procvar.} =
if existsFile(filename): result = filename
else: result = ""
proc newSharedState(options: TRstParseOptions,
findFile: TFindFileHandler,
msgHandler: TMsgHandler): PSharedState =
proc newSharedState(options: RstParseOptions,
findFile: FindFileHandler,
msgHandler: MsgHandler): PSharedState =
new(result)
result.subs = @[]
result.refs = @[]
@@ -316,34 +323,34 @@ proc newSharedState(options: TRstParseOptions,
result.msgHandler = if not isNil(msgHandler): msgHandler else: defaultMsgHandler
result.findFile = if not isNil(findFile): findFile else: defaultFindFile
proc rstMessage(p: TRstParser, msgKind: TMsgKind, arg: string) =
proc rstMessage(p: RstParser, msgKind: MsgKind, arg: string) =
p.s.msgHandler(p.filename, p.line + p.tok[p.idx].line,
p.col + p.tok[p.idx].col, msgKind, arg)
proc rstMessage(p: TRstParser, msgKind: TMsgKind, arg: string, line, col: int) =
proc rstMessage(p: RstParser, msgKind: MsgKind, arg: string, line, col: int) =
p.s.msgHandler(p.filename, p.line + line,
p.col + col, msgKind, arg)
proc rstMessage(p: TRstParser, msgKind: TMsgKind) =
proc rstMessage(p: RstParser, msgKind: MsgKind) =
p.s.msgHandler(p.filename, p.line + p.tok[p.idx].line,
p.col + p.tok[p.idx].col, msgKind,
p.tok[p.idx].symbol)
when false:
proc corrupt(p: TRstParser) =
proc corrupt(p: RstParser) =
assert p.indentStack[0] == 0
for i in 1 .. high(p.indentStack): assert p.indentStack[i] < 1_000
proc currInd(p: TRstParser): int =
proc currInd(p: RstParser): int =
result = p.indentStack[high(p.indentStack)]
proc pushInd(p: var TRstParser, ind: int) =
proc pushInd(p: var RstParser, ind: int) =
add(p.indentStack, ind)
proc popInd(p: var TRstParser) =
proc popInd(p: var RstParser) =
if len(p.indentStack) > 1: setLen(p.indentStack, len(p.indentStack) - 1)
proc initParser(p: var TRstParser, sharedState: PSharedState) =
proc initParser(p: var RstParser, sharedState: PSharedState) =
p.indentStack = @[0]
p.tok = @[]
p.idx = 0
@@ -393,7 +400,7 @@ proc rstnodeToRefname(n: PRstNode): string =
var b = false
rstnodeToRefnameAux(n, result, b)
proc findSub(p: var TRstParser, n: PRstNode): int =
proc findSub(p: var RstParser, n: PRstNode): int =
var key = addNodes(n)
# the spec says: if no exact match, try one without case distinction:
for i in countup(0, high(p.s.subs)):
@@ -404,7 +411,7 @@ proc findSub(p: var TRstParser, n: PRstNode): int =
return i
result = -1
proc setSub(p: var TRstParser, key: string, value: PRstNode) =
proc setSub(p: var RstParser, key: string, value: PRstNode) =
var length = len(p.s.subs)
for i in countup(0, length - 1):
if key == p.s.subs[i].key:
@@ -414,7 +421,7 @@ proc setSub(p: var TRstParser, key: string, value: PRstNode) =
p.s.subs[length].key = key
p.s.subs[length].value = value
proc setRef(p: var TRstParser, key: string, value: PRstNode) =
proc setRef(p: var RstParser, key: string, value: PRstNode) =
var length = len(p.s.refs)
for i in countup(0, length - 1):
if key == p.s.refs[i].key:
@@ -427,15 +434,15 @@ proc setRef(p: var TRstParser, key: string, value: PRstNode) =
p.s.refs[length].key = key
p.s.refs[length].value = value
proc findRef(p: var TRstParser, key: string): PRstNode =
proc findRef(p: var RstParser, key: string): PRstNode =
for i in countup(0, high(p.s.refs)):
if key == p.s.refs[i].key:
return p.s.refs[i].value
proc newLeaf(p: var TRstParser): PRstNode =
proc newLeaf(p: var RstParser): PRstNode =
result = newRstNode(rnLeaf, p.tok[p.idx].symbol)
proc getReferenceName(p: var TRstParser, endStr: string): PRstNode =
proc getReferenceName(p: var RstParser, endStr: string): PRstNode =
var res = newRstNode(rnInner)
while true:
case p.tok[p.idx].kind
@@ -453,17 +460,17 @@ proc getReferenceName(p: var TRstParser, endStr: string): PRstNode =
inc(p.idx)
result = res
proc untilEol(p: var TRstParser): PRstNode =
proc untilEol(p: var RstParser): PRstNode =
result = newRstNode(rnInner)
while not (p.tok[p.idx].kind in {tkIndent, tkEof}):
add(result, newLeaf(p))
inc(p.idx)
proc expect(p: var TRstParser, tok: string) =
proc expect(p: var RstParser, tok: string) =
if p.tok[p.idx].symbol == tok: inc(p.idx)
else: rstMessage(p, meExpected, tok)
proc isInlineMarkupEnd(p: TRstParser, markup: string): bool =
proc isInlineMarkupEnd(p: RstParser, markup: string): bool =
result = p.tok[p.idx].symbol == markup
if not result:
return # Rule 3:
@@ -480,7 +487,7 @@ proc isInlineMarkupEnd(p: TRstParser, markup: string): bool =
if (markup != "``") and (p.tok[p.idx - 1].symbol == "\\"):
result = false
proc isInlineMarkupStart(p: TRstParser, markup: string): bool =
proc isInlineMarkupStart(p: RstParser, markup: string): bool =
var d: char
result = p.tok[p.idx].symbol == markup
if not result:
@@ -507,7 +514,7 @@ proc isInlineMarkupStart(p: TRstParser, markup: string): bool =
else: d = '\0'
if d != '\0': result = p.tok[p.idx + 1].symbol[0] != d
proc match(p: TRstParser, start: int, expr: string): bool =
proc match(p: RstParser, start: int, expr: string): bool =
# regular expressions are:
# special char exact match
# 'w' tkWord
@@ -562,7 +569,7 @@ proc fixupEmbeddedRef(n, a, b: PRstNode) =
for i in countup(0, sep - incr): add(a, n.sons[i])
for i in countup(sep + 1, len(n) - 2): add(b, n.sons[i])
proc parsePostfix(p: var TRstParser, n: PRstNode): PRstNode =
proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode =
result = n
if isInlineMarkupEnd(p, "_") or isInlineMarkupEnd(p, "__"):
inc(p.idx)
@@ -606,7 +613,7 @@ proc parsePostfix(p: var TRstParser, n: PRstNode): PRstNode =
add(result, newRstNode(rnLeaf, p.tok[p.idx + 1].symbol))
inc(p.idx, 3)
proc matchVerbatim(p: TRstParser, start: int, expr: string): int =
proc matchVerbatim(p: RstParser, start: int, expr: string): int =
result = start
var j = 0
while j < expr.len and result < p.tok.len and
@@ -615,7 +622,7 @@ proc matchVerbatim(p: TRstParser, start: int, expr: string): int =
inc result
if j < expr.len: result = 0
proc parseSmiley(p: var TRstParser): PRstNode =
proc parseSmiley(p: var RstParser): PRstNode =
if p.tok[p.idx].symbol[0] notin SmileyStartChars: return
for key, val in items(Smilies):
let m = matchVerbatim(p, p.idx, key)
@@ -631,12 +638,12 @@ when false:
'$', '(', ')', '~', '_', '?', '+', '-', '=', '\\', '.', '&',
'\128'..'\255'}
proc isUrl(p: TRstParser, i: int): bool =
proc isUrl(p: RstParser, i: int): bool =
result = (p.tok[i+1].symbol == ":") and (p.tok[i+2].symbol == "//") and
(p.tok[i+3].kind == tkWord) and
(p.tok[i].symbol in ["http", "https", "ftp", "telnet", "file"])
proc parseUrl(p: var TRstParser, father: PRstNode) =
proc parseUrl(p: var RstParser, father: PRstNode) =
#if p.tok[p.idx].symbol[strStart] == '<':
if isUrl(p, p.idx):
var n = newRstNode(rnStandaloneHyperlink)
@@ -656,7 +663,7 @@ proc parseUrl(p: var TRstParser, father: PRstNode) =
if p.tok[p.idx].symbol == "_": n = parsePostfix(p, n)
add(father, n)
proc parseBackslash(p: var TRstParser, father: PRstNode) =
proc parseBackslash(p: var RstParser, father: PRstNode) =
assert(p.tok[p.idx].kind == tkPunct)
if p.tok[p.idx].symbol == "\\\\":
add(father, newRstNode(rnLeaf, "\\"))
@@ -671,7 +678,7 @@ proc parseBackslash(p: var TRstParser, father: PRstNode) =
inc(p.idx)
when false:
proc parseAdhoc(p: var TRstParser, father: PRstNode, verbatim: bool) =
proc parseAdhoc(p: var RstParser, father: PRstNode, verbatim: bool) =
if not verbatim and isURL(p, p.idx):
var n = newRstNode(rnStandaloneHyperlink)
while true:
@@ -694,7 +701,7 @@ when false:
if p.tok[p.idx].symbol == "_": n = parsePostfix(p, n)
add(father, n)
proc parseUntil(p: var TRstParser, father: PRstNode, postfix: string,
proc parseUntil(p: var RstParser, father: PRstNode, postfix: string,
interpretBackslash: bool) =
let
line = p.tok[p.idx].line
@@ -725,7 +732,7 @@ proc parseUntil(p: var TRstParser, father: PRstNode, postfix: string,
inc(p.idx)
else: rstMessage(p, meExpected, postfix, line, col)
proc parseMarkdownCodeblock(p: var TRstParser): PRstNode =
proc parseMarkdownCodeblock(p: var RstParser): PRstNode =
var args = newRstNode(rnDirArg)
if p.tok[p.idx].kind == tkWord:
add(args, newLeaf(p))
@@ -755,7 +762,7 @@ proc parseMarkdownCodeblock(p: var TRstParser): PRstNode =
add(result, nil)
add(result, lb)
proc parseInline(p: var TRstParser, father: PRstNode) =
proc parseInline(p: var RstParser, father: PRstNode) =
case p.tok[p.idx].kind
of tkPunct:
if isInlineMarkupStart(p, "***"):
@@ -810,7 +817,7 @@ proc parseInline(p: var TRstParser, father: PRstNode) =
inc(p.idx)
else: discard
proc getDirective(p: var TRstParser): string =
proc getDirective(p: var RstParser): string =
if p.tok[p.idx].kind == tkWhite and p.tok[p.idx+1].kind == tkWord:
var j = p.idx
inc(p.idx)
@@ -830,7 +837,7 @@ proc getDirective(p: var TRstParser): string =
else:
result = ""
proc parseComment(p: var TRstParser): PRstNode =
proc parseComment(p: var RstParser): PRstNode =
case p.tok[p.idx].kind
of tkIndent, tkEof:
if p.tok[p.idx].kind != tkEof and p.tok[p.idx + 1].kind == tkIndent:
@@ -851,34 +858,35 @@ proc parseComment(p: var TRstParser): PRstNode =
result = nil
type
TDirKind = enum # must be ordered alphabetically!
DirKind = enum # must be ordered alphabetically!
dkNone, dkAuthor, dkAuthors, dkCode, dkCodeBlock, dkContainer, dkContents,
dkFigure, dkImage, dkInclude, dkIndex, dkRaw, dkTitle
{.deprecated: [TDirKind: DirKind].}
const
DirIds: array[0..12, string] = ["", "author", "authors", "code",
"code-block", "container", "contents", "figure", "image", "include",
"index", "raw", "title"]
proc getDirKind(s: string): TDirKind =
proc getDirKind(s: string): DirKind =
let i = find(DirIds, s)
if i >= 0: result = TDirKind(i)
if i >= 0: result = DirKind(i)
else: result = dkNone
proc parseLine(p: var TRstParser, father: PRstNode) =
proc parseLine(p: var RstParser, father: PRstNode) =
while true:
case p.tok[p.idx].kind
of tkWhite, tkWord, tkOther, tkPunct: parseInline(p, father)
else: break
proc parseUntilNewline(p: var TRstParser, father: PRstNode) =
proc parseUntilNewline(p: var RstParser, father: PRstNode) =
while true:
case p.tok[p.idx].kind
of tkWhite, tkWord, tkAdornment, tkOther, tkPunct: parseInline(p, father)
of tkEof, tkIndent: break
proc parseSection(p: var TRstParser, result: PRstNode) {.gcsafe.}
proc parseField(p: var TRstParser): PRstNode =
proc parseSection(p: var RstParser, result: PRstNode) {.gcsafe.}
proc parseField(p: var RstParser): PRstNode =
## Returns a parsed rnField node.
##
## rnField nodes have two children nodes, a rnFieldName and a rnFieldBody.
@@ -897,7 +905,7 @@ proc parseField(p: var TRstParser): PRstNode =
add(result, fieldname)
add(result, fieldbody)
proc parseFields(p: var TRstParser): PRstNode =
proc parseFields(p: var RstParser): PRstNode =
## Parses fields for a section or directive block.
##
## This proc may return nil if the parsing doesn't find anything of value,
@@ -947,8 +955,8 @@ proc getArgument(n: PRstNode): string =
if n.sons[0] == nil: result = ""
else: result = addNodes(n.sons[0])
proc parseDotDot(p: var TRstParser): PRstNode {.gcsafe.}
proc parseLiteralBlock(p: var TRstParser): PRstNode =
proc parseDotDot(p: var RstParser): PRstNode {.gcsafe.}
proc parseLiteralBlock(p: var RstParser): PRstNode =
result = newRstNode(rnLiteralBlock)
var n = newRstNode(rnLeaf, "")
if p.tok[p.idx].kind == tkIndent:
@@ -974,13 +982,13 @@ proc parseLiteralBlock(p: var TRstParser): PRstNode =
inc(p.idx)
add(result, n)
proc getLevel(map: var TLevelMap, lvl: var int, c: char): int =
proc getLevel(map: var LevelMap, lvl: var int, c: char): int =
if map[c] == 0:
inc(lvl)
map[c] = lvl
result = map[c]
proc tokenAfterNewline(p: TRstParser): int =
proc tokenAfterNewline(p: RstParser): int =
result = p.idx
while true:
case p.tok[result].kind
@@ -991,28 +999,28 @@ proc tokenAfterNewline(p: TRstParser): int =
break
else: inc(result)
proc isLineBlock(p: TRstParser): bool =
proc isLineBlock(p: RstParser): bool =
var j = tokenAfterNewline(p)
result = (p.tok[p.idx].col == p.tok[j].col) and (p.tok[j].symbol == "|") or
(p.tok[j].col > p.tok[p.idx].col)
proc predNL(p: TRstParser): bool =
proc predNL(p: RstParser): bool =
result = true
if p.idx > 0:
result = p.tok[p.idx-1].kind == tkIndent and
p.tok[p.idx-1].ival == currInd(p)
proc isDefList(p: TRstParser): bool =
proc isDefList(p: RstParser): bool =
var j = tokenAfterNewline(p)
result = (p.tok[p.idx].col < p.tok[j].col) and
(p.tok[j].kind in {tkWord, tkOther, tkPunct}) and
(p.tok[j - 2].symbol != "::")
proc isOptionList(p: TRstParser): bool =
proc isOptionList(p: RstParser): bool =
result = match(p, p.idx, "-w") or match(p, p.idx, "--w") or
match(p, p.idx, "/w") or match(p, p.idx, "//w")
proc whichSection(p: TRstParser): TRstNodeKind =
proc whichSection(p: RstParser): RstNodeKind =
case p.tok[p.idx].kind
of tkAdornment:
if match(p, p.idx + 1, "ii"): result = rnTransition
@@ -1053,7 +1061,7 @@ proc whichSection(p: TRstParser): TRstNodeKind =
else: result = rnParagraph
else: result = rnLeaf
proc parseLineBlock(p: var TRstParser): PRstNode =
proc parseLineBlock(p: var RstParser): PRstNode =
result = nil
if p.tok[p.idx + 1].kind == tkWhite:
var col = p.tok[p.idx].col
@@ -1072,7 +1080,7 @@ proc parseLineBlock(p: var TRstParser): PRstNode =
break
popInd(p)
proc parseParagraph(p: var TRstParser, result: PRstNode) =
proc parseParagraph(p: var RstParser, result: PRstNode) =
while true:
case p.tok[p.idx].kind
of tkIndent:
@@ -1103,7 +1111,7 @@ proc parseParagraph(p: var TRstParser, result: PRstNode) =
parseInline(p, result)
else: break
proc parseHeadline(p: var TRstParser): PRstNode =
proc parseHeadline(p: var RstParser): PRstNode =
result = newRstNode(rnHeadline)
parseUntilNewline(p, result)
assert(p.tok[p.idx].kind == tkIndent)
@@ -1113,12 +1121,13 @@ proc parseHeadline(p: var TRstParser): PRstNode =
result.level = getLevel(p.s.underlineToLevel, p.s.uLevel, c)
type
TIntSeq = seq[int]
IntSeq = seq[int]
{.deprecated: [TIntSeq: IntSeq].}
proc tokEnd(p: TRstParser): int =
proc tokEnd(p: RstParser): int =
result = p.tok[p.idx].col + len(p.tok[p.idx].symbol) - 1
proc getColumns(p: var TRstParser, cols: var TIntSeq) =
proc getColumns(p: var RstParser, cols: var IntSeq) =
var L = 0
while true:
inc(L)
@@ -1133,15 +1142,15 @@ proc getColumns(p: var TRstParser, cols: var TIntSeq) =
# last column has no limit:
cols[L - 1] = 32000
proc parseDoc(p: var TRstParser): PRstNode {.gcsafe.}
proc parseDoc(p: var RstParser): PRstNode {.gcsafe.}
proc parseSimpleTable(p: var TRstParser): PRstNode =
proc parseSimpleTable(p: var RstParser): PRstNode =
var
cols: TIntSeq
cols: IntSeq
row: seq[string]
i, last, line: int
c: char
q: TRstParser
q: RstParser
a, b: PRstNode
result = newRstNode(rnTable)
cols = @[]
@@ -1188,13 +1197,13 @@ proc parseSimpleTable(p: var TRstParser): PRstNode =
add(a, b)
add(result, a)
proc parseTransition(p: var TRstParser): PRstNode =
proc parseTransition(p: var RstParser): PRstNode =
result = newRstNode(rnTransition)
inc(p.idx)
if p.tok[p.idx].kind == tkIndent: inc(p.idx)
if p.tok[p.idx].kind == tkIndent: inc(p.idx)
proc parseOverline(p: var TRstParser): PRstNode =
proc parseOverline(p: var RstParser): PRstNode =
var c = p.tok[p.idx].symbol[0]
inc(p.idx, 2)
result = newRstNode(rnOverline)
@@ -1213,7 +1222,7 @@ proc parseOverline(p: var TRstParser): PRstNode =
inc(p.idx) # XXX: check?
if p.tok[p.idx].kind == tkIndent: inc(p.idx)
proc parseBulletList(p: var TRstParser): PRstNode =
proc parseBulletList(p: var RstParser): PRstNode =
result = nil
if p.tok[p.idx + 1].kind == tkWhite:
var bullet = p.tok[p.idx].symbol
@@ -1233,7 +1242,7 @@ proc parseBulletList(p: var TRstParser): PRstNode =
break
popInd(p)
proc parseOptionList(p: var TRstParser): PRstNode =
proc parseOptionList(p: var RstParser): PRstNode =
result = newRstNode(rnOptionList)
while true:
if isOptionList(p):
@@ -1262,7 +1271,7 @@ proc parseOptionList(p: var TRstParser): PRstNode =
else:
break
proc parseDefinitionList(p: var TRstParser): PRstNode =
proc parseDefinitionList(p: var RstParser): PRstNode =
result = nil
var j = tokenAfterNewline(p) - 1
if (j >= 1) and (p.tok[j].kind == tkIndent) and
@@ -1298,7 +1307,7 @@ proc parseDefinitionList(p: var TRstParser): PRstNode =
break
if len(result) == 0: result = nil
proc parseEnumList(p: var TRstParser): PRstNode =
proc parseEnumList(p: var RstParser): PRstNode =
const
wildcards: array[0..2, string] = ["(e) ", "e) ", "e. "]
wildpos: array[0..2, int] = [1, 0, 0]
@@ -1328,11 +1337,11 @@ proc parseEnumList(p: var TRstParser): PRstNode =
dec(p.idx, wildpos[w] + 3)
result = nil
proc sonKind(father: PRstNode, i: int): TRstNodeKind =
proc sonKind(father: PRstNode, i: int): RstNodeKind =
result = rnLeaf
if i < len(father): result = father.sons[i].kind
proc parseSection(p: var TRstParser, result: PRstNode) =
proc parseSection(p: var RstParser, result: PRstNode) =
while true:
var leave = false
assert(p.idx >= 0)
@@ -1380,16 +1389,16 @@ proc parseSection(p: var TRstParser, result: PRstNode) =
if sonKind(result, 0) == rnParagraph and sonKind(result, 1) != rnParagraph:
result.sons[0].kind = rnInner
proc parseSectionWrapper(p: var TRstParser): PRstNode =
proc parseSectionWrapper(p: var RstParser): PRstNode =
result = newRstNode(rnInner)
parseSection(p, result)
while (result.kind == rnInner) and (len(result) == 1):
result = result.sons[0]
proc `$`(t: TToken): string =
proc `$`(t: Token): string =
result = $t.kind & ' ' & (if isNil(t.symbol): "NIL" else: t.symbol)
proc parseDoc(p: var TRstParser): PRstNode =
proc parseDoc(p: var RstParser): PRstNode =
result = parseSectionWrapper(p)
if p.tok[p.idx].kind != tkEof:
when false:
@@ -1403,12 +1412,14 @@ proc parseDoc(p: var TRstParser): PRstNode =
rstMessage(p, meGeneralParseError)
type
TDirFlag = enum
DirFlag = enum
hasArg, hasOptions, argIsFile, argIsWord
TDirFlags = set[TDirFlag]
TSectionParser = proc (p: var TRstParser): PRstNode {.nimcall.}
DirFlags = set[DirFlag]
SectionParser = proc (p: var RstParser): PRstNode {.nimcall.}
{.deprecated: [TDirFlag: DirFlag, TDirFlags: DirFlags,
TSectionParser: SectionParser].}
proc parseDirective(p: var TRstParser, flags: TDirFlags): PRstNode =
proc parseDirective(p: var RstParser, flags: DirFlags): PRstNode =
## Parses arguments and options for a directive block.
##
## A directive block will always have three sons: the arguments for the
@@ -1446,11 +1457,11 @@ proc parseDirective(p: var TRstParser, flags: TDirFlags): PRstNode =
options = parseFields(p)
add(result, options)
proc indFollows(p: TRstParser): bool =
proc indFollows(p: RstParser): bool =
result = p.tok[p.idx].kind == tkIndent and p.tok[p.idx].ival > currInd(p)
proc parseDirective(p: var TRstParser, flags: TDirFlags,
contentParser: TSectionParser): PRstNode =
proc parseDirective(p: var RstParser, flags: DirFlags,
contentParser: SectionParser): PRstNode =
## Returns a generic rnDirective tree.
##
## The children are rnDirArg, rnFieldList and rnLineBlock. Any might be nil.
@@ -1463,13 +1474,13 @@ proc parseDirective(p: var TRstParser, flags: TDirFlags,
else:
add(result, nil)
proc parseDirBody(p: var TRstParser, contentParser: TSectionParser): PRstNode =
proc parseDirBody(p: var RstParser, contentParser: SectionParser): PRstNode =
if indFollows(p):
pushInd(p, p.tok[p.idx].ival)
result = contentParser(p)
popInd(p)
proc dirInclude(p: var TRstParser): PRstNode =
proc dirInclude(p: var RstParser): PRstNode =
#
#The following options are recognized:
#
@@ -1498,7 +1509,7 @@ proc dirInclude(p: var TRstParser): PRstNode =
result = newRstNode(rnLiteralBlock)
add(result, newRstNode(rnLeaf, readFile(path)))
else:
var q: TRstParser
var q: RstParser
initParser(q, p.s)
q.filename = filename
q.col += getTokens(readFile(path), false, q.tok)
@@ -1507,7 +1518,7 @@ proc dirInclude(p: var TRstParser): PRstNode =
# InternalError("Too many binary zeros in include file")
result = parseDoc(q)
proc dirCodeBlock(p: var TRstParser, nimrodExtension = false): PRstNode =
proc dirCodeBlock(p: var RstParser, nimrodExtension = false): PRstNode =
## Parses a code block.
##
## Code blocks are rnDirective trees with a `kind` of rnCodeBlock. See the
@@ -1548,35 +1559,35 @@ proc dirCodeBlock(p: var TRstParser, nimrodExtension = false): PRstNode =
result.kind = rnCodeBlock
proc dirContainer(p: var TRstParser): PRstNode =
proc dirContainer(p: var RstParser): PRstNode =
result = parseDirective(p, {hasArg}, parseSectionWrapper)
assert(result.kind == rnDirective)
assert(len(result) == 3)
result.kind = rnContainer
proc dirImage(p: var TRstParser): PRstNode =
proc dirImage(p: var RstParser): PRstNode =
result = parseDirective(p, {hasOptions, hasArg, argIsFile}, nil)
result.kind = rnImage
proc dirFigure(p: var TRstParser): PRstNode =
proc dirFigure(p: var RstParser): PRstNode =
result = parseDirective(p, {hasOptions, hasArg, argIsFile},
parseSectionWrapper)
result.kind = rnFigure
proc dirTitle(p: var TRstParser): PRstNode =
proc dirTitle(p: var RstParser): PRstNode =
result = parseDirective(p, {hasArg}, nil)
result.kind = rnTitle
proc dirContents(p: var TRstParser): PRstNode =
proc dirContents(p: var RstParser): PRstNode =
result = parseDirective(p, {hasArg}, nil)
result.kind = rnContents
proc dirIndex(p: var TRstParser): PRstNode =
proc dirIndex(p: var RstParser): PRstNode =
result = parseDirective(p, {}, parseSectionWrapper)
result.kind = rnIndex
proc dirRawAux(p: var TRstParser, result: var PRstNode, kind: TRstNodeKind,
contentParser: TSectionParser) =
proc dirRawAux(p: var RstParser, result: var PRstNode, kind: RstNodeKind,
contentParser: SectionParser) =
var filename = getFieldValue(result, "file")
if filename.len > 0:
var path = p.s.findFile(filename)
@@ -1590,7 +1601,7 @@ proc dirRawAux(p: var TRstParser, result: var PRstNode, kind: TRstNodeKind,
result.kind = kind
add(result, parseDirBody(p, contentParser))
proc dirRaw(p: var TRstParser): PRstNode =
proc dirRaw(p: var RstParser): PRstNode =
#
#The following options are recognized:
#
@@ -1610,7 +1621,7 @@ proc dirRaw(p: var TRstParser): PRstNode =
else:
dirRawAux(p, result, rnRaw, parseSectionWrapper)
proc parseDotDot(p: var TRstParser): PRstNode =
proc parseDotDot(p: var RstParser): PRstNode =
result = nil
var col = p.tok[p.idx].col
inc(p.idx)
@@ -1667,7 +1678,7 @@ proc parseDotDot(p: var TRstParser): PRstNode =
else:
result = parseComment(p)
proc resolveSubs(p: var TRstParser, n: PRstNode): PRstNode =
proc resolveSubs(p: var RstParser, n: PRstNode): PRstNode =
result = n
if n == nil: return
case n.kind
@@ -1696,10 +1707,10 @@ proc resolveSubs(p: var TRstParser, n: PRstNode): PRstNode =
proc rstParse*(text, filename: string,
line, column: int, hasToc: var bool,
options: TRstParseOptions,
findFile: TFindFileHandler = nil,
msgHandler: TMsgHandler = nil): PRstNode =
var p: TRstParser
options: RstParseOptions,
findFile: FindFileHandler = nil,
msgHandler: MsgHandler = nil): PRstNode =
var p: RstParser
initParser(p, newSharedState(options, findFile, msgHandler))
p.filename = filename
p.line = line

View File

@@ -12,7 +12,7 @@
import strutils, json
type
TRstNodeKind* = enum ## the possible node kinds of an PRstNode
RstNodeKind* = enum ## the possible node kinds of an PRstNode
rnInner, # an inner node or a root
rnHeadline, # a headline
rnOverline, # an over- and underlined headline
@@ -62,24 +62,26 @@ type
# leaf val
PRstNode* = ref TRstNode ## an RST node
TRstNodeSeq* = seq[PRstNode]
TRstNode* {.acyclic, final.} = object ## an RST node's description
kind*: TRstNodeKind ## the node's kind
PRstNode* = ref RstNode ## an RST node
RstNodeSeq* = seq[PRstNode]
RstNode* {.acyclic, final.} = object ## an RST node's description
kind*: RstNodeKind ## the node's kind
text*: string ## valid for leafs in the AST; and the title of
## the document or the section
level*: int ## valid for some node kinds
sons*: TRstNodeSeq ## the node's sons
sons*: RstNodeSeq ## the node's sons
{.deprecated: [TRstNodeKind: RstNodeKind, TRstNodeSeq: RstNodeSeq,
TRstNode: RstNode].}
proc len*(n: PRstNode): int =
result = len(n.sons)
proc newRstNode*(kind: TRstNodeKind): PRstNode =
proc newRstNode*(kind: RstNodeKind): PRstNode =
new(result)
result.sons = @[]
result.kind = kind
proc newRstNode*(kind: TRstNodeKind, s: string): PRstNode =
proc newRstNode*(kind: RstNodeKind, s: string): PRstNode =
result = newRstNode(kind)
result.text = s
@@ -94,18 +96,19 @@ proc addIfNotNil*(father, son: PRstNode) =
type
TRenderContext {.pure.} = object
RenderContext {.pure.} = object
indent: int
verbatim: int
{.deprecated: [TRenderContext: RenderContext].}
proc renderRstToRst(d: var TRenderContext, n: PRstNode,
proc renderRstToRst(d: var RenderContext, n: PRstNode,
result: var string) {.gcsafe.}
proc renderRstSons(d: var TRenderContext, n: PRstNode, result: var string) =
proc renderRstSons(d: var RenderContext, n: PRstNode, result: var string) =
for i in countup(0, len(n) - 1):
renderRstToRst(d, n.sons[i], result)
proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) =
proc renderRstToRst(d: var RenderContext, n: PRstNode, result: var string) =
# this is needed for the index generation; it may also be useful for
# debugging, but most code is already debugged...
const
@@ -284,7 +287,7 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) =
proc renderRstToRst*(n: PRstNode, result: var string) =
## renders `n` into its string representation and appends to `result`.
var d: TRenderContext
var d: RenderContext
renderRstToRst(d, n, result)
proc renderRstToJsonNode(node: PRstNode): JsonNode =

View File

@@ -18,7 +18,7 @@
## document you provide yourself, so it won't contain the usual ``<header>`` or
## ``<body>`` parts.
##
## You can also create a ``TRstGenerator`` structure and populate it with the
## You can also create a ``RstGenerator`` structure and populate it with the
## other lower level methods to finally build complete documents. This requires
## many options and tweaking, but you are not limited to snippets and can
## generate `LaTeX documents <https://en.wikipedia.org/wiki/LaTeX>`_ too.
@@ -31,29 +31,29 @@ const
IndexExt* = ".idx"
type
TOutputTarget* = enum ## which document type to generate
OutputTarget* = enum ## which document type to generate
outHtml, # output is HTML
outLatex # output is Latex
TTocEntry = object
TocEntry = object
n*: PRstNode
refname*, header*: string
TMetaEnum* = enum
MetaEnum* = enum
metaNone, metaTitle, metaSubtitle, metaAuthor, metaVersion
TRstGenerator* = object of RootObj
target*: TOutputTarget
RstGenerator* = object of RootObj
target*: OutputTarget
config*: StringTableRef
splitAfter*: int # split too long entries in the TOC
tocPart*: seq[TTocEntry]
tocPart*: seq[TocEntry]
hasToc*: bool
theIndex: string # Contents of the index file to be dumped at the end.
options*: TRstParseOptions
findFile*: TFindFileHandler
msgHandler*: TMsgHandler
options*: RstParseOptions
findFile*: FindFileHandler
msgHandler*: MsgHandler
filename*: string
meta*: array[TMetaEnum, string]
meta*: array[MetaEnum, string]
currentSection: string ## \
## Stores the empty string or the last headline/overline found in the rst
## document, so it can be used as a prettier name for term index generation.
@@ -61,14 +61,15 @@ type
## Keeps count of same text index terms to generate different identifiers
## for hyperlinks. See renderIndexTerm proc for details.
PDoc = var TRstGenerator ## Alias to type less.
PDoc = var RstGenerator ## Alias to type less.
CodeBlockParams = object ## Stores code block params.
numberLines: bool ## True if the renderer has to show line numbers.
startLine: int ## The starting line of the code block, by default 1.
langStr: string ## Input string used to specify the language.
lang: TSourceLanguage ## Type of highlighting, by default none.
lang: SourceLanguage ## Type of highlighting, by default none.
{.deprecated: [TRstGenerator: RstGenerator, TTocEntry: TocEntry,
TOutputTarget: OutputTarget, TMetaEnum: MetaEnum].}
proc init(p: var CodeBlockParams) =
## Default initialisation of CodeBlockParams to sane values.
@@ -76,14 +77,14 @@ proc init(p: var CodeBlockParams) =
p.lang = langNone
p.langStr = ""
proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget,
proc initRstGenerator*(g: var RstGenerator, target: OutputTarget,
config: StringTableRef, filename: string,
options: TRstParseOptions,
findFile: TFindFileHandler=nil,
msgHandler: TMsgHandler=nil) =
## Initializes a ``TRstGenerator``.
options: RstParseOptions,
findFile: FindFileHandler=nil,
msgHandler: MsgHandler=nil) =
## Initializes a ``RstGenerator``.
##
## You need to call this before using a ``TRstGenerator`` with any other
## You need to call this before using a ``RstGenerator`` with any other
## procs in this module. Pass a non ``nil`` ``StringTableRef`` value as
## `config` with parameters used by the HTML output generator. If you don't
## know what to use, pass the results of the `defaultConfig()
@@ -96,7 +97,7 @@ proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget,
## filename``. This default title can be overriden by the embedded rst, but
## it helps to prettify the generated index if no title is found.
##
## The ``TRstParseOptions``, ``TFindFileHandler`` and ``TMsgHandler`` types
## The ``RstParseOptions``, ``FindFileHandler`` and ``MsgHandler`` types
## are defined in the the `packages/docutils/rst module <rst.html>`_.
## ``options`` selects the behaviour of the rst parser.
##
@@ -120,7 +121,7 @@ proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget,
##
## import packages/docutils/rstgen
##
## var gen: TRstGenerator
## var gen: RstGenerator
## gen.initRstGenerator(outHtml, defaultConfig(), "filename", {})
g.config = config
g.target = target
@@ -141,7 +142,7 @@ proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget,
if s != "": g.splitAfter = parseInt(s)
for i in low(g.meta)..high(g.meta): g.meta[i] = ""
proc writeIndexFile*(g: var TRstGenerator, outfile: string) =
proc writeIndexFile*(g: var RstGenerator, outfile: string) =
## Writes the current index buffer to the specified output file.
##
## You previously need to add entries to the index with the `setIndexTerm()
@@ -183,7 +184,7 @@ proc addTexChar(dest: var string, c: char) =
var splitter*: string = "<wbr />"
proc escChar*(target: TOutputTarget, dest: var string, c: char) {.inline.} =
proc escChar*(target: OutputTarget, dest: var string, c: char) {.inline.} =
case target
of outHtml: addXmlChar(dest, c)
of outLatex: addTexChar(dest, c)
@@ -200,7 +201,7 @@ proc nextSplitPoint*(s: string, start: int): int =
inc(result)
dec(result) # last valid index
proc esc*(target: TOutputTarget, s: string, splitAfter = -1): string =
proc esc*(target: OutputTarget, s: string, splitAfter = -1): string =
result = ""
if splitAfter >= 0:
var partLen = 0
@@ -217,16 +218,16 @@ proc esc*(target: TOutputTarget, s: string, splitAfter = -1): string =
for i in countup(0, len(s) - 1): escChar(target, result, s[i])
proc disp(target: TOutputTarget, xml, tex: string): string =
proc disp(target: OutputTarget, xml, tex: string): string =
if target != outLatex: result = xml
else: result = tex
proc dispF(target: TOutputTarget, xml, tex: string,
proc dispF(target: OutputTarget, xml, tex: string,
args: varargs[string]): string =
if target != outLatex: result = xml % args
else: result = tex % args
proc dispA(target: TOutputTarget, dest: var string,
proc dispA(target: OutputTarget, dest: var string,
xml, tex: string, args: varargs[string]) =
if target != outLatex: addf(dest, xml, args)
else: addf(dest, tex, args)
@@ -234,10 +235,10 @@ proc dispA(target: TOutputTarget, dest: var string,
proc `or`(x, y: string): string {.inline.} =
result = if x.isNil: y else: x
proc renderRstToOut*(d: var TRstGenerator, n: PRstNode, result: var string)
proc renderRstToOut*(d: var RstGenerator, n: PRstNode, result: var string)
## Writes into ``result`` the rst ast ``n`` using the ``d`` configuration.
##
## Before using this proc you need to initialise a ``TRstGenerator`` with
## Before using this proc you need to initialise a ``RstGenerator`` with
## ``initRstGenerator`` and parse a rst file with ``rstParse`` from the
## `packages/docutils/rst module <rst.html>`_. Example:
##
@@ -277,7 +278,7 @@ proc unquoteIndexColumn(text: string): string =
## Returns the unquoted version generated by ``quoteIndexColumn``.
result = text.replace("\\t", "\t").replace("\\n", "\n").replace("\\\\", "\\")
proc setIndexTerm*(d: var TRstGenerator, id, term: string,
proc setIndexTerm*(d: var RstGenerator, id, term: string,
linkTitle, linkDesc = "") =
## Adds a `term` to the index using the specified hyperlink identifier.
##
@@ -351,30 +352,30 @@ proc renderIndexTerm*(d: PDoc, n: PRstNode, result: var string) =
[id, term])
type
TIndexEntry = object
IndexEntry = object
keyword: string
link: string
linkTitle: string ## If not nil, contains a prettier text for the href
linkDesc: string ## If not nil, the title attribute of the final href
TIndexedDocs = Table[TIndexEntry, seq[TIndexEntry]] ## \
IndexedDocs = Table[IndexEntry, seq[IndexEntry]] ## \
## Contains the index sequences for doc types.
##
## The key is a *fake* TIndexEntry which will contain the title of the
## The key is a *fake* IndexEntry which will contain the title of the
## document in the `keyword` field and `link` will contain the html
## filename for the document. `linkTitle` and `linkDesc` will be nil.
##
## The value indexed by this TIndexEntry is a sequence with the real index
## The value indexed by this IndexEntry is a sequence with the real index
## entries found in the ``.idx`` file.
{.deprecated: [TIndexEntry: IndexEntry, TIndexedDocs: IndexedDocs].}
proc cmp(a, b: TIndexEntry): int =
## Sorts two ``TIndexEntry`` first by `keyword` field, then by `link`.
proc cmp(a, b: IndexEntry): int =
## Sorts two ``IndexEntry`` first by `keyword` field, then by `link`.
result = cmpIgnoreStyle(a.keyword, b.keyword)
if result == 0:
result = cmpIgnoreStyle(a.link, b.link)
proc hash(x: TIndexEntry): THash =
proc hash(x: IndexEntry): Hash =
## Returns the hash for the combined fields of the type.
##
## The hash is computed as the chained hash of the individual string hashes.
@@ -385,7 +386,7 @@ proc hash(x: TIndexEntry): THash =
result = result !& (x.linkDesc or "").hash
result = !$result
proc `<-`(a: var TIndexEntry, b: TIndexEntry) =
proc `<-`(a: var IndexEntry, b: IndexEntry) =
shallowCopy a.keyword, b.keyword
shallowCopy a.link, b.link
if b.linkTitle.isNil: a.linkTitle = nil
@@ -393,7 +394,7 @@ proc `<-`(a: var TIndexEntry, b: TIndexEntry) =
if b.linkDesc.isNil: a.linkDesc = nil
else: shallowCopy a.linkDesc, b.linkDesc
proc sortIndex(a: var openArray[TIndexEntry]) =
proc sortIndex(a: var openArray[IndexEntry]) =
# we use shellsort here; fast and simple
let n = len(a)
var h = 1
@@ -403,7 +404,7 @@ proc sortIndex(a: var openArray[TIndexEntry]) =
while true:
h = h div 3
for i in countup(h, n - 1):
var v: TIndexEntry
var v: IndexEntry
v <- a[i]
var j = i
while cmp(a[j-h], v) >= 0:
@@ -413,7 +414,7 @@ proc sortIndex(a: var openArray[TIndexEntry]) =
a[j] <- v
if h == 1: break
proc generateSymbolIndex(symbols: seq[TIndexEntry]): string =
proc generateSymbolIndex(symbols: seq[IndexEntry]): string =
result = ""
var i = 0
while i < symbols.len:
@@ -466,7 +467,7 @@ proc indentToLevel(level: var int, newLevel: int): string =
result = repeat("</ul>", level - newLevel)
level = newLevel
proc generateDocumentationTOC(entries: seq[TIndexEntry]): string =
proc generateDocumentationTOC(entries: seq[IndexEntry]): string =
## Returns the sequence of index entries in an HTML hierarchical list.
result = ""
# Build a list of levels and extracted titles to make processing easier.
@@ -507,12 +508,12 @@ proc generateDocumentationTOC(entries: seq[TIndexEntry]): string =
assert(not titleRef.isNil,
"Can't use this proc on an API index, docs always have a title entry")
proc generateDocumentationIndex(docs: TIndexedDocs): string =
proc generateDocumentationIndex(docs: IndexedDocs): string =
## Returns all the documentation TOCs in an HTML hierarchical list.
result = ""
# Sort the titles to generate their toc in alphabetical order.
var titles = toSeq(keys[TIndexEntry, seq[TIndexEntry]](docs))
var titles = toSeq(keys[IndexEntry, seq[IndexEntry]](docs))
sort(titles, cmp)
for title in titles:
@@ -520,12 +521,12 @@ proc generateDocumentationIndex(docs: TIndexedDocs): string =
result.add("<ul><li><a href=\"" &
title.link & "\">" & title.keyword & "</a>\n" & tocList & "</ul>\n")
proc generateDocumentationJumps(docs: TIndexedDocs): string =
proc generateDocumentationJumps(docs: IndexedDocs): string =
## Returns a plain list of hyperlinks to documentation TOCs in HTML.
result = "Documents: "
# Sort the titles to generate their toc in alphabetical order.
var titles = toSeq(keys[TIndexEntry, seq[TIndexEntry]](docs))
var titles = toSeq(keys[IndexEntry, seq[IndexEntry]](docs))
sort(titles, cmp)
var chunks: seq[string] = @[]
@@ -545,14 +546,14 @@ proc generateModuleJumps(modules: seq[string]): string =
result.add(chunks.join(", ") & ".<br>")
proc readIndexDir(dir: string):
tuple[modules: seq[string], symbols: seq[TIndexEntry], docs: TIndexedDocs] =
## Walks `dir` reading ``.idx`` files converting them in TIndexEntry items.
tuple[modules: seq[string], symbols: seq[IndexEntry], docs: IndexedDocs] =
## Walks `dir` reading ``.idx`` files converting them in IndexEntry items.
##
## Returns the list of found module names, the list of free symbol entries
## and the different documentation indexes. The list of modules is sorted.
## See the documentation of ``mergeIndexes`` for details.
result.modules = @[]
result.docs = initTable[TIndexEntry, seq[TIndexEntry]](32)
result.docs = initTable[IndexEntry, seq[IndexEntry]](32)
newSeq(result.symbols, 15_000)
setLen(result.symbols, 0)
var L = 0
@@ -560,8 +561,8 @@ proc readIndexDir(dir: string):
for kind, path in walkDir(dir):
if kind == pcFile and path.endsWith(IndexExt):
var
fileEntries: seq[TIndexEntry]
title: TIndexEntry
fileEntries: seq[IndexEntry]
title: IndexEntry
F = 0
newSeq(fileEntries, 500)
setLen(fileEntries, 0)
@@ -662,7 +663,7 @@ proc mergeIndexes*(dir: string): string =
proc stripTOCHTML(s: string): string =
## Ugly quick hack to remove HTML tags from TOC titles.
##
## A TTocEntry.header field already contains rendered HTML tags. Instead of
## A TocEntry.header field already contains rendered HTML tags. Instead of
## implementing a proper version of renderRstToOut() which recursively
## renders an rst tree to plain text, we simply remove text found between
## angled brackets. Given the limited possibilities of rst inside TOC titles
@@ -728,12 +729,12 @@ proc renderOverline(d: PDoc, n: PRstNode, result: var string) =
rstnodeToRefname(n), tmp, $chr(n.level - 1 + ord('A'))])
proc renderTocEntry(d: PDoc, e: TTocEntry, result: var string) =
proc renderTocEntry(d: PDoc, e: TocEntry, result: var string) =
dispA(d.target, result,
"<li><a class=\"reference\" id=\"$1_toc\" href=\"#$1\">$2</a></li>\n",
"\\item\\label{$1_toc} $2\\ref{$1}\n", [e.refname, e.header])
proc renderTocEntries*(d: var TRstGenerator, j: var int, lvl: int,
proc renderTocEntries*(d: var RstGenerator, j: var int, lvl: int,
result: var string) =
var tmp = ""
while j <= high(d.tocPart):
@@ -878,7 +879,7 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) =
d.msgHandler(d.filename, 1, 0, mwUnsupportedLanguage, params.langStr)
for letter in m.text: escChar(d.target, result, letter)
else:
var g: TGeneralTokenizer
var g: GeneralTokenizer
initGeneralTokenizer(g, m.text)
while true:
getNextToken(g, params.lang)
@@ -1214,7 +1215,7 @@ $content
# ---------- forum ---------------------------------------------------------
proc rstToHtml*(s: string, options: TRstParseOptions,
proc rstToHtml*(s: string, options: RstParseOptions,
config: StringTableRef): string =
## Converts an input rst string into embeddable HTML.
##
@@ -1233,7 +1234,7 @@ proc rstToHtml*(s: string, options: TRstParseOptions,
## # --> <em>Hello</em> <strong>world</strong>!
##
## If you need to allow the rst ``include`` directive or tweak the generated
## output you have to create your own ``TRstGenerator`` with
## output you have to create your own ``RstGenerator`` with
## ``initRstGenerator`` and related procs.
proc myFindFile(filename: string): string =
@@ -1241,7 +1242,7 @@ proc rstToHtml*(s: string, options: TRstParseOptions,
result = ""
const filen = "input"
var d: TRstGenerator
var d: RstGenerator
initRstGenerator(d, outHtml, config, filen, options, myFindFile,
rst.defaultMsgHandler)
var dummyHasToc = false

View File

@@ -12,14 +12,15 @@
# Get the platform-dependent flags.
# Structure describing an inotify event.
type
Tinotify_event*{.pure, final, importc: "struct inotify_event",
InotifyEvent*{.pure, final, importc: "struct inotify_event",
header: "<sys/inotify.h>".} = object
wd*{.importc: "wd".}: cint # Watch descriptor.
mask*{.importc: "mask".}: uint32 # Watch mask.
cookie*{.importc: "cookie".}: uint32 # Cookie to synchronize two events.
len*{.importc: "len".}: uint32 # Length (including NULs) of name.
name*{.importc: "name".}: char # Name.
{.deprecated: [Tinotify_event: InotifyEvent].}
# Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH.
const
IN_ACCESS* = 0x00000001 # File was accessed.
@@ -69,4 +70,4 @@ proc inotify_add_watch*(fd: cint; name: cstring; mask: uint32): cint{.
cdecl, importc: "inotify_add_watch", header: "<sys/inotify.h>".}
# Remove the watch specified by WD from the inotify instance FD.
proc inotify_rm_watch*(fd: cint; wd: cint): cint{.cdecl,
importc: "inotify_rm_watch", header: "<sys/inotify.h>".}
importc: "inotify_rm_watch", header: "<sys/inotify.h>".}

View File

@@ -24,5 +24,5 @@ const
# fn should be of type proc (a2: pointer): void {.cdecl.}
proc clone*(fn: pointer; child_stack: pointer; flags: cint;
arg: pointer; ptid: ptr TPid; tls: pointer;
ctid: ptr TPid): cint {.importc, header: "<sched.h>".}
arg: pointer; ptid: ptr Pid; tls: pointer;
ctid: ptr Pid): cint {.importc, header: "<sched.h>".}

File diff suppressed because it is too large Load Diff

View File

@@ -12,17 +12,18 @@ import posix
type
Speed* = cuint
Tcflag* = cuint
Cflag* = cuint
{.deprecated: [Tcflag: Cflag].}
const
NCCS* = 32
type
Termios* {.importc: "struct termios", header: "<termios.h>".} = object
c_iflag*: Tcflag # input mode flags
c_oflag*: Tcflag # output mode flags
c_cflag*: Tcflag # control mode flags
c_lflag*: Tcflag # local mode flags
c_iflag*: Cflag # input mode flags
c_oflag*: Cflag # output mode flags
c_cflag*: Cflag # control mode flags
c_lflag*: Cflag # local mode flags
c_line*: cuchar # line discipline
c_cc*: array[NCCS, cuchar] # control characters
@@ -258,4 +259,4 @@ proc tcFlow*(fd: cint; action: cint): cint {.importc: "tcflow",
header: "<termios.h>".}
# Get process group ID for session leader for controlling terminal FD.
proc tcGetSid*(fd: cint): TPid {.importc: "tcgetsid", header: "<termios.h>".}
proc tcGetSid*(fd: cint): Pid {.importc: "tcgetsid", header: "<termios.h>".}

View File

@@ -16,7 +16,7 @@
## .. code-block:: nim
##
## var
## a: TActorPool[int, void]
## a: ActorPool[int, void]
## createActorPool(a)
## for i in 0 .. < 300:
## a.spawn(i, proc (x: int) {.thread.} = echo x)
@@ -30,75 +30,76 @@
from os import sleep
type
TTask*[TIn, TOut] = object{.pure, final.} ## a task
when TOut isnot void:
receiver*: ptr TChannel[TOut] ## the receiver channel of the response
action*: proc (x: TIn): TOut {.thread.} ## action to execute;
Task*[In, Out] = object{.pure, final.} ## a task
when Out isnot void:
receiver*: ptr Channel[Out] ## the receiver channel of the response
action*: proc (x: In): Out {.thread.} ## action to execute;
## sometimes useful
shutDown*: bool ## set to tell an actor to shut-down
data*: TIn ## the data to process
data*: In ## the data to process
TActor[TIn, TOut] = object{.pure, final.}
i: TChannel[TTask[TIn, TOut]]
t: TThread[ptr TActor[TIn, TOut]]
Actor[In, Out] = object{.pure, final.}
i: Channel[Task[In, Out]]
t: TThread[ptr Actor[In, Out]]
PActor*[TIn, TOut] = ptr TActor[TIn, TOut] ## an actor
proc spawn*[TIn, TOut](action: proc(
self: PActor[TIn, TOut]){.thread.}): PActor[TIn, TOut] =
PActor*[In, Out] = ptr Actor[In, Out] ## an actor
{.deprecated: [TTask: Task, TActor: Actor].}
proc spawn*[In, Out](action: proc(
self: PActor[In, Out]){.thread.}): PActor[In, Out] =
## creates an actor; that is a thread with an inbox. The caller MUST call
## ``join`` because that also frees the actor's associated resources.
result = cast[PActor[TIn, TOut]](allocShared0(sizeof(result[])))
result = cast[PActor[In, Out]](allocShared0(sizeof(result[])))
open(result.i)
createThread(result.t, action, result)
proc inbox*[TIn, TOut](self: PActor[TIn, TOut]): ptr TChannel[TIn] =
proc inbox*[In, Out](self: PActor[In, Out]): ptr Channel[In] =
## gets a pointer to the associated inbox of the actor `self`.
result = addr(self.i)
proc running*[TIn, TOut](a: PActor[TIn, TOut]): bool =
proc running*[In, Out](a: PActor[In, Out]): bool =
## returns true if the actor `a` is running.
result = running(a.t)
proc ready*[TIn, TOut](a: PActor[TIn, TOut]): bool =
proc ready*[In, Out](a: PActor[In, Out]): bool =
## returns true if the actor `a` is ready to process new messages.
result = ready(a.i)
proc join*[TIn, TOut](a: PActor[TIn, TOut]) =
proc join*[In, Out](a: PActor[In, Out]) =
## joins an actor.
joinThread(a.t)
close(a.i)
deallocShared(a)
proc recv*[TIn, TOut](a: PActor[TIn, TOut]): TTask[TIn, TOut] =
proc recv*[In, Out](a: PActor[In, Out]): Task[In, Out] =
## receives a task from `a`'s inbox.
result = recv(a.i)
proc send*[TIn, TOut, X, Y](receiver: PActor[TIn, TOut], msg: TIn,
proc send*[In, Out, X, Y](receiver: PActor[In, Out], msg: In,
sender: PActor[X, Y]) =
## sends a message to `a`'s inbox.
var t: TTask[TIn, TOut]
var t: Task[In, Out]
t.receiver = addr(sender.i)
shallowCopy(t.data, msg)
send(receiver.i, t)
proc send*[TIn, TOut](receiver: PActor[TIn, TOut], msg: TIn,
sender: ptr TChannel[TOut] = nil) =
proc send*[In, Out](receiver: PActor[In, Out], msg: In,
sender: ptr Channel[Out] = nil) =
## sends a message to `receiver`'s inbox.
var t: TTask[TIn, TOut]
var t: Task[In, Out]
t.receiver = sender
shallowCopy(t.data, msg)
send(receiver.i, t)
proc sendShutdown*[TIn, TOut](receiver: PActor[TIn, TOut]) =
proc sendShutdown*[In, Out](receiver: PActor[In, Out]) =
## send a shutdown message to `receiver`.
var t: TTask[TIn, TOut]
var t: Task[In, Out]
t.shutdown = true
send(receiver.i, t)
proc reply*[TIn, TOut](t: TTask[TIn, TOut], m: TOut) =
proc reply*[In, Out](t: Task[In, Out], m: Out) =
## sends a message to io's output message box.
when TOut is void:
when Out is void:
{.error: "you cannot reply to a void outbox".}
assert t.receiver != nil
send(t.receiver[], m)
@@ -107,34 +108,35 @@ proc reply*[TIn, TOut](t: TTask[TIn, TOut], m: TOut) =
# ----------------- actor pools ----------------------------------------------
type
TActorPool*[TIn, TOut] = object{.pure, final.} ## an actor pool
actors: seq[PActor[TIn, TOut]]
when TOut isnot void:
outputs: TChannel[TOut]
ActorPool*[In, Out] = object{.pure, final.} ## an actor pool
actors: seq[PActor[In, Out]]
when Out isnot void:
outputs: Channel[Out]
{.deprecated: [TActorPool: ActorPool].}
proc `^`*[T](f: ptr TChannel[T]): T =
proc `^`*[T](f: ptr Channel[T]): T =
## alias for 'recv'.
result = recv(f[])
proc poolWorker[TIn, TOut](self: PActor[TIn, TOut]) {.thread.} =
proc poolWorker[In, Out](self: PActor[In, Out]) {.thread.} =
while true:
var m = self.recv
if m.shutDown: break
when TOut is void:
when Out is void:
m.action(m.data)
else:
send(m.receiver[], m.action(m.data))
#self.reply()
proc createActorPool*[TIn, TOut](a: var TActorPool[TIn, TOut], poolSize = 4) =
proc createActorPool*[In, Out](a: var ActorPool[In, Out], poolSize = 4) =
## creates an actor pool.
newSeq(a.actors, poolSize)
when TOut isnot void:
when Out isnot void:
open(a.outputs)
for i in 0 .. < a.actors.len:
a.actors[i] = spawn(poolWorker[TIn, TOut])
a.actors[i] = spawn(poolWorker[In, Out])
proc sync*[TIn, TOut](a: var TActorPool[TIn, TOut], polling=50) =
proc sync*[In, Out](a: var ActorPool[In, Out], polling=50) =
## waits for every actor of `a` to finish with its work. Currently this is
## implemented as polling every `polling` ms and has a slight chance
## of failing since we check for every actor to be in `ready` state and not
@@ -157,18 +159,18 @@ proc sync*[TIn, TOut](a: var TActorPool[TIn, TOut], polling=50) =
if allReadyCount > 1: break
sleep(polling)
proc terminate*[TIn, TOut](a: var TActorPool[TIn, TOut]) =
proc terminate*[In, Out](a: var ActorPool[In, Out]) =
## terminates each actor in the actor pool `a` and frees the
## resources attached to `a`.
var t: TTask[TIn, TOut]
var t: Task[In, Out]
t.shutdown = true
for i in 0.. <a.actors.len: send(a.actors[i].i, t)
for i in 0.. <a.actors.len: join(a.actors[i])
when TOut isnot void:
when Out isnot void:
close(a.outputs)
a.actors = nil
proc join*[TIn, TOut](a: var TActorPool[TIn, TOut]) =
proc join*[In, Out](a: var ActorPool[In, Out]) =
## short-cut for `sync` and then `terminate`.
sync(a)
terminate(a)
@@ -202,28 +204,28 @@ template schedule =
else:
raise newException(DeadThreadError, "cannot send message; thread died")
proc spawn*[TIn, TOut](p: var TActorPool[TIn, TOut], input: TIn,
action: proc (input: TIn): TOut {.thread.}
): ptr TChannel[TOut] =
proc spawn*[In, Out](p: var ActorPool[In, Out], input: In,
action: proc (input: In): Out {.thread.}
): ptr Channel[Out] =
## uses the actor pool to run ``action(input)`` concurrently.
## `spawn` is guaranteed to not block.
var t: TTask[TIn, TOut]
var t: Task[In, Out]
setupTask()
result = addr(p.outputs)
t.receiver = result
schedule()
proc spawn*[TIn](p: var TActorPool[TIn, void], input: TIn,
action: proc (input: TIn) {.thread.}) =
proc spawn*[In](p: var ActorPool[In, void], input: In,
action: proc (input: In) {.thread.}) =
## uses the actor pool to run ``action(input)`` concurrently.
## `spawn` is guaranteed to not block.
var t: TTask[TIn, void]
var t: Task[In, void]
setupTask()
schedule()
when not defined(testing) and isMainModule:
var
a: TActorPool[int, void]
a: ActorPool[int, void]
createActorPool(a)
for i in 0 .. < 300:
a.spawn(i, proc (x: int) {.thread.} = echo x)

View File

@@ -99,16 +99,13 @@ proc lowerBound*[T](a: openArray[T], key: T, cmp: proc(x,y: T): int {.closure.})
## arr.insert(4, arr.lowerBound(4))
## `after running the above arr is `[1,2,3,4,5,6,7,8,9]`
result = a.low
var pos = result
var count, step: int
count = a.high - a.low + 1
var count = a.high - a.low + 1
var step, pos: int
while count != 0:
pos = result
step = count div 2
pos += step
pos = result + step
if cmp(a[pos], key) < 0:
pos.inc
result = pos
result = pos + 1
count -= step + 1
else:
count = step
@@ -331,3 +328,16 @@ proc prevPermutation*[T](x: var openarray[T]): bool {.discardable.} =
swap x[i-1], x[j]
result = true
when isMainModule:
# Tests for lowerBound
var arr = @[1,2,3,5,6,7,8,9]
assert arr.lowerBound(0) == 0
assert arr.lowerBound(4) == 3
assert arr.lowerBound(5) == 3
assert arr.lowerBound(10) == 8
arr = @[1,5,10]
assert arr.lowerBound(4) == 1
assert arr.lowerBound(5) == 1
assert arr.lowerBound(6) == 2

View File

@@ -323,32 +323,34 @@ proc processTimers(p: PDispatcherBase) =
when defined(windows) or defined(nimdoc):
import winlean, sets, hashes
type
TCompletionKey = Dword
CompletionKey = Dword
TCompletionData* = object
fd*: TAsyncFD # TODO: Rename this.
cb*: proc (fd: TAsyncFD, bytesTransferred: Dword,
CompletionData* = object
fd*: AsyncFD # TODO: Rename this.
cb*: proc (fd: AsyncFD, bytesTransferred: Dword,
errcode: OSErrorCode) {.closure,gcsafe.}
PDispatcher* = ref object of PDispatcherBase
ioPort: THandle
handles: HashSet[TAsyncFD]
ioPort: Handle
handles: HashSet[AsyncFD]
TCustomOverlapped = object of TOVERLAPPED
data*: TCompletionData
CustomOverlapped = object of TOVERLAPPED
data*: CompletionData
PCustomOverlapped* = ref TCustomOverlapped
PCustomOverlapped* = ref CustomOverlapped
TAsyncFD* = distinct int
AsyncFD* = distinct int
{.deprecated: [TCompletionKey: CompletionKey, TAsyncFD: AsyncFD,
TCustomOverlapped: CustomOverlapped, TCompletionData: CompletionData].}
proc hash(x: TAsyncFD): THash {.borrow.}
proc `==`*(x: TAsyncFD, y: TAsyncFD): bool {.borrow.}
proc hash(x: AsyncFD): Hash {.borrow.}
proc `==`*(x: AsyncFD, y: AsyncFD): bool {.borrow.}
proc newDispatcher*(): PDispatcher =
## Creates a new Dispatcher instance.
new result
result.ioPort = createIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1)
result.handles = initSet[TAsyncFD]()
result.handles = initSet[AsyncFD]()
result.timers = @[]
var gDisp{.threadvar.}: PDispatcher ## Global dispatcher
@@ -357,15 +359,15 @@ when defined(windows) or defined(nimdoc):
if gDisp.isNil: gDisp = newDispatcher()
result = gDisp
proc register*(fd: TAsyncFD) =
proc register*(fd: AsyncFD) =
## Registers ``fd`` with the dispatcher.
let p = getGlobalDispatcher()
if createIoCompletionPort(fd.THandle, p.ioPort,
cast[TCompletionKey](fd), 1) == 0:
if createIoCompletionPort(fd.Handle, p.ioPort,
cast[CompletionKey](fd), 1) == 0:
raiseOSError(osLastError())
p.handles.incl(fd)
proc verifyPresence(fd: TAsyncFD) =
proc verifyPresence(fd: AsyncFD) =
## Ensures that file descriptor has been registered with the dispatcher.
let p = getGlobalDispatcher()
if fd notin p.handles:
@@ -394,7 +396,7 @@ when defined(windows) or defined(nimdoc):
# TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html
if res:
# This is useful for ensuring the reliability of the overlapped struct.
assert customOverlapped.data.fd == lpCompletionKey.TAsyncFD
assert customOverlapped.data.fd == lpCompletionKey.AsyncFD
customOverlapped.data.cb(customOverlapped.data.fd,
lpNumberOfBytesTransferred, OSErrorCode(-1))
@@ -402,7 +404,7 @@ when defined(windows) or defined(nimdoc):
else:
let errCode = osLastError()
if customOverlapped != nil:
assert customOverlapped.data.fd == lpCompletionKey.TAsyncFD
assert customOverlapped.data.fd == lpCompletionKey.AsyncFD
customOverlapped.data.cb(customOverlapped.data.fd,
lpNumberOfBytesTransferred, errCode)
GC_unref(customOverlapped)
@@ -480,7 +482,7 @@ when defined(windows) or defined(nimdoc):
dwRemoteAddressLength, LocalSockaddr, LocalSockaddrLength,
RemoteSockaddr, RemoteSockaddrLength)
proc connect*(socket: TAsyncFD, address: string, port: Port,
proc connect*(socket: AsyncFD, address: string, port: Port,
af = AF_INET): Future[void] =
## Connects ``socket`` to server at ``address:port``.
##
@@ -506,8 +508,8 @@ when defined(windows) or defined(nimdoc):
# http://blogs.msdn.com/b/oldnewthing/archive/2011/02/02/10123392.aspx
var ol = PCustomOverlapped()
GC_ref(ol)
ol.data = TCompletionData(fd: socket, cb:
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
ol.data = CompletionData(fd: socket, cb:
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
if not retFuture.finished:
if errcode == OSErrorCode(-1):
retFuture.complete()
@@ -542,7 +544,7 @@ when defined(windows) or defined(nimdoc):
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
return retFuture
proc recv*(socket: TAsyncFD, size: int,
proc recv*(socket: AsyncFD, size: int,
flags = {SocketFlag.SafeDisconn}): Future[string] =
## Reads **up to** ``size`` bytes from ``socket``. Returned future will
## complete once all the data requested is read, a part of the data has been
@@ -570,8 +572,8 @@ when defined(windows) or defined(nimdoc):
var flagsio = flags.toOSFlags().Dword
var ol = PCustomOverlapped()
GC_ref(ol)
ol.data = TCompletionData(fd: socket, cb:
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
ol.data = CompletionData(fd: socket, cb:
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
if not retFuture.finished:
if errcode == OSErrorCode(-1):
if bytesCount == 0 and dataBuf.buf[0] == '\0':
@@ -634,7 +636,7 @@ when defined(windows) or defined(nimdoc):
# free ``ol``.
return retFuture
proc recvInto*(socket: TAsyncFD, buf: cstring, size: int,
proc recvInto*(socket: AsyncFD, buf: cstring, size: int,
flags = {SocketFlag.SafeDisconn}): Future[int] =
## Reads **up to** ``size`` bytes from ``socket`` into ``buf``, which must
## at least be of that size. Returned future will complete once all the
@@ -665,8 +667,8 @@ when defined(windows) or defined(nimdoc):
var flagsio = flags.toOSFlags().Dword
var ol = PCustomOverlapped()
GC_ref(ol)
ol.data = TCompletionData(fd: socket, cb:
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
ol.data = CompletionData(fd: socket, cb:
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
if not retFuture.finished:
if errcode == OSErrorCode(-1):
if bytesCount == 0 and dataBuf.buf[0] == '\0':
@@ -721,7 +723,7 @@ when defined(windows) or defined(nimdoc):
# free ``ol``.
return retFuture
proc send*(socket: TAsyncFD, data: string,
proc send*(socket: AsyncFD, data: string,
flags = {SocketFlag.SafeDisconn}): Future[void] =
## Sends ``data`` to ``socket``. The returned future will complete once all
## data has been sent.
@@ -735,8 +737,8 @@ when defined(windows) or defined(nimdoc):
var bytesReceived, lowFlags: Dword
var ol = PCustomOverlapped()
GC_ref(ol)
ol.data = TCompletionData(fd: socket, cb:
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
ol.data = CompletionData(fd: socket, cb:
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
if not retFuture.finished:
if errcode == OSErrorCode(-1):
retFuture.complete()
@@ -764,8 +766,8 @@ when defined(windows) or defined(nimdoc):
# free ``ol``.
return retFuture
proc acceptAddr*(socket: TAsyncFD, flags = {SocketFlag.SafeDisconn}):
Future[tuple[address: string, client: TAsyncFD]] =
proc acceptAddr*(socket: AsyncFD, flags = {SocketFlag.SafeDisconn}):
Future[tuple[address: string, client: AsyncFD]] =
## Accepts a new connection. Returns a future containing the client socket
## corresponding to that connection and the remote address of the client.
## The future will complete when the connection is successfully accepted.
@@ -778,7 +780,7 @@ when defined(windows) or defined(nimdoc):
## flag is specified then this error will not be raised and instead
## accept will be called again.
verifyPresence(socket)
var retFuture = newFuture[tuple[address: string, client: TAsyncFD]]("acceptAddr")
var retFuture = newFuture[tuple[address: string, client: AsyncFD]]("acceptAddr")
var clientSock = newRawSocket()
if clientSock == osInvalidSocket: raiseOSError(osLastError())
@@ -803,11 +805,11 @@ when defined(windows) or defined(nimdoc):
dwLocalAddressLength, dwRemoteAddressLength,
addr localSockaddr, addr localLen,
addr remoteSockaddr, addr remoteLen)
register(clientSock.TAsyncFD)
register(clientSock.AsyncFD)
# TODO: IPv6. Check ``sa_family``. http://stackoverflow.com/a/9212542/492186
retFuture.complete(
(address: $inet_ntoa(cast[ptr Sockaddr_in](remoteSockAddr).sin_addr),
client: clientSock.TAsyncFD)
client: clientSock.AsyncFD)
)
template failAccept(errcode): stmt =
@@ -824,8 +826,8 @@ when defined(windows) or defined(nimdoc):
var ol = PCustomOverlapped()
GC_ref(ol)
ol.data = TCompletionData(fd: socket, cb:
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
ol.data = CompletionData(fd: socket, cb:
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
if not retFuture.finished:
if errcode == OSErrorCode(-1):
completeAccept()
@@ -853,26 +855,26 @@ when defined(windows) or defined(nimdoc):
return retFuture
proc newAsyncRawSocket*(domain, typ, protocol: cint): TAsyncFD =
proc newAsyncRawSocket*(domain, typ, protocol: cint): AsyncFD =
## Creates a new socket and registers it with the dispatcher implicitly.
result = newRawSocket(domain, typ, protocol).TAsyncFD
result = newRawSocket(domain, typ, protocol).AsyncFD
result.SocketHandle.setBlocking(false)
register(result)
proc newAsyncRawSocket*(domain: Domain = AF_INET,
typ: SockType = SOCK_STREAM,
protocol: Protocol = IPPROTO_TCP): TAsyncFD =
protocol: Protocol = IPPROTO_TCP): AsyncFD =
## Creates a new socket and registers it with the dispatcher implicitly.
result = newRawSocket(domain, typ, protocol).TAsyncFD
result = newRawSocket(domain, typ, protocol).AsyncFD
result.SocketHandle.setBlocking(false)
register(result)
proc closeSocket*(socket: TAsyncFD) =
proc closeSocket*(socket: AsyncFD) =
## Closes a socket and ensures that it is unregistered.
socket.SocketHandle.close()
getGlobalDispatcher().handles.excl(socket)
proc unregister*(fd: TAsyncFD) =
proc unregister*(fd: AsyncFD) =
## Unregisters ``fd``.
getGlobalDispatcher().handles.excl(fd)
@@ -892,18 +894,19 @@ else:
MSG_NOSIGNAL
type
TAsyncFD* = distinct cint
TCallback = proc (fd: TAsyncFD): bool {.closure,gcsafe.}
AsyncFD* = distinct cint
Callback = proc (fd: AsyncFD): bool {.closure,gcsafe.}
PData* = ref object of RootRef
fd: TAsyncFD
readCBs: seq[TCallback]
writeCBs: seq[TCallback]
fd: AsyncFD
readCBs: seq[Callback]
writeCBs: seq[Callback]
PDispatcher* = ref object of PDispatcherBase
selector: Selector
{.deprecated: [TAsyncFD: AsyncFD, TCallback: Callback].}
proc `==`*(x, y: TAsyncFD): bool {.borrow.}
proc `==`*(x, y: AsyncFD): bool {.borrow.}
proc newDispatcher*(): PDispatcher =
new result
@@ -915,18 +918,18 @@ else:
if gDisp.isNil: gDisp = newDispatcher()
result = gDisp
proc update(fd: TAsyncFD, events: set[Event]) =
proc update(fd: AsyncFD, events: set[Event]) =
let p = getGlobalDispatcher()
assert fd.SocketHandle in p.selector
discard p.selector.update(fd.SocketHandle, events)
proc register*(fd: TAsyncFD) =
proc register*(fd: AsyncFD) =
let p = getGlobalDispatcher()
var data = PData(fd: fd, readCBs: @[], writeCBs: @[])
p.selector.register(fd.SocketHandle, {}, data.RootRef)
proc newAsyncRawSocket*(domain: cint, typ: cint, protocol: cint): TAsyncFD =
result = newRawSocket(domain, typ, protocol).TAsyncFD
proc newAsyncRawSocket*(domain: cint, typ: cint, protocol: cint): AsyncFD =
result = newRawSocket(domain, typ, protocol).AsyncFD
result.SocketHandle.setBlocking(false)
when defined(macosx):
result.SocketHandle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1)
@@ -934,29 +937,29 @@ else:
proc newAsyncRawSocket*(domain: Domain = AF_INET,
typ: SockType = SOCK_STREAM,
protocol: Protocol = IPPROTO_TCP): TAsyncFD =
result = newRawSocket(domain, typ, protocol).TAsyncFD
protocol: Protocol = IPPROTO_TCP): AsyncFD =
result = newRawSocket(domain, typ, protocol).AsyncFD
result.SocketHandle.setBlocking(false)
when defined(macosx):
result.SocketHandle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1)
register(result)
proc closeSocket*(sock: TAsyncFD) =
proc closeSocket*(sock: AsyncFD) =
let disp = getGlobalDispatcher()
sock.SocketHandle.close()
disp.selector.unregister(sock.SocketHandle)
proc unregister*(fd: TAsyncFD) =
proc unregister*(fd: AsyncFD) =
getGlobalDispatcher().selector.unregister(fd.SocketHandle)
proc addRead*(fd: TAsyncFD, cb: TCallback) =
proc addRead*(fd: AsyncFD, cb: Callback) =
let p = getGlobalDispatcher()
if fd.SocketHandle notin p.selector:
raise newException(ValueError, "File descriptor not registered.")
p.selector[fd.SocketHandle].data.PData.readCBs.add(cb)
update(fd, p.selector[fd.SocketHandle].events + {EvRead})
proc addWrite*(fd: TAsyncFD, cb: TCallback) =
proc addWrite*(fd: AsyncFD, cb: Callback) =
let p = getGlobalDispatcher()
if fd.SocketHandle notin p.selector:
raise newException(ValueError, "File descriptor not registered.")
@@ -967,7 +970,7 @@ else:
let p = getGlobalDispatcher()
for info in p.selector.select(timeout):
let data = PData(info.key.data)
assert data.fd == info.key.fd.TAsyncFD
assert data.fd == info.key.fd.AsyncFD
#echo("In poll ", data.fd.cint)
if EvError in info.events:
closeSocket(data.fd)
@@ -1005,11 +1008,11 @@ else:
processTimers(p)
proc connect*(socket: TAsyncFD, address: string, port: Port,
proc connect*(socket: AsyncFD, address: string, port: Port,
af = AF_INET): Future[void] =
var retFuture = newFuture[void]("connect")
proc cb(fd: TAsyncFD): bool =
proc cb(fd: AsyncFD): bool =
# We have connected.
retFuture.complete()
return true
@@ -1040,13 +1043,13 @@ else:
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
return retFuture
proc recv*(socket: TAsyncFD, size: int,
proc recv*(socket: AsyncFD, size: int,
flags = {SocketFlag.SafeDisconn}): Future[string] =
var retFuture = newFuture[string]("recv")
var readBuffer = newString(size)
proc cb(sock: TAsyncFD): bool =
proc cb(sock: AsyncFD): bool =
result = true
let res = recv(sock.SocketHandle, addr readBuffer[0], size.cint,
flags.toOSFlags())
@@ -1070,11 +1073,11 @@ else:
addRead(socket, cb)
return retFuture
proc recvInto*(socket: TAsyncFD, buf: cstring, size: int,
proc recvInto*(socket: AsyncFD, buf: cstring, size: int,
flags = {SocketFlag.SafeDisconn}): Future[int] =
var retFuture = newFuture[int]("recvInto")
proc cb(sock: TAsyncFD): bool =
proc cb(sock: AsyncFD): bool =
result = true
let res = recv(sock.SocketHandle, buf, size.cint,
flags.toOSFlags())
@@ -1094,13 +1097,13 @@ else:
addRead(socket, cb)
return retFuture
proc send*(socket: TAsyncFD, data: string,
proc send*(socket: AsyncFD, data: string,
flags = {SocketFlag.SafeDisconn}): Future[void] =
var retFuture = newFuture[void]("send")
var written = 0
proc cb(sock: TAsyncFD): bool =
proc cb(sock: AsyncFD): bool =
result = true
let netSize = data.len-written
var d = data.cstring
@@ -1126,11 +1129,11 @@ else:
addWrite(socket, cb)
return retFuture
proc acceptAddr*(socket: TAsyncFD, flags = {SocketFlag.SafeDisconn}):
Future[tuple[address: string, client: TAsyncFD]] =
proc acceptAddr*(socket: AsyncFD, flags = {SocketFlag.SafeDisconn}):
Future[tuple[address: string, client: AsyncFD]] =
var retFuture = newFuture[tuple[address: string,
client: TAsyncFD]]("acceptAddr")
proc cb(sock: TAsyncFD): bool =
client: AsyncFD]]("acceptAddr")
proc cb(sock: AsyncFD): bool =
result = true
var sockAddress: SockAddr_in
var addrLen = sizeof(sockAddress).Socklen
@@ -1147,8 +1150,8 @@ else:
else:
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
else:
register(client.TAsyncFD)
retFuture.complete(($inet_ntoa(sockAddress.sin_addr), client.TAsyncFD))
register(client.AsyncFD)
retFuture.complete(($inet_ntoa(sockAddress.sin_addr), client.AsyncFD))
addRead(socket, cb)
return retFuture
@@ -1160,15 +1163,15 @@ proc sleepAsync*(ms: int): Future[void] =
p.timers.add((epochTime() + (ms / 1000), retFuture))
return retFuture
proc accept*(socket: TAsyncFD,
flags = {SocketFlag.SafeDisconn}): Future[TAsyncFD] =
proc accept*(socket: AsyncFD,
flags = {SocketFlag.SafeDisconn}): Future[AsyncFD] =
## Accepts a new connection. Returns a future containing the client socket
## corresponding to that connection.
## The future will complete when the connection is successfully accepted.
var retFut = newFuture[TAsyncFD]("accept")
var retFut = newFuture[AsyncFD]("accept")
var fut = acceptAddr(socket, flags)
fut.callback =
proc (future: Future[tuple[address: string, client: TAsyncFD]]) =
proc (future: Future[tuple[address: string, client: AsyncFD]]) =
assert future.finished
if future.failed:
retFut.fail(future.error)
@@ -1495,7 +1498,7 @@ macro async*(prc: stmt): stmt {.immediate.} =
#if prc[0].getName == "test":
# echo(toStrLit(result))
proc recvLine*(socket: TAsyncFD): Future[string] {.async.} =
proc recvLine*(socket: AsyncFD): Future[string] {.async.} =
## Reads a line of data from ``socket``. Returned future will complete once
## a full line is read or an error occurs.
##

View File

@@ -31,7 +31,7 @@ else:
type
AsyncFile* = ref object
fd: TAsyncFd
fd: AsyncFd
offset: int64
when defined(windows) or defined(nimdoc):
@@ -72,7 +72,7 @@ proc getFileSize(f: AsyncFile): int64 =
## Retrieves the specified file's size.
when defined(windows) or defined(nimdoc):
var high: DWord
let low = getFileSize(f.fd.THandle, addr high)
let low = getFileSize(f.fd.Handle, addr high)
if low == INVALID_FILE_SIZE:
raiseOSError(osLastError())
return (high shl 32) or low
@@ -88,13 +88,13 @@ proc openAsync*(filename: string, mode = fmRead): AsyncFile =
when useWinUnicode:
result.fd = createFileW(newWideCString(filename), desiredAccess,
FILE_SHARE_READ,
nil, creationDisposition, flags, 0).TAsyncFd
nil, creationDisposition, flags, 0).AsyncFd
else:
result.fd = createFileA(filename, desiredAccess,
FILE_SHARE_READ,
nil, creationDisposition, flags, 0).TAsyncFd
nil, creationDisposition, flags, 0).AsyncFd
if result.fd.THandle == INVALID_HANDLE_VALUE:
if result.fd.Handle == INVALID_HANDLE_VALUE:
raiseOSError(osLastError())
register(result.fd)
@@ -106,7 +106,7 @@ proc openAsync*(filename: string, mode = fmRead): AsyncFile =
let flags = getPosixFlags(mode)
# RW (Owner), RW (Group), R (Other)
let perm = S_IRUSR or S_IWUSR or S_IRGRP or S_IWGRP or S_IROTH
result.fd = open(filename, flags, perm).TAsyncFD
result.fd = open(filename, flags, perm).AsyncFD
if result.fd.cint == -1:
raiseOSError(osLastError())
@@ -125,8 +125,8 @@ proc read*(f: AsyncFile, size: int): Future[string] =
var ol = PCustomOverlapped()
GC_ref(ol)
ol.data = TCompletionData(fd: f.fd, cb:
proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
ol.data = CompletionData(fd: f.fd, cb:
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
if not retFuture.finished:
if errcode == OSErrorCode(-1):
assert bytesCount > 0
@@ -148,7 +148,7 @@ proc read*(f: AsyncFile, size: int): Future[string] =
ol.offsetHigh = DWord(f.offset shr 32)
# According to MSDN we're supposed to pass nil to lpNumberOfBytesRead.
let ret = readFile(f.fd.THandle, buffer, size.int32, nil,
let ret = readFile(f.fd.Handle, buffer, size.int32, nil,
cast[POVERLAPPED](ol))
if not ret.bool:
let err = osLastError()
@@ -161,7 +161,7 @@ proc read*(f: AsyncFile, size: int): Future[string] =
else:
# Request completed immediately.
var bytesRead: DWord
let overlappedRes = getOverlappedResult(f.fd.THandle,
let overlappedRes = getOverlappedResult(f.fd.Handle,
cast[POverlapped](ol)[], bytesRead, false.WinBool)
if not overlappedRes.bool:
let err = osLastError()
@@ -179,7 +179,7 @@ proc read*(f: AsyncFile, size: int): Future[string] =
else:
var readBuffer = newString(size)
proc cb(fd: TAsyncFD): bool =
proc cb(fd: AsyncFD): bool =
result = true
let res = read(fd.cint, addr readBuffer[0], size.cint)
if res < 0:
@@ -251,8 +251,8 @@ proc write*(f: AsyncFile, data: string): Future[void] =
var ol = PCustomOverlapped()
GC_ref(ol)
ol.data = TCompletionData(fd: f.fd, cb:
proc (fd: TAsyncFD, bytesCount: DWord, errcode: OSErrorCode) =
ol.data = CompletionData(fd: f.fd, cb:
proc (fd: AsyncFD, bytesCount: DWord, errcode: OSErrorCode) =
if not retFuture.finished:
if errcode == OSErrorCode(-1):
assert bytesCount == data.len.int32
@@ -268,7 +268,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
ol.offsetHigh = DWord(f.offset shr 32)
# According to MSDN we're supposed to pass nil to lpNumberOfBytesWritten.
let ret = writeFile(f.fd.THandle, buffer, data.len.int32, nil,
let ret = writeFile(f.fd.Handle, buffer, data.len.int32, nil,
cast[POVERLAPPED](ol))
if not ret.bool:
let err = osLastError()
@@ -281,7 +281,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
else:
# Request completed immediately.
var bytesWritten: DWord
let overlappedRes = getOverlappedResult(f.fd.THandle,
let overlappedRes = getOverlappedResult(f.fd.Handle,
cast[POverlapped](ol)[], bytesWritten, false.WinBool)
if not overlappedRes.bool:
retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
@@ -292,7 +292,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
else:
var written = 0
proc cb(fd: TAsyncFD): bool =
proc cb(fd: AsyncFD): bool =
result = true
let remainderSize = data.len-written
let res = write(fd.cint, addr copy[written], remainderSize.cint)
@@ -317,7 +317,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
proc close*(f: AsyncFile) =
## Closes the file specified.
when defined(windows) or defined(nimdoc):
if not closeHandle(f.fd.THandle).bool:
if not closeHandle(f.fd.Handle).bool:
raiseOSError(osLastError())
else:
if close(f.fd.cint) == -1:

View File

@@ -210,6 +210,7 @@ proc processClient(client: AsyncSocket, address: string,
var contentLength = 0
if parseInt(request.headers["Content-Length"], contentLength) == 0:
await request.respond(Http400, "Bad Request. Invalid Content-Length.")
continue
else:
request.body = await client.recv(contentLength)
assert request.body.len == contentLength

View File

@@ -188,8 +188,8 @@ proc asyncSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
result.socket.setBlocking(false)
proc toAsyncSocket*(sock: Socket, state: SocketStatus = SockConnected): AsyncSocket =
## Wraps an already initialized ``TSocket`` into a AsyncSocket.
## This is useful if you want to use an already connected TSocket as an
## Wraps an already initialized ``Socket`` into a AsyncSocket.
## This is useful if you want to use an already connected Socket as an
## asynchronous AsyncSocket in asyncio's event loop.
##
## ``state`` may be overriden, i.e. if ``sock`` is not connected it should be

View File

@@ -91,13 +91,13 @@ type
# TODO: Save AF, domain etc info and reuse it in procs which need it like connect.
proc newAsyncSocket*(fd: TAsyncFD, isBuff: bool): AsyncSocket =
proc newAsyncSocket*(fd: AsyncFD, buffered = true): AsyncSocket =
## Creates a new ``AsyncSocket`` based on the supplied params.
assert fd != osInvalidSocket.TAsyncFD
assert fd != osInvalidSocket.AsyncFD
new(result)
result.fd = fd.SocketHandle
result.isBuffered = isBuff
if isBuff:
result.isBuffered = buffered
if buffered:
result.currPos = 0
proc newAsyncSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
@@ -142,7 +142,7 @@ when defined(ssl):
if read < 0:
raiseSslError()
data.setLen(read)
await socket.fd.TAsyncFd.send(data, flags)
await socket.fd.AsyncFd.send(data, flags)
proc appeaseSsl(socket: AsyncSocket, flags: set[SocketFlag],
sslError: cint) {.async.} =
@@ -150,7 +150,7 @@ when defined(ssl):
of SSL_ERROR_WANT_WRITE:
await sendPendingSslData(socket, flags)
of SSL_ERROR_WANT_READ:
var data = await recv(socket.fd.TAsyncFD, BufferSize, flags)
var data = await recv(socket.fd.AsyncFD, BufferSize, flags)
let ret = bioWrite(socket.bioIn, addr data[0], data.len.cint)
if ret < 0:
raiseSSLError()
@@ -175,7 +175,7 @@ proc connect*(socket: AsyncSocket, address: string, port: Port,
##
## Returns a ``Future`` which will complete when the connection succeeds
## or an error occurs.
await connect(socket.fd.TAsyncFD, address, port, af)
await connect(socket.fd.AsyncFD, address, port, af)
if socket.isSsl:
when defined(ssl):
let flags = {SocketFlag.SafeDisconn}
@@ -194,7 +194,7 @@ template readInto(buf: cstring, size: int, socket: AsyncSocket,
sslRead(socket.sslHandle, buf, size.cint))
res = opResult
else:
var recvIntoFut = recvInto(socket.fd.TAsyncFD, buf, size, flags)
var recvIntoFut = recvInto(socket.fd.AsyncFD, buf, size, flags)
yield recvIntoFut
# Not in SSL mode.
res = recvIntoFut.read()
@@ -271,7 +271,7 @@ proc send*(socket: AsyncSocket, data: string,
sslWrite(socket.sslHandle, addr copy[0], copy.len.cint))
await sendPendingSslData(socket, flags)
else:
await send(socket.fd.TAsyncFD, data, flags)
await send(socket.fd.AsyncFD, data, flags)
proc acceptAddr*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn}):
Future[tuple[address: string, client: AsyncSocket]] =
@@ -279,9 +279,9 @@ proc acceptAddr*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn}):
## corresponding to that connection and the remote address of the client.
## The future will complete when the connection is successfully accepted.
var retFuture = newFuture[tuple[address: string, client: AsyncSocket]]("asyncnet.acceptAddr")
var fut = acceptAddr(socket.fd.TAsyncFD, flags)
var fut = acceptAddr(socket.fd.AsyncFD, flags)
fut.callback =
proc (future: Future[tuple[address: string, client: TAsyncFD]]) =
proc (future: Future[tuple[address: string, client: AsyncFD]]) =
assert future.finished
if future.failed:
retFuture.fail(future.readError)
@@ -445,7 +445,7 @@ proc bindAddr*(socket: AsyncSocket, port = Port(0), address = "") {.
proc close*(socket: AsyncSocket) =
## Closes the socket.
defer:
socket.fd.TAsyncFD.closeSocket()
socket.fd.AsyncFD.closeSocket()
when defined(ssl):
if socket.isSSL:
let res = SslShutdown(socket.sslHandle)

View File

@@ -20,20 +20,20 @@ import strutils
##
## # Create a matrix which first rotates, then scales and at last translates
##
## var m:TMatrix2d=rotate(DEG90) & scale(2.0) & move(100.0,200.0)
## var m:Matrix2d=rotate(DEG90) & scale(2.0) & move(100.0,200.0)
##
## # Create a 2d point at (100,0) and a vector (5,2)
##
## var pt:TPoint2d=point2d(100.0,0.0)
## var pt:Point2d=point2d(100.0,0.0)
##
## var vec:TVector2d=vector2d(5.0,2.0)
## var vec:Vector2d=vector2d(5.0,2.0)
##
##
## pt &= m # transforms pt in place
##
## var pt2:TPoint2d=pt & m #concatenates pt with m and returns a new point
## var pt2:Point2d=pt & m #concatenates pt with m and returns a new point
##
## var vec2:TVector2d=vec & m #concatenates vec with m and returns a new vector
## var vec2:Vector2d=vec & m #concatenates vec with m and returns a new vector
const
@@ -57,46 +57,46 @@ const
## used internally by DegToRad and RadToDeg
type
TMatrix2d* = object
Matrix2d* = object
## Implements a row major 2d matrix, which means
## transformations are applied the order they are concatenated.
## The rightmost column of the 3x3 matrix is left out since normally
## not used for geometric transformations in 2d.
ax*,ay*,bx*,by*,tx*,ty*:float
TPoint2d* = object
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

View File

@@ -25,25 +25,25 @@ import times
##
## # Create a matrix which first rotates, then scales and at last translates
##
## var m:TMatrix3d=rotate(PI,vector3d(1,1,2.5)) & scale(2.0) & move(100.0,200.0,300.0)
## var m:Matrix3d=rotate(PI,vector3d(1,1,2.5)) & scale(2.0) & move(100.0,200.0,300.0)
##
## # Create a 3d point at (100,150,200) and a vector (5,2,3)
##
## var pt:TPoint3d=point3d(100.0,150.0,200.0)
## var pt:Point3d=point3d(100.0,150.0,200.0)
##
## var vec:TVector3d=vector3d(5.0,2.0,3.0)
## var vec:Vector3d=vector3d(5.0,2.0,3.0)
##
##
## pt &= m # transforms pt in place
##
## var pt2:TPoint3d=pt & m #concatenates pt with m and returns a new point
## var pt2:Point3d=pt & m #concatenates pt with m and returns a new point
##
## var vec2:TVector3d=vec & m #concatenates vec with m and returns a new vector
## var vec2:Vector3d=vec & m #concatenates vec with m and returns a new vector
type
TMatrix3d* =object
Matrix3d* =object
## Implements a row major 3d matrix, which means
## transformations are applied the order they are concatenated.
## This matrix is stored as an 4x4 matrix:
@@ -52,31 +52,31 @@ type
## [ cx cy cz cw ]
## [ tx ty tz tw ]
ax*,ay*,az*,aw*, bx*,by*,bz*,bw*, cx*,cy*,cz*,cw*, tx*,ty*,tz*,tw*:float
TPoint3d* = object
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

View File

@@ -46,24 +46,25 @@ const
when sizeof(int) == 4: # 32bit
type
TRaw = range[0..1073741823]
Raw = range[0..1073741823]
## The range of uint values that can be stored directly in a value slot
## when on a 32 bit platform
{.deprecated: [TRaw: Raw].}
elif sizeof(int) == 8: # 64bit
type
TRaw = range[0..4611686018427387903]
Raw = range[0..4611686018427387903]
## The range of uint values that can be stored directly in a value slot
## when on a 64 bit platform
{.deprecated: [TRaw: Raw].}
else:
{.error: "unsupported platform".}
type
TEntry = tuple
Entry = tuple
key: int
value: int
TEntryArr = ptr array[0..10_000_000, TEntry]
EntryArr = ptr array[0..10_000_000, Entry]
PConcTable[K,V] = ptr object {.pure.}
len: int
@@ -72,8 +73,8 @@ type
copyIdx: int
copyDone: int
next: PConcTable[K,V]
data: TEntryArr
data: EntryArr
{.deprecated: [TEntry: Entry, TEntryArr: EntryArr.}
proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
expVal: int, match: bool): int
@@ -84,7 +85,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
proc newLFTable*[K,V](size: int = minTableSize): PConcTable[K,V] =
let
dataLen = max(nextPowerOfTwo(size), minTableSize)
dataSize = dataLen*sizeof(TEntry)
dataSize = dataLen*sizeof(Entry)
dataMem = allocShared0(dataSize)
tableSize = 7 * intSize
tableMem = allocShared0(tableSize)
@@ -95,7 +96,7 @@ proc newLFTable*[K,V](size: int = minTableSize): PConcTable[K,V] =
table.copyIdx = 0
table.copyDone = 0
table.next = nil
table.data = cast[TEntryArr](dataMem)
table.data = cast[EntryArr](dataMem)
result = table
#------------------------------------------------------------------------------
@@ -107,7 +108,7 @@ proc deleteConcTable[K,V](tbl: PConcTable[K,V]) =
#------------------------------------------------------------------------------
proc `[]`[K,V](table: var PConcTable[K,V], i: int): var TEntry {.inline.} =
proc `[]`[K,V](table: var PConcTable[K,V], i: int): var Entry {.inline.} =
table.data[i]
#------------------------------------------------------------------------------
@@ -191,7 +192,7 @@ proc resize[K,V](self: PConcTable[K,V]): PConcTable[K,V] =
#proc keyEQ[K](key1: ptr K, key2: ptr K): bool {.inline.} =
proc keyEQ[K](key1: int, key2: int): bool {.inline.} =
result = false
when K is TRaw:
when K is Raw:
if key1 == key2:
result = true
else:
@@ -236,7 +237,7 @@ proc copySlot[K,V](idx: int, oldTbl: var PConcTable[K,V], newTbl: var PConcTable
break
#echo("oldVal was = ", oldVal, " set it to prime ", box)
if isPrime(oldVal) and isTomb(oldVal):
#when not (K is TRaw):
#when not (K is Raw):
# deallocShared(popPtr[K](oldKey))
return false
if isTomb(oldVal):
@@ -343,7 +344,7 @@ proc helpCopy[K,V](table: var PConcTable[K,V]): PConcTable[K,V] =
proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
expVal: int, match: bool): int =
#echo("-try set- in table ", " key = ", (popPtr[K](key)[]), " val = ", val)
when K is TRaw:
when K is Raw:
var idx = hashInt(key)
else:
var idx = popPtr[K](key)[].hash
@@ -428,7 +429,7 @@ proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
proc getVal[K,V](table: var PConcTable[K,V], key: int): int =
#echo("-try get- key = " & $key)
when K is TRaw:
when K is Raw:
var idx = hashInt(key)
else:
var idx = popPtr[K](key)[].hash
@@ -468,37 +469,37 @@ proc getVal[K,V](table: var PConcTable[K,V], key: int): int =
#------------------------------------------------------------------------------
#proc set*(table: var PConcTable[TRaw,TRaw], key: TRaw, val: TRaw) =
#proc set*(table: var PConcTable[Raw,Raw], key: Raw, val: Raw) =
# discard setVal(table, pack(key), pack(key), 0, false)
#proc set*[V](table: var PConcTable[TRaw,V], key: TRaw, val: ptr V) =
#proc set*[V](table: var PConcTable[Raw,V], key: Raw, val: ptr V) =
# discard setVal(table, pack(key), cast[int](val), 0, false)
proc set*[K,V](table: var PConcTable[K,V], key: var K, val: var V) =
when not (K is TRaw):
when not (K is Raw):
var newKey = cast[int](copyShared(key))
else:
var newKey = pack(key)
when not (V is TRaw):
when not (V is Raw):
var newVal = cast[int](copyShared(val))
else:
var newVal = pack(val)
var oldPtr = pop(setVal(table, newKey, newVal, 0, false))
#echo("oldPtr = ", cast[int](oldPtr), " newPtr = ", cast[int](newPtr))
when not (V is TRaw):
when not (V is Raw):
if newVal != oldPtr and oldPtr != 0:
deallocShared(cast[ptr V](oldPtr))
proc get*[K,V](table: var PConcTable[K,V], key: var K): V =
when not (V is TRaw):
when not (K is TRaw):
when not (V is Raw):
when not (K is Raw):
return popPtr[V](getVal(table, cast[int](key.addr)))[]
else:
return popPtr[V](getVal(table, pack(key)))[]
else:
when not (K is TRaw):
when not (K is Raw):
return popRaw(getVal(table, cast[int](key.addr)))
else:
return popRaw(getVal(table, pack(key)))
@@ -535,23 +536,24 @@ when not defined(testing) and isMainModule:
type
TTestObj = tuple
TestObj = tuple
thr: int
f0: int
f1: int
TData = tuple[k: string,v: TTestObj]
PDataArr = array[0..numTests-1, TData]
Dict = PConcTable[string,TTestObj]
Data = tuple[k: string,v: TestObj]
PDataArr = array[0..numTests-1, Data]
Dict = PConcTable[string,TestObj]
{.deprecated: [TTestObj: TestObj, TData: Data].}
var
thr: array[0..numThreads-1, TThread[Dict]]
thr: array[0..numThreads-1, Thread[Dict]]
table = newLFTable[string,TTestObj](8)
table = newLFTable[string,TestObj](8)
rand = newMersenneTwister(2525)
proc createSampleData(len: int): PDataArr =
#result = cast[PDataArr](allocShared0(sizeof(TData)*numTests))
#result = cast[PDataArr](allocShared0(sizeof(Data)*numTests))
for i in 0..len-1:
result[i].k = "mark" & $(i+1)
#echo("mark" & $(i+1), " ", hash("mark" & $(i+1)))

View File

@@ -30,25 +30,25 @@ const
IntMask = 1 shl IntShift - 1
type
PTrunk = ref TTrunk
TTrunk {.final.} = object
PTrunk = ref Trunk
Trunk {.final.} = object
next: PTrunk # all nodes are connected with this pointer
key: int # start address at bit 0
bits: array[0..IntsPerTrunk - 1, BitScalar] # a bit vector
TTrunkSeq = seq[PTrunk]
TrunkSeq = seq[PTrunk]
IntSet* = object ## an efficient set of 'int' implemented as a sparse bit set
counter, max: int
head: PTrunk
data: TTrunkSeq
data: TrunkSeq
{.deprecated: [TIntSet: IntSet].}
{.deprecated: [TIntSet: IntSet, TTrunk: Trunk, TTrunkSeq: TrunkSeq].}
proc mustRehash(length, counter: int): bool {.inline.} =
assert(length > counter)
result = (length * 2 < counter * 3) or (length - counter < 4)
proc nextTry(h, maxHash: THash): THash {.inline.} =
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
result = ((5 * h) + 1) and maxHash
proc intSetGet(t: IntSet, key: int): PTrunk =
@@ -59,7 +59,7 @@ proc intSetGet(t: IntSet, key: int): PTrunk =
h = nextTry(h, t.max)
result = nil
proc intSetRawInsert(t: IntSet, data: var TTrunkSeq, desc: PTrunk) =
proc intSetRawInsert(t: IntSet, data: var TrunkSeq, desc: PTrunk) =
var h = desc.key and t.max
while data[h] != nil:
assert(data[h] != desc)
@@ -68,7 +68,7 @@ proc intSetRawInsert(t: IntSet, data: var TTrunkSeq, desc: PTrunk) =
data[h] = desc
proc intSetEnlarge(t: var IntSet) =
var n: TTrunkSeq
var n: TrunkSeq
var oldMax = t.max
t.max = ((t.max + 1) * 2) - 1
newSeq(n, t.max + 1)

View File

@@ -29,7 +29,7 @@ when not defined(nimhygiene):
# codes should never be needed, and this can pack more entries per cache-line.
# Losing hcode entirely is also possible - if some element value is forbidden.
type
KeyValuePair[A] = tuple[hcode: THash, key: A]
KeyValuePair[A] = tuple[hcode: Hash, key: A]
KeyValuePairSeq[A] = seq[KeyValuePair[A]]
HashSet* {.myShallow.}[A] = object ## \
## A generic hash set.
@@ -43,10 +43,10 @@ type
# hcode for real keys cannot be zero. hcode==0 signifies an empty slot. These
# two procs retain clarity of that encoding without the space cost of an enum.
proc isEmpty(hcode: THash): bool {.inline.} =
proc isEmpty(hcode: Hash): bool {.inline.} =
result = hcode == 0
proc isFilled(hcode: THash): bool {.inline.} =
proc isFilled(hcode: Hash): bool {.inline.} =
result = hcode != 0
proc isValid*[A](s: HashSet[A]): bool =
@@ -58,7 +58,7 @@ proc isValid*[A](s: HashSet[A]): bool =
## initialized. Example:
##
## .. code-block ::
## proc savePreferences(options: TSet[string]) =
## proc savePreferences(options: Set[string]) =
## assert options.isValid, "Pass an initialized set!"
## # Do stuff here, may crash in release builds!
result = not s.data.isNil
@@ -72,7 +72,7 @@ proc len*[A](s: HashSet[A]): int =
##
## .. code-block::
##
## var values: TSet[int]
## var values: Set[int]
## assert(not values.isValid)
## assert values.len == 0
result = s.counter
@@ -123,15 +123,15 @@ proc rightSize*(count: Natural): int {.inline.} =
## Internally, we want mustRehash(rightSize(x), x) == false.
result = nextPowerOfTwo(count * 3 div 2 + 4)
proc nextTry(h, maxHash: THash): THash {.inline.} =
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
result = (h + 1) and maxHash
template rawGetKnownHCImpl() {.dirty.} =
var h: THash = hc and high(s.data) # start with real hash value
var h: Hash = hc and high(s.data) # start with real hash value
while isFilled(s.data[h].hcode):
# Compare hc THEN key with boolean short circuit. This makes the common case
# zero ==key's for missing (e.g.inserts) and exactly one ==key for present.
# It does slow down succeeding lookups by one extra THash cmp&and..usually
# It does slow down succeeding lookups by one extra Hash cmp&and..usually
# just a few clock cycles, generally worth it for any non-integer-like A.
if s.data[h].hcode == hc and s.data[h].key == key: # compare hc THEN key
return h
@@ -148,10 +148,10 @@ template rawInsertImpl() {.dirty.} =
data[h].key = key
data[h].hcode = hc
proc rawGetKnownHC[A](s: HashSet[A], key: A, hc: THash): int {.inline.} =
proc rawGetKnownHC[A](s: HashSet[A], key: A, hc: Hash): int {.inline.} =
rawGetKnownHCImpl()
proc rawGet[A](s: HashSet[A], key: A, hc: var THash): int {.inline.} =
proc rawGet[A](s: HashSet[A], key: A, hc: var Hash): int {.inline.} =
rawGetImpl()
proc mget*[A](s: var HashSet[A], key: A): var A =
@@ -160,7 +160,7 @@ proc mget*[A](s: var HashSet[A], key: A): var A =
## when one overloaded 'hash' and '==' but still needs reference semantics
## for sharing.
assert s.isValid, "The set needs to be initialized."
var hc: THash
var hc: Hash
var index = rawGet(s, key, hc)
if index >= 0: result = s.data[index].key
else: raise newException(KeyError, "key not found: " & $key)
@@ -178,12 +178,12 @@ proc contains*[A](s: HashSet[A], key: A): bool =
## values.excl(2)
## assert(not values.contains(2))
assert s.isValid, "The set needs to be initialized."
var hc: THash
var hc: Hash
var index = rawGet(s, key, hc)
result = index >= 0
proc rawInsert[A](s: var HashSet[A], data: var KeyValuePairSeq[A], key: A,
hc: THash, h: THash) =
hc: Hash, h: Hash) =
rawInsertImpl()
proc enlarge[A](s: var HashSet[A]) =
@@ -196,7 +196,7 @@ proc enlarge[A](s: var HashSet[A]) =
rawInsert(s, s.data, n[i].key, n[i].hcode, j)
template inclImpl() {.dirty.} =
var hc: THash
var hc: Hash
var index = rawGet(s, key, hc)
if index < 0:
if mustRehash(len(s.data), s.counter):
@@ -206,7 +206,7 @@ template inclImpl() {.dirty.} =
inc(s.counter)
template containsOrInclImpl() {.dirty.} =
var hc: THash
var hc: Hash
var index = rawGet(s, key, hc)
if index >= 0:
result = true
@@ -261,7 +261,7 @@ proc excl*[A](s: var HashSet[A], key: A) =
## s.excl(2)
## assert s.len == 3
assert s.isValid, "The set needs to be initialized."
var hc: THash
var hc: Hash
var i = rawGet(s, key, hc)
var msk = high(s.data)
if i >= 0:
@@ -323,7 +323,7 @@ proc init*[A](s: var HashSet[A], initialSize=64) =
## existing values and calling `excl() <#excl,TSet[A],A>`_ on them. Example:
##
## .. code-block ::
## var a: TSet[int]
## var a: Set[int]
## a.init(4)
## a.incl(2)
## a.init
@@ -552,7 +552,7 @@ proc map*[A, B](data: HashSet[A], op: proc (x: A): B {.closure.}): HashSet[B] =
type
OrderedKeyValuePair[A] = tuple[
hcode: THash, next: int, key: A]
hcode: Hash, next: int, key: A]
OrderedKeyValuePairSeq[A] = seq[OrderedKeyValuePair[A]]
OrderedSet* {.myShallow.}[A] = object ## \
## A generic hash set that remembers insertion order.
@@ -574,7 +574,7 @@ proc isValid*[A](s: OrderedSet[A]): bool =
## correctly initialized. Example:
##
## .. code-block::
## proc saveTarotCards(cards: TOrderedSet[int]) =
## proc saveTarotCards(cards: OrderedSet[int]) =
## assert cards.isValid, "Pass an initialized set!"
## # Do stuff here, may crash in release builds!
result = not s.data.isNil
@@ -588,7 +588,7 @@ proc len*[A](s: OrderedSet[A]): int {.inline.} =
##
## .. code-block::
##
## var values: TOrderedSet[int]
## var values: OrderedSet[int]
## assert(not values.isValid)
## assert values.len == 0
result = s.counter
@@ -629,10 +629,10 @@ iterator items*[A](s: OrderedSet[A]): A =
forAllOrderedPairs:
yield s.data[h].key
proc rawGetKnownHC[A](s: OrderedSet[A], key: A, hc: THash): int {.inline.} =
proc rawGetKnownHC[A](s: OrderedSet[A], key: A, hc: Hash): int {.inline.} =
rawGetKnownHCImpl()
proc rawGet[A](s: OrderedSet[A], key: A, hc: var THash): int {.inline.} =
proc rawGet[A](s: OrderedSet[A], key: A, hc: var Hash): int {.inline.} =
rawGetImpl()
proc contains*[A](s: OrderedSet[A], key: A): bool =
@@ -646,12 +646,12 @@ proc contains*[A](s: OrderedSet[A], key: A): bool =
## values.incl(2)
## assert values.contains(2)
assert s.isValid, "The set needs to be initialized."
var hc: THash
var hc: Hash
var index = rawGet(s, key, hc)
result = index >= 0
proc rawInsert[A](s: var OrderedSet[A], data: var OrderedKeyValuePairSeq[A],
key: A, hc: THash, h: THash) =
key: A, hc: Hash, h: Hash) =
rawInsertImpl()
data[h].next = -1
if s.first < 0: s.first = h
@@ -729,7 +729,7 @@ proc init*[A](s: var OrderedSet[A], initialSize=64) =
## from an ordered hash set. Example:
##
## .. code-block ::
## var a: TOrderedSet[int]
## var a: OrderedSet[int]
## a.init(4)
## a.incl(2)
## a.init

View File

@@ -24,13 +24,13 @@
##
## Error: type mismatch: got (Person)
## but expected one of:
## hashes.hash(x: openarray[A]): THash
## hashes.hash(x: int): THash
## hashes.hash(x: float): THash
## hashes.hash(x: openarray[A]): Hash
## hashes.hash(x: int): Hash
## hashes.hash(x: float): Hash
## …
##
## What is happening here is that the types used for table keys require to have
## a ``hash()`` proc which will convert them to a `THash <hashes.html#THash>`_
## a ``hash()`` proc which will convert them to a `Hash <hashes.html#Hash>`_
## value, and the compiler is listing all the hash functions it knows.
## Additionally there has to be a ``==`` operator that provides the same
## semantics as its corresponding ``hash`` proc.
@@ -46,7 +46,7 @@
## Person = object
## firstName, lastName: string
##
## proc hash(x: Person): THash =
## proc hash(x: Person): Hash =
## ## Piggyback on the already available string hash proc.
## ##
## ## Without this proc nothing works!
@@ -71,7 +71,7 @@ import
{.pragma: myShallow.}
type
KeyValuePair[A, B] = tuple[hcode: THash, key: A, val: B]
KeyValuePair[A, B] = tuple[hcode: Hash, key: A, val: B]
KeyValuePairSeq[A, B] = seq[KeyValuePair[A, B]]
Table* {.myShallow.}[A, B] = object ## generic hash table
data: KeyValuePairSeq[A, B]
@@ -85,10 +85,10 @@ when not defined(nimhygiene):
# hcode for real keys cannot be zero. hcode==0 signifies an empty slot. These
# two procs retain clarity of that encoding without the space cost of an enum.
proc isEmpty(hcode: THash): bool {.inline.} =
proc isEmpty(hcode: Hash): bool {.inline.} =
result = hcode == 0
proc isFilled(hcode: THash): bool {.inline.} =
proc isFilled(hcode: Hash): bool {.inline.} =
result = hcode != 0
proc len*[A, B](t: Table[A, B]): int =
@@ -137,15 +137,15 @@ proc rightSize*(count: Natural): int {.inline.} =
## Internally, we want mustRehash(rightSize(x), x) == false.
result = nextPowerOfTwo(count * 3 div 2 + 4)
proc nextTry(h, maxHash: THash): THash {.inline.} =
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
result = (h + 1) and maxHash
template rawGetKnownHCImpl() {.dirty.} =
var h: THash = hc and high(t.data) # start with real hash value
var h: Hash = hc and high(t.data) # start with real hash value
while isFilled(t.data[h].hcode):
# Compare hc THEN key with boolean short circuit. This makes the common case
# zero ==key's for missing (e.g.inserts) and exactly one ==key for present.
# It does slow down succeeding lookups by one extra THash cmp&and..usually
# It does slow down succeeding lookups by one extra Hash cmp&and..usually
# just a few clock cycles, generally worth it for any non-integer-like A.
if t.data[h].hcode == hc and t.data[h].key == key:
return h
@@ -162,7 +162,7 @@ template rawGetDeepImpl() {.dirty.} = # Search algo for unconditional add
hc = hash(key)
if hc == 0:
hc = 314159265
var h: THash = hc and high(t.data)
var h: Hash = hc and high(t.data)
while isFilled(t.data[h].hcode):
h = nextTry(h, high(t.data))
result = h
@@ -172,13 +172,13 @@ template rawInsertImpl() {.dirty.} =
data[h].val = val
data[h].hcode = hc
proc rawGetKnownHC[A, B](t: Table[A, B], key: A, hc: THash): int {.inline.} =
proc rawGetKnownHC[A, B](t: Table[A, B], key: A, hc: Hash): int {.inline.} =
rawGetKnownHCImpl()
proc rawGetDeep[A, B](t: Table[A, B], key: A, hc: var THash): int {.inline.} =
proc rawGetDeep[A, B](t: Table[A, B], key: A, hc: var Hash): int {.inline.} =
rawGetDeepImpl()
proc rawGet[A, B](t: Table[A, B], key: A, hc: var THash): int {.inline.} =
proc rawGet[A, B](t: Table[A, B], key: A, hc: var Hash): int {.inline.} =
rawGetImpl()
proc `[]`*[A, B](t: Table[A, B], key: A): B =
@@ -186,14 +186,14 @@ proc `[]`*[A, B](t: Table[A, B], key: A): B =
## default empty value for the type `B` is returned
## and no exception is raised. One can check with ``hasKey`` whether the key
## exists.
var hc: THash
var hc: Hash
var index = rawGet(t, key, hc)
if index >= 0: result = t.data[index].val
proc mget*[A, B](t: var Table[A, B], key: A): var B =
## retrieves the value at ``t[key]``. The value can be modified.
## If `key` is not in `t`, the ``KeyError`` exception is raised.
var hc: THash
var hc: Hash
var index = rawGet(t, key, hc)
if index >= 0: result = t.data[index].val
else:
@@ -204,7 +204,7 @@ proc mget*[A, B](t: var Table[A, B], key: A): var B =
iterator allValues*[A, B](t: Table[A, B]; key: A): B =
## iterates over any value in the table `t` that belongs to the given `key`.
var h: THash = hash(key) and high(t.data)
var h: Hash = hash(key) and high(t.data)
while isFilled(t.data[h].hcode):
if t.data[h].key == key:
yield t.data[h].val
@@ -212,7 +212,7 @@ iterator allValues*[A, B](t: Table[A, B]; key: A): B =
proc hasKey*[A, B](t: Table[A, B], key: A): bool =
## returns true iff `key` is in the table `t`.
var hc: THash
var hc: Hash
result = rawGet(t, key, hc) >= 0
proc contains*[A, B](t: Table[A, B], key: A): bool =
@@ -220,7 +220,7 @@ proc contains*[A, B](t: Table[A, B], key: A): bool =
return hasKey[A, B](t, key)
proc rawInsert[A, B](t: var Table[A, B], data: var KeyValuePairSeq[A, B],
key: A, val: B, hc: THash, h: THash) =
key: A, val: B, hc: Hash, h: Hash) =
rawInsertImpl()
proc enlarge[A, B](t: var Table[A, B]) =
@@ -234,7 +234,7 @@ proc enlarge[A, B](t: var Table[A, B]) =
template addImpl() {.dirty.} =
if mustRehash(len(t.data), t.counter): enlarge(t)
var hc: THash
var hc: Hash
var j = rawGetDeep(t, key, hc)
rawInsert(t, t.data, key, val, hc, j)
inc(t.counter)
@@ -248,19 +248,19 @@ template maybeRehashPutImpl() {.dirty.} =
inc(t.counter)
template putImpl() {.dirty.} =
var hc: THash
var hc: Hash
var index = rawGet(t, key, hc)
if index >= 0: t.data[index].val = val
else: maybeRehashPutImpl()
template mgetOrPutImpl() {.dirty.} =
var hc: THash
var hc: Hash
var index = rawGet(t, key, hc)
if index < 0: maybeRehashPutImpl() # not present: insert (flipping index)
result = t.data[index].val # either way return modifiable val
template hasKeyOrPutImpl() {.dirty.} =
var hc: THash
var hc: Hash
var index = rawGet(t, key, hc)
if index < 0:
result = false
@@ -291,7 +291,7 @@ template doWhile(a: expr, b: stmt): stmt =
proc del*[A, B](t: var Table[A, B], key: A) =
## deletes `key` from hash table `t`.
var hc: THash
var hc: Hash
var i = rawGet(t, key, hc)
let msk = high(t.data)
if i >= 0:
@@ -460,7 +460,7 @@ proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): TableRef[C, B]
type
OrderedKeyValuePair[A, B] = tuple[
hcode: THash, next: int, key: A, val: B]
hcode: Hash, next: int, key: A, val: B]
OrderedKeyValuePairSeq[A, B] = seq[OrderedKeyValuePair[A, B]]
OrderedTable* {.
myShallow.}[A, B] = object ## table that remembers insertion order
@@ -509,13 +509,13 @@ iterator mvalues*[A, B](t: var OrderedTable[A, B]): var B =
forAllOrderedPairs:
yield t.data[h].val
proc rawGetKnownHC[A, B](t: OrderedTable[A, B], key: A, hc: THash): int =
proc rawGetKnownHC[A, B](t: OrderedTable[A, B], key: A, hc: Hash): int =
rawGetKnownHCImpl()
proc rawGetDeep[A, B](t: OrderedTable[A, B], key: A, hc: var THash): int {.inline.} =
proc rawGetDeep[A, B](t: OrderedTable[A, B], key: A, hc: var Hash): int {.inline.} =
rawGetDeepImpl()
proc rawGet[A, B](t: OrderedTable[A, B], key: A, hc: var THash): int =
proc rawGet[A, B](t: OrderedTable[A, B], key: A, hc: var Hash): int =
rawGetImpl()
proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B =
@@ -523,21 +523,21 @@ proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B =
## default empty value for the type `B` is returned
## and no exception is raised. One can check with ``hasKey`` whether the key
## exists.
var hc: THash
var hc: Hash
var index = rawGet(t, key, hc)
if index >= 0: result = t.data[index].val
proc mget*[A, B](t: var OrderedTable[A, B], key: A): var B =
## retrieves the value at ``t[key]``. The value can be modified.
## If `key` is not in `t`, the ``EInvalidKey`` exception is raised.
var hc: THash
var hc: Hash
var index = rawGet(t, key, hc)
if index >= 0: result = t.data[index].val
else: raise newException(KeyError, "key not found: " & $key)
proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool =
## returns true iff `key` is in the table `t`.
var hc: THash
var hc: Hash
result = rawGet(t, key, hc) >= 0
proc contains*[A, B](t: OrderedTable[A, B], key: A): bool =
@@ -546,7 +546,7 @@ proc contains*[A, B](t: OrderedTable[A, B], key: A): bool =
proc rawInsert[A, B](t: var OrderedTable[A, B],
data: var OrderedKeyValuePairSeq[A, B],
key: A, val: B, hc: THash, h: THash) =
key: A, val: B, hc: Hash, h: Hash) =
rawInsertImpl()
data[h].next = -1
if t.first < 0: t.first = h
@@ -796,7 +796,7 @@ iterator mvalues*[A](t: CountTable[A]): var int =
if t.data[h].val != 0: yield t.data[h].val
proc rawGet[A](t: CountTable[A], key: A): int =
var h: THash = hash(key) and high(t.data) # start with real hash value
var h: Hash = hash(key) and high(t.data) # start with real hash value
while t.data[h].val != 0:
if t.data[h].key == key: return h
h = nextTry(h, high(t.data))
@@ -826,7 +826,7 @@ proc contains*[A](t: CountTable[A], key: A): bool =
proc rawInsert[A](t: CountTable[A], data: var seq[tuple[key: A, val: int]],
key: A, val: int) =
var h: THash = hash(key) and high(data)
var h: Hash = hash(key) and high(data)
while data[h].val != 0: h = nextTry(h, high(data))
data[h].key = key
data[h].val = val
@@ -1032,7 +1032,7 @@ when isMainModule:
Person = object
firstName, lastName: string
proc hash(x: Person): THash =
proc hash(x: Person): Hash =
## Piggyback on the already available string hash proc.
##
## Without this proc nothing works!

View File

@@ -13,7 +13,7 @@
when defined(windows):
import winlean, os, strutils, math
proc `-`(a, b: TFILETIME): int64 = a.rdFileTime - b.rdFileTime
proc `-`(a, b: FILETIME): int64 = a.rdFileTime - b.rdFileTime
elif defined(linux):
from cpuinfo import countProcessors
@@ -25,16 +25,16 @@ type
ThreadPoolState* = object
when defined(windows):
prevSysKernel, prevSysUser, prevProcKernel, prevProcUser: TFILETIME
prevSysKernel, prevSysUser, prevProcKernel, prevProcUser: FILETIME
calls*: int
proc advice*(s: var ThreadPoolState): ThreadPoolAdvice =
when defined(windows):
var
sysIdle, sysKernel, sysUser,
procCreation, procExit, procKernel, procUser: TFILETIME
procCreation, procExit, procKernel, procUser: FILETIME
if getSystemTimes(sysIdle, sysKernel, sysUser) == 0 or
getProcessTimes(THandle(-1), procCreation, procExit,
getProcessTimes(Handle(-1), procCreation, procExit,
procKernel, procUser) == 0:
return doNothing
if s.calls > 0:
@@ -57,7 +57,7 @@ proc advice*(s: var ThreadPoolState): ThreadPoolAdvice =
s.prevProcKernel = procKernel
s.prevProcUser = procUser
elif defined(linux):
proc fscanf(c: File, frmt: cstring) {.varargs, importc,
proc fscanf(c: File, frmt: cstring) {.varargs, importc,
header: "<stdio.h>".}
var f = open("/proc/loadavg")

View File

@@ -18,8 +18,8 @@ import cpuinfo, cpuload, locks
type
Semaphore = object
c: TCond
L: TLock
c: Cond
L: Lock
counter: int
proc createSemaphore(): Semaphore =
@@ -113,7 +113,7 @@ type
ToFreeQueue = object
len: int
lock: TLock
lock: Lock
empty: Semaphore
data: array[128, pointer]
@@ -221,11 +221,17 @@ proc awaitAndThen*[T](fv: FlowVar[T]; action: proc (x: T) {.closure.}) =
action(fv.blob)
finished(fv)
proc `^`*[T](fv: FlowVar[ref T]): foreign ptr T =
proc unsafeRead*[T](fv: FlowVar[ref T]): foreign ptr T =
## blocks until the value is available and then returns this value.
await(fv)
result = cast[foreign ptr T](fv.data)
proc `^`*[T](fv: FlowVar[ref T]): ref T =
## blocks until the value is available and then returns this value.
await(fv)
let src = cast[ref T](fv.data)
deepCopy result, src
proc `^`*[T](fv: FlowVar[T]): T =
## blocks until the value is available and then returns this value.
await(fv)
@@ -349,7 +355,7 @@ proc parallel*(body: stmt) {.magic: "Parallel".}
var
state: ThreadPoolState
stateLock: TLock
stateLock: Lock
initLock stateLock

View File

@@ -211,12 +211,13 @@ when defined(windows):
when false:
# not needed yet:
type
TCpInfo = object
CpInfo = object
maxCharSize: int32
defaultChar: array[0..1, char]
leadByte: array[0..12-1, char]
{.deprecated: [TCpInfo: CpInfo].}
proc getCPInfo(codePage: CodePage, lpCPInfo: var TCpInfo): int32 {.
proc getCPInfo(codePage: CodePage, lpCPInfo: var CpInfo): int32 {.
stdcall, importc: "GetCPInfo", dynlib: "kernel32".}
proc nameToCodePage(name: string): CodePage =
@@ -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
View File

@@ -0,0 +1,23 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2015 Nim Authors
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module contains utils that are less then easy to categorize and
## don't really warrant a specific module. They are private to compiler
## and stdlib usage, and should not be used outside of that - they may
## change or disappear at any time.
# Used by pure/hashes.nim, and the compiler parsing
const magicIdentSeparatorRuneByteWidth* = 3
# Used by pure/hashes.nim, and the compiler parsing
proc isMagicIdentSeparatorRune*(cs: cstring, i: int): bool {. inline } =
result = cs[i] == '\226' and
cs[i + 1] == '\128' and
cs[i + 2] == '\147' # en-dash # 145 = nb-hyphen

View File

@@ -108,7 +108,7 @@ proc del*(monitor: FSMonitor, wd: cint) =
proc getEvent(m: FSMonitor, fd: cint): seq[MonitorEvent] =
result = @[]
let size = (sizeof(TINotifyEvent)+2000)*MaxEvents
let size = (sizeof(INotifyEvent)+2000)*MaxEvents
var buffer = newString(size)
let le = read(fd, addr(buffer[0]), size)
@@ -117,7 +117,7 @@ proc getEvent(m: FSMonitor, fd: cint): seq[MonitorEvent] =
var i = 0
while i < le:
var event = cast[ptr TINotifyEvent](addr(buffer[i]))
var event = cast[ptr INotifyEvent](addr(buffer[i]))
var mev: MonitorEvent
mev.wd = event.wd
if event.len.int != 0:
@@ -129,7 +129,7 @@ proc getEvent(m: FSMonitor, fd: cint): seq[MonitorEvent] =
if (event.mask.int and IN_MOVED_FROM) != 0:
# Moved from event, add to m's collection
movedFrom.add(event.cookie.cint, (mev.wd, mev.name))
inc(i, sizeof(TINotifyEvent) + event.len.int)
inc(i, sizeof(INotifyEvent) + event.len.int)
continue
elif (event.mask.int and IN_MOVED_TO) != 0:
mev.kind = MonitorMoved
@@ -159,7 +159,7 @@ proc getEvent(m: FSMonitor, fd: cint): seq[MonitorEvent] =
mev.fullname = ""
result.add(mev)
inc(i, sizeof(TINotifyEvent) + event.len.int)
inc(i, sizeof(INotifyEvent) + event.len.int)
# If movedFrom events have not been matched with a moveTo. File has
# been moved to an unwatched location, emit a MonitorDelete.

View File

@@ -18,20 +18,22 @@ import
os, hashes, strutils
type
TGenTableMode* = enum ## describes the table's key matching mode
GenTableMode* = enum ## describes the table's key matching mode
modeCaseSensitive, ## case sensitive matching of keys
modeCaseInsensitive, ## case insensitive matching of keys
modeStyleInsensitive ## style sensitive matching of keys
TGenKeyValuePair[T] = tuple[key: string, val: T]
TGenKeyValuePairSeq[T] = seq[TGenKeyValuePair[T]]
TGenTable*[T] = object of RootObj
GenKeyValuePair[T] = tuple[key: string, val: T]
GenKeyValuePairSeq[T] = seq[GenKeyValuePair[T]]
GenTable*[T] = object of RootObj
counter: int
data: TGenKeyValuePairSeq[T]
mode: TGenTableMode
data: GenKeyValuePairSeq[T]
mode: GenTableMode
PGenTable*[T] = ref TGenTable[T] ## use this type to declare hash tables
PGenTable*[T] = ref GenTable[T] ## use this type to declare hash tables
{.deprecated: [TGenTableMode: GenTableMode, TGenKeyValuePair: GenKeyValuePair,
TGenKeyValuePairSeq: GenKeyValuePairSeq, TGenTable: GenTable].}
const
growthFactor = 2
@@ -48,7 +50,7 @@ iterator pairs*[T](tbl: PGenTable[T]): tuple[key: string, value: T] =
if not isNil(tbl.data[h].key):
yield (tbl.data[h].key, tbl.data[h].val)
proc myhash[T](tbl: PGenTable[T], key: string): THash =
proc myhash[T](tbl: PGenTable[T], key: string): Hash =
case tbl.mode
of modeCaseSensitive: result = hashes.hash(key)
of modeCaseInsensitive: result = hashes.hashIgnoreCase(key)
@@ -64,18 +66,18 @@ proc mustRehash(length, counter: int): bool =
assert(length > counter)
result = (length * 2 < counter * 3) or (length - counter < 4)
proc newGenTable*[T](mode: TGenTableMode): PGenTable[T] =
proc newGenTable*[T](mode: GenTableMode): PGenTable[T] =
## creates a new generic hash table that is empty.
new(result)
result.mode = mode
result.counter = 0
newSeq(result.data, startSize)
proc nextTry(h, maxHash: THash): THash {.inline.} =
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
result = ((5 * h) + 1) and maxHash
proc rawGet[T](tbl: PGenTable[T], key: string): int =
var h: THash
var h: Hash
h = myhash(tbl, key) and high(tbl.data) # start with real hash value
while not isNil(tbl.data[h].key):
if myCmp(tbl, tbl.data[h].key, key):
@@ -83,9 +85,9 @@ proc rawGet[T](tbl: PGenTable[T], key: string): int =
h = nextTry(h, high(tbl.data))
result = - 1
proc rawInsert[T](tbl: PGenTable[T], data: var TGenKeyValuePairSeq[T],
proc rawInsert[T](tbl: PGenTable[T], data: var GenKeyValuePairSeq[T],
key: string, val: T) =
var h: THash
var h: Hash
h = myhash(tbl, key) and high(data)
while not isNil(data[h].key):
h = nextTry(h, high(data))
@@ -93,7 +95,7 @@ proc rawInsert[T](tbl: PGenTable[T], data: var TGenKeyValuePairSeq[T],
data[h].val = val
proc enlarge[T](tbl: PGenTable[T]) =
var n: TGenKeyValuePairSeq[T]
var n: GenKeyValuePairSeq[T]
newSeq(n, len(tbl.data) * growthFactor)
for i in countup(0, high(tbl.data)):
if not isNil(tbl.data[i].key):
@@ -146,19 +148,20 @@ when isMainModule:
# Verify a table of user-defined types
#
type
TMyType = tuple[first, second: string] # a pair of strings
MyType = tuple[first, second: string] # a pair of strings
{.deprecated: [TMyType: MyType].}
var y = newGenTable[TMyType](modeCaseInsensitive) # hash table where each
# value is TMyType tuple
var y = newGenTable[MyType](modeCaseInsensitive) # hash table where each
# value is MyType tuple
#var junk: TMyType = ("OK", "Here")
#var junk: MyType = ("OK", "Here")
#echo junk.first, " ", junk.second
y["Hello"] = ("Hello", "World")
y["Goodbye"] = ("Goodbye", "Everyone")
#y["Hello"] = TMyType( ("Hello", "World") )
#y["Goodbye"] = TMyType( ("Goodbye", "Everyone") )
#y["Hello"] = MyType( ("Hello", "World") )
#y["Goodbye"] = MyType( ("Goodbye", "Everyone") )
assert( not isNil(y["Hello"].first) )
assert( y["Hello"].first == "Hello" )

View File

@@ -15,9 +15,9 @@
## code:
##
## .. code-block:: Nim
## proc hash(x: Something): THash =
## ## Computes a THash from `x`.
## var h: THash = 0
## proc hash(x: Something): Hash =
## ## Computes a Hash from `x`.
## var h: Hash = 0
## # Iterate over parts of `x`.
## for xAtom in x:
## # Mix the atom with the partial hash.
@@ -30,38 +30,39 @@
## together the hash value of the individual fields:
##
## .. code-block:: Nim
## proc hash(x: Something): THash =
## ## Computes a THash from `x`.
## var h: THash = 0
## proc hash(x: Something): Hash =
## ## Computes a Hash from `x`.
## var h: Hash = 0
## h = h !& hash(x.foo)
## h = h !& hash(x.bar)
## result = !$h
import
strutils
strutils, etcpriv
type
THash* = int ## a hash value; hash tables using these values should
Hash* = int ## a hash value; hash tables using these values should
## always have a size of a power of two and can use the ``and``
## operator instead of ``mod`` for truncation of the hash value.
{.deprecated: [THash: Hash].}
proc `!&`*(h: THash, val: int): THash {.inline.} =
proc `!&`*(h: Hash, val: int): Hash {.inline.} =
## mixes a hash value `h` with `val` to produce a new hash value. This is
## only needed if you need to implement a hash proc for a new datatype.
result = h +% val
result = result +% result shl 10
result = result xor (result shr 6)
proc `!$`*(h: THash): THash {.inline.} =
proc `!$`*(h: Hash): Hash {.inline.} =
## finishes the computation of the hash value. This is
## only needed if you need to implement a hash proc for a new datatype.
result = h +% h shl 3
result = result xor (result shr 11)
result = result +% result shl 15
proc hashData*(data: pointer, size: int): THash =
proc hashData*(data: pointer, size: int): Hash =
## hashes an array of bytes of size `size`
var h: THash = 0
var h: Hash = 0
when defined(js):
var p: cstring
asm """`p` = `Data`;"""
@@ -78,7 +79,7 @@ proc hashData*(data: pointer, size: int): THash =
when defined(js):
var objectID = 0
proc hash*(x: pointer): THash {.inline.} =
proc hash*(x: pointer): Hash {.inline.} =
## efficient hashing of pointers
when defined(js):
asm """
@@ -92,50 +93,57 @@ proc hash*(x: pointer): THash {.inline.} =
}
"""
else:
result = (cast[THash](x)) shr 3 # skip the alignment
result = (cast[Hash](x)) shr 3 # skip the alignment
when not defined(booting):
proc hash*[T: proc](x: T): THash {.inline.} =
proc hash*[T: proc](x: T): Hash {.inline.} =
## efficient hashing of proc vars; closures are supported too.
when T is "closure":
result = hash(rawProc(x)) !& hash(rawEnv(x))
else:
result = hash(pointer(x))
proc hash*(x: int): THash {.inline.} =
proc hash*(x: int): Hash {.inline.} =
## efficient hashing of integers
result = x
proc hash*(x: int64): THash {.inline.} =
proc hash*(x: int64): Hash {.inline.} =
## efficient hashing of integers
result = toU32(x)
proc hash*(x: char): THash {.inline.} =
proc hash*(x: char): Hash {.inline.} =
## efficient hashing of characters
result = ord(x)
proc hash*(x: string): THash =
proc hash*(x: string): Hash =
## efficient hashing of strings
var h: THash = 0
var h: Hash = 0
for i in 0..x.len-1:
h = h !& ord(x[i])
result = !$h
proc hashIgnoreStyle*(x: string): THash =
proc hashIgnoreStyle*(x: string): Hash =
## efficient hashing of strings; style is ignored
var h: THash = 0
for i in 0..x.len-1:
var h: Hash = 0
var i = 0
let xLen = x.len
while i < xLen:
var c = x[i]
if c == '_':
continue # skip _
if c in {'A'..'Z'}:
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
h = h !& ord(c)
inc(i)
elif isMagicIdentSeparatorRune(cstring(x), i):
inc(i, magicIdentSeparatorRuneByteWidth)
else:
if c in {'A'..'Z'}:
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
h = h !& ord(c)
inc(i)
result = !$h
proc hashIgnoreCase*(x: string): THash =
proc hashIgnoreCase*(x: string): Hash =
## efficient hashing of strings; case is ignored
var h: THash = 0
var h: Hash = 0
for i in 0..x.len-1:
var c = x[i]
if c in {'A'..'Z'}:
@@ -143,28 +151,28 @@ proc hashIgnoreCase*(x: string): THash =
h = h !& ord(c)
result = !$h
proc hash*(x: float): THash {.inline.} =
proc hash*(x: float): Hash {.inline.} =
var y = x + 1.0
result = cast[ptr THash](addr(y))[]
result = cast[ptr Hash](addr(y))[]
# Forward declarations before methods that hash containers. This allows
# containers to contain other containers
proc hash*[A](x: openArray[A]): THash
proc hash*[A](x: set[A]): THash
proc hash*[A](x: openArray[A]): Hash
proc hash*[A](x: set[A]): Hash
proc hash*[T: tuple](x: T): THash =
proc hash*[T: tuple](x: T): Hash =
## efficient hashing of tuples.
for f in fields(x):
result = result !& hash(f)
result = !$result
proc hash*[A](x: openArray[A]): THash =
proc hash*[A](x: openArray[A]): Hash =
for it in items(x): result = result !& hash(it)
result = !$result
proc hash*[A](x: set[A]): THash =
proc hash*[A](x: set[A]): Hash =
for it in items(x): result = result !& hash(it)
result = !$result

View File

@@ -52,7 +52,7 @@
import strutils, streams, parsexml, xmltree, unicode, strtabs
type
THtmlTag* = enum ## list of all supported HTML tags; order will always be
HtmlTag* = enum ## list of all supported HTML tags; order will always be
## alphabetically
tagUnknown, ## unknown HTML element
tagA, ## the HTML ``a`` element
@@ -178,6 +178,7 @@ type
tagVar, ## the HTML ``var`` element
tagVideo, ## the HTML ``video`` element
tagWbr ## the HTML ``wbr`` element
{.deprecated: [THtmlTag: HtmlTag].}
const
tagToStr* = [
@@ -295,7 +296,7 @@ proc allLower(s: string): bool =
if c < 'a' or c > 'z': return false
return true
proc toHtmlTag(s: string): THtmlTag =
proc toHtmlTag(s: string): HtmlTag =
case s
of "a": tagA
of "abbr": tagAbbr
@@ -422,14 +423,14 @@ proc toHtmlTag(s: string): THtmlTag =
of "wbr": tagWbr
else: tagUnknown
proc htmlTag*(n: XmlNode): THtmlTag =
## gets `n`'s tag as a ``THtmlTag``.
proc htmlTag*(n: XmlNode): HtmlTag =
## gets `n`'s tag as a ``HtmlTag``.
if n.clientData == 0:
n.clientData = toHtmlTag(n.tag).ord
result = THtmlTag(n.clientData)
result = HtmlTag(n.clientData)
proc htmlTag*(s: string): THtmlTag =
## converts `s` to a ``THtmlTag``. If `s` is no HTML tag, ``tagUnknown`` is
proc htmlTag*(s: string): HtmlTag =
## converts `s` to a ``HtmlTag``. If `s` is no HTML tag, ``tagUnknown`` is
## returned.
let s = if allLower(s): s else: s.toLower
result = toHtmlTag(s)

View File

@@ -106,9 +106,10 @@ proc serveFile*(client: Socket, filename: string) =
when false:
# TODO: Fix this, or get rid of it.
type
TRequestMethod = enum reqGet, reqPost
RequestMethod = enum reqGet, reqPost
{.deprecated: [TRequestMethod: RequestMethod].}
proc executeCgi(client: Socket, path, query: string, meth: TRequestMethod) =
proc executeCgi(client: Socket, path, query: string, meth: RequestMethod) =
var env = newStringTable(modeCaseInsensitive)
var contentLength = -1
case meth
@@ -208,7 +209,7 @@ when false:
executeCgi(client, path, query, meth)
type
TServer* = object of RootObj ## contains the current server state
Server* = object of RootObj ## contains the current server state
socket: Socket
port: Port
client*: Socket ## the socket to write the file data to
@@ -218,11 +219,12 @@ type
body*: string ## only set with POST requests
ip*: string ## ip address of the requesting client
PAsyncHTTPServer* = ref TAsyncHTTPServer
TAsyncHTTPServer = object of TServer
PAsyncHTTPServer* = ref AsyncHTTPServer
AsyncHTTPServer = object of Server
asyncSocket: AsyncSocket
{.deprecated: [TAsyncHTTPServer: AsyncHTTPServer, TServer: Server].}
proc open*(s: var TServer, port = Port(80), reuseAddr = false) =
proc open*(s: var Server, port = Port(80), reuseAddr = false) =
## creates a new server at port `port`. If ``port == 0`` a free port is
## acquired that can be accessed later by the ``port`` proc.
s.socket = socket(AF_INET)
@@ -243,11 +245,11 @@ proc open*(s: var TServer, port = Port(80), reuseAddr = false) =
s.query = ""
s.headers = {:}.newStringTable()
proc port*(s: var TServer): Port =
proc port*(s: var Server): Port =
## get the port number the server has acquired.
result = s.port
proc next*(s: var TServer) =
proc next*(s: var Server) =
## proceed to the first/next request.
var client: Socket
new(client)
@@ -354,7 +356,7 @@ proc next*(s: var TServer) =
s.query = ""
s.path = data.substr(i, last-1)
proc close*(s: TServer) =
proc close*(s: Server) =
## closes the server (and the socket the server uses).
close(s.socket)
@@ -362,7 +364,7 @@ proc run*(handleRequest: proc (client: Socket,
path, query: string): bool {.closure.},
port = Port(80)) =
## encapsulates the server object and main loop
var s: TServer
var s: Server
open(s, port, reuseAddr = true)
#echo("httpserver running on port ", s.port)
while true:
@@ -517,7 +519,7 @@ proc close*(h: PAsyncHTTPServer) =
when not defined(testing) and isMainModule:
var counter = 0
var s: TServer
var s: Server
open(s, Port(0))
echo("httpserver running on port ", s.port)
while true:

View File

@@ -68,7 +68,7 @@ type
jsonArrayStart, ## start of an array: the ``[`` token
jsonArrayEnd ## start of an array: the ``]`` token
TTokKind = enum # must be synchronized with TJsonEventKind!
TokKind = enum # must be synchronized with TJsonEventKind!
tkError,
tkEof,
tkString,
@@ -103,14 +103,14 @@ type
JsonParser* = object of BaseLexer ## the parser object.
a: string
tok: TTokKind
tok: TokKind
kind: JsonEventKind
err: JsonError
state: seq[ParserState]
filename: string
{.deprecated: [TJsonEventKind: JsonEventKind, TJsonError: JsonError,
TJsonParser: JsonParser].}
TJsonParser: JsonParser, TTokKind: TokKind].}
const
errorMessages: array [JsonError, string] = [
@@ -126,7 +126,7 @@ const
"EOF expected",
"expression expected"
]
tokToStr: array [TTokKind, string] = [
tokToStr: array [TokKind, string] = [
"invalid token",
"EOF",
"string literal",
@@ -203,7 +203,7 @@ proc handleHexChar(c: char, x: var int): bool =
of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
else: result = false # error
proc parseString(my: var JsonParser): TTokKind =
proc parseString(my: var JsonParser): TokKind =
result = tkString
var pos = my.bufpos + 1
var buf = my.buf
@@ -359,7 +359,7 @@ proc parseName(my: var JsonParser) =
inc(pos)
my.bufpos = pos
proc getTok(my: var JsonParser): TTokKind =
proc getTok(my: var JsonParser): TokKind =
setLen(my.a, 0)
skip(my) # skip whitespace, comments
case my.buf[my.bufpos]
@@ -734,7 +734,7 @@ proc `==`* (a,b: JsonNode): bool =
of JObject:
a.fields == b.fields
proc hash* (n:JsonNode): THash =
proc hash* (n:JsonNode): Hash =
## Compute the hash for a JSON node
case n.kind
of JArray:
@@ -1016,7 +1016,7 @@ iterator mpairs*(node: var JsonNode): var tuple[key: string, val: JsonNode] =
for keyVal in mitems(node.fields):
yield keyVal
proc eat(p: var JsonParser, tok: TTokKind) =
proc eat(p: var JsonParser, tok: TokKind) =
if p.tok == tok: discard getTok(p)
else: raiseParseErr(p, tokToStr[tok])
@@ -1091,8 +1091,10 @@ when not defined(js):
else:
from math import `mod`
type
TJSObject = object
proc parseNativeJson(x: cstring): TJSObject {.importc: "JSON.parse".}
JSObject = object
{.deprecated: [TJSObject: JSObject].}
proc parseNativeJson(x: cstring): JSObject {.importc: "JSON.parse".}
proc getVarType(x): JsonNodeKind =
result = JNull
@@ -1111,25 +1113,25 @@ else:
of "[object String]": return JString
else: assert false
proc len(x: TJSObject): int =
proc len(x: JSObject): int =
assert x.getVarType == JArray
asm """
return `x`.length;
"""
proc `[]`(x: TJSObject, y: string): TJSObject =
proc `[]`(x: JSObject, y: string): JSObject =
assert x.getVarType == JObject
asm """
return `x`[`y`];
"""
proc `[]`(x: TJSObject, y: int): TJSObject =
proc `[]`(x: JSObject, y: int): JSObject =
assert x.getVarType == JArray
asm """
return `x`[`y`];
"""
proc convertObject(x: TJSObject): JsonNode =
proc convertObject(x: JSObject): JsonNode =
case getVarType(x)
of JArray:
result = newJArray()
@@ -1141,7 +1143,7 @@ else:
if (`x`.hasOwnProperty(property)) {
"""
var nimProperty: cstring
var nimValue: TJSObject
var nimValue: JSObject
asm "`nimProperty` = property; `nimValue` = `x`[property];"
result[$nimProperty] = nimValue.convertObject()
asm "}}"

View File

@@ -39,7 +39,7 @@ type
{.deprecated: [TBaseLexer: BaseLexer].}
proc open*(L: var BaseLexer, input: Stream, bufLen: int = 8192)
## inits the TBaseLexer with a stream to read from
## inits the BaseLexer with a stream to read from
proc close*(L: var BaseLexer)
## closes the base lexer. This closes `L`'s associated stream too.

View File

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

View File

@@ -17,13 +17,13 @@
## .. code-block:: nim
##
## type
## TA = object
## TB = object of TA
## A = object
## B = object of A
## f: int
##
## var
## a: ref TA
## b: ref TB
## a: ref A
## b: ref B
##
## new(b)
## a = b
@@ -36,7 +36,7 @@ import streams, typeinfo, json, intsets, tables
proc ptrToInt(x: pointer): int {.inline.} =
result = cast[int](x) # don't skip alignment
proc storeAny(s: Stream, a: TAny, stored: var IntSet) =
proc storeAny(s: Stream, a: Any, stored: var IntSet) =
case a.kind
of akNone: assert false
of akBool: s.write($getBool(a))
@@ -96,7 +96,7 @@ proc storeAny(s: Stream, a: TAny, stored: var IntSet) =
of akInt..akInt64, akUInt..akUInt64: s.write($getBiggestInt(a))
of akFloat..akFloat128: s.write($getBiggestFloat(a))
proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
proc loadAny(p: var JsonParser, a: Any, t: var Table[BiggestInt, pointer]) =
case a.kind
of akNone: assert false
of akBool:
@@ -222,7 +222,7 @@ proc loadAny(p: var JsonParser, a: TAny, t: var Table[BiggestInt, pointer]) =
raiseParseErr(p, "float expected")
of akRange: loadAny(p, a.skipRange, t)
proc loadAny(s: Stream, a: TAny, t: var Table[BiggestInt, pointer]) =
proc loadAny(s: Stream, a: Any, t: var Table[BiggestInt, pointer]) =
var p: JsonParser
open(p, s, "unknown file")
next(p)
@@ -278,10 +278,11 @@ when not defined(testing) and isMainModule:
else:
nil
PNode = ref TNode
TNode = object
PNode = ref Node
Node = object
next, prev: PNode
data: string
{.deprecated: [TNode: Node].}
proc buildList(): PNode =
new(result)
@@ -317,14 +318,15 @@ when not defined(testing) and isMainModule:
testit(test7)
type
TA {.inheritable.} = object
TB = object of TA
A {.inheritable.} = object
B = object of A
f: int
var
a: ref TA
b: ref TB
a: ref A
b: ref B
new(b)
a = b
echo($$a[]) # produces "{}", not "{f: 0}"

View File

@@ -196,7 +196,7 @@ proc open*(filename: string, mode: FileMode = fmRead,
if mappedSize != -1:
result.size = mappedSize
else:
var stat: TStat
var stat: Stat
if fstat(result.handle, stat) != -1:
# XXX: Hmm, this could be unsafe
# Why is mmap taking int anyway?

View File

@@ -86,7 +86,7 @@ type
IPv6, ## IPv6 address
IPv4 ## IPv4 address
TIpAddress* = object ## stores an arbitrary IP address
IpAddress* = object ## stores an arbitrary IP address
case family*: IpAddressFamily ## the type of the IP address (IPv4 or IPv6)
of IpAddressFamily.IPv6:
address_v6*: array[0..15, uint8] ## Contains the IP address in bytes in
@@ -94,9 +94,10 @@ type
of IpAddressFamily.IPv4:
address_v4*: array[0..3, uint8] ## Contains the IP address in bytes in
## case of IPv4
{.deprecated: [TIpAddress: IpAddress].}
proc isIpAddress*(address_str: string): bool {.tags: [].}
proc parseIpAddress*(address_str: string): TIpAddress
proc parseIpAddress*(address_str: string): IpAddress
proc isDisconnectionError*(flags: set[SocketFlag],
lastError: OSErrorCode): bool =
@@ -118,13 +119,13 @@ proc toOSFlags*(socketFlags: set[SocketFlag]): cint =
result = result or MSG_PEEK
of SocketFlag.SafeDisconn: continue
proc newSocket(fd: SocketHandle, isBuff: bool): Socket =
proc newSocket*(fd: SocketHandle, buffered = true): Socket =
## Creates a new socket as specified by the params.
assert fd != osInvalidSocket
new(result)
result.fd = fd
result.isBuffered = isBuff
if isBuff:
result.isBuffered = buffered
if buffered:
result.currPos = 0
proc newSocket*(domain, typ, protocol: cint, buffered = true): Socket =
@@ -395,7 +396,7 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string,
when false: #defined(ssl):
proc acceptAddrSSL*(server: Socket, client: var Socket,
address: var string): TSSLAcceptResult {.
address: var string): SSLAcceptResult {.
tags: [ReadIOEffect].} =
## This procedure should only be used for non-blocking **SSL** sockets.
## It will immediately return with one of the following values:
@@ -992,39 +993,39 @@ proc isSsl*(socket: Socket): bool =
proc getFd*(socket: Socket): SocketHandle = return socket.fd
## Returns the socket's file descriptor
proc IPv4_any*(): TIpAddress =
proc IPv4_any*(): IpAddress =
## Returns the IPv4 any address, which can be used to listen on all available
## network adapters
result = TIpAddress(
result = IpAddress(
family: IpAddressFamily.IPv4,
address_v4: [0'u8, 0, 0, 0])
proc IPv4_loopback*(): TIpAddress =
proc IPv4_loopback*(): IpAddress =
## Returns the IPv4 loopback address (127.0.0.1)
result = TIpAddress(
result = IpAddress(
family: IpAddressFamily.IPv4,
address_v4: [127'u8, 0, 0, 1])
proc IPv4_broadcast*(): TIpAddress =
proc IPv4_broadcast*(): IpAddress =
## Returns the IPv4 broadcast address (255.255.255.255)
result = TIpAddress(
result = IpAddress(
family: IpAddressFamily.IPv4,
address_v4: [255'u8, 255, 255, 255])
proc IPv6_any*(): TIpAddress =
proc IPv6_any*(): IpAddress =
## Returns the IPv6 any address (::0), which can be used
## to listen on all available network adapters
result = TIpAddress(
result = IpAddress(
family: IpAddressFamily.IPv6,
address_v6: [0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
proc IPv6_loopback*(): TIpAddress =
proc IPv6_loopback*(): IpAddress =
## Returns the IPv6 loopback address (::1)
result = TIpAddress(
result = IpAddress(
family: IpAddressFamily.IPv6,
address_v6: [0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])
proc `==`*(lhs, rhs: TIpAddress): bool =
proc `==`*(lhs, rhs: IpAddress): bool =
## Compares two IpAddresses for Equality. Returns two if the addresses are equal
if lhs.family != rhs.family: return false
if lhs.family == IpAddressFamily.IPv4:
@@ -1035,8 +1036,8 @@ proc `==`*(lhs, rhs: TIpAddress): bool =
if lhs.address_v6[i] != rhs.address_v6[i]: return false
return true
proc `$`*(address: TIpAddress): string =
## Converts an TIpAddress into the textual representation
proc `$`*(address: IpAddress): string =
## Converts an IpAddress into the textual representation
result = ""
case address.family
of IpAddressFamily.IPv4:
@@ -1095,7 +1096,7 @@ proc `$`*(address: TIpAddress): string =
mask = mask shr 4
printedLastGroup = true
proc parseIPv4Address(address_str: string): TIpAddress =
proc parseIPv4Address(address_str: string): IpAddress =
## Parses IPv4 adresses
## Raises EInvalidValue on errors
var
@@ -1129,7 +1130,7 @@ proc parseIPv4Address(address_str: string): TIpAddress =
raise newException(ValueError, "Invalid IP Address")
result.address_v4[byteCount] = cast[uint8](currentByte)
proc parseIPv6Address(address_str: string): TIpAddress =
proc parseIPv6Address(address_str: string): IpAddress =
## Parses IPv6 adresses
## Raises EInvalidValue on errors
result.family = IpAddressFamily.IPv6
@@ -1250,7 +1251,7 @@ proc parseIPv6Address(address_str: string): TIpAddress =
raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups")
proc parseIpAddress(address_str: string): TIpAddress =
proc parseIpAddress(address_str: string): IpAddress =
## Parses an IP address
## Raises EInvalidValue on error
if address_str == nil:

View File

@@ -26,17 +26,19 @@ const
withThreads = compileOption("threads")
tickCountCorrection = 50_000
when not declared(system.TStackTrace):
type TStackTrace = array [0..20, cstring]
when not declared(system.StackTrace):
type StackTrace = array [0..20, cstring]
{.deprecated: [TStackTrace: StackTrace].}
# We use a simple hash table of bounded size to keep track of the stack traces:
type
TProfileEntry = object
ProfileEntry = object
total: int
st: TStackTrace
TProfileData = array [0..64*1024-1, ptr TProfileEntry]
st: StackTrace
ProfileData = array [0..64*1024-1, ptr ProfileEntry]
{.deprecated: [TProfileEntry: ProfileEntry, TProfileData: ProfileData].}
proc `==`(a, b: TStackTrace): bool =
proc `==`(a, b: StackTrace): bool =
for i in 0 .. high(a):
if a[i] != b[i]: return false
result = true
@@ -44,13 +46,13 @@ proc `==`(a, b: TStackTrace): bool =
# XXX extract this data structure; it is generally useful ;-)
# However a chain length of over 3000 is suspicious...
var
profileData: TProfileData
profileData: ProfileData
emptySlots = profileData.len * 3 div 2
maxChainLen = 0
totalCalls = 0
when not defined(memProfiler):
var interval: TNanos = 5_000_000 - tickCountCorrection # 5ms
var interval: Nanos = 5_000_000 - tickCountCorrection # 5ms
proc setSamplingFrequency*(intervalInUs: int) =
## set this to change the sampling frequency. Default value is 5ms.
@@ -62,11 +64,11 @@ when not defined(memProfiler):
when withThreads:
import locks
var
profilingLock: TLock
profilingLock: Lock
initLock profilingLock
proc hookAux(st: TStackTrace, costs: int) =
proc hookAux(st: StackTrace, costs: int) =
# this is quite performance sensitive!
when withThreads: acquire profilingLock
inc totalCalls
@@ -94,8 +96,8 @@ proc hookAux(st: TStackTrace, costs: int) =
var chain = 0
while true:
if profileData[h] == nil:
profileData[h] = cast[ptr TProfileEntry](
allocShared0(sizeof(TProfileEntry)))
profileData[h] = cast[ptr ProfileEntry](
allocShared0(sizeof(ProfileEntry)))
profileData[h].total = costs
profileData[h].st = st
dec emptySlots
@@ -115,7 +117,7 @@ when defined(memProfiler):
var
gTicker {.threadvar.}: int
proc hook(st: TStackTrace, size: int) {.nimcall.} =
proc hook(st: StackTrace, size: int) {.nimcall.} =
if gTicker == 0:
gTicker = -1
when defined(ignoreAllocationSize):
@@ -127,26 +129,26 @@ when defined(memProfiler):
else:
var
t0 {.threadvar.}: TTicks
t0 {.threadvar.}: Ticks
proc hook(st: TStackTrace) {.nimcall.} =
proc hook(st: StackTrace) {.nimcall.} =
if interval == 0:
hookAux(st, 1)
elif int64(t0) == 0 or getTicks() - t0 > interval:
hookAux(st, 1)
t0 = getTicks()
proc getTotal(x: ptr TProfileEntry): int =
proc getTotal(x: ptr ProfileEntry): int =
result = if isNil(x): 0 else: x.total
proc cmpEntries(a, b: ptr TProfileEntry): int =
proc cmpEntries(a, b: ptr ProfileEntry): int =
result = b.getTotal - a.getTotal
proc `//`(a, b: int): string =
result = format("$1/$2 = $3%", a, b, formatFloat(a / b * 100.0, ffDefault, 2))
proc writeProfile() {.noconv.} =
when declared(system.TStackTrace):
when declared(system.StackTrace):
system.profilerHook = nil
const filename = "profile_results.txt"
echo "writing " & filename & "..."
@@ -161,7 +163,7 @@ proc writeProfile() {.noconv.} =
var perProc = initCountTable[string]()
for i in 0..entries-1:
var dups = initSet[string]()
for ii in 0..high(TStackTrace):
for ii in 0..high(StackTrace):
let procname = profileData[i].st[ii]
if isNil(procname): break
let p = $procname
@@ -176,7 +178,7 @@ proc writeProfile() {.noconv.} =
writeln(f, "Entry: ", i+1, "/", entries, " Calls: ",
profileData[i].total // totalCalls, " [sum: ", sum, "; ",
sum // totalCalls, "]")
for ii in 0..high(TStackTrace):
for ii in 0..high(StackTrace):
let procname = profileData[i].st[ii]
if isNil(procname): break
writeln(f, " ", procname, " ", perProc[$procname] // totalCalls)
@@ -189,16 +191,16 @@ var
disabled: int
proc disableProfiling*() =
when declared(system.TStackTrace):
when declared(system.StackTrace):
atomicDec disabled
system.profilerHook = nil
proc enableProfiling*() =
when declared(system.TStackTrace):
when declared(system.StackTrace):
if atomicInc(disabled) >= 0:
system.profilerHook = hook
when declared(system.TStackTrace):
when declared(system.StackTrace):
system.profilerHook = hook
addQuitProc(writeProfile)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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